Merge "Adds SwaggerUI"
authorAric Gardner <agardner@linuxfoundation.org>
Tue, 22 Aug 2017 18:36:04 +0000 (18:36 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Tue, 22 Aug 2017 18:36:04 +0000 (18:36 +0000)
28 files changed:
jjb/armband/armband-deploy.sh
jjb/calipso/calipso.yml [moved from jjb/multisite/multisite-verify-jobs.yml with 59% similarity]
jjb/compass4nfv/compass-ci-jobs.yml
jjb/compass4nfv/compass-deploy.sh
jjb/dovetail/dovetail-ci-jobs.yml
jjb/dovetail/dovetail-run.sh
jjb/fuel/fuel-deploy.sh
jjb/global/installer-params.yml
jjb/multisite/fuel-deploy-for-multisite.sh [deleted file]
jjb/multisite/multisite-daily-jobs.yml [deleted file]
jjb/releng/automate.yml
jjb/releng/docker-deploy.sh
jjb/xci/xci-verify-jobs.yml [new file with mode: 0644]
utils/push-test-logs.sh
utils/test/reporting/api/handlers/landing.py
utils/test/reporting/pages/app/scripts/controllers/table.controller.js
utils/test/reporting/pages/app/scripts/factory/table.factory.js
utils/test/reporting/pages/app/views/commons/table.html
utils/test/testapi/opnfv_testapi/common/raises.py
utils/test/testapi/opnfv_testapi/resources/handlers.py
utils/test/testapi/opnfv_testapi/resources/models.py
utils/test/testapi/opnfv_testapi/resources/result_handlers.py
utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
utils/test/testapi/opnfv_testapi/resources/scenario_models.py
utils/test/testapi/opnfv_testapi/router/url_mappings.py
utils/test/testapi/opnfv_testapi/tests/unit/resources/test_base.py
utils/test/testapi/opnfv_testapi/tests/unit/resources/test_scenario.py
utils/test/testapi/opnfv_testapi/tornado_swagger/swagger.py

index 08d3233..fc59120 100755 (executable)
@@ -91,7 +91,7 @@ fi
 # construct the command
 DEPLOY_COMMAND="$WORKSPACE/ci/deploy.sh -b ${LAB_CONFIG_URL} \
     -l $LAB_NAME -p $POD_NAME -s $DEPLOY_SCENARIO -i file://${ISO_FILE} \
-    -B ${DEFAULT_BRIDGE:-,,,} -S $TMPDIR -L $WORKSPACE/$FUEL_LOG_FILENAME \
+    -B ${DEFAULT_BRIDGE:-pxebr} -S $TMPDIR -L $WORKSPACE/$FUEL_LOG_FILENAME \
     ${DEPLOY_CACHE}"
 
 # log info to console
similarity index 59%
rename from jjb/multisite/multisite-verify-jobs.yml
rename to jjb/calipso/calipso.yml
index 9431e0b..c808e9b 100644 (file)
@@ -1,39 +1,27 @@
-###################################################
-# All the jobs except verify have been removed!
-# They will only be enabled on request by projects!
-###################################################
 - project:
-    name: multisite
+    name: calipso
 
     project: '{name}'
 
     jobs:
-        - 'multisite-verify-{stream}'
+        - 'calipso-verify-{stream}'
 
     stream:
         - master:
             branch: '{stream}'
-            gs-pathname: ''
             disabled: false
-            timed: '@midnight'
-        - danube:
-            branch: 'stable/{stream}'
-            gs-pathname: '/{stream}'
-            disabled: false
-            timed: ''
 
 - job-template:
-    name: 'multisite-verify-{stream}'
+    name: 'calipso-verify-{stream}'
 
     disabled: '{obj:disabled}'
 
-    concurrent: true
-
     parameters:
         - project-parameter:
             project: '{project}'
             branch: '{branch}'
-        - 'opnfv-build-ubuntu-defaults'
+        - 'opnfv-build-defaults'
+
 
     scm:
         - git-scm-gerrit
                 branches:
                   - branch-compare-type: 'ANT'
                     branch-pattern: '**/{branch}'
-                disable-strict-forbidden-file-verification: 'true'
-                forbidden-file-paths:
-                  - compare-type: ANT
-                    pattern: 'docs/**|.gitignore'
 
+    builders:
+        - verify-unit-tests
+
+- builder:
+    name: verify-unit-tests
     builders:
         - shell: |
             #!/bin/bash
-
-            echo "Hello World"
+            set -o errexit
+            set -o nounset
+            set -o pipefail
+            cd $WORKSPACE
+            PYTHONPATH=$PWD/app python3 -m unittest discover -s app/test/fetch
index 4c12f19..8b4a74b 100644 (file)
         - 'k8-nosdn-nofeature-ha':
             disabled: false
             auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+        - 'os-nosdn-nofeature-noha':
+            disabled: false
+            auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+        - 'os-odl_l3-nofeature-noha':
+            disabled: false
+            auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+        - 'os-odl_l2-moon-noha':
+            disabled: false
+            auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+        - 'os-nosdn-kvm-noha':
+            disabled: false
+            auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+        - 'os-odl-sfc-noha':
+            disabled: false
+            auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+        - 'os-nosdn-dpdk-noha':
+            disabled: false
+            auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
 
 
     jobs:
 ########################
 # trigger macros
 ########################
+
+#---------------------------
+# ha-baremetal-centos-master
+#---------------------------
 - trigger:
     name: 'compass-os-nosdn-nofeature-ha-baremetal-centos-master-trigger'
     triggers:
     triggers:
         - timed: ''
 
+#-----------------------------
+# noha-baremetal-centos-master
+#-----------------------------
+- trigger:
+    name: 'compass-os-nosdn-nofeature-noha-baremetal-centos-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l3-nofeature-noha-baremetal-centos-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l2-moon-noha-baremetal-centos-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-kvm-noha-baremetal-centos-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl-sfc-noha-baremetal-centos-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-dpdk-noha-baremetal-centos-master-trigger'
+    triggers:
+        - timed: ''
+
+#--------------------
+# ha-baremetal-master
+#--------------------
 - trigger:
     name: 'compass-os-nosdn-nofeature-ha-baremetal-master-trigger'
     triggers:
-        - timed: '0 2 * * *'
+        - timed: '0 20 * * *'
 - trigger:
     name: 'compass-os-nosdn-openo-ha-baremetal-master-trigger'
     triggers:
 - trigger:
     name: 'compass-os-odl-sfc-ha-baremetal-master-trigger'
     triggers:
-        - timed: '0 4 * * *'
+        - timed: '0 10 * * *'
 
+#----------------------
+# noha-baremetal-master
+#----------------------
+- trigger:
+    name: 'compass-os-nosdn-kvm-noha-baremetal-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-nofeature-noha-baremetal-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l3-nofeature-noha-baremetal-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l2-moon-noha-baremetal-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl-sfc-noha-baremetal-master-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-dpdk-noha-baremetal-master-trigger'
+    triggers:
+        - timed: ''
 
+#--------------------
+# ha-baremetal-danube
+#--------------------
 - trigger:
     name: 'compass-os-nosdn-nofeature-ha-baremetal-danube-trigger'
     triggers:
     triggers:
         - timed: ''
 
+#----------------------
+# noha-baremetal-danube
+#----------------------
+- trigger:
+    name: 'compass-os-nosdn-kvm-noha-baremetal-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-nofeature-noha-baremetal-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l3-nofeature-noha-baremetal-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l2-moon-noha-baremetal-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl-sfc-noha-baremetal-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-dpdk-noha-baremetal-danube-trigger'
+    triggers:
+        - timed: ''
 
+#------------------
+# ha-virtual-master
+#------------------
 - trigger:
     name: 'compass-os-nosdn-nofeature-ha-virtual-master-trigger'
     triggers:
 - trigger:
     name: 'compass-os-odl_l2-moon-ha-virtual-master-trigger'
     triggers:
-        - timed: '0 22 * * *'
+        - timed: '0 12 * * *'
 - trigger:
     name: 'compass-os-nosdn-kvm-ha-virtual-master-trigger'
     triggers:
-        - timed: '0 23 * * *'
+        - timed: '0 13 * * *'
 - trigger:
     name: 'compass-os-nosdn-dpdk-ha-virtual-master-trigger'
     triggers:
     triggers:
         - timed: '0 16 * * *'
 
+#--------------------
+# noha-virtual-master
+#--------------------
+- trigger:
+    name: 'compass-os-nosdn-kvm-noha-virtual-master-trigger'
+    triggers:
+        - timed: '0 13 * * *'
+- trigger:
+    name: 'compass-os-nosdn-nofeature-noha-virtual-master-trigger'
+    triggers:
+        - timed: '0 14 * * *'
+- trigger:
+    name: 'compass-os-odl_l3-nofeature-noha-virtual-master-trigger'
+    triggers:
+        - timed: '0 15 * * *'
+- trigger:
+    name: 'compass-os-odl_l2-moon-noha-virtual-master-trigger'
+    triggers:
+        - timed: '0 18 * * *'
+- trigger:
+    name: 'compass-os-odl-sfc-noha-virtual-master-trigger'
+    triggers:
+        - timed: '0 20 * * *'
+- trigger:
+    name: 'compass-os-nosdn-dpdk-noha-virtual-master-trigger'
+    triggers:
+        - timed: '0 11 * * *'
+
+#------------------
+# ha-virtual-danube
+#------------------
 - trigger:
     name: 'compass-os-nosdn-nofeature-ha-virtual-danube-trigger'
     triggers:
     name: 'compass-k8-nosdn-nofeature-ha-virtual-danube-trigger'
     triggers:
         - timed: ''
+
+#--------------------
+# noha-virtual-danube
+#--------------------
+- trigger:
+    name: 'compass-os-nosdn-kvm-noha-virtual-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-nofeature-noha-virtual-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l3-nofeature-noha-virtual-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl_l2-moon-noha-virtual-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-odl-sfc-noha-virtual-danube-trigger'
+    triggers:
+        - timed: ''
+- trigger:
+    name: 'compass-os-nosdn-dpdk-noha-virtual-danube-trigger'
+    triggers:
+        - timed: ''
index 7a5af5f..9d4ae51 100644 (file)
@@ -52,6 +52,11 @@ fi
 if [[ "$NODE_NAME" =~ "-virtual" ]]; then
     export NETWORK_CONF=$CONFDIR/vm_environment/$NODE_NAME/${NETWORK_CONF_FILE}
     export DHA_CONF=$CONFDIR/vm_environment/${DEPLOY_SCENARIO}.yml
+    if [[ "${DEPLOY_SCENARIO}" =~ "-moon-noha" ]]; then
+        export VIRT_NUMBER=3
+    elif [[ "${DEPLOY_SCENARIO}" =~ "-noha" ]]; then
+        export VIRT_NUMBER=2
+    fi
 else
     export INSTALL_NIC=eth1
     export NETWORK_CONF=$CONFDIR/hardware_environment/$NODE_NAME/${NETWORK_CONF_FILE}
index 3f130c9..42e1ad5 100644 (file)
@@ -25,7 +25,7 @@
         branch: 'stable/{stream}'
         dovetail-branch: master
         gs-pathname: '/{stream}'
-        docker-tag: 'cvp.0.4.0'
+        docker-tag: 'cvp.0.5.0'
 
 #-----------------------------------
 # POD, PLATFORM, AND BRANCH MAPPING
index a078c8f..d05b309 100755 (executable)
@@ -126,6 +126,10 @@ fi
 echo "Download image ubuntu-16.04-server-cloudimg-amd64-disk1.img ..."
 wget -q -nc http://artifacts.opnfv.org/sdnvpn/ubuntu-16.04-server-cloudimg-amd64-disk1.img -P ${DOVETAIL_CONFIG}
 
+# functest needs to download this image first before running
+echo "Download image cirros-0.3.5-x86_64-disk.img ..."
+wget -q -nc http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img -P ${DOVETAIL_CONFIG}
+
 opts="--privileged=true -id"
 
 docker_volume="-v /var/run/docker.sock:/var/run/docker.sock"
@@ -156,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}"
index 29b173a..eebd8bc 100755 (executable)
@@ -33,7 +33,7 @@ fi
 
 # set deployment parameters
 export TMPDIR=$HOME/tmpdir
-BRIDGE=${BRIDGE:-,,,}
+BRIDGE=${BRIDGE:-pxebr}
 LAB_NAME=${NODE_NAME/-*}
 POD_NAME=${NODE_NAME/*-}
 
@@ -63,13 +63,20 @@ echo "Cloning securedlab repo $BRANCH"
 git clone ssh://jenkins-ericsson@gerrit.opnfv.org:29418/securedlab --quiet \
     --branch $BRANCH
 
+# Source local_env if present, which contains POD-specific config
+local_env="${WORKSPACE}/securedlab/labs/$LAB_NAME/$POD_NAME/fuel/config/local_env"
+if [ -e "${local_env}" ]; then
+    echo "-- Sourcing local environment file"
+    source "${local_env}"
+fi
+
 # log file name
 FUEL_LOG_FILENAME="${JOB_NAME}_${BUILD_NUMBER}.log.tar.gz"
 
 # construct the command
 DEPLOY_COMMAND="sudo $WORKSPACE/ci/deploy.sh -b file://$WORKSPACE/securedlab \
     -l $LAB_NAME -p $POD_NAME -s $DEPLOY_SCENARIO -i file://$WORKSPACE/opnfv.iso \
-    -B $BRIDGE -S $TMPDIR -L $WORKSPACE/$FUEL_LOG_FILENAME"
+    -B ${DEFAULT_BRIDGE:-${BRIDGE}} -S $TMPDIR -L $WORKSPACE/$FUEL_LOG_FILENAME"
 
 # log info to console
 echo "Deployment parameters"
index c4e715d..a81dce3 100644 (file)
@@ -55,7 +55,7 @@
             description: 'external network for test'
         - string:
             name: BRIDGE
-            default: ',,,'
+            default: 'pxebr'
             description: 'Bridge(s) to be used by salt master'
 
 - parameter:
diff --git a/jjb/multisite/fuel-deploy-for-multisite.sh b/jjb/multisite/fuel-deploy-for-multisite.sh
deleted file mode 100755 (executable)
index 827e5c2..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/bin/bash
-# SPDX-license-identifier: Apache-2.0
-##############################################################################
-# Copyright (c) 2016 Ericsson AB and others.
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-set -o nounset
-set -o pipefail
-
-# do not continue with the deployment if FRESH_INSTALL is not requested
-if [[ "$FRESH_INSTALL" == "true" ]]; then
-    echo "Fresh install requested. Proceeding with the installation."
-else
-    echo "Fresh install is not requested. Skipping the installation."
-    exit 0
-fi
-
-export TERM="vt220"
-export BRANCH=$(echo $BRANCH | sed 's/stable\///g')
-# get the latest successful job console log and extract the properties filename
-FUEL_DEPLOY_BUILD_URL="https://build.opnfv.org/ci/job/fuel-deploy-virtual-daily-$BRANCH/lastSuccessfulBuild/consoleText"
-FUEL_PROPERTIES_FILE=$(curl -s -L ${FUEL_DEPLOY_BUILD_URL} | grep 'ISO:' | awk '{print $2}' | sed 's/iso/properties/g')
-if [[ -z "FUEL_PROPERTIES_FILE" ]]; then
-    echo "Unable to extract the url to Fuel ISO properties from ${FUEL_DEPLOY_URL}"
-    exit 1
-fi
-
-# use known/working version of fuel
-#FUEL_PROPERTIES_FILE="opnfv-2017-03-06_16-00-15.properties"
-curl -L -s -o $WORKSPACE/latest.properties $GS_PATH/$FUEL_PROPERTIES_FILE
-
-# source the file so we get OPNFV vars
-source latest.properties
-
-# echo the info about artifact that is used during the deployment
-echo "Using ${OPNFV_ARTIFACT_URL/*\/} for deployment"
-
-# download the iso
-echo "Downloading the ISO using the link http://$OPNFV_ARTIFACT_URL"
-curl -L -s -o $WORKSPACE/opnfv.iso http://$OPNFV_ARTIFACT_URL > gsutil.iso.log 2>&1
-
-
-# set deployment parameters
-DEPLOY_SCENARIO="os-nosdn-nofeature-noha"
-export TMPDIR=$HOME/tmpdir
-BRIDGE=${BRIDGE:-,,,}
-LAB_NAME=${NODE_NAME/-*}
-POD_NAME=${NODE_NAME/*-}
-
-if [[ "$NODE_NAME" =~ "virtual" ]]; then
-    POD_NAME="virtual_kvm"
-fi
-
-# we currently support ericsson, intel, lf and zte labs
-if [[ ! "$LAB_NAME" =~ (ericsson|intel|lf|zte) ]]; then
-    echo "Unsupported/unidentified lab $LAB_NAME. Cannot continue!"
-    exit 1
-else
-    echo "Using configuration for $LAB_NAME"
-fi
-
-# create TMPDIR if it doesn't exist
-export TMPDIR=$HOME/tmpdir
-mkdir -p $TMPDIR
-
-# change permissions down to TMPDIR
-chmod a+x $HOME
-chmod a+x $TMPDIR
-
-# clone fuel repo and checkout the sha1 that corresponds to the ISO
-echo "Cloning fuel repo"
-git clone https://gerrit.opnfv.org/gerrit/p/fuel.git fuel
-cd $WORKSPACE/fuel
-echo "Checking out $OPNFV_GIT_SHA1"
-git checkout $OPNFV_GIT_SHA1 --quiet
-
-# clone the securedlab repo
-cd $WORKSPACE
-echo "Cloning securedlab repo ${GIT_BRANCH##origin/}"
-git clone ssh://jenkins-ericsson@gerrit.opnfv.org:29418/securedlab --quiet \
-    --branch ${GIT_BRANCH##origin/}
-
-# log file name
-FUEL_LOG_FILENAME="${JOB_NAME}_${BUILD_NUMBER}.log.tar.gz"
-
-# construct the command
-DEPLOY_COMMAND="sudo $WORKSPACE/fuel/ci/deploy.sh -b file://$WORKSPACE/securedlab \
-    -l $LAB_NAME -p $POD_NAME -s $DEPLOY_SCENARIO -i file://$WORKSPACE/opnfv.iso \
-    -B $BRIDGE -S $TMPDIR -L $WORKSPACE/$FUEL_LOG_FILENAME"
-
-# log info to console
-echo "Deployment parameters"
-echo "--------------------------------------------------------"
-echo "Scenario: $DEPLOY_SCENARIO"
-echo "Lab: $LAB_NAME"
-echo "POD: $POD_NAME"
-echo "ISO: ${OPNFV_ARTIFACT_URL/*\/}"
-echo
-echo "Starting the deployment using $INSTALLER_TYPE. This could take some time..."
-echo "--------------------------------------------------------"
-echo
-
-# start the deployment
-echo "Issuing command"
-echo "$DEPLOY_COMMAND"
-echo
-
-$DEPLOY_COMMAND
-exit_code=$?
-
-echo
-echo "--------------------------------------------------------"
-echo "Deployment is done!"
-
-if [[ $exit_code -ne 0 ]]; then
-    echo "Deployment failed!"
-    exit $exit_code
-else
-    echo "Deployment is successful!"
-    exit 0
-fi
diff --git a/jjb/multisite/multisite-daily-jobs.yml b/jjb/multisite/multisite-daily-jobs.yml
deleted file mode 100644 (file)
index c5e1866..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-- project:
-    name: kingbird
-
-    project: 'multisite'
-
-    jobs:
-        - 'multisite-kingbird-virtual-daily-{stream}'
-        - 'multisite-{phase}-{stream}'
-
-    phase:
-        - 'fuel-deploy-regionone-virtual':
-            slave-label: ericsson-virtual12
-        - 'fuel-deploy-regiontwo-virtual':
-            slave-label: ericsson-virtual13
-        - 'register-endpoints':
-            slave-label: ericsson-virtual12
-        - 'update-auth':
-            slave-label: ericsson-virtual13
-        - 'kingbird-deploy-virtual':
-            slave-label: ericsson-virtual12
-
-    stream:
-        - master:
-            branch: '{stream}'
-            gs-pathname: ''
-            disabled: false
-            timed: '0 12 * * *'
-        - danube:
-            branch: 'stable/{stream}'
-            gs-pathname: '/{stream}'
-            disabled: false
-            timed: '0 0 * * *'
-
-- job-template:
-    name: 'multisite-kingbird-virtual-daily-{stream}'
-
-    project-type: multijob
-
-    disabled: '{obj:disabled}'
-
-    concurrent: false
-
-    parameters:
-        - project-parameter:
-            project: '{project}'
-            branch: '{branch}'
-        - choice:
-            name: FRESH_INSTALL
-            choices:
-                - 'true'
-                - 'false'
-        - string:
-            name: KINGBIRD_LOG_FILE
-            default: $WORKSPACE/kingbird.log
-        - 'opnfv-build-defaults'
-
-    triggers:
-         - timed: '{timed}'
-
-    builders:
-        - description-setter:
-            description: "Built on $NODE_NAME"
-        - multijob:
-            name: fuel-deploy-virtual
-            condition: SUCCESSFUL
-            projects:
-                - name: 'multisite-fuel-deploy-regionone-virtual-{stream}'
-                  current-parameters: false
-                  predefined-parameters: |
-                    FUEL_VERSION=latest
-                    DEPLOY_SCENARIO=os-nosdn-nofeature-noha
-                    OS_REGION=RegionOne
-                    REGIONONE_IP=100.64.209.10
-                    REGIONTWO_IP=100.64.209.11
-                    FRESH_INSTALL=$FRESH_INSTALL
-                  node-parameters: false
-                  node-label-name: SLAVE_LABEL
-                  node-label: ericsson-virtual12
-                  kill-phase-on: FAILURE
-                  abort-all-job: true
-                - name: 'multisite-fuel-deploy-regiontwo-virtual-{stream}'
-                  current-parameters: false
-                  predefined-parameters: |
-                    FUEL_VERSION=latest
-                    DEPLOY_SCENARIO=os-nosdn-nofeature-noha
-                    OS_REGION=RegionTwo
-                    REGIONONE_IP=100.64.209.10
-                    REGIONTWO_IP=100.64.209.11
-                    FRESH_INSTALL=$FRESH_INSTALL
-                  node-parameters: false
-                  node-label-name: SLAVE_LABEL
-                  node-label: ericsson-virtual13
-                  kill-phase-on: FAILURE
-                  abort-all-job: true
-        - multijob:
-            name: centralize-keystone
-            condition: SUCCESSFUL
-            projects:
-                - name: 'multisite-register-endpoints-{stream}'
-                  current-parameters: false
-                  predefined-parameters: |
-                    OS_REGION=RegionOne
-                    REGIONONE_IP=100.64.209.10
-                    REGIONTWO_IP=100.64.209.11
-                    FRESH_INSTALL=$FRESH_INSTALL
-                  node-parameters: false
-                  node-label-name: SLAVE_LABEL
-                  node-label: ericsson-virtual12
-                  kill-phase-on: FAILURE
-                  abort-all-job: true
-                - name: 'multisite-update-auth-{stream}'
-                  current-parameters: false
-                  predefined-parameters: |
-                    OS_REGION=RegionTwo
-                    REGIONONE_IP=100.64.209.10
-                    REGIONTWO_IP=100.64.209.11
-                    FRESH_INSTALL=$FRESH_INSTALL
-                  node-parameters: false
-                  node-label-name: SLAVE_LABEL
-                  node-label: ericsson-virtual13
-                  kill-phase-on: FAILURE
-                  abort-all-job: true
-        - multijob:
-            name: kingbird-deploy-virtual
-            condition: SUCCESSFUL
-            projects:
-                - name: 'multisite-kingbird-deploy-virtual-{stream}'
-                  current-parameters: false
-                  predefined-parameters: |
-                    OS_REGION=RegionOne
-                    REGIONONE_IP=100.64.209.10
-                    REGIONTWO_IP=100.64.209.11
-                    FRESH_INSTALL=$FRESH_INSTALL
-                  node-parameters: false
-                  node-label-name: SLAVE_LABEL
-                  node-label: ericsson-virtual12
-                  kill-phase-on: FAILURE
-                  abort-all-job: true
-        - multijob:
-            name: kingbird-functest
-            condition: SUCCESSFUL
-            projects:
-                - name: 'functest-fuel-virtual-suite-{stream}'
-                  current-parameters: false
-                  predefined-parameters: |
-                    DEPLOY_SCENARIO=os-nosdn-multisite-noha
-                    FUNCTEST_SUITE_NAME=multisite
-                    OS_REGION=RegionOne
-                    REGIONONE_IP=100.64.209.10
-                    REGIONTWO_IP=100.64.209.11
-                    FRESH_INSTALL=$FRESH_INSTALL
-                  node-parameters: false
-                  node-label-name: SLAVE_LABEL
-                  node-label: ericsson-virtual12
-                  kill-phase-on: NEVER
-                  abort-all-job: false
-
-- job-template:
-    name: 'multisite-{phase}-{stream}'
-
-    concurrent: false
-
-    disabled: '{obj:disabled}'
-
-    concurrent: false
-
-    parameters:
-        - project-parameter:
-            project: '{project}'
-            branch: '{branch}'
-        - string:
-            name: KINGBIRD_LOG_FILE
-            default: $WORKSPACE/kingbird.log
-        - string:
-            name: GS_PATH
-            default: 'http://artifacts.opnfv.org/fuel{gs-pathname}'
-        - 'fuel-defaults'
-        - '{slave-label}-defaults'
-        - choice:
-            name: FRESH_INSTALL
-            choices:
-                - 'true'
-                - 'false'
-
-    scm:
-        - git-scm
-
-    builders:
-        - description-setter:
-            description: "Built on $NODE_NAME"
-        - 'multisite-{phase}-builder':
-            stream: '{stream}'
-
-    publishers:
-        - 'multisite-{phase}-publisher'
-        - email-jenkins-admins-on-failure
-
-########################
-# builder macros
-########################
-- builder:
-    name: 'multisite-fuel-deploy-regionone-virtual-builder'
-    builders:
-        - shell:
-            !include-raw-escape: ./fuel-deploy-for-multisite.sh
-        - shell: |
-            #!/bin/bash
-
-            echo "This is where we deploy fuel, extract passwords and save into file"
-
-            cd $WORKSPACE/tools/keystone/
-            ./run.sh -t controller -r fetchpass.sh -o servicepass.ini
-
-- builder:
-    name: 'multisite-fuel-deploy-regiontwo-virtual-builder'
-    builders:
-        - shell:
-            !include-raw-escape: ./fuel-deploy-for-multisite.sh
-        - shell: |
-            #!/bin/bash
-
-            echo "This is where we deploy fuel, extract publicUrl, privateUrl, and adminUrl and save into file"
-
-            cd $WORKSPACE/tools/keystone/
-            ./run.sh -t controller -r endpoint.sh -o endpoints.ini
-- builder:
-    name: 'multisite-register-endpoints-builder'
-    builders:
-        - copyartifact:
-            project: 'multisite-fuel-deploy-regiontwo-virtual-{stream}'
-            which-build: multijob-build
-            filter: "endpoints.ini"
-        - shell: |
-            #!/bin/bash
-
-            echo "This is where we register RegionTwo in RegionOne keystone using endpoints.ini"
-
-            cd $WORKSPACE/tools/keystone/
-            ./run.sh -t controller -r region.sh -d $WORKSPACE/endpoints.ini
-- builder:
-    name: 'multisite-update-auth-builder'
-    builders:
-        - copyartifact:
-            project: 'multisite-fuel-deploy-regionone-virtual-{stream}'
-            which-build: multijob-build
-            filter: "servicepass.ini"
-        - shell: |
-            #!/bin/bash
-
-            echo "This is where we read passwords from servicepass.ini and replace passwords in RegionTwo"
-
-            cd $WORKSPACE/tools/keystone/
-            ./run.sh -t controller -r writepass.sh -d $WORKSPACE/servicepass.ini
-            ./run.sh -t compute -r writepass.sh -d $WORKSPACE/servicepass.ini
-- builder:
-    name: 'multisite-kingbird-deploy-virtual-builder'
-    builders:
-        - shell: |
-            #!/bin/bash
-
-            echo "This is where we install kingbird"
-            cd $WORKSPACE/tools/kingbird
-            ./deploy.sh
-########################
-# publisher macros
-########################
-- publisher:
-    name: 'multisite-fuel-deploy-regionone-virtual-publisher'
-    publishers:
-        - archive:
-            artifacts: 'servicepass.ini'
-            allow-empty: false
-            only-if-success: true
-            fingerprint: true
-- publisher:
-    name: 'multisite-fuel-deploy-regiontwo-virtual-publisher'
-    publishers:
-        - archive:
-            artifacts: 'endpoints.ini'
-            allow-empty: false
-            only-if-success: true
-            fingerprint: true
-- publisher:
-    name: 'multisite-register-endpoints-publisher'
-    publishers:
-        - archive:
-            artifacts: 'dummy.txt'
-            allow-empty: true
-- publisher:
-    name: 'multisite-update-auth-publisher'
-    publishers:
-        - archive:
-            artifacts: 'dummy.txt'
-            allow-empty: true
-- publisher:
-    name: 'multisite-kingbird-deploy-virtual-publisher'
-    publishers:
-        - archive:
-            artifacts: 'dummy.txt'
-            allow-empty: true
-- publisher:
-    name: 'multisite-kingbird-functest-publisher'
-    publishers:
-        - archive:
-            artifacts: 'dummy.txt'
-            allow-empty: true
index d12ee5d..c6ca37f 100644 (file)
     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:
index 2a3e078..1e83577 100644 (file)
 #  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
new file mode 100644 (file)
index 0000000..8d1ee55
--- /dev/null
@@ -0,0 +1,226 @@
+- project:
+    name: 'opnfv-xci-verify'
+
+    project: releng-xci
+#--------------------------------
+# branches
+#--------------------------------
+    stream:
+        - master:
+            branch: '{stream}'
+#--------------------------------
+# distros
+#--------------------------------
+    distro:
+        - 'xenial':
+            disabled: false
+        - 'centos7':
+            disabled: true
+        - 'suse':
+            disabled: true
+#--------------------------------
+# type
+#--------------------------------
+    type:
+        - virtual
+#--------------------------------
+# patch verification phases
+#--------------------------------
+    phase:
+        - 'deploy'
+        - 'healthcheck'
+#--------------------------------
+# jobs
+#--------------------------------
+    jobs:
+        - 'xci-verify-{distro}-{type}-{stream}'
+        - 'xci-verify-{phase}-{type}-{stream}'
+#--------------------------------
+# job templates
+#--------------------------------
+- job-template:
+    name: 'xci-verify-{distro}-{type}-{stream}'
+
+    project-type: multijob
+
+    disabled: '{obj:disabled}'
+
+    concurrent: true
+
+    properties:
+        - logrotate-default
+        - build-blocker:
+            use-build-blocker: true
+            blocking-jobs:
+                - 'xci-verify-.*'
+                - 'bifrost-verify-.*'
+                - 'bifrost-periodic-.*'
+                - 'osa-verify-.*'
+                - 'osa-periodic-.*'
+            block-level: 'NODE'
+
+    wrappers:
+        - ssh-agent-wrapper
+        - build-timeout:
+            timeout: 240
+        - fix-workspace-permissions
+
+    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}'
+                file-paths:
+                  - compare-type: ANT
+                    pattern: 'xci/**'
+                disable-strict-forbidden-file-verification: 'true'
+                forbidden-file-paths:
+                  - compare-type: ANT
+                    pattern: 'bifrost/**'
+                  - compare-type: ANT
+                    pattern: 'openstack-ansible/**'
+                  - compare-type: ANT
+                    pattern: 'puppet-infracloud/**'
+                  - compare-type: ANT
+                    pattern: 'README.rst'
+            readable-message: true
+
+    parameters:
+        - project-parameter:
+            project: '{project}'
+            branch: '{branch}'
+        - 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:
+            description: "Built on $NODE_NAME"
+        - multijob:
+            name: deploy
+            condition: SUCCESSFUL
+            projects:
+                - name: 'xci-verify-deploy-{type}-{stream}'
+                  current-parameters: true
+                  predefined-parameters: |
+                    DISTRO={distro}
+                    DEPLOY_SCENARIO=os-nosdn-nofeature-noha
+                  node-parameters: true
+                  kill-phase-on: FAILURE
+                  abort-all-job: true
+        - multijob:
+            name: healthcheck
+            condition: SUCCESSFUL
+            projects:
+                - name: 'xci-verify-healthcheck-{type}-{stream}'
+                  current-parameters: true
+                  predefined-parameters: |
+                    DISTRO={distro}
+                    DEPLOY_SCENARIO=os-nosdn-nofeature-noha
+                    FUNCTEST_SUITE_NAME=healthcheck
+                  node-parameters: true
+                  kill-phase-on: NEVER
+                  abort-all-job: true
+
+- job-template:
+    name: 'xci-verify-{phase}-{type}-{stream}'
+
+    disabled: false
+
+    concurrent: true
+
+    properties:
+        - logrotate-default
+        - build-blocker:
+            use-build-blocker: true
+            blocking-jobs:
+                - 'xci-verify-deploy-.*'
+                - 'xci-verify-healthcheck-.*'
+                - 'bifrost-verify-.*'
+                - 'bifrost-periodic-.*'
+                - 'osa-verify-.*'
+                - 'osa-periodic-.*'
+            block-level: 'NODE'
+
+    parameters:
+        - string:
+            name: DISTRO
+            default: 'xenial'
+        - string:
+            name: DEPLOY_SCENARIO
+            default: 'os-nosdn-nofeature-noha'
+        - string:
+            name: FUNCTEST_SUITE_NAME
+            default: 'healthcheck'
+        - string:
+            name: XCI_FLAVOR
+            default: 'mini'
+        - string:
+            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
+        - build-timeout:
+            timeout: 240
+        - fix-workspace-permissions
+
+    scm:
+        - git-scm-gerrit
+
+    builders:
+        - description-setter:
+            description: "Built on $NODE_NAME"
+        - 'xci-verify-{phase}-macro'
+
+#--------------------------------
+# builder macros
+#--------------------------------
+- builder:
+    name: 'xci-verify-deploy-macro'
+    builders:
+        - shell: |
+            #!/bin/bash
+
+            # 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:
+    name: 'xci-verify-healthcheck-macro'
+    builders:
+        - shell: |
+            #!/bin/bash
+
+            echo "Hello World!"
index eb57deb..79190ec 100644 (file)
@@ -31,6 +31,7 @@ node_list=(\
 'huawei-pod1' 'huawei-pod2' 'huawei-pod3' 'huawei-pod4' 'huawei-pod5' \
 'huawei-pod6' 'huawei-pod7' 'huawei-pod12' \
 'huawei-virtual1' 'huawei-virtual2' 'huawei-virtual3' 'huawei-virtual4' \
+'huawei-virtual5' 'huawei-virtual8' 'huawei-virtual9' \
 'zte-pod2' \
 'zte-virtual1')
 
index 749916f..0bf602d 100644 (file)
@@ -7,6 +7,7 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 import requests
+import time
 
 from tornado.escape import json_encode
 from tornado.escape import json_decode
@@ -24,7 +25,7 @@ class FiltersHandler(BaseHandler):
                 'status': ['success', 'warning', 'danger'],
                 'projects': ['functest', 'yardstick'],
                 'installers': ['apex', 'compass', 'fuel', 'joid'],
-                'version': ['colorado', 'master'],
+                'version': ['master', 'colorado', 'danube'],
                 'loops': ['daily', 'weekly', 'monthly'],
                 'time': ['10 days', '30 days']
             }
@@ -53,27 +54,27 @@ class ScenariosHandler(BaseHandler):
     def _get_scenario_result(self, scenario, data, args):
         result = {
             'status': data.get('status'),
-            'installers': self._get_installers_result(data['installers'], args)
+            'installers': self._get_installers_result(data, args)
         }
         return result
 
     def _get_installers_result(self, data, args):
         func = self._get_installer_result
-        return {k: func(k, data.get(k, {}), args) for k in args['installers']}
+        return {k: func(data.get(k, {}), args) for k in args['installers']}
 
-    def _get_installer_result(self, installer, data, args):
-        projects = data.get(args['version'], [])
-        return [self._get_project_data(projects, p) for p in args['projects']]
+    def _get_installer_result(self, data, args):
+        return self._get_version_data(data.get(args['version'], {}), args)
 
-    def _get_project_data(self, projects, project):
+    def _get_version_data(self, data, args):
+        return {k: self._get_project_data(data.get(k, {}))
+                for k in args['projects']}
+
+    def _get_project_data(self, data):
         atom = {
-            'project': project,
-            'score': None,
-            'status': None
+            'score': data.get('score', ''),
+            'status': data.get('status', '')
         }
-        for p in projects:
-            if p['project'] == project:
-                return p
+
         return atom
 
     def _get_scenarios(self):
@@ -88,41 +89,42 @@ class ScenariosHandler(BaseHandler):
                                                                     [])
                                                               ) for a in data}
         scenario = {
-            'status': self._get_status(),
-            'installers': installers
+            'status': self._get_status()
         }
+        scenario.update(installers)
+
         return scenario
 
     def _get_status(self):
         return 'success'
 
     def _get_installer(self, data):
-        return {a.get('version'): self._get_version(a) for a in data}
+        return {a.get('version'): self._get_version(a.get('projects'))
+                for a in data}
 
     def _get_version(self, data):
+        return {a.get('project'): self._get_project(a) for a in data}
+
+    def _get_project(self, data):
+        scores = data.get('scores', [])
+        trusts = data.get('trust_indicators', [])
+
         try:
-            scores = data.get('score', {}).get('projects')[0]
-            trusts = data.get('trust_indicator', {}).get('projects')[0]
-        except (TypeError, IndexError):
-            return []
-        else:
-            scores = {key: [dict(date=a.get('date')[:10],
-                                 score=a.get('score')
-                                 ) for a in scores[key]] for key in scores}
-            trusts = {key: [dict(date=a.get('date')[:10],
-                                 status=a.get('status')
-                                 ) for a in trusts[key]] for key in trusts}
-            atom = self._get_atom(scores, trusts)
-            return [dict(project=k,
-                         score=sorted(atom[k], reverse=True)[0].get('score'),
-                         status=sorted(atom[k], reverse=True)[0].get('status')
-                         ) for k in atom if atom[k]]
-
-    def _get_atom(self, scores, trusts):
-        s = {k: {a['date']: a['score'] for a in scores[k]} for k in scores}
-        t = {k: {a['date']: a['status'] for a in trusts[k]} for k in trusts}
-        return {k: [dict(score=s[k][a], status=t[k][a], data=a
-                         ) for a in s[k] if a in t[k]] for k in s}
+            date = sorted(scores, reverse=True)[0].get('date')
+        except IndexError:
+            data = time.time()
+
+        try:
+            score = sorted(scores, reverse=True)[0].get('score')
+        except IndexError:
+            score = None
+
+        try:
+            status = sorted(trusts, reverse=True)[0].get('status')
+        except IndexError:
+            status = None
+
+        return {'date': date, 'score': score, 'status': status}
 
     def _change_to_utf8(self, obj):
         if isinstance(obj, dict):
index 44d9441..8d494c3 100644 (file)
@@ -11,396 +11,268 @@ angular.module('opnfvApp')
     .controller('TableController', ['$scope', '$state', '$stateParams', '$http', 'TableFactory', '$timeout',
         function($scope, $state, $stateParams, $http, TableFactory, $timeout) {
 
-            $scope.filterlist = [];
-            $scope.selection = [];
-            $scope.statusList = [];
-            $scope.projectList = [];
-            $scope.installerList = [];
-            $scope.versionlist = [];
-            $scope.loopci = [];
-            $scope.time = [];
-            $scope.tableDataAll = {};
-            $scope.tableInfoAll = {};
-            $scope.scenario = {};
-            // $scope.selectProjects = [];
-
-
-            $scope.VersionConfig = {
-                create: true,
-                valueField: 'title',
-                labelField: 'title',
-                delimiter: '|',
-                maxItems: 1,
-                placeholder: 'Version',
-                onChange: function(value) {
-                    checkElementArrayValue($scope.selection, $scope.VersionOption);
-                    $scope.selection.push(value);
-                    // console.log($scope.selection);
-                    getScenarioData();
+            init();
 
-                }
-            }
+            function init() {
+                $scope.filterlist = [];
+                $scope.selection = [];
 
-            $scope.LoopConfig = {
-                create: true,
-                valueField: 'title',
-                labelField: 'title',
-                delimiter: '|',
-                maxItems: 1,
-                placeholder: 'Loop',
-                onChange: function(value) {
-                    checkElementArrayValue($scope.selection, $scope.LoopOption);
-                    $scope.selection.push(value);
-                    // console.log($scope.selection);
-                    getScenarioData();
+                $scope.statusList = [];
+                $scope.projectList = [];
+                $scope.installerList = [];
+                $scope.versionlist = [];
+                $scope.loopList = [];
+                $scope.timeList = [];
 
-                }
-            }
+                $scope.selectStatus = [];
+                $scope.selectProjects = [];
+                $scope.selectInstallers = [];
+                $scope.selectVersion = null;
+                $scope.selectLoop = null;
+                $scope.selectTime = null;
+
+                $scope.statusClicked = false;
+                $scope.installerClicked = false;
+                $scope.projectClicked = false;
 
-            $scope.TimeConfig = {
-                create: true,
-                valueField: 'title',
-                labelField: 'title',
-                delimiter: '|',
-                maxItems: 1,
-                placeholder: 'Time',
-                onChange: function(value) {
-                    checkElementArrayValue($scope.selection, $scope.TimeOption);
-                    $scope.selection.push(value);
-                    // console.log($scope.selection)
-                    getScenarioData();
+                $scope.scenarios = {};
 
+                $scope.VersionConfig = {
+                    create: true,
+                    valueField: 'title',
+                    labelField: 'title',
+                    delimiter: '|',
+                    maxItems: 1,
+                    placeholder: 'Version',
+                    onChange: function(value) {
+                        $scope.selectVersion = value;
 
+                        getScenarioData();
+
+                    }
                 }
-            }
 
+                $scope.LoopConfig = {
+                    create: true,
+                    valueField: 'title',
+                    labelField: 'title',
+                    delimiter: '|',
+                    maxItems: 1,
+                    placeholder: 'Loop',
+                    onChange: function(value) {
+                        $scope.selectLoop = value;
 
-            init();
+                        getScenarioData();
+
+                    }
+                }
+
+                $scope.TimeConfig = {
+                    create: true,
+                    valueField: 'title',
+                    labelField: 'title',
+                    delimiter: '|',
+                    maxItems: 1,
+                    placeholder: 'Time',
+                    onChange: function(value) {
+                        $scope.selectTime = value;
+
+                        getScenarioData();
+                    }
+                }
 
-            function init() {
-                $scope.toggleSelection = toggleSelection;
-                getScenarioData();
                 getFilters();
             }
 
             function getFilters() {
                 TableFactory.getFilter().get({
-
                 }).$promise.then(function(response) {
                     if (response != null) {
                         $scope.statusList = response.filters.status;
                         $scope.projectList = response.filters.projects;
                         $scope.installerList = response.filters.installers;
-                        $scope.versionlist = response.filters.version;
-                        $scope.loopci = response.filters.loops;
-                        $scope.time = response.filters.time;
-
-                        $scope.statusListString = $scope.statusList.toString();
-                        $scope.projectListString = $scope.projectList.toString();
-                        $scope.installerListString = $scope.installerList.toString();
-                        $scope.VersionSelected = $scope.versionlist[1];
-                        $scope.LoopCiSelected = $scope.loopci[0];
-                        $scope.TimeSelected = $scope.time[0];
-                        radioSetting($scope.versionlist, $scope.loopci, $scope.time);
+                        $scope.versionList = toSelectList(response.filters.version);
+                        $scope.loopList = toSelectList(response.filters.loops);
+                        $scope.timeList = toSelectList(response.filters.time);
+
+                        $scope.selectStatus = copy($scope.statusList);
+                        $scope.selectInstallers = copy($scope.installerList);
+                        $scope.selectProjects = copy($scope.projectList);
+                        $scope.selectVersion = response.filters.version[0];
+                        $scope.selectLoop = response.filters.loops[0];
+                        $scope.selectTime = response.filters.time[0];
+
+                        getScenarioData();
 
                     } else {
-                        alert("网络错误");
                     }
-                })
+                });
+            }
+
+            function toSelectList(arr){
+                var tempList = [];
+                angular.forEach(arr, function(ele){
+                    tempList.push({'title': ele});
+                });
+                return tempList;
+            }
+
+            function copy(arr){
+                var tempList = [];
+                angular.forEach(arr, function(ele){
+                    tempList.push(ele);
+                });
+                return tempList;
             }
 
             function getScenarioData() {
 
-                // var utl = BASE_URL + '/scenarios';
                 var data = {
-                    'status': ['success', 'danger', 'warning'],
-                    'projects': ['functest', 'yardstick'],
-                    'installers': ['apex', 'compass', 'fuel', 'joid'],
-                    'version': $scope.VersionSelected,
-                    'loops': $scope.LoopCiSelected,
-                    'time': $scope.TimeSelected
+                    'status': $scope.selectStatus,
+                    'projects': $scope.selectProjects,
+                    'installers': $scope.selectInstallers,
+                    'version': $scope.selectVersion,
+                    'loops': $scope.selectLoop,
+                    'time': $scope.selectTime
                 };
 
                 TableFactory.getScenario(data).then(function(response) {
                     if (response.status == 200) {
-                        $scope.scenario = response.data;
-
-                        reSettingcolspan();
+                        $scope.scenarios = response.data.scenarios;
+                        getScenario();
                     }
 
                 }, function(error) {
-
-                })
+                });
 
             }
 
-            function reSettingcolspan() {
-                if ($scope.selectProjects == undefined || $scope.selectProjects == null) {
-                    constructJson();
-                    $scope.colspan = $scope.tableDataAll.colspan;
+            function getScenario(){
 
-                } else {
-                    constructJson();
-                    $scope.colspan = $scope.tempColspan;
-                }
-                // console.log("test")
-            }
-
-            //construct json 
-            function constructJson(selectProject) {
+                $scope.project_row = [];
+                angular.forEach($scope.selectInstallers, function(installer){
+                    angular.forEach($scope.selectProjects, function(project){
+                        var temp = {
+                            'installer': installer,
+                            'project': project
+                        }
+                        $scope.project_row.push(temp);
 
-                var colspan;
-                var InstallerData;
-                var projectsInfo;
-                $scope.tableDataAll["scenario"] = [];
+                    });
+                });
 
 
-                for (var item in $scope.scenario.scenarios) {
+                $scope.scenario_rows = [];
+                angular.forEach($scope.scenarios, function(scenario, name){
+                    var scenario_row = {
+                        'name': null,
+                        'status': null,
+                        'statusDisplay': null,
+                        'datadisplay': [],
+                    };
+                    scenario_row.name = name;
+                    scenario_row.status = scenario.status;
 
-                    var headData = Object.keys($scope.scenario.scenarios[item].installers).sort();
-                    var scenarioStatus = $scope.scenario.scenarios[item].status;
                     var scenarioStatusDisplay;
-                    if (scenarioStatus == "success") {
+                    if (scenario.status == "success") {
                         scenarioStatusDisplay = "navy";
-                    } else if (scenarioStatus == "danger") {
+                    } else if (scenario.status == "danger") {
                         scenarioStatusDisplay = "danger";
-                    } else if (scenarioStatus == "warning") {
+                    } else if (scenario.status == "warning") {
                         scenarioStatusDisplay = "warning";
                     }
-
-                    InstallerData = headData;
-                    var projectData = [];
-                    var datadisplay = [];
-                    var projects = [];
-
-                    for (var j = 0; j < headData.length; j++) {
-
-                        projectData.push($scope.scenario.scenarios[item].installers[headData[j]]);
-                    }
-                    for (var j = 0; j < projectData.length; j++) {
-
-                        for (var k = 0; k < projectData[j].length; k++) {
-                            projects.push(projectData[j][k].project);
-                            var temArray = [];
-                            if (projectData[j][k].score == null) {
-                                temArray.push("null");
-                                temArray.push(projectData[j][k].project);
-                                temArray.push(headData[j]);
-                            } else {
-                                temArray.push(projectData[j][k].score);
-                                temArray.push(projectData[j][k].project);
-                                temArray.push(headData[j]);
-                            }
-
-
-                            if (projectData[j][k].status == "platinium") {
-                                temArray.push("primary");
-                                temArray.push("P");
-                            } else if (projectData[j][k].status == "gold") {
-                                temArray.push("danger");
-                                temArray.push("G");
-                            } else if (projectData[j][k].status == "silver") {
-                                temArray.push("warning");
-                                temArray.push("S");
-                            } else if (projectData[j][k].status == null) {
-                                temArray.push("null");
+                    scenario_row.statusDisplay = scenarioStatusDisplay;
+
+                    angular.forEach($scope.selectInstallers, function(installer){
+                        angular.forEach($scope.selectProjects, function(project){
+                            var datadisplay = {
+                                'installer': null,
+                                'project': null,
+                                'value': null,
+                                'label': null,
+                                'label_value': null
+                            };
+                            datadisplay.installer = installer;
+                            datadisplay.project = project;
+                            datadisplay.value = scenario.installers[installer][project].score;
+
+                            var single_status = scenario.installers[installer][project].status;
+                            if (single_status == "platinium") {
+                                datadisplay.label = 'primary';
+                                datadisplay.label_value = 'P';
+                            } else if (single_status == "gold") {
+                                datadisplay.label = 'danger';
+                                datadisplay.label_value = 'G';
+                            } else if (single_status == "silver") {
+                                datadisplay.label = 'warning';
+                                datadisplay.label_value = 'S';
+                            } else if (single_status == null) {
                             }
+                            scenario_row.datadisplay.push(datadisplay);
 
-                            datadisplay.push(temArray);
-
-                        }
-
-                    }
-
-                    colspan = projects.length / headData.length;
-
-                    var tabledata = {
-                        scenarioName: item,
-                        Installer: InstallerData,
-                        projectData: projectData,
-                        projects: projects,
-                        datadisplay: datadisplay,
-                        colspan: colspan,
-                        status: scenarioStatus,
-                        statusDisplay: scenarioStatusDisplay
-                    };
-
-                    JSON.stringify(tabledata);
-                    $scope.tableDataAll.scenario.push(tabledata);
-
-
-                    // console.log(tabledata);
-
-                }
-
-
-                projectsInfo = $scope.tableDataAll.scenario[0].projects;
-
-                var tempHeadData = [];
-
-                for (var i = 0; i < InstallerData.length; i++) {
-                    for (var j = 0; j < colspan; j++) {
-                        tempHeadData.push(InstallerData[i]);
-                    }
-                }
-
-                //console.log(tempHeadData);
-
-                var projectsInfoAll = [];
-
-                for (var i = 0; i < projectsInfo.length; i++) {
-                    var tempA = [];
-                    tempA.push(projectsInfo[i]);
-                    tempA.push(tempHeadData[i]);
-                    projectsInfoAll.push(tempA);
-
-                }
-                //console.log(projectsInfoAll);
-
-                $scope.tableDataAll["colspan"] = colspan;
-                $scope.tableDataAll["Installer"] = InstallerData;
-                $scope.tableDataAll["Projects"] = projectsInfoAll;
-
-                // console.log($scope.tableDataAll);
-                $scope.colspan = $scope.tableDataAll.colspan;
-                console.log($scope.tableDataAll);
-
-            }
-
-            //get json element size
-            function getSize(jsondata) {
-                var size = 0;
-                for (var item in jsondata) {
-                    size++;
-                }
-                return size;
+                        });
+                    });
+                    $scope.scenario_rows.push(scenario_row);
+                });
             }
 
 
-            // console.log($scope.colspan);
-
-
-            //find all same element index 
-            function getSameElementIndex(array, element) {
-                var indices = [];
-                var idx = array.indexOf(element);
-                while (idx != -1) {
-                    indices.push(idx);
-                    idx = array.indexOf(element, idx + 1);
+            function clickBase(eleList, ele){
+                var idx = eleList.indexOf(ele);
+                if(idx > -1){
+                    eleList.splice(idx, 1);
+                }else{
+                    eleList.push(ele);
                 }
-                //return indices;
-                var result = { element: element, index: indices };
-                JSON.stringify(result);
-                return result;
             }
 
-            //delete element in array
-            function deletElement(array, index) {
-                array.splice(index, 1);
+            $scope.clickStatus = function(status){
+                if($scope.selectStatus.length == $scope.statusList.length && $scope.statusClicked == false){
+                    $scope.selectStatus = [];
+                    $scope.statusClicked = true;
+                }
 
-            }
+                clickBase($scope.selectStatus, status);
 
-            function radioSetting(array1, array2, array3) {
-                var tempVersion = [];
-                var tempLoop = [];
-                var tempTime = [];
-                for (var i = 0; i < array1.length; i++) {
-                    var temp = {
-                        title: array1[i]
-                    };
-                    tempVersion.push(temp);
-                }
-                for (var i = 0; i < array2.length; i++) {
-                    var temp = {
-                        title: array2[i]
-                    };
-                    tempLoop.push(temp);
+                if($scope.selectStatus.length == 0 && $scope.statusClicked == true){
+                    $scope.selectStatus = copy($scope.statusList);
+                    $scope.statusClicked = false;
                 }
-                for (var i = 0; i < array3.length; i++) {
-                    var temp = {
-                        title: array3[i]
-                    };
-                    tempTime.push(temp);
-                }
-                $scope.VersionOption = tempVersion;
-                $scope.LoopOption = tempLoop;
-                $scope.TimeOption = tempTime;
-            }
 
-            //remove element in the array
-            function removeArrayValue(arr, value) {
-                for (var i = 0; i < arr.length; i++) {
-                    if (arr[i] == value) {
-                        arr.splice(i, 1);
-                        break;
-                    }
-                }
+                getScenarioData();
             }
 
-            //check if exist element
-            function checkElementArrayValue(arrayA, arrayB) {
-                for (var i = 0; i < arrayB.length; i++) {
-                    if (arrayA.indexOf(arrayB[i].title) > -1) {
-                        removeArrayValue(arrayA, arrayB[i].title);
-                    }
+            $scope.clickInstaller = function(installer){
+                if($scope.selectInstallers.length == $scope.installerList.length && $scope.installerClicked == false){
+                    $scope.selectInstallers = [];
+                    $scope.installerClicked = true;
                 }
-            }
 
-            function toggleSelection(status) {
-                var idx = $scope.selection.indexOf(status);
+                clickBase($scope.selectInstallers, installer);
 
-                if (idx > -1) {
-                    $scope.selection.splice(idx, 1);
-                    filterData($scope.selection)
-                } else {
-                    $scope.selection.push(status);
-                    filterData($scope.selection)
+                if($scope.selectInstallers.length == 0 && $scope.installerClicked == true){
+                    $scope.selectInstallers = copy($scope.installerList);
+                    $scope.installerClicked = false;
                 }
-                // console.log($scope.selection);
 
+                getScenarioData();
             }
 
-            //filter function
-            function filterData(selection) {
-
-                $scope.selectInstallers = [];
-                $scope.selectProjects = [];
-                $scope.selectStatus = [];
-                for (var i = 0; i < selection.length; i++) {
-                    if ($scope.statusListString.indexOf(selection[i]) > -1) {
-                        $scope.selectStatus.push(selection[i]);
-                    }
-                    if ($scope.projectListString.indexOf(selection[i]) > -1) {
-                        $scope.selectProjects.push(selection[i]);
-                    }
-                    if ($scope.installerListString.indexOf(selection[i]) > -1) {
-                        $scope.selectInstallers.push(selection[i]);
-                    }
+            $scope.clickProject = function(project){
+                if($scope.selectProjects.length == $scope.projectList.length && $scope.projectClicked == false){
+                    $scope.selectProjects = [];
+                    $scope.projectClicked = true;
                 }
 
+                clickBase($scope.selectProjects, project);
 
-                // $scope.colspan = $scope.selectProjects.length;
-                //when some selection is empty, we set it full
-                if ($scope.selectInstallers.length == 0) {
-                    $scope.selectInstallers = $scope.installerList;
-
-                }
-                if ($scope.selectProjects.length == 0) {
-                    $scope.selectProjects = $scope.projectList;
-                    $scope.colspan = $scope.tableDataAll.colspan;
-                } else {
-                    $scope.colspan = $scope.selectProjects.length;
-                    $scope.tempColspan = $scope.colspan;
-                }
-                if ($scope.selectStatus.length == 0) {
-                    $scope.selectStatus = $scope.statusList
+                if($scope.selectProjects.length == 0 && $scope.projectClicked == true){
+                    $scope.selectProjects = copy($scope.projectList);
+                    $scope.projectClicked = false;
                 }
 
-                // console.log($scope.selectStatus);
-                // console.log($scope.selectProjects);
-
+                getScenarioData();
             }
 
-
         }
-    ]);
\ No newline at end of file
+    ]);
index f0af34f..e715c5c 100644 (file)
@@ -28,7 +28,7 @@ angular.module('opnfvApp')
                     }
                 });
             },
-            getScenario: function() {
+            getScenario: function(data) {
 
                 var config = {
                     headers: {
@@ -36,7 +36,7 @@ angular.module('opnfvApp')
                     }
                 }
 
-                return $http.post(BASE_URL + '/landing-page/scenarios', {}, config);
+                return $http.post(BASE_URL + '/landing-page/scenarios', data, config);
             },
 
 
index f504bd7..a33c483 100644 (file)
@@ -29,9 +29,9 @@
                     <div class=" col-md-12" data-toggle="buttons" aria-pressed="false">
 
                         <label> Status </label> &nbsp;&nbsp; &nbsp;
-                        <label class="btn btn-outline btn-success btn-sm" style="height:25px; margin-right: 5px;" ng-repeat="status in statusList" value={{status}} ng-checked="selection.indexOf(status)>-1" ng-click="toggleSelection(status)">
+                        <label class="btn btn-outline btn-success btn-sm" style="height:25px; margin-right: 5px;" ng-repeat="status in statusList" value={{status}} ng-checked="selectStatus.indexOf(status)>-1" ng-click="clickStatus(status)">
                               <input type="checkbox"  disabled="disabled" > {{status}}
-                            
+
                           </label>
                     </div>
 
@@ -39,7 +39,7 @@
 
                     <div class=" col-md-12" data-toggle="buttons">
                         <label> Projects </label> &nbsp;
-                        <label class="btn btn-outline btn-success btn-sm " style="height:25px;margin-right: 5px;" ng-repeat="project in projectList" value={{project}} ng-checked="selection.indexOf(project)>-1" ng-click="toggleSelection(project)">
+                        <label class="btn btn-outline btn-success btn-sm " style="height:25px;margin-right: 5px;" ng-repeat="project in projectList" value={{project}} ng-checked="selectProjects.indexOf(project)>-1" ng-click="clickProject(project)">
                             <input type="checkbox" disabled="disabled"> {{project}}
                         </label>
 
@@ -47,7 +47,7 @@
                     <hr class="myhr">
                     <div class=" col-md-12" data-toggle="buttons">
                         <label> Installers </label>
-                        <label class="btn btn-outline btn-success btn-sm" style="height:25px;margin-right: 5px;" ng-repeat="installer in installerList" value={{installer}} ng-checked="selection.indexOf(installer)>-1" ng-click="toggleSelection(installer)">
+                        <label class="btn btn-outline btn-success btn-sm" style="height:25px;margin-right: 5px;" ng-repeat="installer in installerList" value={{installer}} ng-checked="selectInstallers.indexOf(installer)>-1" ng-click="clickInstaller(installer)">
                             <input type="checkbox" disabled="disabled"> {{installer}}
                             </label>
                     </div>
 
 
                     <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
-                        <selectize options="VersionOption" ng-model="VersionSelected" config="VersionConfig"></selectize>
+                        <selectize options="versionList" ng-model="selectVersion" config="VersionConfig"></selectize>
 
                     </div>
 
                     <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
-                        <selectize options="LoopOption" ng-model="LoopCiSelected" config="LoopConfig"></selectize>
+                        <selectize options="loopList" ng-model="selectLoop" config="LoopConfig"></selectize>
 
                     </div>
 
                     <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
-                        <selectize options="TimeOption" ng-model="TimeSelected" config="TimeConfig"></selectize>
+                        <selectize options="timeList" ng-model="selectTime" config="TimeConfig"></selectize>
                     </div>
                 </div>
                 <div class="table-responsive">
                         <thead class="thead">
                             <tr>
                                 <th>Scenario </th>
-                                <th colspan={{colspan}} ng-show="selectInstallers.indexOf(key)!=-1" value={{key}} ng-repeat="key in tableDataAll.Installer"><a href="notfound.html">{{key}}</a> </th>
+                                <th colspan={{selectProjects.length}} ng-show="selectInstallers.indexOf(key)!=-1" value={{key}} ng-repeat="key in selectInstallers"><a href="notfound.html">{{key}}</a> </th>
                             </tr>
 
                             <tr>
 
                                 <td></td>
-                                <td ng-show="selectProjects.indexOf(project[0])!=-1 && selectInstallers.indexOf(project[1])!=-1" ng-repeat="project in tableDataAll.Projects track by $index" data={{project[1]}} value={{project[0]}}>{{project[0]}}</td>
+                                <td ng-show="selectProjects.indexOf(project.project)!=-1 && selectInstallers.indexOf(project.installer)!=-1" ng-repeat="project in project_row track by $index" data={{project.installer}} value={{project.project}}>{{ project.project }}</td>
 
                             </tr>
                         </thead>
                         <tbody class="tbody">
-                            <tr ng-repeat="scenario in tableDataAll.scenario" ng-show="selectStatus.indexOf(scenario.status)!=-1">
+                            <tr ng-repeat="scenario in scenario_rows" ng-show="selectStatus.indexOf(scenario.status)!=-1">
 
-                                <td nowrap="nowrap" data={{scenario.status}}><span class="fa fa-circle text-{{scenario.statusDisplay}}"></span> <a href="notfound.html">{{scenario.scenarioName}}</a> </td>
+                                <td nowrap="nowrap" data={{scenario.status}}><span class="fa fa-circle text-{{scenario.statusDisplay}}"></span> <a href="notfound.html">{{scenario.name}}</a> </td>
 
                                 <!--<td style="background-color:#e7eaec" align="justify" ng-if="data[0]=='Not Support'" ng-repeat="data in scenario.datadisplay track by $index" data={{data[1]}} value={{data[2]}}></td>-->
 
-                                <td nowrap="nowrap" ng-show="selectInstallers.indexOf(data[2])!=-1 && selectProjects.indexOf(data[1])!=-1" ng-repeat="data in scenario.datadisplay track by $index" data={{data[1]}} value={{data[2]}} class={{data[0]}}>
-                                    <span class="label label-{{data[3]}}">{{data[4]}}</a></span> {{data[0]}}</td>
+                                <td nowrap="nowrap" ng-show="selectInstallers.indexOf(data.installer)!=-1 && selectProjects.indexOf(data.project)!=-1" ng-repeat="data in scenario.datadisplay track by $index" data={{data.project}} value={{data.installer}} class={{data.value}}>
+                                    <span class="label label-{{data.label}}">{{data.label_value}}</a></span> {{data.value}}</td>
 
 
                             </tr>
         </div>
     </div>
 
-</section>
\ No newline at end of file
+</section>
index ec6b8a5..55c58c9 100644 (file)
@@ -26,6 +26,10 @@ class Forbidden(Raiser):
     code = httplib.FORBIDDEN
 
 
+class Conflict(Raiser):
+    code = httplib.CONFLICT
+
+
 class NotFound(Raiser):
     code = httplib.NOT_FOUND
 
index 8a3a2db..ed55c70 100644 (file)
@@ -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):
@@ -106,20 +106,27 @@ class GenericApiHandler(web.RequestHandler):
         per_page = kwargs.get('per_page', 0)
         if query is None:
             query = {}
+        pipelines = list()
+        pipelines.append({'$match': query})
 
         total_pages = 0
-        if page > 0:
-            cursor = dbapi.db_list(self.table, query)
-            records_count = yield cursor.count()
-            total_pages = self._calc_total_pages(records_count,
-                                                 last,
-                                                 page,
-                                                 per_page)
-        pipelines = self._set_pipelines(query, sort, last, 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:
@@ -145,21 +152,17 @@ class GenericApiHandler(web.RequestHandler):
         if page > 1 and page > total_pages:
             raises.BadRequest(
                 'Request page > total_pages [{}]'.format(total_pages))
-        return total_pages
+        return total_pages, records_nr
 
     @staticmethod
-    def _set_pipelines(query, sort, last, page, per_page):
-        pipelines = list()
-        if query:
-            pipelines.append({'$match': query})
+    def _set_pipelines(pipelines, sort, return_nr, page, per_page):
         if sort:
             pipelines.append({'$sort': sort})
 
-        if page > 0:
-            pipelines.append({'$skip': (page - 1) * per_page})
-            pipelines.append({'$limit': per_page})
-        elif last > 0:
-            pipelines.append({'$limit': last})
+        over = (page - 1) * per_page
+        left = return_nr - over
+        pipelines.append({'$skip': over})
+        pipelines.append({'$limit': per_page if per_page < left else left})
 
         return pipelines
 
@@ -186,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():
index e8fc532..6f04cc2 100644 (file)
@@ -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 {}
index 2bf1792..9389d26 100644 (file)
@@ -155,7 +155,7 @@ class ResultsCLHandler(GenericResultHandler):
             @type last: L{string}
             @in last: query
             @required last: False
-            @param page: which page to list
+            @param page: which page to list, default to 1
             @type page: L{int}
             @in page: query
             @required page: False
@@ -180,7 +180,7 @@ class ResultsCLHandler(GenericResultHandler):
             return self.get_int('last', self.get_query_argument('last', 0))
 
         def page_limit():
-            return self.get_int('page', self.get_query_argument('page', 0))
+            return self.get_int('page', self.get_query_argument('page', 1))
 
         limitations = {
             'sort': {'_id': descend_limit()},
index a89e7ee..bd06400 100644 (file)
@@ -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
@@ -13,10 +15,10 @@ class GenericScenarioHandler(handlers.GenericApiHandler):
         self.table = self.db_scenarios
         self.table_cls = models.Scenario
 
-    def set_query(self, filters):
+    def set_query(self, locators):
         query = dict()
         elem_query = dict()
-        for k, v in filters.iteritems():
+        for k, v in locators.iteritems():
             if k == 'scenario':
                 query['name'] = v
             elif k == 'installer':
@@ -134,11 +136,19 @@ class ScenarioUpdater(object):
         self.version = version
         self.project = project
 
-    def update(self, item, op):
+    def update(self, item, action):
         updates = {
-            ('score', 'add'): self._update_requests_add_score,
+            ('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, op)](self.data)
+        updates[(item, action)](self.data)
 
         return self.data.format()
 
@@ -170,6 +180,83 @@ class ScenarioUpdater(object):
         project.scores.append(
             models.ScenarioScore.from_dict(self.body))
 
+    @iter_installers
+    @iter_versions
+    @iter_projects
+    def _update_requests_add_ti(self, project):
+        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)
 
@@ -179,13 +266,50 @@ 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 ScenarioScoresHandler(GenericScenarioHandler):
+class GenericScenarioUpdateHandler(GenericScenarioHandler):
+    def __init__(self, application, request, **kwargs):
+        super(GenericScenarioUpdateHandler, self).__init__(application,
+                                                           request,
+                                                           **kwargs)
+        self.installer = None
+        self.version = None
+        self.project = None
+        self.item = None
+        self.action = None
+
+    def do_update(self, item, action, locators):
+        self.item = item
+        self.action = action
+        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,
+                               self.json_args,
+                               self.installer,
+                               self.version,
+                               self.project).update(self.item, self.action)
+
+
+class ScenarioScoresHandler(GenericScenarioUpdateHandler):
     @swagger.operation(nickname="addScoreRecord")
     def post(self, scenario):
         """
@@ -210,24 +334,271 @@ class ScenarioScoresHandler(GenericScenarioHandler):
         @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.installer = self.get_query_argument('installer')
-        self.version = self.get_query_argument('version')
-        self.project = self.get_query_argument('project')
+        self.do_update('scores',
+                       'post',
+                       locators={'scenario': scenario,
+                                 'installer': None,
+                                 'version': None,
+                                 'project': None})
 
-        filters = {'scenario': scenario,
-                   'installer': self.installer,
-                   'version': self.version,
-                   'project': self.project}
-        db_keys = ['name']
-        self._update(query=self.set_query(filters=filters), db_keys=db_keys)
 
-    def _update_requests(self, data):
-        return ScenarioUpdater(data,
-                               self.json_args,
-                               self.installer,
-                               self.version,
-                               self.project).update('score', 'add')
+class ScenarioTIsHandler(GenericScenarioUpdateHandler):
+    @swagger.operation(nickname="addTrustIndicatorRecord")
+    def post(self, scenario):
+        """
+        @description: add a new trust indicator record
+        @notes: add a new trust indicator record to a project
+            POST /api/v1/scenarios/<scenario_name>/trust_indicators? \
+                installer=<installer_name>& \
+                version=<version_name>& \
+                project=<project_name>
+        @param body: trust indicator to be added
+        @type body: L{ScenarioTI}
+        @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: trust indicator is added.
+        @raise 404:  scenario/installer/version/project not existed
+        """
+        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})
index 9f5a074..7d07707 100644 (file)
@@ -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)
index 4f990f0..9c9556c 100644 (file)
@@ -56,6 +56,14 @@ mappings = [
     (r"/api/v1/scenarios/([^/]+)", scenario_handlers.ScenarioGURHandler),
     (r"/api/v1/scenarios/([^/]+)/scores",
      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))',
index aa6b835..77a8d18 100644 (file)
@@ -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):
index c12c52b..466caaf 100644 (file)
@@ -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):
@@ -189,9 +204,112 @@ class TestScenarioUpdate(TestScenarioBase):
 
         return add, scenario
 
+    @update_partial('_add', '_success')
+    def test_addTrustIndicator(self, scenario):
+        add = models.ScenarioTI(date=str(datetime.now()), status='gold')
+        projects = scenario['installers'][0]['versions'][0]['projects']
+        functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+        functest['trust_indicators'].append(add.format())
+        self.update_url = '{}/trust_indicators?{}'.format(self.scenario_url,
+                                                          self.locate_project)
+
+        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)
@@ -201,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)
index 83f389a..6125c95 100644 (file)
@@ -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)