summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--jjb/calipso/calipso.yml59
-rwxr-xr-xjjb/dovetail/dovetail-run.sh31
-rw-r--r--jjb/releng/automate.yml6
-rw-r--r--jjb/releng/docker-deploy.sh156
-rw-r--r--jjb/xci/xci-verify-jobs.yml22
-rw-r--r--utils/test/testapi/opnfv_testapi/common/raises.py4
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/handlers.py43
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/models.py23
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py350
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/scenario_models.py7
-rw-r--r--utils/test/testapi/opnfv_testapi/router/url_mappings.py6
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/resources/test_base.py33
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/resources/test_scenario.py118
-rw-r--r--utils/test/testapi/opnfv_testapi/tornado_swagger/swagger.py17
14 files changed, 757 insertions, 118 deletions
diff --git a/jjb/calipso/calipso.yml b/jjb/calipso/calipso.yml
new file mode 100644
index 000000000..b8d10eb89
--- /dev/null
+++ b/jjb/calipso/calipso.yml
@@ -0,0 +1,59 @@
+- project:
+ name: calipso
+
+ project: '{name}'
+
+ jobs:
+ - 'calipso-verify-{stream}'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ disabled: false
+
+- job-template:
+ name: 'calipso-verify-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+
+ builders:
+ - verify-unit-tests
+
+- builder:
+ name: verify-unit-tests
+ builders:
+ - shell: |
+ #!/bin/bash
+ set -o errexit
+ set -o nounset
+ set -o pipefail
+ cd $WORKSPACE
+ PYTHONPATH=$PWD/app python3 -m unittest discover -s app/test/fetch
diff --git a/jjb/dovetail/dovetail-run.sh b/jjb/dovetail/dovetail-run.sh
index 7bbecc129..d05b309cd 100755
--- a/jjb/dovetail/dovetail-run.sh
+++ b/jjb/dovetail/dovetail-run.sh
@@ -160,6 +160,37 @@ if [ $(docker ps | grep "opnfv/dovetail:${DOCKER_TAG}" | wc -l) == 0 ]; then
exit 1
fi
+# Modify tempest_conf.yaml file
+tempest_conf_file=${DOVETAIL_CONFIG}/tempest_conf.yaml
+if [ ${INSTALLER_TYPE} == 'compass' ]; then
+ volume_device='vdb'
+else
+ volume_device='vdc'
+fi
+
+cat << EOF >$tempest_conf_file
+
+compute:
+ min_compute_nodes: 2
+ volume_device_name: ${volume_device}
+ min_microversion: 2.0
+ max_microversion: latest
+
+compute-feature-enabled:
+ live_migration: True
+ block_migration_for_live_migration: True
+ block_migrate_cinder_iscsi: True
+ attach_encrypted_volume: True
+
+EOF
+
+echo "${tempest_conf_file}..."
+cat ${tempest_conf_file}
+
+cp_tempest_cmd="docker cp ${DOVETAIL_CONFIG}/tempest_conf.yaml $container_id:/home/opnfv/dovetail/dovetail/userconfig"
+echo "exec command: ${cp_tempest_cmd}"
+$cp_tempest_cmd
+
list_cmd="dovetail list ${TESTSUITE}"
run_cmd="dovetail run --testsuite ${TESTSUITE} -d"
echo "Container exec command: ${list_cmd}"
diff --git a/jjb/releng/automate.yml b/jjb/releng/automate.yml
index d12ee5d68..c6ca37fa9 100644
--- a/jjb/releng/automate.yml
+++ b/jjb/releng/automate.yml
@@ -237,16 +237,16 @@
name: 'testapi-automate-docker-deploy-macro'
builders:
- shell: |
- bash ./jjb/releng/docker-deploy.sh "sudo docker run -dti -p 8082:8000
+ sudo bash ./jjb/releng/docker-deploy.sh "sudo docker run -dti --name testapi -p 8082:8000
-e mongodb_url=mongodb://172.17.0.1:27017
-e base_url=http://testresults.opnfv.org/test opnfv/testapi" \
- "http://testresults.opnfv.org/test/swagger/APIs" "testapi"
+ "http://testresults.opnfv.org/test/" "testapi"
- builder:
name: 'reporting-automate-docker-deploy-macro'
builders:
- shell: |
- bash ./jjb/releng/docker-deploy.sh "sudo docker run -itd -p 8084:8000 opnfv/reporting" \
+ sudo bash ./jjb/releng/docker-deploy.sh "sudo docker run -itd --name reporting -p 8084:8000 opnfv/reporting" \
"http://testresults.opnfv.org/reporting2/reporting/index.html" "reporting"
- builder:
diff --git a/jjb/releng/docker-deploy.sh b/jjb/releng/docker-deploy.sh
index 2a3e078ae..1e8357717 100644
--- a/jjb/releng/docker-deploy.sh
+++ b/jjb/releng/docker-deploy.sh
@@ -16,89 +16,137 @@
# specific language governing permissions and limitations *
# under the License. *
-# Assigning Variables
+
command=$1
url=$2
module=$3
-function check() {
+REPO="opnfv"
+latest_image=$REPO/$module:latest
+old_image=$REPO/$module:old
+latest_container_name=$module
+old_container_name=$module"_old"
+latest_container_id=
+old_container_id=
+new_start_container=
+
+function DEBUG() {
+ echo `date "+%Y-%m-%d %H:%M:%S.%N"` ": $1"
+}
- # Verify hosted
+function check_connectivity() {
+ # check update status via test the connectivity of provide url
sleep 5
cmd=`curl -s --head --request GET ${url} | grep '200 OK' > /dev/null`
rc=$?
- echo $rc
-
- if [[ $rc == 0 ]]
- then
+ DEBUG $rc
+ if [[ $rc == 0 ]]; then
return 0
else
return 1
fi
-
}
-echo "Getting contianer Id of the currently running one"
-contId=$(sudo docker ps | grep "opnfv/${module}:latest" | awk '{print $1}')
-
-echo $contId
-echo "Pulling the latest image"
-sudo docker pull opnfv/${module}:latest
+function pull_latest_image() {
+ DEBUG "pull latest image $latest_image"
+ docker pull $latest_image
+}
-echo "Deleting old containers of opnfv/${module}:old"
-sudo docker ps -a | grep "opnfv/${module}" | grep "old" | awk '{print $1}' | xargs -r sudo docker rm -f
+function get_latest_running_container() {
+ latest_container_id=`docker ps -q --filter name=^/$latest_container_name$`
+}
-echo "Deleting old images of opnfv/${module}:latest"
-sudo docker images | grep "opnfv/${module}" | grep "old" | awk '{print $3}' | xargs -r sudo docker rmi -f
+function get_old_running_container() {
+ old_container_id=`docker ps -q --filter name=^/$old_container_name$`
+}
+function delete_old_image() {
+ DEBUG "delete old image: $old_image"
+ docker rmi -f $old_image
+}
-if [[ -z "$contId" ]]
-then
- echo "No running ${module} container"
+function delete_old_container() {
+ DEBUG "delete old container: $old_container_name"
+ docker ps -a -q --filter name=^/$old_container_name$ | xargs docker rm -f &>/dev/null
+}
- echo "Removing stopped ${module} containers in the previous iterations"
- sudo docker ps -f status=exited | grep "opnfv_${module}" | awk '{print $1}' | xargs -r sudo docker rm -f
-else
- echo $contId
+function delete_latest_container() {
+ DEBUG "delete latest container: $module"
+ docker ps -a -q --filter name=^/$latest_container_name$ | xargs docker rm -f &>/dev/null
+}
- echo "Get the image id of the currently running conatiner"
- currImgId=$(sudo docker ps | grep "$contId" | awk '{print $2}')
- echo $currImgId
+function delete_latest_image() {
+ DEBUG "delete latest image: $REPO/$module:latest"
+ docker rmi -f $latest_image
+}
- if [[ -z "$currImgId" ]]
- then
- echo "No image id found for the container id"
- exit 1
- fi
+function change_image_tag_2_old() {
+ DEBUG "change image tag 2 old"
+ docker tag $latest_image $old_image
+ docker rmi -f $latest_image
+}
- echo "Changing current image tag to old"
- sudo docker tag "$currImgId" opnfv/${module}:old
+function mark_latest_container_2_old() {
+ DEBUG "mark latest container to be old"
+ docker rename "$latest_container_name" "$old_container_name"
+}
- echo "Removing stopped ${module} containers in the previous iteration"
- sudo docker ps -f status=exited | grep "opnfv_${module}" | awk '{print $1}' | xargs -r sudo docker rm -f
+function stop_old_container() {
+ DEBUG "stop old container"
+ docker stop "$old_container_name"
+}
- echo "Renaming the running container name to opnfv_${module} as to identify it."
- sudo docker rename $contId opnfv_${module}
+function run_latest_image() {
+ new_start_container=`$command`
+ DEBUG "run latest image: $new_start_container"
+}
- echo "Stop the currently running container"
- sudo docker stop $contId
+get_latest_running_container
+get_old_running_container
+
+if [[ ! -z $latest_container_id ]]; then
+ DEBUG "latest container is running: $latest_container_id"
+ delete_old_container
+ delete_old_image
+ change_image_tag_2_old
+ mark_latest_container_2_old
+ pull_latest_image
+ stop_old_container
+ run_latest_image
+
+elif [[ ! -z $old_container_id ]]; then
+ DEBUG "old container is running: $old_container_id"
+ delete_latest_container
+ delete_latest_image
+ pull_latest_image
+ stop_old_container
+ run_latest_image
+else
+ DEBUG "no container is running"
+ delete_old_container
+ delete_old_image
+ delete_latest_container
+ delete_latest_image
+ pull_latest_image
+ run_latest_image
fi
-echo "Running a container with the new image"
-$command:latest
-
-if check; then
- echo "TestResults Module Hosted."
+if check_connectivity; then
+ DEBUG "CONGRATS: $module update successfully"
else
- echo "TestResults Module Failed"
- if [[ $(sudo docker images | grep "opnfv/${module}" | grep "old" | awk '{print $3}') ]]; then
- echo "Running old Image"
- $command:old
- exit 1
+ DEBUG "ATTENTION: $module update failed"
+ id=`docker ps -a -q --filter name=^/$old_container_name$`
+ if [[ ! -z $id ]]; then
+ DEBUG "start old container instead"
+ docker stop $new_start_container
+ docker start $id
+ fi
+ if ! check_connectivity; then
+ DEBUG "BIG ISSUE: no container is running normally"
fi
+ exit 1
fi
-# Echo Images and Containers
-sudo docker images
-sudo docker ps -a
+docker images
+docker ps -a
diff --git a/jjb/xci/xci-verify-jobs.yml b/jjb/xci/xci-verify-jobs.yml
index 6bc064253..8d1ee55a4 100644
--- a/jjb/xci/xci-verify-jobs.yml
+++ b/jjb/xci/xci-verify-jobs.yml
@@ -109,6 +109,10 @@
- label:
name: SLAVE_LABEL
default: 'xci-virtual-{distro}'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
builders:
- description-setter:
@@ -130,7 +134,7 @@
condition: SUCCESSFUL
projects:
- name: 'xci-verify-healthcheck-{type}-{stream}'
- current-parameters: false
+ current-parameters: true
predefined-parameters: |
DISTRO={distro}
DEPLOY_SCENARIO=os-nosdn-nofeature-noha
@@ -162,7 +166,7 @@
parameters:
- string:
name: DISTRO
- default: '{distro}'
+ default: 'xenial'
- string:
name: DEPLOY_SCENARIO
default: 'os-nosdn-nofeature-noha'
@@ -173,11 +177,15 @@
name: XCI_FLAVOR
default: 'mini'
- string:
- name: XCI_DEVEL_ROOT
- default: $WORKSPACE
+ name: OPNFV_RELENG_DEV_PATH
+ default: $WORKSPACE/
- string:
name: ANSIBLE_VERBOSITY
default: '-vvvv'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
wrappers:
- ssh-agent-wrapper
@@ -202,7 +210,11 @@
- shell: |
#!/bin/bash
- cd $WORKSPACE
+ # for some reason, the PATH is not set correctly
+ # setting PATH for ansible stuff
+ export PATH=/home/jenkins/.local/bin:$PATH
+
+ cd $WORKSPACE/xci
./xci-deploy.sh
- builder:
diff --git a/utils/test/testapi/opnfv_testapi/common/raises.py b/utils/test/testapi/opnfv_testapi/common/raises.py
index ec6b8a564..55c58c9e2 100644
--- a/utils/test/testapi/opnfv_testapi/common/raises.py
+++ b/utils/test/testapi/opnfv_testapi/common/raises.py
@@ -26,6 +26,10 @@ class Forbidden(Raiser):
code = httplib.FORBIDDEN
+class Conflict(Raiser):
+ code = httplib.CONFLICT
+
+
class NotFound(Raiser):
code = httplib.NOT_FOUND
diff --git a/utils/test/testapi/opnfv_testapi/resources/handlers.py b/utils/test/testapi/opnfv_testapi/resources/handlers.py
index 474a2033b..ed55c7028 100644
--- a/utils/test/testapi/opnfv_testapi/resources/handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/handlers.py
@@ -50,7 +50,7 @@ class GenericApiHandler(web.RequestHandler):
self.auth = self.settings["auth"]
def prepare(self):
- if self.request.method != "GET" and self.request.method != "DELETE":
+ if self.request.body:
if self.request.headers.get("Content-Type") is not None:
if self.request.headers["Content-Type"].startswith(
DEFAULT_REPRESENTATION):
@@ -110,22 +110,23 @@ class GenericApiHandler(web.RequestHandler):
pipelines.append({'$match': query})
total_pages = 0
- if page > 0:
- cursor = dbapi.db_list(self.table, query)
- records_count = yield cursor.count()
- total_pages, return_nr = self._calc_total_pages(records_count,
- last,
- page,
- per_page)
- pipelines = self._set_pipelines(pipelines,
- sort,
- return_nr,
- page,
- per_page)
- cursor = dbapi.db_aggregate(self.table, pipelines)
data = list()
- while (yield cursor.fetch_next):
- data.append(self.format_data(cursor.next_object()))
+ cursor = dbapi.db_list(self.table, query)
+ records_count = yield cursor.count()
+ if records_count > 0:
+ if page > 0:
+ total_pages, return_nr = self._calc_total_pages(records_count,
+ last,
+ page,
+ per_page)
+ pipelines = self._set_pipelines(pipelines,
+ sort,
+ return_nr,
+ page,
+ per_page)
+ cursor = dbapi.db_aggregate(self.table, pipelines)
+ while (yield cursor.fetch_next):
+ data.append(self.format_data(cursor.next_object()))
if res_op is None:
res = {self.table: data}
else:
@@ -188,6 +189,16 @@ class GenericApiHandler(web.RequestHandler):
update_req['_id'] = str(data._id)
self.finish_request(update_req)
+ @check.authenticate
+ @check.no_body
+ @check.not_exist
+ @check.updated_one_not_exist
+ def pure_update(self, data, query=None, **kwargs):
+ data = self.table_cls.from_dict(data)
+ update_req = self._update_requests(data)
+ yield dbapi.db_update(self.table, query, update_req)
+ self.finish_request()
+
def _update_requests(self, data):
request = dict()
for k, v in self.json_args.iteritems():
diff --git a/utils/test/testapi/opnfv_testapi/resources/models.py b/utils/test/testapi/opnfv_testapi/resources/models.py
index e8fc532b7..6f04cc236 100644
--- a/utils/test/testapi/opnfv_testapi/resources/models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/models.py
@@ -48,6 +48,29 @@ class ModelBase(object):
return t
+ @classmethod
+ def from_dict_with_raise(cls, a_dict):
+ if a_dict is None:
+ return None
+
+ attr_parser = cls.attr_parser()
+ t = cls()
+ for k, v in a_dict.iteritems():
+ if k not in t.__dict__:
+ raise AttributeError(
+ '{} has no attribute {}'.format(cls.__name__, k))
+ value = v
+ if isinstance(v, dict) and k in attr_parser:
+ value = attr_parser[k].from_dict(v)
+ elif isinstance(v, list) and k in attr_parser:
+ value = []
+ for item in v:
+ value.append(attr_parser[k].from_dict(item))
+
+ t.__setattr__(k, value)
+
+ return t
+
@staticmethod
def attr_parser():
return {}
diff --git a/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py b/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
index 66e8559f7..bd06400b4 100644
--- a/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
@@ -1,5 +1,7 @@
import functools
+from opnfv_testapi.common import message
+from opnfv_testapi.common import raises
from opnfv_testapi.resources import handlers
import opnfv_testapi.resources.scenario_models as models
from opnfv_testapi.tornado_swagger import swagger
@@ -138,6 +140,13 @@ class ScenarioUpdater(object):
updates = {
('scores', 'post'): self._update_requests_add_score,
('trust_indicators', 'post'): self._update_requests_add_ti,
+ ('customs', 'post'): self._update_requests_add_customs,
+ ('customs', 'put'): self._update_requests_update_customs,
+ ('customs', 'delete'): self._update_requests_delete_customs,
+ ('projects', 'post'): self._update_requests_add_projects,
+ ('projects', 'put'): self._update_requests_update_projects,
+ ('projects', 'delete'): self._update_requests_delete_projects,
+ ('owner', 'put'): self._update_requests_change_owner,
}
updates[(item, action)](self.data)
@@ -178,6 +187,76 @@ class ScenarioUpdater(object):
project.trust_indicators.append(
models.ScenarioTI.from_dict(self.body))
+ @iter_installers
+ @iter_versions
+ @iter_projects
+ def _update_requests_add_customs(self, project):
+ project.customs = list(set(project.customs + self.body))
+
+ @iter_installers
+ @iter_versions
+ @iter_projects
+ def _update_requests_update_customs(self, project):
+ project.customs = list(set(self.body))
+
+ @iter_installers
+ @iter_versions
+ @iter_projects
+ def _update_requests_delete_customs(self, project):
+ project.customs = filter(
+ lambda f: f not in self.body,
+ project.customs)
+
+ @iter_installers
+ @iter_versions
+ def _update_requests_add_projects(self, version):
+ exists = list()
+ malformat = list()
+ for n in self.body:
+ try:
+ f_n = models.ScenarioProject.from_dict_with_raise(n)
+ if not any(o.project == f_n.project for o in version.projects):
+ version.projects.append(f_n)
+ else:
+ exists.append(n['project'])
+ except Exception as e:
+ malformat.append(e.message)
+ if malformat:
+ raises.BadRequest(message.bad_format(malformat))
+ elif exists:
+ raises.Conflict(message.exist('projects', exists))
+
+ @iter_installers
+ @iter_versions
+ def _update_requests_update_projects(self, version):
+ exists = list()
+ malformat = list()
+ projects = list()
+ for n in self.body:
+ try:
+ f_n = models.ScenarioProject.from_dict_with_raise(n)
+ if not any(o.project == f_n.project for o in projects):
+ projects.append(models.ScenarioProject.from_dict(n))
+ else:
+ exists.append(n['project'])
+ except:
+ malformat.append(n)
+ if malformat:
+ raises.BadRequest(message.bad_format(malformat))
+ elif exists:
+ raises.Forbidden(message.exist('projects', exists))
+ version.projects = projects
+
+ @iter_installers
+ @iter_versions
+ def _update_requests_delete_projects(self, version):
+ version.projects = self._remove_projects(version.projects)
+
+ @iter_installers
+ @iter_versions
+ def _update_requests_change_owner(self, version):
+ version.owner = self.body
+
def _filter_installers(self, installers):
return self._filter('installer', installers)
@@ -187,11 +266,19 @@ class ScenarioUpdater(object):
def _filter_projects(self, projects):
return self._filter('project', projects)
+ def _remove_projects(self, projects):
+ return self._remove('project', projects)
+
def _filter(self, item, items):
return filter(
lambda f: getattr(f, item) == getattr(self, item),
items)
+ def _remove(self, field, fields):
+ return filter(
+ lambda f: getattr(f, field) not in self.body,
+ fields)
+
class GenericScenarioUpdateHandler(GenericScenarioHandler):
def __init__(self, application, request, **kwargs):
@@ -204,15 +291,15 @@ class GenericScenarioUpdateHandler(GenericScenarioHandler):
self.item = None
self.action = None
- def do_post(self, scenario, item, action, locators):
+ def do_update(self, item, action, locators):
self.item = item
self.action = action
- for k in locators.keys():
- v = self.get_query_argument(k)
- setattr(self, k, v)
- locators[k] = v
- db_keys = ['name']
- self._update(query=self.set_query(locators=locators), db_keys=db_keys)
+ for k, v in locators.iteritems():
+ if not v:
+ v = self.get_query_argument(k)
+ setattr(self, k, v)
+ locators[k] = v
+ self.pure_update(query=self.set_query(locators=locators))
def _update_requests(self, data):
return ScenarioUpdater(data,
@@ -247,16 +334,15 @@ class ScenarioScoresHandler(GenericScenarioUpdateHandler):
@type project: L{string}
@in project: query
@required project: True
- @rtype: L{Scenario}
@return 200: score is created.
@raise 404: scenario/installer/version/project not existed
"""
- self.do_post(scenario,
- 'scores',
- 'post',
- locators={'installer': None,
- 'version': None,
- 'project': None})
+ self.do_update('scores',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
class ScenarioTIsHandler(GenericScenarioUpdateHandler):
@@ -284,13 +370,235 @@ class ScenarioTIsHandler(GenericScenarioUpdateHandler):
@type project: L{string}
@in project: query
@required project: True
- @rtype: L{Scenario}
@return 200: trust indicator is added.
@raise 404: scenario/installer/version/project not existed
"""
- self.do_post(scenario,
- 'trust_indicators',
- 'post',
- locators={'installer': None,
- 'version': None,
- 'project': None})
+ self.do_update('trust_indicators',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+
+class ScenarioCustomsHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addCustomizedTestCases")
+ def post(self, scenario):
+ """
+ @description: add customized test cases
+ @notes: add several test cases to a project
+ POST /api/v1/scenarios/<scenario_name>/customs? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: test cases to be added
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: test cases are added.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('customs',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+ @swagger.operation(nickname="updateCustomizedTestCases")
+ def put(self, scenario):
+ """
+ @description: update customized test cases
+ @notes: substitute all the customized test cases
+ PUT /api/v1/scenarios/<scenario_name>/customs? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: new supported test cases
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: substitute test cases success.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('customs',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+ @swagger.operation(nickname="deleteCustomizedTestCases")
+ def delete(self, scenario):
+ """
+ @description: delete one or several customized test cases
+ @notes: delete one or some customized test cases
+ DELETE /api/v1/scenarios/<scenario_name>/customs? \
+ installer=<installer_name>& \
+ version=<version_name>& \
+ project=<project_name>
+ @param body: test case(s) to be deleted
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: True
+ @return 200: delete test case(s) success.
+ @raise 404: scenario/installer/version/project not existed
+ """
+ self.do_update('customs',
+ 'delete',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None,
+ 'project': None})
+
+
+class ScenarioProjectsHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="addProjectsUnderScenario")
+ def post(self, scenario):
+ """
+ @description: add projects to scenario
+ @notes: add one or multiple projects
+ POST /api/v1/scenarios/<scenario_name>/projects? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: projects to be added
+ @type body: C{list} of L{ScenarioProject}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: projects are added.
+ @raise 400: bad schema
+ @raise 409: conflict, project already exists
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('projects',
+ 'post',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
+
+ @swagger.operation(nickname="updateScenarioProjects")
+ def put(self, scenario):
+ """
+ @description: replace all projects
+ @notes: substitute all projects, delete existed ones with new provides
+ PUT /api/v1/scenarios/<scenario_name>/projects? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: new projects
+ @type body: C{list} of L{ScenarioProject}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: replace projects success.
+ @raise 400: bad schema
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('projects',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
+
+ @swagger.operation(nickname="deleteProjectsUnderScenario")
+ def delete(self, scenario):
+ """
+ @description: delete one or multiple projects
+ @notes: delete one or multiple projects
+ DELETE /api/v1/scenarios/<scenario_name>/projects? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: projects(names) to be deleted
+ @type body: C{list} of L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: delete project(s) success.
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('projects',
+ 'delete',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
+
+
+class ScenarioOwnerHandler(GenericScenarioUpdateHandler):
+ @swagger.operation(nickname="changeScenarioOwner")
+ def put(self, scenario):
+ """
+ @description: change scenario owner
+ @notes: substitute all projects, delete existed ones with new provides
+ PUT /api/v1/scenarios/<scenario_name>/owner? \
+ installer=<installer_name>& \
+ version=<version_name>
+ @param body: new owner
+ @type body: L{string}
+ @in body: body
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: True
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: True
+ @return 200: change owner success.
+ @raise 404: scenario/installer/version not existed
+ """
+ self.do_update('owner',
+ 'put',
+ locators={'scenario': scenario,
+ 'installer': None,
+ 'version': None})
diff --git a/utils/test/testapi/opnfv_testapi/resources/scenario_models.py b/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
index 9f5a07434..7d0770759 100644
--- a/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
@@ -74,7 +74,8 @@ class ScenarioVersion(models.ModelBase):
@property projects:
@ptype projects: C{list} of L{ScenarioProject}
"""
- def __init__(self, version=None, projects=None):
+ def __init__(self, owner=None, version=None, projects=None):
+ self.owner = owner
self.version = version
self.projects = list_default(projects)
@@ -83,7 +84,9 @@ class ScenarioVersion(models.ModelBase):
return {'projects': ScenarioProject}
def __eq__(self, other):
- return [self.version == other.version and self._projects_eq(other)]
+ return [self.version == other.version and
+ self.owner == other.owner and
+ self._projects_eq(other)]
def __ne__(self, other):
return not self.__eq__(other)
diff --git a/utils/test/testapi/opnfv_testapi/router/url_mappings.py b/utils/test/testapi/opnfv_testapi/router/url_mappings.py
index 458942576..9c9556c6b 100644
--- a/utils/test/testapi/opnfv_testapi/router/url_mappings.py
+++ b/utils/test/testapi/opnfv_testapi/router/url_mappings.py
@@ -58,6 +58,12 @@ mappings = [
scenario_handlers.ScenarioScoresHandler),
(r"/api/v1/scenarios/([^/]+)/trust_indicators",
scenario_handlers.ScenarioTIsHandler),
+ (r"/api/v1/scenarios/([^/]+)/customs",
+ scenario_handlers.ScenarioCustomsHandler),
+ (r"/api/v1/scenarios/([^/]+)/projects",
+ scenario_handlers.ScenarioProjectsHandler),
+ (r"/api/v1/scenarios/([^/]+)/owner",
+ scenario_handlers.ScenarioOwnerHandler),
# static path
(r'/(.*\.(css|png|gif|js|html|json|map|woff2|woff|ttf))',
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_base.py b/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_base.py
index aa6b83544..77a8d18c1 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_base.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_base.py
@@ -92,21 +92,35 @@ class TestBase(testing.AsyncHTTPTestCase):
headers=self.headers)
return self._get_return(res, self.list_res)
- def update(self, new=None, *args):
- if new:
+ def update_direct_url(self, url, new=None):
+ if new and hasattr(new, 'format'):
new = new.format()
- res = self.fetch(self._get_uri(*args),
+ res = self.fetch(url,
method='PUT',
body=json.dumps(new),
headers=self.headers)
return self._get_return(res, self.update_res)
- def delete(self, *args):
- res = self.fetch(self._get_uri(*args),
- method='DELETE',
- headers=self.headers)
+ def update(self, new=None, *args):
+ return self.update_direct_url(self._get_uri(*args), new)
+
+ def delete_direct_url(self, url, body):
+ if body:
+ res = self.fetch(url,
+ method='DELETE',
+ body=json.dumps(body),
+ headers=self.headers,
+ allow_nonstandard_methods=True)
+ else:
+ res = self.fetch(url,
+ method='DELETE',
+ headers=self.headers)
+
return res.code, res.body
+ def delete(self, *args):
+ return self.delete_direct_url(self._get_uri(*args), None)
+
@staticmethod
def _get_valid_args(*args):
new_args = tuple(['%s' % arg for arg in args if arg is not None])
@@ -132,7 +146,10 @@ class TestBase(testing.AsyncHTTPTestCase):
def _get_return(self, res, cls):
code = res.code
body = res.body
- return code, self._get_return_body(code, body, cls)
+ if body:
+ return code, self._get_return_body(code, body, cls)
+ else:
+ return code, None
@staticmethod
def _get_return_body(code, body, cls):
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_scenario.py b/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_scenario.py
index 0558ea30d..466caaf13 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_scenario.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/resources/test_scenario.py
@@ -168,15 +168,30 @@ class TestScenarioUpdate(TestScenarioBase):
self.version,
'functest')
+ def update_url_fixture(item):
+ def _update_url_fixture(xstep):
+ def wrapper(self, *args, **kwargs):
+ locator = None
+ if item in ['projects', 'owner']:
+ locator = 'installer={}&version={}'.format(
+ self.installer,
+ self.version)
+ self.update_url = '{}/{}?{}'.format(self.scenario_url,
+ item,
+ locator)
+ xstep(self, *args, **kwargs)
+ return wrapper
+ return _update_url_fixture
+
def update_partial(operate, expected):
- def _update(set_update):
+ def _update_partial(set_update):
@functools.wraps(set_update)
- def wrap(self):
+ def wrapper(self):
update, scenario = set_update(self, deepcopy(self.req_d))
code, body = getattr(self, operate)(update, self.scenario)
getattr(self, expected)(code, scenario)
- return wrap
- return _update
+ return wrapper
+ return _update_partial
@update_partial('_add', '_success')
def test_addScore(self, scenario):
@@ -200,9 +215,101 @@ class TestScenarioUpdate(TestScenarioBase):
return add, scenario
+ @update_partial('_add', '_success')
+ def test_addCustoms(self, scenario):
+ add = ['odl', 'parser', 'vping_ssh']
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['customs'] = list(set(functest['customs'] + add))
+ self.update_url = '{}/customs?{}'.format(self.scenario_url,
+ self.locate_project)
+ return add, scenario
+
+ @update_partial('_update', '_success')
+ def test_updateCustoms(self, scenario):
+ news = ['odl', 'parser', 'vping_ssh']
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['customs'] = news
+ self.update_url = '{}/customs?{}'.format(self.scenario_url,
+ self.locate_project)
+
+ return news, scenario
+
+ @update_partial('_delete', '_success')
+ def test_deleteCustoms(self, scenario):
+ obsoletes = ['vping_ssh']
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['customs'] = ['healthcheck']
+ self.update_url = '{}/customs?{}'.format(self.scenario_url,
+ self.locate_project)
+
+ return obsoletes, scenario
+
+ @update_url_fixture('projects')
+ @update_partial('_add', '_success')
+ def test_addProjects_succ(self, scenario):
+ add = models.ScenarioProject(project='qtip').format()
+ scenario['installers'][0]['versions'][0]['projects'].append(add)
+ return [add], scenario
+
+ @update_url_fixture('projects')
+ @update_partial('_add', '_conflict')
+ def test_addProjects_already_exist(self, scenario):
+ add = models.ScenarioProject(project='functest').format()
+ scenario['installers'][0]['versions'][0]['projects'].append(add)
+ return [add], scenario
+
+ @update_url_fixture('projects')
+ @update_partial('_add', '_bad_request')
+ def test_addProjects_bad_schema(self, scenario):
+ add = models.ScenarioProject(project='functest').format()
+ add['score'] = None
+ scenario['installers'][0]['versions'][0]['projects'].append(add)
+ return [add], scenario
+
+ @update_url_fixture('projects')
+ @update_partial('_update', '_success')
+ def test_updateProjects_succ(self, scenario):
+ update = models.ScenarioProject(project='qtip').format()
+ scenario['installers'][0]['versions'][0]['projects'] = [update]
+ return [update], scenario
+
+ @update_url_fixture('projects')
+ @update_partial('_update', '_bad_request')
+ def test_updateProjects_bad_schema(self, scenario):
+ update = models.ScenarioProject(project='functest').format()
+ update['score'] = None
+ scenario['installers'][0]['versions'][0]['projects'] = [update]
+ return [update], scenario
+
+ @update_url_fixture('projects')
+ @update_partial('_delete', '_success')
+ def test_deleteProjects(self, scenario):
+ deletes = ['functest']
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ scenario['installers'][0]['versions'][0]['projects'] = filter(
+ lambda f: f['project'] != 'functest',
+ projects)
+ return deletes, scenario
+
+ @update_url_fixture('owner')
+ @update_partial('_update', '_success')
+ def test_changeOwner(self, scenario):
+ new_owner = 'new_owner'
+ scenario['installers'][0]['versions'][0]['owner'] = 'www'
+ return new_owner, scenario
+
def _add(self, update_req, new_scenario):
return self.post_direct_url(self.update_url, update_req)
+ def _update(self, update_req, new_scenario):
+ return self.update_direct_url(self.update_url, update_req)
+
+ def _delete(self, update_req, new_scenario):
+ return self.delete_direct_url(self.update_url, update_req)
+
def _success(self, status, new_scenario):
self.assertEqual(status, httplib.OK)
self._get_and_assert(new_scenario.get('name'), new_scenario)
@@ -212,3 +319,6 @@ class TestScenarioUpdate(TestScenarioBase):
def _bad_request(self, status, new_scenario):
self.assertEqual(status, httplib.BAD_REQUEST)
+
+ def _conflict(self, status, new_scenario):
+ self.assertEqual(status, httplib.CONFLICT)
diff --git a/utils/test/testapi/opnfv_testapi/tornado_swagger/swagger.py b/utils/test/testapi/opnfv_testapi/tornado_swagger/swagger.py
index 83f389a6b..6125c9554 100644
--- a/utils/test/testapi/opnfv_testapi/tornado_swagger/swagger.py
+++ b/utils/test/testapi/opnfv_testapi/tornado_swagger/swagger.py
@@ -94,11 +94,18 @@ class DocParser(object):
def _parse_type(self, **kwargs):
arg = kwargs.get('arg', None)
- body = self._get_body(**kwargs)
- self.params.setdefault(arg, {}).update({
- 'name': arg,
- 'dataType': body
- })
+ code = self._parse_epytext_para('code', **kwargs)
+ link = self._parse_epytext_para('link', **kwargs)
+ if code is None:
+ self.params.setdefault(arg, {}).update({
+ 'name': arg,
+ 'type': link
+ })
+ elif code == 'list':
+ self.params.setdefault(arg, {}).update({
+ 'type': 'array',
+ 'items': {'type': link}
+ })
def _parse_in(self, **kwargs):
arg = kwargs.get('arg', None)