summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fcaps/docker/Dockerfile6
-rw-r--r--fcaps/requirements.txt6
-rw-r--r--fcaps/test-requirements.txt5
-rw-r--r--lenovo/.gitignore27
-rw-r--r--lenovo/README.md15
-rw-r--r--lenovo/assembly.xml77
-rw-r--r--lenovo/docker/Dockerfile50
-rwxr-xr-xlenovo/docker/build_image.sh45
-rwxr-xr-xlenovo/initialize.sh16
-rw-r--r--lenovo/logs/empty.txt0
-rw-r--r--lenovo/manage.py22
-rwxr-xr-xlenovo/mvn-phase-script.sh83
-rw-r--r--lenovo/pom.xml117
-rw-r--r--lenovo/requirements.txt41
-rwxr-xr-xlenovo/run.sh29
-rwxr-xr-xlenovo/stop.sh18
-rw-r--r--lenovo/test-requirements.txt6
-rw-r--r--lenovo/thinkcloud/__init__.py18
-rw-r--r--lenovo/thinkcloud/extensions/__init__.py (renamed from ocata/ocata/vesagent/__init__.py)4
-rw-r--r--lenovo/thinkcloud/extensions/tests/__init__.py (renamed from ocata/ocata/vesagent/event_domain/__init__.py)4
-rw-r--r--lenovo/thinkcloud/extensions/tests/test_extensions.py38
-rw-r--r--lenovo/thinkcloud/extensions/urls.py24
-rw-r--r--lenovo/thinkcloud/extensions/urlsV1.py24
-rw-r--r--lenovo/thinkcloud/extensions/views/__init__.py13
-rw-r--r--lenovo/thinkcloud/extensions/views/extensions.py43
-rw-r--r--lenovo/thinkcloud/middleware.py64
-rw-r--r--lenovo/thinkcloud/proxy/__init__.py13
-rw-r--r--lenovo/thinkcloud/proxy/tests/__init__.py13
-rw-r--r--lenovo/thinkcloud/proxy/tests/test_identity_proxy.py735
-rw-r--r--lenovo/thinkcloud/proxy/tests/test_service_proxy.py853
-rw-r--r--lenovo/thinkcloud/proxy/urls.py41
-rw-r--r--lenovo/thinkcloud/proxy/urlsV1.py39
-rw-r--r--lenovo/thinkcloud/proxy/views/__init__.py13
-rw-r--r--lenovo/thinkcloud/proxy/views/identityV3.py81
-rw-r--r--lenovo/thinkcloud/proxy/views/services.py143
-rw-r--r--lenovo/thinkcloud/pub/__init__.py13
-rw-r--r--lenovo/thinkcloud/pub/config/__init__.py13
-rw-r--r--lenovo/thinkcloud/pub/config/config.py13
-rw-r--r--lenovo/thinkcloud/pub/config/log.yml51
-rw-r--r--lenovo/thinkcloud/registration/__init__.py13
-rw-r--r--lenovo/thinkcloud/registration/tests/__init__.py13
-rw-r--r--lenovo/thinkcloud/registration/tests/test_registration.py472
-rw-r--r--lenovo/thinkcloud/registration/tests/test_registration2.py116
-rw-r--r--lenovo/thinkcloud/registration/views/__init__.py13
-rw-r--r--lenovo/thinkcloud/registration/views/registration.py51
-rw-r--r--lenovo/thinkcloud/requests/__init__.py13
-rw-r--r--lenovo/thinkcloud/requests/urls.py49
-rw-r--r--lenovo/thinkcloud/requests/urlsV1.py49
-rw-r--r--lenovo/thinkcloud/requests/views/__init__.py13
-rw-r--r--lenovo/thinkcloud/resource/__init__.py13
-rw-r--r--lenovo/thinkcloud/resource/tests/__init__.py13
-rw-r--r--lenovo/thinkcloud/resource/tests/test_capacity.py277
-rw-r--r--lenovo/thinkcloud/resource/tests/test_events.py350
-rw-r--r--lenovo/thinkcloud/resource/tests/tests_infra_workload.py332
-rw-r--r--lenovo/thinkcloud/resource/views/__init__.py13
-rw-r--r--lenovo/thinkcloud/resource/views/capacity.py144
-rw-r--r--lenovo/thinkcloud/resource/views/events.py99
-rw-r--r--lenovo/thinkcloud/resource/views/infra_workload.py420
-rw-r--r--lenovo/thinkcloud/samples/__init__.py13
-rw-r--r--lenovo/thinkcloud/samples/tests.py31
-rw-r--r--lenovo/thinkcloud/samples/urls.py19
-rw-r--r--lenovo/thinkcloud/samples/views.py29
-rw-r--r--lenovo/thinkcloud/settings.py141
-rw-r--r--lenovo/thinkcloud/swagger/__init__.py13
-rw-r--r--lenovo/thinkcloud/swagger/tests.py31
-rw-r--r--lenovo/thinkcloud/swagger/urls.py26
-rw-r--r--lenovo/thinkcloud/swagger/views.py59
-rw-r--r--lenovo/thinkcloud/urls.py68
-rw-r--r--lenovo/thinkcloud/wsgi.py21
-rw-r--r--lenovo/tox.ini48
-rw-r--r--ocata/docker/Dockerfile23
-rw-r--r--ocata/ocata/__init__.py6
-rw-r--r--ocata/ocata/celery.py39
-rw-r--r--ocata/ocata/urls.py5
-rw-r--r--ocata/ocata/vesagent/event_domain/fault_vm.py313
-rw-r--r--ocata/ocata/vesagent/event_domain/tests_fault_vm.py284
-rw-r--r--ocata/ocata/vesagent/tasks.py193
-rw-r--r--ocata/ocata/vesagent/tests.py193
-rw-r--r--ocata/ocata/vesagent/tests_tasks.py153
-rw-r--r--ocata/ocata/vesagent/tests_vespublish.py54
-rw-r--r--ocata/ocata/vesagent/vesagent_ctrl.py448
-rw-r--r--ocata/ocata/vesagent/vespublish.py51
-rw-r--r--ocata/requirements.txt8
-rwxr-xr-xocata/run.sh4
-rw-r--r--ocata/test-requirements.txt5
-rw-r--r--pike/docker/Dockerfile24
-rw-r--r--pike/requirements.txt6
-rw-r--r--pike/test-requirements.txt5
-rw-r--r--pom.xml1
-rw-r--r--starlingx/docker/Dockerfile24
-rw-r--r--starlingx/requirements.txt6
-rw-r--r--starlingx/test-requirements.txt6
-rw-r--r--starlingx/tox.ini4
93 files changed, 5840 insertions, 1793 deletions
diff --git a/fcaps/docker/Dockerfile b/fcaps/docker/Dockerfile
index 8a3c6b2f..6484905b 100644
--- a/fcaps/docker/Dockerfile
+++ b/fcaps/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:2
+FROM python:2-slim
ARG HTTP_PROXY=${HTTP_PROXY}
ARG HTTPS_PROXY=${HTTPS_PROXY}
@@ -20,7 +20,7 @@ RUN groupadd -r onap && useradd -r -g onap onap
# COPY ./ /opt/fcaps/
RUN apt-get update && \
- apt-get install -y memcached && \
+ apt-get install -y memcached wget unzip gcc && \
apt-get install -y unzip && \
cd /opt/ && \
wget -O multicloud-openstack-fcaps.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-fcaps&e=zip&v=1.3.0-SNAPSHOT" && \
@@ -28,6 +28,8 @@ RUN apt-get update && \
chmod +x /opt/fcaps/*.sh && \
rm -f multicloud-openstack-fcaps.zip && \
pip install -r /opt/fcaps/requirements.txt && \
+ apt-get --purge remove -y wget unzip gcc && \
+ apt-get -y autoremove && \
chown onap:onap /opt/fcaps -R
USER onap
diff --git a/fcaps/requirements.txt b/fcaps/requirements.txt
index 15b2190e..6a81e502 100644
--- a/fcaps/requirements.txt
+++ b/fcaps/requirements.txt
@@ -15,9 +15,9 @@ python-memcached
uwsgi
# for unit test
-coverage==4.2
-mock==2.0.0
-unittest_xml_reporting==1.12.0
+# coverage==4.2
+# mock==2.0.0
+# unittest_xml_reporting==1.12.0
# for onap logging
onappylog>=1.0.8
diff --git a/fcaps/test-requirements.txt b/fcaps/test-requirements.txt
index 97044b5c..7cfc85fb 100644
--- a/fcaps/test-requirements.txt
+++ b/fcaps/test-requirements.txt
@@ -1 +1,6 @@
+# Add for unit test
+coverage==4.2
+mock==2.0.0
+unittest_xml_reporting==1.12.0
pylint # GPLv2
+
diff --git a/lenovo/.gitignore b/lenovo/.gitignore
new file mode 100644
index 00000000..c3bfb454
--- /dev/null
+++ b/lenovo/.gitignore
@@ -0,0 +1,27 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.project
+.classpath
+.settings/
+.checkstyle
+target/
+logs/*.log
+*.pyc
+.tox
+.coverage
+htmlcov/
+coverage.xml
+test-reports/
+
diff --git a/lenovo/README.md b/lenovo/README.md
new file mode 100644
index 00000000..3751a585
--- /dev/null
+++ b/lenovo/README.md
@@ -0,0 +1,15 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Micro service of MultiCloud plugin for thinkcloud.
diff --git a/lenovo/assembly.xml b/lenovo/assembly.xml
new file mode 100644
index 00000000..97ee7411
--- /dev/null
+++ b/lenovo/assembly.xml
@@ -0,0 +1,77 @@
+<!--
+ Copyright (c) 2017-2018 Lenovo Systems, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>thinkcloud</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>thinkcloud</directory>
+ <outputDirectory>/thinkcloud</outputDirectory>
+ <includes>
+ <include>**/*.py</include>
+ <include>**/*.json</include>
+ <include>**/*.xml</include>
+ <include>**/*.wsdl</include>
+ <include>**/*.xsd</include>
+ <include>**/*.bpel</include>
+ <include>**/*.yml</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>../share</directory>
+ <outputDirectory>/lib/share</outputDirectory>
+ <includes>
+ <include>**/*.py</include>
+ <include>**/*.json</include>
+ <include>**/*.xml</include>
+ <include>**/*.wsdl</include>
+ <include>**/*.xsd</include>
+ <include>**/*.bpel</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>logs</directory>
+ <outputDirectory>/logs</outputDirectory>
+ <includes>
+ <include>*.txt</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>docker</directory>
+ <outputDirectory>/docker</outputDirectory>
+ <includes>
+ <include>*.sh</include>
+ <include>Dockerfile</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>.</directory>
+ <outputDirectory>/</outputDirectory>
+ <includes>
+ <include>*.py</include>
+ <include>*.txt</include>
+ <include>*.sh</include>
+ <include>*.ini</include>
+ <include>*.md</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+ <baseDirectory>thinkcloud</baseDirectory>
+</assembly>
diff --git a/lenovo/docker/Dockerfile b/lenovo/docker/Dockerfile
new file mode 100644
index 00000000..2a619128
--- /dev/null
+++ b/lenovo/docker/Dockerfile
@@ -0,0 +1,50 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM python:2.7-slim
+
+ARG HTTP_PROXY=${HTTP_PROXY}
+ARG HTTPS_PROXY=${HTTPS_PROXY}
+
+ENV http_proxy $HTTP_PROXY
+ENV https_proxy $HTTPS_PROXY
+
+ENV MSB_ADDR "127.0.0.1"
+ENV MSB_PORT "80"
+ENV AAI_ADDR "aai.api.simpledemo.openecomp.org"
+ENV AAI_PORT "8443"
+ENV AAI_SERVICE_URL ""
+ENV AAI_SCHEMA_VERSION "v13"
+ENV AAI_USERNAME "AAI"
+ENV AAI_PASSWORD "AAI"
+
+EXPOSE 9010
+
+RUN groupadd -r onap && useradd -r -g onap onap
+
+RUN apt-get update && apt-get install -y memcached wget unzip gcc && \
+ cd /opt/ && \
+ wget -O multicloud-openstack-thinkcloud.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-lenovo&e=zip&v=1.3.0-SNAPSHOT" && \
+ unzip -q -o -B multicloud-openstack-thinkcloud.zip && \
+ chmod +x /opt/thinkcloud/*.sh &&\
+ rm -f multicloud-openstack-thinkcloud.zip &&\
+ pip install -r /opt/thinkcloud/requirements.txt &&\
+ apt-get --purge remove -y wget unzip gcc && \
+ apt-get -y autoremove && \
+ chown onap:onap /opt/thinkcloud -R
+
+USER onap
+
+WORKDIR /opt/thinkcloud
+CMD /bin/sh -c /opt/thinkcloud/run.sh
diff --git a/lenovo/docker/build_image.sh b/lenovo/docker/build_image.sh
new file mode 100755
index 00000000..eca84326
--- /dev/null
+++ b/lenovo/docker/build_image.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DIRNAME=`dirname $0`
+DOCKER_BUILD_DIR=`cd $DIRNAME/; pwd`
+echo "DOCKER_BUILD_DIR=${DOCKER_BUILD_DIR}"
+cd ${DOCKER_BUILD_DIR}
+
+BUILD_ARGS="--no-cache"
+VERSION="1.3.0-SNAPSHOT"
+STAGING="1.3.0-STAGING"
+OS_VERSION="thinkcloud"
+IMAGE_NAME="nexus3.onap.org:10003/onap/multicloud/openstack-${OS_VERSION}"
+
+if [ $HTTP_PROXY ]; then
+ BUILD_ARGS+=" --build-arg HTTP_PROXY=${HTTP_PROXY}"
+fi
+if [ $HTTPS_PROXY ]; then
+ BUILD_ARGS+=" --build-arg HTTPS_PROXY=${HTTPS_PROXY}"
+fi
+
+function build_image {
+ docker build ${BUILD_ARGS} -t ${IMAGE_NAME}:${VERSION} -t ${IMAGE_NAME}:latest -t ${IMAGE_NAME}:${STAGING} .
+}
+
+function push_image {
+ docker push ${IMAGE_NAME}:${VERSION}
+ docker push ${IMAGE_NAME}:latest
+ docker push ${IMAGE_NAME}:${STAGING}
+}
+
+build_image
+push_image
diff --git a/lenovo/initialize.sh b/lenovo/initialize.sh
new file mode 100755
index 00000000..79859858
--- /dev/null
+++ b/lenovo/initialize.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+pip install -r requirements.txt
diff --git a/lenovo/logs/empty.txt b/lenovo/logs/empty.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/lenovo/logs/empty.txt
diff --git a/lenovo/manage.py b/lenovo/manage.py
new file mode 100644
index 00000000..7415ab9b
--- /dev/null
+++ b/lenovo/manage.py
@@ -0,0 +1,22 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "thinkcloud.settings")
+
+if __name__ == "__main__":
+ from django.core.management import execute_from_command_line
+ execute_from_command_line(sys.argv)
diff --git a/lenovo/mvn-phase-script.sh b/lenovo/mvn-phase-script.sh
new file mode 100755
index 00000000..7e8b3ee4
--- /dev/null
+++ b/lenovo/mvn-phase-script.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+set -e
+
+echo "running script: [$0] for module [$1] at stage [$2]"
+
+export SETTINGS_FILE=${SETTINGS_FILE:-$HOME/.m2/settings.xml}
+MVN_PROJECT_MODULEID="$1"
+MVN_PHASE="$2"
+
+
+FQDN="${MVN_PROJECT_GROUPID}.${MVN_PROJECT_ARTIFACTID}"
+if [ "$MVN_PROJECT_MODULEID" == "__" ]; then
+ MVN_PROJECT_MODULEID=""
+fi
+
+if [ -z "$WORKSPACE" ]; then
+ WORKSPACE=$(pwd)
+fi
+
+# mvn phase in life cycle
+MVN_PHASE="$2"
+
+
+echo "MVN_PROJECT_MODULEID is [$MVN_PROJECT_MODULEID]"
+echo "MVN_PHASE is [$MVN_PHASE]"
+echo "MVN_PROJECT_GROUPID is [$MVN_PROJECT_GROUPID]"
+echo "MVN_PROJECT_ARTIFACTID is [$MVN_PROJECT_ARTIFACTID]"
+echo "MVN_PROJECT_VERSION is [$MVN_PROJECT_VERSION]"
+
+run_tox_test()
+{
+ set -x
+ echo $PWD
+ CURDIR=$(pwd)
+ TOXINIS=$(find . -name "tox.ini")
+ cd ..
+ for TOXINI in "${TOXINIS[@]}"; do
+ DIR=$(echo "$TOXINI" | rev | cut -f2- -d'/' | rev)
+ cd "${CURDIR}/${DIR}"
+ rm -rf ./venv-tox ./.tox
+ virtualenv ./venv-tox
+ source ./venv-tox/bin/activate
+ pip install --upgrade pip
+ pip install --upgrade tox argparse
+ pip freeze
+ cd ${CURDIR}
+ tox
+ deactivate
+ cd ..
+ rm -rf ./venv-tox ./.tox
+ done
+}
+
+
+case $MVN_PHASE in
+clean)
+ echo "==> clean phase script"
+ rm -rf ./venv-*
+ ;;
+test)
+ echo "==> test phase script"
+ run_tox_test
+ ;;
+*)
+ echo "==> unprocessed phase"
+ ;;
+esac
+
diff --git a/lenovo/pom.xml b/lenovo/pom.xml
new file mode 100644
index 00000000..2e586c4e
--- /dev/null
+++ b/lenovo/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+<!--
+ Copyright (c) 2017-2018 Lenovo Systems, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.onap.oparent</groupId>
+ <artifactId>oparent</artifactId>
+ <version>1.2.0</version>
+ <relativePath>../oparent</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.onap.multicloud.openstack</groupId>
+ <artifactId>multicloud-openstack-lenovo</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <name>multicloud-openstack-lenovo</name>
+ <description>multicloud for openstack lenovo thinkcloud</description>
+ <properties>
+ <encoding>UTF-8</encoding>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <nexusproxy>https://nexus.onap.org</nexusproxy>
+ <sonar.sources>.</sonar.sources>
+ <sonar.junit.reportsPath>xunit-results.xml</sonar.junit.reportsPath>
+ <sonar.python.coverage.reportPath>coverage.xml</sonar.python.coverage.reportPath>
+ <sonar.language>py</sonar.language>
+ <sonar.pluginName>Python</sonar.pluginName>
+ <sonar.inclusions>**/*.py</sonar.inclusions>
+ <sonar.exclusions>**/venv-tox/**,**/.tox/**, **/tests/**,setup.py</sonar.exclusions>
+ </properties>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <configuration>
+ <executable>${project.basedir}/mvn-phase-script.sh</executable>
+ <environmentVariables>
+ <!-- make mvn properties as env for our script -->
+ <MVN_PROJECT_GROUPID>${project.groupId}</MVN_PROJECT_GROUPID>
+ <MVN_PROJECT_ARTIFACTID>${project.artifactId}</MVN_PROJECT_ARTIFACTID>
+ <MVN_PROJECT_VERSION>${project.version}</MVN_PROJECT_VERSION>
+ </environmentVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <id>clean phase script</id>
+ <phase>clean</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <arguments>
+ <argument>__</argument>
+ <argument>clean</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ <execution>
+ <id>test script</id>
+ <phase>test</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <arguments>
+ <argument>__</argument>
+ <argument>test</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/lenovo/requirements.txt b/lenovo/requirements.txt
new file mode 100644
index 00000000..ffeb9821
--- /dev/null
+++ b/lenovo/requirements.txt
@@ -0,0 +1,41 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# rest framework
+Django==1.9.6
+djangorestframework==3.3.3
+
+# for call rest api
+httplib2==0.9.2
+
+# for call openstack auth and transport api
+keystoneauth1==2.18.0
+
+#python-memcached
+python-memcached
+
+#uwsgi for parallel processing
+uwsgi
+
+# for unit test
+#coverage==4.2
+#mock==2.0.0
+#unittest_xml_reporting==1.12.0
+
+# for onap logging
+onappylog>=1.0.8
+
+# for background tasks
+#celery >= 4.0
+
diff --git a/lenovo/run.sh b/lenovo/run.sh
new file mode 100755
index 00000000..b3d98ef3
--- /dev/null
+++ b/lenovo/run.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+memcached -d -m 2048 -u root -c 1024 -p 11211 -P /tmp/memcached1.pid
+export PYTHONPATH=lib/share
+
+uwsgi --http :9010 --module thinkcloud.wsgi --master --processes 4
+
+logDir="/var/log/onap/multicloud/openstack/lenovo"
+if [ ! -x $logDir ]; then
+ mkdir -p $logDir
+fi
+while [ ! -f $logDir/thinkcloud.log ]; do
+ sleep 1
+done
+
+tail -F $logDir/thinkcloud.log \ No newline at end of file
diff --git a/lenovo/stop.sh b/lenovo/stop.sh
new file mode 100755
index 00000000..f43fab1b
--- /dev/null
+++ b/lenovo/stop.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#ps auxww | grep 'manage.py runserver 0.0.0.0:9010' | awk '{print $2}' | xargs kill -9
+ps auxww |grep 'uwsgi --http :9010 --module thinkcloud.wsgi --master' |awk '{print $2}' |xargs kill -9
+ps auxww | grep 'memcached -d -m 2048 -u root -c 1024 -p 11211 -P /tmp/memcached1.pid' | awk '{print $2}' | xargs kill -9
diff --git a/lenovo/test-requirements.txt b/lenovo/test-requirements.txt
new file mode 100644
index 00000000..cc3059e2
--- /dev/null
+++ b/lenovo/test-requirements.txt
@@ -0,0 +1,6 @@
+# for unit test
+coverage==4.2
+mock==2.0.0
+unittest_xml_reporting==1.12.0
+
+pylint # GPLv2
diff --git a/lenovo/thinkcloud/__init__.py b/lenovo/thinkcloud/__init__.py
new file mode 100644
index 00000000..b4536b4d
--- /dev/null
+++ b/lenovo/thinkcloud/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, unicode_literals
+
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
diff --git a/ocata/ocata/vesagent/__init__.py b/lenovo/thinkcloud/extensions/__init__.py
index 5f8b0d18..5b09b2fd 100644
--- a/ocata/ocata/vesagent/__init__.py
+++ b/lenovo/thinkcloud/extensions/__init__.py
@@ -1,6 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/ocata/ocata/vesagent/event_domain/__init__.py b/lenovo/thinkcloud/extensions/tests/__init__.py
index 5f8b0d18..5b09b2fd 100644
--- a/ocata/ocata/vesagent/event_domain/__init__.py
+++ b/lenovo/thinkcloud/extensions/tests/__init__.py
@@ -1,6 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/lenovo/thinkcloud/extensions/tests/test_extensions.py b/lenovo/thinkcloud/extensions/tests/test_extensions.py
new file mode 100644
index 00000000..4bc64aad
--- /dev/null
+++ b/lenovo/thinkcloud/extensions/tests/test_extensions.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.test import Client
+from rest_framework import status
+import unittest
+
+
+class TestExtensions(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ def test_get_extensions_info(self):
+ cloud_owner = "lenovo-hudson-dc"
+ cloud_region_id = "RegionOne"
+ vimid = cloud_owner + "_" + cloud_region_id
+
+ response = self.client.get(
+ "/api/multicloud-thinkcloud/v0/" + vimid + "/extensions/")
+ json_content = response.json()
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEquals(4, len(json_content.keys()))
+
+ self.assertEquals(cloud_owner, json_content["cloud-owner"])
+ self.assertEquals(cloud_region_id, json_content["cloud-region-id"])
+ self.assertEquals(vimid, json_content["vimid"])
diff --git a/lenovo/thinkcloud/extensions/urls.py b/lenovo/thinkcloud/extensions/urls.py
new file mode 100644
index 00000000..e88c607d
--- /dev/null
+++ b/lenovo/thinkcloud/extensions/urls.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from thinkcloud.extensions.views import extensions
+
+urlpatterns = [
+ url(r'^sions/?$', extensions.Extensions.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/extensions/urlsV1.py b/lenovo/thinkcloud/extensions/urlsV1.py
new file mode 100644
index 00000000..f6c23770
--- /dev/null
+++ b/lenovo/thinkcloud/extensions/urlsV1.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from thinkcloud.extensions.views import extensions
+
+urlpatterns = [
+ url(r'^sions/?$', extensions.APIv1Extensions.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/extensions/views/__init__.py b/lenovo/thinkcloud/extensions/views/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/extensions/views/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/extensions/views/extensions.py b/lenovo/thinkcloud/extensions/views/extensions.py
new file mode 100644
index 00000000..056ad5a3
--- /dev/null
+++ b/lenovo/thinkcloud/extensions/views/extensions.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from common.msapi import extsys
+from django.conf import settings
+from newton_base.extensions import extensions as newton_extensions
+
+logger = logging.getLogger(__name__)
+
+# DEBUG=True
+
+
+class Extensions(newton_extensions.Extensions):
+
+ def __init__(self):
+ self._logger = logger
+ self.proxy_prefix = settings.MULTICLOUD_PREFIX
+
+
+class APIv1Extensions(Extensions):
+
+ def __init__(self):
+ self._logger = logger
+ self.proxy_prefix = settings.MULTICLOUD_API_V1_PREFIX
+
+ def get(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Extensions, self).get(request, vimid)
diff --git a/lenovo/thinkcloud/middleware.py b/lenovo/thinkcloud/middleware.py
new file mode 100644
index 00000000..33f04267
--- /dev/null
+++ b/lenovo/thinkcloud/middleware.py
@@ -0,0 +1,64 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import uuid
+from django.conf import settings
+from onaplogging.mdcContext import MDC
+
+FORWARDED_FOR_FIELDS = ["HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_HOST",
+ "HTTP_X_FORWARDED_SERVER"]
+
+
+class LogContextMiddleware(object):
+
+ # the last IP behind multiple proxies, if no exist proxies
+ # get local host ip.
+ def _getLastIp(self, request):
+
+ ip = ""
+ try:
+ for field in FORWARDED_FOR_FIELDS:
+ if field in request.META:
+ if ',' in request.META[field]:
+ parts = request.META[field].split(',')
+ ip = parts[-1].strip().split(":")[0]
+ else:
+ ip = request.META[field].split(":")[0]
+
+ if ip == "":
+ ip = request.META.get("HTTP_HOST").split(":")[0]
+
+ except Exception:
+ pass
+
+ return ip
+
+ def process_request(self, request):
+ # fetch propageted Id from other component. if do not fetch id,
+ # generate one.
+ ReqeustID = request.META.get("HTTP_X_TRANSACTIONID", None)
+ if ReqeustID is None:
+ ReqeustID = str(uuid.uuid3(uuid.NAMESPACE_URL, settings.MULTIVIM_VERSION))
+ MDC.put("requestID", ReqeustID)
+ # generate the reqeust id
+ InvocationID = str(uuid.uuid4())
+ MDC.put("invocationID", InvocationID)
+ MDC.put("serviceName", settings.MULTIVIM_VERSION)
+ MDC.put("serviceIP", self._getLastIp(request))
+ return None
+
+ def process_response(self, request, response):
+
+ MDC.clear()
+ return response
diff --git a/lenovo/thinkcloud/proxy/__init__.py b/lenovo/thinkcloud/proxy/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/proxy/tests/__init__.py b/lenovo/thinkcloud/proxy/tests/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/proxy/tests/test_identity_proxy.py b/lenovo/thinkcloud/proxy/tests/test_identity_proxy.py
new file mode 100644
index 00000000..66d924a5
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/tests/test_identity_proxy.py
@@ -0,0 +1,735 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+
+import mock
+import unittest
+
+from django.test import Client
+from rest_framework import status
+
+from newton_base.util import VimDriverUtils
+from newton_base.tests import mock_info
+
+mock_viminfo = {
+ "createTime": "2017-04-01 02:22:27",
+ "domain": "Default",
+ "name": "TiS_R4",
+ "password": "admin",
+ "tenant": "admin",
+ "type": "openstack",
+ "url": "http://128.224.180.14:5000/v3",
+ "userName": "admin",
+ "vendor": "Lenovo",
+ "version": "thinkcloud",
+ "vimId": "lenovo-hudson-dc_RegionOne",
+ 'cloud_owner': 'lenovo-hudson-dc',
+ 'cloud_region_id': 'RegionOne',
+ 'cloud_extra_info': '',
+ 'insecure': 'True',
+}
+
+mock_token_id = "1a62b3971d774404a504c5d9a3e506e3"
+
+mock_catalog_response = {
+ "catalog": [
+ {
+ "id": "99aefcc82a9246f98f8c281e61ffc754",
+ "endpoints": [
+ {
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:9696",
+ "id": "39583c1508ad4b71b380570a745ee10a",
+ "interface": "public",
+ "region_id": "RegionOne"
+ },
+ {
+ "url": "http://192.168.204.2:9696",
+ "region": "RegionOne",
+ "id": "37e8d07ba24e4b8f93490c9daaba06e2",
+ "interface": "internal",
+ "region_id": "RegionOne"
+ },
+ {
+ "interface": "admin",
+ "id": "7eee4ca98d444b1abb00a50d4b89373f",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:9696"
+ }
+ ],
+ "name": "neutron",
+ "type": "network"
+ },
+ {
+ "endpoints": [
+ {
+ "interface": "public",
+ "id": "10496738fa374295a4a88a63b81a1589",
+ "region_id": "RegionOne",
+ "url": "http://128.224.180.14:8777",
+ "region": "RegionOne"
+ },
+ {
+ "id": "02dcb8c0bd464c4489fa0a0c9f28571f",
+ "region_id": "RegionOne",
+ "interface": "internal",
+ "url": "http://192.168.204.2:8777",
+ "region": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "8a73b0d3743b4e78b87614690f6e97fe",
+ "interface": "admin",
+ "url": "http://192.168.204.2:8777",
+ "region": "RegionOne"
+ }
+ ],
+ "id": "d131054da83f4c93833799747a0f4709",
+ "name": "ceilometer",
+ "type": "metering"
+ },
+ {
+ "type": "volumev2",
+ "name": "cinderv2",
+ "endpoints": [
+ {
+ "id": "35a67ad36f0447d19c9662babf7cf609",
+ "interface": "public",
+ "region_id": "RegionOne",
+ "url": "http://128.224.180.14:8776/v2/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v2/fcca3cc49d5e42caae15459e27103efc",
+ "id": "c6ea42052268420fa2c8d351ee68c922",
+ "interface": "internal",
+ "region_id": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "91cb24853dc3450d847b0c286a2e44ea",
+ "interface": "admin",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v2/fcca3cc49d5e42caae15459e27103efc"
+ }
+ ],
+ "id": "40440057102440739c30be10a66bc5d1"
+ },
+ {
+ "name": "heat",
+ "type": "orchestration",
+ "id": "35300cce88db4bd4bb5a72ffe3b88b00",
+ "endpoints": [
+ {
+ "id": "58999d7b4a94439089ecfb2aca2d7f6c",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:8004/v1/fcca3cc49d5e42caae15459e27103efc"
+ },
+ {
+ "url": "http://192.168.204.2:8004/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "interface": "internal",
+ "id": "1e0ee1a2aef84802b921d422372a567e",
+ "region_id": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8004/v1/fcca3cc49d5e42caae15459e27103efc",
+ "id": "17661bf4859741b8a43a461dedad1871",
+ "region_id": "RegionOne",
+ "interface": "admin"
+ }
+ ]
+ },
+ {
+ "id": "08dc6912aea64c01925012c8a6df250a",
+ "endpoints": [
+ {
+ "id": "02792c4eed77486083f9b2e52d7b94b0",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:5000/v3"
+ },
+ {
+ "id": "b6d5cad394b94309ae40d8de88059c5f",
+ "region_id": "RegionOne",
+ "interface": "internal",
+ "url": "http://192.168.204.2:5000/v3",
+ "region": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:35357/v3",
+ "region_id": "RegionOne",
+ "id": "1f18e2b7c6a34493b86853b65917888e",
+ "interface": "admin"
+ }
+ ],
+ "type": "identity",
+ "name": "keystone"
+ },
+ {
+ "name": "vim",
+ "type": "nfv",
+ "endpoints": [
+ {
+ "url": "http://128.224.180.14:4545",
+ "region": "RegionOne",
+ "id": "b33e317345e4480ab0786e4960995ec9",
+ "interface": "public",
+ "region_id": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:4545",
+ "interface": "internal",
+ "id": "03c85828d5bf432ab04831aa65ac9c52",
+ "region_id": "RegionOne"
+ },
+ {
+ "id": "067983abb061476cb53a9e23a740d98f",
+ "region_id": "RegionOne",
+ "interface": "admin",
+ "url": "http://192.168.204.2:4545",
+ "region": "RegionOne"
+ }
+ ],
+ "id": "01636c856fc84988b38b9117eb4a8021"
+ },
+ {
+ "name": "aodh",
+ "type": "alarming",
+ "id": "eb269151d0e44744a5b5449657bdc61c",
+ "endpoints": [
+ {
+ "id": "5bfc6c056e0244c493642eb82f6aaa11",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "url": "http://128.224.180.14:8042",
+ "region": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8042",
+ "region_id": "RegionOne",
+ "id": "ad69c7f76dce4089a195b9221ddbfb44",
+ "interface": "internal"
+ },
+ {
+ "interface": "admin",
+ "id": "3e8fcdfa7bcb40b0ae33c282adfcc9ff",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8042"
+ }
+ ]
+ },
+ {
+ "name": "sysinv",
+ "type": "platform",
+ "endpoints": [
+ {
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:6385/v1",
+ "interface": "public",
+ "id": "ba4ba8104590421b84672306c7e0e1f1",
+ "region_id": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:6385/v1",
+ "interface": "internal",
+ "id": "a1cba34b163f496ab1acd6e9b51e39a2",
+ "region_id": "RegionOne"
+ },
+ {
+ "url": "http://192.168.204.2:6385/v1",
+ "region": "RegionOne",
+ "id": "7c171210a2c841a6a52a5713e316d6fc",
+ "interface": "admin",
+ "region_id": "RegionOne"
+ }
+ ],
+ "id": "256bbad671f946fea543e6bd71f98875"
+ },
+ {
+ "id": "e84665dcce814c05b4c5084964547534",
+ "endpoints": [
+ {
+ "url": "http://128.224.180.14:8000/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "region_id": "RegionOne",
+ "id": "b2ed1a23dc6944bea129c20861e0286a",
+ "interface": "public"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8000/v1/fcca3cc49d5e42caae15459e27103efc",
+ "interface": "internal",
+ "id": "c4df7c6bc15646848eff35caf6ffea8e",
+ "region_id": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "61b3dabb761443a89ab549f437c05ab0",
+ "interface": "admin",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8000/v1/fcca3cc49d5e42caae15459e27103efc"
+ }
+ ],
+ "name": "heat-cfn",
+ "type": "cloudformation"
+ },
+ {
+ "id": "823024424a014981a3721229491c0b1a",
+ "endpoints": [
+ {
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:8776/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region_id": "RegionOne",
+ "id": "4a52e4e54ff440789f9a797919c4a0f2",
+ "interface": "public"
+ },
+ {
+ "url": "http://192.168.204.2:8776/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "id": "d4f9a84476524a39844f0fce63f1022e",
+ "region_id": "RegionOne",
+ "interface": "internal"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v1/fcca3cc49d5e42caae15459e27103efc",
+ "interface": "admin",
+ "id": "81bf3810a8cc4697b68c6e93b5b8fe1f",
+ "region_id": "RegionOne"
+ }
+ ],
+ "type": "volume",
+ "name": "cinder"
+ },
+ {
+ "name": "glance",
+ "type": "image",
+ "endpoints": [
+ {
+ "id": "bd930aba961946cfb1401bada56d55e3",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:9292"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:9292",
+ "id": "c11da585f0b141b99d1e18bb9a607beb",
+ "region_id": "RegionOne",
+ "interface": "internal"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:9292",
+ "id": "31b26c625a6a4fc7910dc5935155996e",
+ "interface": "admin",
+ "region_id": "RegionOne"
+ }
+ ],
+ "id": "3b78cf039bc54d1bbb99ab3a4be15ef1"
+ },
+ {
+ "id": "b8701374bf254de1beee8a2c9ecc6b33",
+ "endpoints": [
+ {
+ "region_id": "RegionOne",
+ "id": "f7407f330c8b4577b1d377d3fab9c2f8",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:15491"
+ },
+ {
+ "url": "http://192.168.204.2:5491",
+ "region": "RegionOne",
+ "interface": "internal",
+ "id": "0b37ce31a32f4b6fa5e1aa0d6c20680f",
+ "region_id": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "7b87ea72adf245e1991e9e0df29b7ea9",
+ "interface": "admin",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:5491"
+ }
+ ],
+ "type": "patching",
+ "name": "patching"
+ },
+ {
+ "id": "0ec0923a58f04ffeb6fced3bbc5c0947",
+ "endpoints": [
+ {
+ "url": "http://128.224.180.14:8774/v2.1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "id": "13168b12da17451fb39630de67db168f",
+ "region_id": "RegionOne",
+ "interface": "public"
+ },
+ {
+ "id": "22dd6a44209f42d986b82e3aa6535f82",
+ "interface": "internal",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8774/v2.1/fcca3cc49d5e42caae15459e27103efc"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8774/v2.1/fcca3cc49d5e42caae15459e27103efc",
+ "id": "552a991ae501492f841c1b6e2ff38fc5",
+ "region_id": "RegionOne",
+ "interface": "admin"
+ }
+ ],
+ "type": "compute",
+ "name": "nova"
+ },
+ {
+ "id": "50b219650f1049b097b3f14e8c70cdf8",
+ "endpoints": [
+ {
+ "interface": "public",
+ "id": "5a4276cd6e4d43e883cf8640d4e13f7d",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:8776/v3/fcca3cc49d5e42caae15459e27103efc"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v3/fcca3cc49d5e42caae15459e27103efc",
+ "region_id": "RegionOne",
+ "id": "c796df3ca5a84fc18db5b43a55283953",
+ "interface": "internal"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "cf55c2b34d0049ba835a2e48b9ad0e2e",
+ "interface": "admin",
+ "url": "http://192.168.204.2:8776/v3/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne"
+ }
+ ],
+ "type": "volumev3",
+ "name": "cinderv3"
+ }
+ ],
+}
+
+mock_auth_state = {
+ "body": {
+ "token": {
+ "is_domain": "false",
+ "expires_at": "2017-08-27T14:19:15.000000Z",
+ "issued_at": "2017-08-27T13:19:15.000000Z",
+ "roles": [
+ {
+ "id": "9fe2ff9ee4384b1894a90878d3e92bab",
+ "name": "_member_"
+ },
+ {
+ "id": "b86a7e02935844b899d3d326f83c1b1f",
+ "name": "admin"
+ },
+ {
+ "name": "heat_stack_owner",
+ "id": "7de502236e954c8282de32e773fc052e"
+ }
+ ],
+ "methods": [
+ "password"
+ ],
+ "catalog": mock_catalog_response['catalog'],
+ "project": {
+ "name": "admin",
+ "id": "fcca3cc49d5e42caae15459e27103efc",
+ "domain": {
+ "id": "default",
+ "name": "Default"
+ }
+ },
+ "user": {
+ "name": "admin",
+ "id": "9efb043c7629497a8028d7325ca1afb0",
+ "domain": {
+ "id": "default",
+ "name": "Default"
+ }
+ },
+ "audit_ids": [
+ "_ZWT10DtSZKRXIvIcxun7w"
+ ]
+ }
+ },
+ "auth_token": mock_token_id
+}
+
+
+class TestIdentityService(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_auth_state')
+ @mock.patch.object(VimDriverUtils, 'update_token_cache')
+ def test_token(self, mock_update_token_cache, mock_get_auth_state, mock_get_session, mock_get_vim_info):
+ '''
+ test API: get token
+ :param mock_update_token_cache:
+ :param mock_get_auth_state:
+ :param mock_get_session:
+ :param mock_get_vim_info:
+ :return:
+ '''
+
+ # mock VimDriverUtils APIs
+ mock_session_specs = ["get"]
+ mock_session_get_response = {'status': 200}
+ mock_session = mock.Mock(name='mock_session', spec=mock_session_specs)
+ mock_session.get.return_value = mock_session_get_response
+
+ mock_get_vim_info.return_value = mock_viminfo
+ mock_get_session.return_value = mock_session
+ mock_get_auth_state.return_value = json.dumps(mock_auth_state)
+ mock_update_token_cache.return_value = mock_token_id
+
+ # simulate client to make the request
+ data = {}
+ response = self.client.post("/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/identity/v3/auth/tokens", data=data, format='json')
+ self.failUnlessEqual(status.HTTP_201_CREATED, response.status_code)
+ context = response.json()
+
+ self.assertTrue(response['X-Subject-Token'] == mock_token_id)
+ self.assertTrue(context['token']['catalog'] is not None)
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_auth_state')
+ @mock.patch.object(VimDriverUtils, 'update_token_cache')
+ def test_tokensV2(self, mock_update_token_cache, mock_get_auth_state,
+ mock_get_session, mock_get_vim_info):
+ '''
+ test API: get token
+ :param mock_update_token_cache:
+ :param mock_get_auth_state:
+ :param mock_get_session:
+ :param mock_get_vim_info:
+ :return:
+ '''
+
+ # mock VimDriverUtils APIs
+ mock_session_specs = ["get"]
+ mock_session_get_response = {'status': 200}
+ mock_session = mock.Mock(name='mock_session',
+ spec=mock_session_specs)
+ mock_session.get.return_value = mock_session_get_response
+
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = mock_session
+ mock_get_auth_state.return_value = json.dumps(mock_auth_state)
+ mock_update_token_cache.return_value = mock_info.MOCK_TOKEN_ID
+
+ # simulate client to make the request
+ data = {}
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/identity/v2.0/tokens",
+ data=data, format='json')
+ self.failUnlessEqual(status.HTTP_200_OK,
+ response.status_code)
+ context = response.json()
+
+ self.assertIsNotNone(context['access']['token'])
+ self.assertEqual(mock_info.MOCK_TOKEN_ID,
+ context['access']['token']["id"])
+ self.assertIsNotNone(context['access']['serviceCatalog'])
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_auth_state')
+ @mock.patch.object(VimDriverUtils, 'update_token_cache')
+ def test_token_with_tenantname(self, mock_update_token_cache, mock_get_auth_state,
+ mock_get_session, mock_get_vim_info):
+ '''
+ test API: get token
+ :param mock_update_token_cache:
+ :param mock_get_auth_state:
+ :param mock_get_session:
+ :param mock_get_vim_info:
+ :return:
+ '''
+
+ # mock VimDriverUtils APIs
+ mock_session_specs = ["get"]
+ mock_session_get_response = {'status': 200}
+ mock_session = mock.Mock(name='mock_session',
+ spec=mock_session_specs)
+ mock_session.get.return_value = mock_session_get_response
+
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = mock_session
+ mock_get_auth_state.return_value = json.dumps(mock_auth_state)
+ mock_update_token_cache.return_value = mock_info.MOCK_TOKEN_ID
+
+ # simulate client to make the request
+ token_data = {
+ "auth": {
+ "identity": {
+ "methods": ["password"],
+ "password": {
+ "user": {
+ "name": "demo",
+ "domain": {"name": "Default"},
+ "password": "demo"
+ }
+ }
+ },
+ "scope": {
+ "project": {
+ "domain": {"name": "Default"},
+ "name": "Integration"
+ }
+ }
+ }
+ }
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/identity/v3/auth/tokens",
+ data=json.dumps(token_data), content_type='application/json')
+ self.failUnlessEqual(status.HTTP_201_CREATED,
+ response.status_code)
+ context = response.json()
+
+ self.assertEqual(mock_info.MOCK_TOKEN_ID,
+ response['X-Subject-Token'])
+ self.assertIsNotNone(context['token']['catalog'])
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_auth_state')
+ @mock.patch.object(VimDriverUtils, 'update_token_cache')
+ def test_tokensV2_with_tenantname(self, mock_update_token_cache, mock_get_auth_state,
+ mock_get_session, mock_get_vim_info):
+ '''
+ test API: get token
+ :param mock_update_token_cache:
+ :param mock_get_auth_state:
+ :param mock_get_session:
+ :param mock_get_vim_info:
+ :return:
+ '''
+
+ # mock VimDriverUtils APIs
+ mock_session_specs = ["get"]
+ mock_session_get_response = {'status': 200}
+ mock_session = mock.Mock(name='mock_session',
+ spec=mock_session_specs)
+ mock_session.get.return_value = mock_session_get_response
+
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = mock_session
+ mock_get_auth_state.return_value = json.dumps(mock_auth_state)
+ mock_update_token_cache.return_value = mock_info.MOCK_TOKEN_ID
+
+ # simulate client to make the request
+ token_data = {
+ "auth": {
+ "tenantName": "Integration",
+ "passwordCredentials": {
+ "username": "demo",
+ "password": "demo"
+ }
+ }
+ }
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/identity/v2.0/tokens",
+ data=json.dumps(token_data), content_type='application/json')
+ self.failUnlessEqual(status.HTTP_200_OK,
+ response.status_code)
+ context = response.json()
+
+ self.assertIsNotNone(context['access']['token'])
+ self.assertEqual(mock_info.MOCK_TOKEN_ID,
+ context['access']['token']["id"])
+ self.assertIsNotNone(context['access']['serviceCatalog'])
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_auth_state')
+ @mock.patch.object(VimDriverUtils, 'update_token_cache')
+ def test_token_with_projectid(self, mock_update_token_cache, mock_get_auth_state,
+ mock_get_session, mock_get_vim_info):
+ '''
+ test API: get token
+ :param mock_update_token_cache:
+ :param mock_get_auth_state:
+ :param mock_get_session:
+ :param mock_get_vim_info:
+ :return:
+ '''
+
+ # mock VimDriverUtils APIs
+ mock_session_specs = ["get"]
+ mock_session_get_response = {'status': 200}
+ mock_session = mock.Mock(name='mock_session',
+ spec=mock_session_specs)
+ mock_session.get.return_value = mock_session_get_response
+
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = mock_session
+ mock_get_auth_state.return_value = json.dumps(mock_auth_state)
+ mock_update_token_cache.return_value = mock_info.MOCK_TOKEN_ID
+
+ # simulate client to make the request
+ token_data = {
+ "auth": {
+ "identity": {
+ "methods": ["password"],
+ "password": {
+ "user": {
+ "name": "demo",
+ "password": "demo"
+ }
+ }
+ },
+ "scope": {
+ "project": {"id": "dd327af0542e47d7853e0470fe9ad625"}
+ }
+ }
+ }
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/identity/v3/auth/tokens",
+ data=json.dumps(token_data), content_type='application/json')
+ self.failUnlessEqual(status.HTTP_201_CREATED,
+ response.status_code)
+ context = response.json()
+
+ self.assertEqual(mock_info.MOCK_TOKEN_ID,
+ response['X-Subject-Token'])
+ self.assertIsNotNone(context['token']['catalog'])
diff --git a/lenovo/thinkcloud/proxy/tests/test_service_proxy.py b/lenovo/thinkcloud/proxy/tests/test_service_proxy.py
new file mode 100644
index 00000000..36a797e9
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/tests/test_service_proxy.py
@@ -0,0 +1,853 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+import json
+
+from django.test import Client
+import mock
+from rest_framework import status
+import unittest
+
+
+from newton_base.util import VimDriverUtils
+
+MOCK_VIM_INFO = {
+ "createTime": "2017-04-01 02:22:27",
+ "domain": "Default",
+ "name": "TiS_R4",
+ "password": "admin",
+ "tenant": "admin",
+ "type": "openstack",
+ "url": "http://128.224.180.14:5000/v3",
+ "userName": "admin",
+ "vendor": "Lenovo",
+ "version": "thinkcloud",
+ "vimId": "lenovo-hudson-dc_RegionOne",
+ 'cloud_owner': 'lenovo-hudson-dc',
+ 'cloud_region_id': 'RegionOne',
+ 'cloud_extra_info': '',
+ 'insecure': 'True',
+}
+
+MOCK_TOKEN_ID = "1a62b3971d774404a504c5d9a3e506e3"
+
+MOCK_CATALOG_RESPONSE = {
+ "catalog": [
+ {
+ "id": "99aefcc82a9246f98f8c281e61ffc754",
+ "endpoints": [
+ {
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:9696",
+ "id": "39583c1508ad4b71b380570a745ee10a",
+ "interface": "public",
+ "region_id": "RegionOne"
+ },
+ {
+ "url": "http://192.168.204.2:9696",
+ "region": "RegionOne",
+ "id": "37e8d07ba24e4b8f93490c9daaba06e2",
+ "interface": "internal",
+ "region_id": "RegionOne"
+ },
+ {
+ "interface": "admin",
+ "id": "7eee4ca98d444b1abb00a50d4b89373f",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:9696"
+ }
+ ],
+ "name": "neutron",
+ "type": "network"
+ },
+ {
+ "endpoints": [
+ {
+ "interface": "public",
+ "id": "10496738fa374295a4a88a63b81a1589",
+ "region_id": "RegionOne",
+ "url": "http://128.224.180.14:8777",
+ "region": "RegionOne"
+ },
+ {
+ "id": "02dcb8c0bd464c4489fa0a0c9f28571f",
+ "region_id": "RegionOne",
+ "interface": "internal",
+ "url": "http://192.168.204.2:8777",
+ "region": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "8a73b0d3743b4e78b87614690f6e97fe",
+ "interface": "admin",
+ "url": "http://192.168.204.2:8777",
+ "region": "RegionOne"
+ }
+ ],
+ "id": "d131054da83f4c93833799747a0f4709",
+ "name": "ceilometer",
+ "type": "metering"
+ },
+ {
+ "type": "volumev2",
+ "name": "cinderv2",
+ "endpoints": [
+ {
+ "id": "35a67ad36f0447d19c9662babf7cf609",
+ "interface": "public",
+ "region_id": "RegionOne",
+ "url": "http://128.224.180.14:8776/v2/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v2/fcca3cc49d5e42caae15459e27103efc",
+ "id": "c6ea42052268420fa2c8d351ee68c922",
+ "interface": "internal",
+ "region_id": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "91cb24853dc3450d847b0c286a2e44ea",
+ "interface": "admin",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v2/fcca3cc49d5e42caae15459e27103efc"
+ }
+ ],
+ "id": "40440057102440739c30be10a66bc5d1"
+ },
+ {
+ "name": "heat",
+ "type": "orchestration",
+ "id": "35300cce88db4bd4bb5a72ffe3b88b00",
+ "endpoints": [
+ {
+ "id": "58999d7b4a94439089ecfb2aca2d7f6c",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:8004/v1/fcca3cc49d5e42caae15459e27103efc"
+ },
+ {
+ "url": "http://192.168.204.2:8004/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "interface": "internal",
+ "id": "1e0ee1a2aef84802b921d422372a567e",
+ "region_id": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8004/v1/fcca3cc49d5e42caae15459e27103efc",
+ "id": "17661bf4859741b8a43a461dedad1871",
+ "region_id": "RegionOne",
+ "interface": "admin"
+ }
+ ]
+ },
+ {
+ "id": "08dc6912aea64c01925012c8a6df250a",
+ "endpoints": [
+ {
+ "id": "02792c4eed77486083f9b2e52d7b94b0",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:5000/v3"
+ },
+ {
+ "id": "b6d5cad394b94309ae40d8de88059c5f",
+ "region_id": "RegionOne",
+ "interface": "internal",
+ "url": "http://192.168.204.2:5000/v3",
+ "region": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:35357/v3",
+ "region_id": "RegionOne",
+ "id": "1f18e2b7c6a34493b86853b65917888e",
+ "interface": "admin"
+ }
+ ],
+ "type": "identity",
+ "name": "keystone"
+ },
+ {
+ "name": "vim",
+ "type": "nfv",
+ "endpoints": [
+ {
+ "url": "http://128.224.180.14:4545",
+ "region": "RegionOne",
+ "id": "b33e317345e4480ab0786e4960995ec9",
+ "interface": "public",
+ "region_id": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:4545",
+ "interface": "internal",
+ "id": "03c85828d5bf432ab04831aa65ac9c52",
+ "region_id": "RegionOne"
+ },
+ {
+ "id": "067983abb061476cb53a9e23a740d98f",
+ "region_id": "RegionOne",
+ "interface": "admin",
+ "url": "http://192.168.204.2:4545",
+ "region": "RegionOne"
+ }
+ ],
+ "id": "01636c856fc84988b38b9117eb4a8021"
+ },
+ {
+ "name": "aodh",
+ "type": "alarming",
+ "id": "eb269151d0e44744a5b5449657bdc61c",
+ "endpoints": [
+ {
+ "id": "5bfc6c056e0244c493642eb82f6aaa11",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "url": "http://128.224.180.14:8042",
+ "region": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8042",
+ "region_id": "RegionOne",
+ "id": "ad69c7f76dce4089a195b9221ddbfb44",
+ "interface": "internal"
+ },
+ {
+ "interface": "admin",
+ "id": "3e8fcdfa7bcb40b0ae33c282adfcc9ff",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8042"
+ }
+ ]
+ },
+ {
+ "name": "sysinv",
+ "type": "platform",
+ "endpoints": [
+ {
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:6385/v1",
+ "interface": "public",
+ "id": "ba4ba8104590421b84672306c7e0e1f1",
+ "region_id": "RegionOne"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:6385/v1",
+ "interface": "internal",
+ "id": "a1cba34b163f496ab1acd6e9b51e39a2",
+ "region_id": "RegionOne"
+ },
+ {
+ "url": "http://192.168.204.2:6385/v1",
+ "region": "RegionOne",
+ "id": "7c171210a2c841a6a52a5713e316d6fc",
+ "interface": "admin",
+ "region_id": "RegionOne"
+ }
+ ],
+ "id": "256bbad671f946fea543e6bd71f98875"
+ },
+ {
+ "id": "e84665dcce814c05b4c5084964547534",
+ "endpoints": [
+ {
+ "url": "http://128.224.180.14:8000/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "region_id": "RegionOne",
+ "id": "b2ed1a23dc6944bea129c20861e0286a",
+ "interface": "public"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8000/v1/fcca3cc49d5e42caae15459e27103efc",
+ "interface": "internal",
+ "id": "c4df7c6bc15646848eff35caf6ffea8e",
+ "region_id": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "61b3dabb761443a89ab549f437c05ab0",
+ "interface": "admin",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8000/v1/fcca3cc49d5e42caae15459e27103efc"
+ }
+ ],
+ "name": "heat-cfn",
+ "type": "cloudformation"
+ },
+ {
+ "id": "823024424a014981a3721229491c0b1a",
+ "endpoints": [
+ {
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:8776/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region_id": "RegionOne",
+ "id": "4a52e4e54ff440789f9a797919c4a0f2",
+ "interface": "public"
+ },
+ {
+ "url": "http://192.168.204.2:8776/v1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "id": "d4f9a84476524a39844f0fce63f1022e",
+ "region_id": "RegionOne",
+ "interface": "internal"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v1/fcca3cc49d5e42caae15459e27103efc",
+ "interface": "admin",
+ "id": "81bf3810a8cc4697b68c6e93b5b8fe1f",
+ "region_id": "RegionOne"
+ }
+ ],
+ "type": "volume",
+ "name": "cinder"
+ },
+ {
+ "name": "glance",
+ "type": "image",
+ "endpoints": [
+ {
+ "id": "bd930aba961946cfb1401bada56d55e3",
+ "region_id": "RegionOne",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:9292"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:9292",
+ "id": "c11da585f0b141b99d1e18bb9a607beb",
+ "region_id": "RegionOne",
+ "interface": "internal"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:9292",
+ "id": "31b26c625a6a4fc7910dc5935155996e",
+ "interface": "admin",
+ "region_id": "RegionOne"
+ }
+ ],
+ "id": "3b78cf039bc54d1bbb99ab3a4be15ef1"
+ },
+ {
+ "id": "b8701374bf254de1beee8a2c9ecc6b33",
+ "endpoints": [
+ {
+ "region_id": "RegionOne",
+ "id": "f7407f330c8b4577b1d377d3fab9c2f8",
+ "interface": "public",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:15491"
+ },
+ {
+ "url": "http://192.168.204.2:5491",
+ "region": "RegionOne",
+ "interface": "internal",
+ "id": "0b37ce31a32f4b6fa5e1aa0d6c20680f",
+ "region_id": "RegionOne"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "7b87ea72adf245e1991e9e0df29b7ea9",
+ "interface": "admin",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:5491"
+ }
+ ],
+ "type": "patching",
+ "name": "patching"
+ },
+ {
+ "id": "0ec0923a58f04ffeb6fced3bbc5c0947",
+ "endpoints": [
+ {
+ "url": "http://128.224.180.14:8774/v2.1/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne",
+ "id": "13168b12da17451fb39630de67db168f",
+ "region_id": "RegionOne",
+ "interface": "public"
+ },
+ {
+ "id": "22dd6a44209f42d986b82e3aa6535f82",
+ "interface": "internal",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8774/v2.1/fcca3cc49d5e42caae15459e27103efc"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8774/v2.1/fcca3cc49d5e42caae15459e27103efc",
+ "id": "552a991ae501492f841c1b6e2ff38fc5",
+ "region_id": "RegionOne",
+ "interface": "admin"
+ }
+ ],
+ "type": "compute",
+ "name": "nova"
+ },
+ {
+ "id": "50b219650f1049b097b3f14e8c70cdf8",
+ "endpoints": [
+ {
+ "interface": "public",
+ "id": "5a4276cd6e4d43e883cf8640d4e13f7d",
+ "region_id": "RegionOne",
+ "region": "RegionOne",
+ "url": "http://128.224.180.14:8776/v3/fcca3cc49d5e42caae15459e27103efc"
+ },
+ {
+ "region": "RegionOne",
+ "url": "http://192.168.204.2:8776/v3/fcca3cc49d5e42caae15459e27103efc",
+ "region_id": "RegionOne",
+ "id": "c796df3ca5a84fc18db5b43a55283953",
+ "interface": "internal"
+ },
+ {
+ "region_id": "RegionOne",
+ "id": "cf55c2b34d0049ba835a2e48b9ad0e2e",
+ "interface": "admin",
+ "url": "http://192.168.204.2:8776/v3/fcca3cc49d5e42caae15459e27103efc",
+ "region": "RegionOne"
+ }
+ ],
+ "type": "volumev3",
+ "name": "cinderv3"
+ }
+ ]
+}
+
+MOCK_AUTH_STATE = {
+ "body": {
+ "token": {
+ "is_domain": "false",
+ "expires_at": "2017-08-27T14:19:15.000000Z",
+ "issued_at": "2017-08-27T13:19:15.000000Z",
+ "roles": [
+ {
+ "id": "9fe2ff9ee4384b1894a90878d3e92bab",
+ "name": "_member_"
+ },
+ {
+ "id": "b86a7e02935844b899d3d326f83c1b1f",
+ "name": "admin"
+ },
+ {
+ "name": "heat_stack_owner",
+ "id": "7de502236e954c8282de32e773fc052e"
+ }
+ ],
+ "methods": [
+ "password"
+ ],
+ "catalog": MOCK_CATALOG_RESPONSE['catalog'],
+ "project": {
+ "name": "admin",
+ "id": "fcca3cc49d5e42caae15459e27103efc",
+ "domain": {
+ "id": "default",
+ "name": "Default"
+ }
+ },
+ "user": {
+ "name": "admin",
+ "id": "9efb043c7629497a8028d7325ca1afb0",
+ "domain": {
+ "id": "default",
+ "name": "Default"
+ }
+ },
+ "audit_ids": [
+ "_ZWT10DtSZKRXIvIcxun7w"
+ ]
+ }
+ },
+ "auth_token": MOCK_TOKEN_ID
+}
+
+MOCK_INTERNAL_METADATA_CATALOG = {
+ "identity": {
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/identity",
+ "prefix": "http://128.224.180.14:5000",
+ "suffix": "v3"
+ },
+ "patching": {
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/patching",
+ "suffix": "",
+ "prefix": "http://128.224.180.14:15491"
+ },
+ "orchestration": {
+ "suffix": "v1/fcca3cc49d5e42caae15459e27103efc",
+ "prefix": "http://128.224.180.14:8004",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/orchestration"
+ },
+ "volume": {
+ "prefix": "http://128.224.180.14:8776",
+ "suffix": "v1/fcca3cc49d5e42caae15459e27103efc",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/volume"
+ },
+ "metering": {
+ "suffix": "",
+ "prefix": "http://128.224.180.14:8777",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/metering"
+ },
+ "volumev3": {
+ "prefix": "http://128.224.180.14:8776",
+ "suffix": "v3/fcca3cc49d5e42caae15459e27103efc",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/volumev3"
+ },
+ "compute": {
+ "suffix": "v2.1/fcca3cc49d5e42caae15459e27103efc",
+ "prefix": "http://128.224.180.14:8774",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute"
+ },
+ "platform": {
+ "prefix": "http://128.224.180.14:6385",
+ "suffix": "v1",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/platform"
+ },
+ "nfv": {
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/nfv",
+ "prefix": "http://128.224.180.14:4545",
+ "suffix": ""
+ },
+ "volumev2": {
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/volumev2",
+ "suffix": "v2/fcca3cc49d5e42caae15459e27103efc",
+ "prefix": "http://128.224.180.14:8776"
+ },
+ "image": {
+ "suffix": "",
+ "prefix": "http://128.224.180.14:9292",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/image"
+ },
+ "network": {
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/network",
+ "prefix": "http://128.224.180.14:9696",
+ "suffix": ""
+ },
+ "alarming": {
+ "suffix": "",
+ "prefix": "http://128.224.180.14:8042",
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/alarming"
+ },
+ "cloudformation": {
+ "proxy_prefix": "http://172.16.77.20:9003/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/cloudformation",
+ "prefix": "http://128.224.180.14:8000",
+ "suffix": "v1/fcca3cc49d5e42caae15459e27103efc"
+ }
+}
+
+
+MOCK_GET_SERVERS_RESPONSE = {
+ "servers": [
+ {
+ "links": [
+ {
+ "href": "http://128.224.180.14:8774/v2.1/fcca3cc49d5e42caae15459e27103efc/servers/b2581b5c-7c56-4564-819d-fe7a2ce9c261",
+ "rel": "self"
+ },
+ {
+ "href": "http://128.224.180.14:8774/fcca3cc49d5e42caae15459e27103efc/servers/b2581b5c-7c56-4564-819d-fe7a2ce9c261",
+ "rel": "bookmark"
+ }
+ ],
+ "id": "b2581b5c-7c56-4564-819d-fe7a2ce9c261",
+ "name": "t1"
+ },
+ {
+ "id": "ff7b51ca-a272-45f4-b54c-e40b8099e67d",
+ "name": "t2",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://128.224.180.14:8774/v2.1/fcca3cc49d5e42caae15459e27103efc/servers/ff7b51ca-a272-45f4-b54c-e40b8099e67d"
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://128.224.180.14:8774/fcca3cc49d5e42caae15459e27103efc/servers/ff7b51ca-a272-45f4-b54c-e40b8099e67d"
+ }
+ ]
+ }
+ ]
+}
+
+MOCK_POST_SERVER_REQUEST = {
+ "server": {
+ "accessIPv4": "1.2.3.4",
+ "accessIPv6": "80fe::",
+ "name": "new-server-test",
+ "imageRef": "70a599e0-31e7-49b7-b260-868f441e862b",
+ "flavorRef": "1",
+ "availability_zone": "nova",
+ "OS-DCF:diskConfig": "AUTO",
+ "metadata": {
+ "My Server Name": "Apache1"
+ },
+ "personality": [
+ {
+ "path": "/etc/banner.txt",
+ "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
+ }
+ ],
+ "security_groups": [
+ {
+ "name": "default"
+ }
+ ],
+ "user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg=="
+ },
+ "OS-SCH-HNT:scheduler_hints": {
+ "same_host": "48e6a9f6-30af-47e0-bc04-acaed113bb4e"
+ }
+}
+
+MOCK_POST_SERVER_RESPONSE = {
+ "server": {
+ "OS-DCF:diskConfig": "AUTO",
+ "adminPass": "6NpUwoz2QDRN",
+ "id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
+ "rel": "bookmark"
+ }
+ ],
+ "security_groups": [
+ {
+ "name": "default"
+ }
+ ]
+ }
+}
+
+
+MOCK_PATCH_IMAGE_REQUEST = [
+ {
+ "op": "replace",
+ "path": "/name",
+ "value": "Fedora 17"
+ },
+ {
+ "op": "replace",
+ "path": "/tags",
+ "value": [
+ "fedora",
+ "beefy"
+ ]
+ }
+]
+
+MOCK_PATCH_IMAGE_RESPONSE = {
+ "checksum": "710544e7f0c828b42f51207342622d33",
+ "container_format": "ovf",
+ "created_at": "2016-06-29T16:13:07Z",
+ "disk_format": "vhd",
+ "file": "/v2/images/2b61ed2b-f800-4da0-99ff-396b742b8646/file",
+ "id": "2b61ed2b-f800-4da0-99ff-396b742b8646",
+ "min_disk": 20,
+ "min_ram": 512,
+ "name": "Fedora 17",
+ "owner": "02a7fb2dd4ef434c8a628c511dcbbeb6",
+ "protected": "false",
+ "schema": "/v2/schemas/image",
+ "self": "/v2/images/2b61ed2b-f800-4da0-99ff-396b742b8646",
+ "size": 21909,
+ "status": "active",
+ "tags": [
+ "beefy",
+ "fedora"
+ ],
+ "updated_at": "2016-07-25T14:48:18Z",
+ "virtual_size": "",
+ "visibility": "private"
+}
+
+
+class MockResponse(object):
+ status_code = 200
+ content = ''
+
+ def json(self):
+ pass
+
+
+class TestServiceProxy(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_token_cache')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_get_token(self, mock_get_vim_info, mock_get_token_cache, mock_get_session):
+ mock_session_specs = ["head"]
+ mock_session = mock.Mock(name='mock_session', spec=mock_session_specs)
+ mock_get_servers_response_obj = mock.Mock(spec=MockResponse)
+ mock_get_servers_response_obj.status_code = 200
+ mock_get_servers_response_obj.content = MOCK_GET_SERVERS_RESPONSE
+ mock_get_servers_response_obj.json.return_value = MOCK_GET_SERVERS_RESPONSE
+ mock_session.head.return_value = mock_get_servers_response_obj
+
+ mock_get_vim_info.return_value = MOCK_VIM_INFO
+ mock_get_session.return_value = mock_session
+ mock_get_token_cache.return_value = (json.dumps(MOCK_AUTH_STATE),
+ json.dumps(MOCK_INTERNAL_METADATA_CATALOG))
+ response = self.client.head(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers",
+ {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+
+ def test_unauthorized_access(self):
+ response = self.client.get(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers")
+ self.assertEquals(status.HTTP_403_FORBIDDEN, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_expired_auth_token(self, mock_get_vim_info):
+ mock_get_vim_info.return_value = MOCK_VIM_INFO
+
+ response = self.client.get("/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers",
+ {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_403_FORBIDDEN, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_token_cache')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_request_without_servicetype(self, mock_get_vim_info, mock_get_token_cache):
+ mock_get_vim_info.return_value = MOCK_VIM_INFO
+ mock_get_token_cache.return_value = (json.dumps(MOCK_AUTH_STATE), {})
+ servicetype = "compute"
+ base_url = "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ server_url = "/v2.1/fcca3cc49d5e42caae15459e27103efc/servers"
+ url = (base_url + servicetype + server_url)
+ response = self.client.get(url, {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_500_INTERNAL_SERVER_ERROR, response.status_code)
+
+ metadata_catalog = copy.deepcopy(MOCK_INTERNAL_METADATA_CATALOG)
+ metadata_catalog[servicetype] = None
+ mock_get_token_cache.return_value = (json.dumps(MOCK_AUTH_STATE), json.dumps(metadata_catalog))
+
+ response = self.client.get(url, {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_500_INTERNAL_SERVER_ERROR, response.status_code)
+
+ metadata_catalog = copy.deepcopy(MOCK_INTERNAL_METADATA_CATALOG)
+ metadata_catalog[servicetype]['prefix'] = None
+ metadata_catalog[servicetype]['proxy_prefix'] = None
+ mock_get_token_cache.return_value = (json.dumps(MOCK_AUTH_STATE), json.dumps(metadata_catalog))
+
+ response = self.client.get(url, {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_500_INTERNAL_SERVER_ERROR, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_auth_state')
+ @mock.patch.object(VimDriverUtils, 'update_token_cache')
+ @mock.patch.object(VimDriverUtils, 'get_token_cache')
+ def test_crud_resources(self, mock_get_token_cache, mock_update_token_cache, mock_get_auth_state, mock_get_session, mock_get_vim_info):
+ '''
+ Test service proxy API: GET
+
+ :param mock_get_token_cache:
+ :param mock_update_token_cache:
+ :param mock_get_auth_state:
+ :param mock_get_session:
+ :param mock_get_vim_info:
+ :return:
+ '''
+
+ # mock VimDriverUtils APIs
+ mock_session_specs = ["get", "post", "put", "patch", "delete"]
+
+ mock_get_servers_response_obj = mock.Mock(spec=MockResponse)
+ mock_get_servers_response_obj.status_code = 200
+ mock_get_servers_response_obj.content = MOCK_GET_SERVERS_RESPONSE
+ mock_get_servers_response_obj.json.return_value = MOCK_GET_SERVERS_RESPONSE
+
+ mock_post_server_response_obj = mock.Mock(spec=MockResponse)
+ mock_post_server_response_obj.status_code = 202
+ mock_post_server_response_obj.content = MOCK_POST_SERVER_RESPONSE
+ mock_post_server_response_obj.json.return_value = MOCK_POST_SERVER_RESPONSE
+
+ mock_patch_server_response_obj = mock.Mock(spec=MockResponse)
+ mock_patch_server_response_obj.status_code = 202
+ mock_patch_server_response_obj.content = MOCK_PATCH_IMAGE_REQUEST
+ mock_patch_server_response_obj.json.return_value = MOCK_PATCH_IMAGE_REQUEST
+
+ mock_delete_server_response_obj = mock.Mock(spec=MockResponse)
+ mock_delete_server_response_obj.status_code = 204
+
+ mock_session = mock.Mock(name='mock_session', spec=mock_session_specs)
+ mock_session.get.return_value = mock_get_servers_response_obj
+ mock_session.post.return_value = mock_post_server_response_obj
+ mock_session.patch.return_value = mock_patch_server_response_obj
+ mock_session.delete.return_value = mock_delete_server_response_obj
+
+ mock_get_vim_info.return_value = MOCK_VIM_INFO
+ mock_get_session.return_value = mock_session
+ mock_get_auth_state.return_value = json.dumps(MOCK_AUTH_STATE)
+ mock_update_token_cache.return_value = MOCK_TOKEN_ID
+ mock_get_token_cache.return_value = (json.dumps(MOCK_AUTH_STATE), json.dumps(MOCK_INTERNAL_METADATA_CATALOG))
+
+ # Create resource
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers",
+ MOCK_POST_SERVER_REQUEST, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
+ context = response.json()
+ self.assertEquals(MOCK_TOKEN_ID, response['X-Subject-Token'])
+ self.assertIsNotNone(context['server'])
+
+ # Retrieve resource
+ response = self.client.get(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers",
+ {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ context = response.json()
+
+ self.assertEquals(MOCK_TOKEN_ID, response['X-Subject-Token'])
+ self.assertIsNotNone(context['servers'])
+
+ # Update resource
+ response = self.client.get(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers",
+ {}, HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ context = response.json()
+
+ self.assertEquals(MOCK_TOKEN_ID, response['X-Subject-Token'])
+ self.assertIsNotNone(context['servers'])
+
+ # simulate client to make the request
+ response = self.client.delete(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/compute/v2.1/fcca3cc49d5e42caae15459e27103efc/servers/324dfb7d-f4a9-419a-9a19-237df04b443b",
+ HTTP_X_AUTH_TOKEN=MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_204_NO_CONTENT, response.status_code)
+ self.assertEquals(MOCK_TOKEN_ID, response['X-Subject-Token'])
diff --git a/lenovo/thinkcloud/proxy/urls.py b/lenovo/thinkcloud/proxy/urls.py
new file mode 100644
index 00000000..41e9b120
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/urls.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from thinkcloud.proxy.views import identityV3
+from thinkcloud.proxy.views import services
+from newton_base.proxy import dnsaasdelegate
+
+urlpatterns = [
+ # url(r'^identity/v2)$',
+ # identityV2.Tokens.as_view()),
+ url(r'^identity/v3/auth/tokens/?$',
+ identityV3.Tokens.as_view()),
+ url(r'^identity/v3/?$',
+ identityV3.Tokens.as_view()),
+ url(r'^identity/v2.0/?',
+ identityV3.TokensV2.as_view()),
+ url(r'^identity/v2.0/tokens/?$',
+ identityV3.TokensV2.as_view()),
+ url(r'^identity/v2.0/tenants/?$',
+ services.GetTenants.as_view()),
+ url(r'dns-delegate/(?P<requri>[0-9a-zA-Z./_-]*)$',
+ dnsaasdelegate.DnsaasDelegate.as_view()),
+ url(r'^(?P<servicetype>[0-9a-zA-Z_-]{,18})/(?P<requri>[0-9a-zA-Z./_-]*)$',
+ services.Services.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/proxy/urlsV1.py b/lenovo/thinkcloud/proxy/urlsV1.py
new file mode 100644
index 00000000..33fd6862
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/urlsV1.py
@@ -0,0 +1,39 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from thinkcloud.proxy.views import identityV3
+from thinkcloud.proxy.views import services
+from newton_base.proxy import dnsaasdelegate
+
+urlpatterns = [
+ url(r'^identity/v3/auth/tokens/?$',
+ identityV3.APIv1Tokens.as_view()),
+ url(r'^identity/v3/?$',
+ identityV3.APIv1Tokens.as_view()),
+ url(r'^identity/v2.0/?$',
+ identityV3.APIv1TokensV2.as_view()),
+ url(r'^identity/v2.0/tokens/?$',
+ identityV3.APIv1TokensV2.as_view()),
+ url(r'^identity/v2.0/tenants/?$',
+ services.APIv1GetTenants.as_view()),
+ url(r'dns-delegate/(?P<requri>[0-9a-zA-Z./_-]*)$',
+ dnsaasdelegate.APIv1DnsaasDelegate.as_view()),
+ url(r'^(?P<servicetype>[0-9a-zA-Z_-]{,18})/(?P<requri>[0-9a-zA-Z./_-]*)$',
+ services.APIv1Services.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/proxy/views/__init__.py b/lenovo/thinkcloud/proxy/views/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/views/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/proxy/views/identityV3.py b/lenovo/thinkcloud/proxy/views/identityV3.py
new file mode 100644
index 00000000..85bc5738
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/views/identityV3.py
@@ -0,0 +1,81 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from django.conf import settings
+from newton_base.proxy import identityV3 as newton_identityV3
+from common.msapi import extsys
+
+logger = logging.getLogger(__name__)
+
+# DEBUG=True
+
+
+class Tokens(newton_identityV3.Tokens):
+
+ def __init__(self):
+ self.proxy_prefix = settings.MULTICLOUD_PREFIX
+ self._logger = logger
+
+
+class TokensV2(newton_identityV3.TokensV2):
+
+ def __init__(self):
+ self.proxy_prefix = settings.MULTICLOUD_PREFIX
+ self._logger = logger
+
+
+class APIv1Tokens(Tokens):
+ def __init__(self):
+ super(APIv1Tokens, self).__init__()
+ self.proxy_prefix = settings.MULTICLOUD_API_V1_PREFIX
+
+ def get(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.debug("META> %s" % request.META)
+ # self._logger.debug("data> %s" % request.data)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Tokens, self).get(request, vimid)
+
+ def post(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.debug("META> %s" % request.META)
+ # self._logger.debug("data> %s" % request.data)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Tokens, self).post(request, vimid)
+
+
+class APIv1TokensV2(TokensV2):
+ def __init__(self):
+ super(APIv1TokensV2, self).__init__()
+ self.proxy_prefix = settings.MULTICLOUD_API_V1_PREFIX
+
+ def get(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.debug("META> %s" % request.META)
+ # self._logger.debug("data> %s" % request.data)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1TokensV2, self).get(request, vimid)
+
+ def post(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.debug("META> %s" % request.META)
+ # self._logger.debug("data> %s" % request.data)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1TokensV2, self).post(request, vimid)
diff --git a/lenovo/thinkcloud/proxy/views/services.py b/lenovo/thinkcloud/proxy/views/services.py
new file mode 100644
index 00000000..ba849f3e
--- /dev/null
+++ b/lenovo/thinkcloud/proxy/views/services.py
@@ -0,0 +1,143 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from newton_base.proxy import services as newton_services
+from common.msapi import extsys
+
+logger = logging.getLogger(__name__)
+
+# DEBUG=True
+
+
+class Services(newton_services.Services):
+
+ def __init__(self):
+ self._logger = logger
+
+
+class GetTenants(newton_services.GetTenants):
+ '''
+ Backward compatible API for /v2.0/tenants
+ '''
+
+ def __init__(self):
+ self._logger = logger
+
+ def get(self, request, vimid="", servicetype="identity", requri='v3/projects'):
+ self._logger.debug("GetTenants--get::META> %s" % request.META)
+ self._logger.debug("GetTenants--get::data> %s" % request.data)
+ self._logger.debug("GetTenants--get::vimid, servicetype, requri> %s,%s,%s" %
+ (vimid, servicetype, requri))
+
+ return super(GetTenants, self).get(request, vimid, servicetype, requri)
+
+
+class APIv1Services(Services):
+
+ def __init__(self):
+ super(APIv1Services, self).__init__()
+ # self._logger = logger
+
+ def head(self, request, cloud_owner="", cloud_region_id="", servicetype="", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.info("servicetype, requri> %s,%s" % (servicetype, requri))
+ # self._logger.debug("META, data> %s , %s" % (request.META, request.data))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Services, self).head(request, vimid, servicetype, requri)
+
+ def get(self, request, cloud_owner="", cloud_region_id="", servicetype="", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Services, self).get(request, vimid, servicetype, requri)
+
+ def post(self, request, cloud_owner="", cloud_region_id="", servicetype="", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Services, self).post(request, vimid, servicetype, requri)
+
+ def put(self, request, cloud_owner="", cloud_region_id="", servicetype="", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Services, self).put(request, vimid, servicetype, requri)
+
+ def patch(self, request, cloud_owner="", cloud_region_id="", servicetype="", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Services, self).patch(request, vimid, servicetype, requri)
+
+ def delete(self, request, cloud_owner="", cloud_region_id="", servicetype="", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1Services, self).delete(request, vimid, servicetype, requri)
+
+
+class APIv1GetTenants(GetTenants):
+ '''
+ Backward compatible API for /v2.0/tenants
+ '''
+
+ def __init__(self):
+ super(APIv1GetTenants, self).__init__()
+ # self._logger = logger
+
+ def head(self, request, cloud_owner="", cloud_region_id="", servicetype="identity", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.info("servicetype, requri> %s,%s" % (servicetype, requri))
+ # self._logger.debug("META, data> %s , %s" % (request.META, request.data))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1GetTenants, self).head(request, vimid, servicetype, requri)
+
+ def get(self, request, cloud_owner="", cloud_region_id="", servicetype="identity", requri='v3/projects'):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.debug("with servicetype, requri> %s,%s" % (servicetype, requri))
+ # self._logger.debug("with META> %s" % request.META)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1GetTenants, self).get(request, vimid, servicetype, requri)
+
+ def post(self, request, cloud_owner="", cloud_region_id="", servicetype="identity", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+ # self._logger.debug("with servicetype, requri> %s,%s" % (servicetype, requri))
+ # self._logger.debug("with META> %s" % request.META)
+ # self._logger.debug("with data> %s" % request.data)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1GetTenants, self).post(request, vimid, servicetype, requri)
+
+ def put(self, request, cloud_owner="", cloud_region_id="", servicetype="identity", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1GetTenants, self).put(request, vimid, servicetype, requri)
+
+ def patch(self, request, cloud_owner="", cloud_region_id="", servicetype="identity", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1GetTenants, self).patch(request, vimid, servicetype, requri)
+
+ def delete(self, request, cloud_owner="", cloud_region_id="", servicetype="identity", requri=""):
+ self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1GetTenants, self).delete(request, vimid, servicetype, requri)
diff --git a/lenovo/thinkcloud/pub/__init__.py b/lenovo/thinkcloud/pub/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/pub/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/pub/config/__init__.py b/lenovo/thinkcloud/pub/config/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/pub/config/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/pub/config/config.py b/lenovo/thinkcloud/pub/config/config.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/pub/config/config.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/pub/config/log.yml b/lenovo/thinkcloud/pub/config/log.yml
new file mode 100644
index 00000000..1dfd67f4
--- /dev/null
+++ b/lenovo/thinkcloud/pub/config/log.yml
@@ -0,0 +1,51 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: 1
+disable_existing_loggers: False
+
+loggers:
+ thinkcloud:
+ handlers: [console_handler, file_handler]
+ level: "DEBUG"
+ propagate: False
+ newton_base:
+ handlers: [console_handler, file_handler]
+ level: "DEBUG"
+ propagate: False
+ common:
+ handlers: [console_handler, file_handler]
+ level: "DEBUG"
+ propagate: False
+handlers:
+ console_handler:
+ level: "DEBUG"
+ class: "logging.StreamHandler"
+ formatter: "mdcFormat"
+ file_handler:
+ level: "DEBUG"
+ class: "logging.handlers.RotatingFileHandler"
+ filename: "/var/log/onap/multicloud/openstack/thinkcloud/thinkcloud.log"
+ formatter: "mdcFormat"
+ maxBytes: 1024*1024*50
+ backupCount: 10
+formatters:
+ standard:
+ format: "%(asctime)s|||||%(name)s||%(thread)||%(funcName)s||%(levelname)s||%(message)s"
+ mdcFormat:
+ format: "%(asctime)s|||||%(name)s||%(thread)s||%(funcName)s||%(levelname)s||%(message)s||||%(mdc)s \t"
+ mdcfmt: "{requestID} {invocationID} {serviceName} {serviceIP}"
+ datefmt: "%Y-%m-%d %H:%M:%S"
+ (): onaplogging.mdcformatter.MDCFormatter
+
diff --git a/lenovo/thinkcloud/registration/__init__.py b/lenovo/thinkcloud/registration/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/registration/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/registration/tests/__init__.py b/lenovo/thinkcloud/registration/tests/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/registration/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/registration/tests/test_registration.py b/lenovo/thinkcloud/registration/tests/test_registration.py
new file mode 100644
index 00000000..c8c8e6b5
--- /dev/null
+++ b/lenovo/thinkcloud/registration/tests/test_registration.py
@@ -0,0 +1,472 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+
+from rest_framework import status
+
+from common.utils import restcall
+from newton_base.tests import mock_info
+from newton_base.tests import test_base
+from newton_base.util import VimDriverUtils
+
+PIKE_MOCK_VIM_INFO = {
+ "createTime": "2017-04-01 02:22:27",
+ "domain": "Default",
+ "name": "TiS_R4",
+ "password": "admin",
+ "tenant": "admin",
+ "type": "openstack",
+ "url": "http://128.224.180.14:5000/v3",
+ "userName": "admin",
+ "vendor": "Lenovo",
+ "version": "newton",
+ "vimId": "lenovo-hudson-dc_RegionOne",
+ 'cloud_owner': 'lenovo-hudson-dc',
+ 'cloud_region_id': 'RegionOne',
+ 'cloud_extra_info': {
+ "ovsDpdk": {
+ "version": "v1",
+ "arch": "Intel64",
+ "libname": "dataProcessingAccelerationLibrary",
+ "libversion": "v12.1",
+ }
+ },
+ 'insecure': 'True'
+}
+
+MOCK_GET_TENANT_RESPONSE = {
+ "projects": [
+ {"id": "1", "name": "project"},
+ {"id": "2", "name": "project2"},
+ ]
+}
+
+MOCK_GET_FLAVOR_RESPONSE = {
+ "flavors": [
+ {
+ "id": "1", "name": "micro", "vcpus": 1, "ram": "1MB",
+ "disk": "1G", "OS-FLV-EXT-DATA:ephemeral": False,
+ "swap": True, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True, "link": [{"href": 1}]
+ },
+ {
+ "id": "2", "name": "mini", "vcpus": 2, "ram": "2",
+ "disk": "2G", "OS-FLV-EXT-DATA:ephemeral": True,
+ "swap": False, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True
+ },
+ ]
+}
+
+MOCK_GET_IMAGE_RESPONSE = {
+ "images": [
+ {
+ "id": "1", "name": "cirros", "self": "test",
+ "os_distro": "CirrOS", "os_version": "0.3",
+ "application": "test", "application_vendor": "ONAP",
+ "application_version": 1, "architecture": "x86",
+ "schema": None
+ },
+ {
+ "id": "2", "name": "cirros", "self": "test",
+ "os_distro": "CirrOS", "os_version": "0.3",
+ "application": "test", "application_vendor": "ONAP",
+ "application_version": 1, "architecture": "x86",
+ "schema": "req_resource"
+ },
+ ]
+}
+
+MOCK_GET_AZ_RESPONSE = {
+ "availabilityZoneInfo": [
+ {
+ "zoneName": "production",
+ "zoneState": {"available": True},
+ "hosts": {"hypervisor": "kvm"}
+ },
+ {
+ "zoneName": "testing",
+ },
+ ]
+}
+
+MOCK_HYPERVISOR_RESPONSE = {
+ "hypervisors": [
+ {"hypervisor_type": "kvm"}
+ ]
+}
+
+MOCK_GET_SNAPSHOT_RESPONSE = {
+ "snapshots": [
+ {
+ "id": 1, "name": "test", "metadata":
+ {
+ "architecture": "x86",
+ "os-distro": "clearlinux",
+ "os-version": "276",
+ "vendor": "intel",
+ "version": 3,
+ "selflink": "test",
+ "prev-snapshot-id": "test-id"
+ }
+ },
+ {"id": 2, "name": "test2"}
+ ]
+}
+
+MOCK_GET_HYPERVISOR_RESPONSE = {
+ "hypervisors": [
+ {
+ "hypervisor_hostname": "testing", "state": "ACTIVE",
+ "id": 1, "local_gb": 256, "memory_mb": 1024,
+ "hypervisor_links": "link", "host_ip": "127.0.0.1",
+ "cpu_info": u'{"topology": {"cores": 8, "threads": 16, "sockets": 4}}'
+ },
+ {
+ "hypervisor_hostname": "testing2", "state": "XXX",
+ "id": 1, "local_gb": 256, "memory_mb": 1024,
+ "hypervisor_links": "link", "host_ip": "127.0.0.1",
+ }
+ ]
+}
+
+TEST_REGISTER_ENDPOINT_REQUEST = {
+ "defaultTenant": "project1"
+}
+
+
+# HPA UT1: CPU-PINNING
+MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE = {
+ "flavors": [
+ {
+ "id": "1", "name": "micro", "vcpus": 1, "ram": "1024",
+ "disk": "1G", "OS-FLV-EXT-DATA:ephemeral": False,
+ "swap": True, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True, "link": [{"href": 1}]
+ },
+ {
+ "id": "2", "name": "onap.mini", "vcpus": 2, "ram": "2048",
+ "disk": "2G", "OS-FLV-EXT-DATA:ephemeral": True,
+ "swap": False, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True
+ },
+ ]
+}
+
+# HPA UT2: CPU-Topology
+MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS2_RESPONSE = {
+ "extra_specs": {
+ "aggregate_instance_extra_specs:storage": "local_image",
+ "capabilities:cpu_info:model": "Haswell",
+ "hw:cpu_sockets": "2",
+ "hw:cpu_cores": "4",
+ "hw:cpu_threads": "16"
+ }
+}
+
+# HPA UT3: mem_page_size
+MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS3_RESPONSE = {
+ "extra_specs": {
+ "aggregate_instance_extra_specs:storage": "local_image",
+ "capabilities:cpu_info:model": "Haswell",
+ "hw:mem_page_size": "large"
+ }
+}
+
+# HPA UT4: numa_nodes
+MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS4_RESPONSE = {
+ "extra_specs": {
+ "aggregate_instance_extra_specs:storage": "local_image",
+ "capabilities:cpu_info:model": "Haswell",
+ "hw:numa_nodes": "2",
+ "hw:numa_cpus.0": "0,1",
+ "hw:numa_cpus.1": "2,3,4,5",
+ "hw:numa_mem.0": "2048",
+ "hw:numa_mem.1": "2048"
+ }
+}
+
+# HPA UT5: instruction set
+MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS5_RESPONSE = {
+ "extra_specs": {
+ "aggregate_instance_extra_specs:storage": "local_image",
+ "capabilities:cpu_info:model": "Haswell",
+ "hw:capabilities:cpu_info:features": "avx,acpi"
+ }
+}
+
+# HPA UT6: pci passthrough
+MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS6_RESPONSE = {
+ "extra_specs": {
+ "aggregate_instance_extra_specs:storage": "local_image",
+ "capabilities:cpu_info:model": "Haswell",
+ "pci_passthrough:alias": "sriov-vf-intel-8086-15b3:4"
+ }
+}
+
+MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS_RESPONSE = {
+ "extra_specs": {
+ "aggregate_instance_extra_specs:storage": "local_image",
+ "capabilities:cpu_info:model": "Haswell",
+ "hw:cpu_policy": "dedicated",
+ "hw:cpu_thread_policy": "prefer"
+ }
+}
+
+
+class TestRegistration(test_base.TestRequest):
+
+ def setUp(self):
+ super(TestRegistration, self).setUp()
+ self.req_to_aai_backup = restcall.req_to_aai
+
+ def tearDown(self):
+ super(TestRegistration, self).tearDown()
+ restcall.req_to_aai = self.req_to_aai_backup
+
+ def _get_mock_response(self, return_value=None):
+ mock_response = mock.Mock(spec=test_base.MockResponse)
+ mock_response.status_code = status.HTTP_200_OK
+ mock_response.json.return_value = return_value
+ return mock_response
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_endpoint_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = PIKE_MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_FLAVOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED,
+ response.status_code)
+
+# @mock.patch.object(VimDriverUtils, 'delete_vim_info')
+# def test_unregister_endpoint_successfully(
+# self, mock_delete_vim_info):
+# mock_delete_vim_info.return_value = 0
+
+# response = self.client.delete((
+# "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+# "registry"), "{}", content_type="application/json",
+# HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+# self.assertEquals(status.HTTP_202_ACCEPTED,
+# response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'delete_vim_info')
+ def test_fail_unregister_endpoint(
+ self, mock_delete_vim_info):
+ mock_delete_vim_info.return_value = 1
+
+ response = self.client.delete((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), "{}", content_type="application/json",
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_500_INTERNAL_SERVER_ERROR,
+ response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_hpa_cpupinning_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_hpa_cputopology_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS2_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_hpa_hugepage_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS3_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_hpa_numa_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS4_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_hpa_instructionset_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS5_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_register_hpa_pcipassthrough_successfully(
+ self, mock_get_vim_info, mock_get_session):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_LIST1_RESPONSE),
+ self._get_mock_response(MOCK_GET_HPA_FLAVOR_onap_mini_EXTRA_SPECS6_RESPONSE),
+ self._get_mock_response(MOCK_GET_IMAGE_RESPONSE),
+ self._get_mock_response(),
+ self._get_mock_response(MOCK_GET_AZ_RESPONSE),
+ self._get_mock_response(MOCK_HYPERVISOR_RESPONSE),
+ self._get_mock_response(MOCK_GET_SNAPSHOT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPERVISOR_RESPONSE)
+ ]
+ })
+
+ response = self.client.post((
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/"
+ "registry"), TEST_REGISTER_ENDPOINT_REQUEST,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_202_ACCEPTED, response.status_code)
diff --git a/lenovo/thinkcloud/registration/tests/test_registration2.py b/lenovo/thinkcloud/registration/tests/test_registration2.py
new file mode 100644
index 00000000..d799c90a
--- /dev/null
+++ b/lenovo/thinkcloud/registration/tests/test_registration2.py
@@ -0,0 +1,116 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+
+import unittest
+from django.test import Client
+from newton_base.tests import test_base
+from rest_framework import status
+
+from common.utils import restcall
+from thinkcloud.registration.views import registration
+
+MOCK_VIM_INFO = {
+ "createTime": "2017-04-01 02:22:27",
+ "domain": "Default",
+ "name": "TiS_R4",
+ "password": "admin",
+ "tenant": "admin",
+ "type": "openstack",
+ "url": "http://128.224.180.14:5000/v3",
+ "userName": "admin",
+ "vendor": "Lenovo",
+ "version": "newton",
+ "vimId": "lenovo-hudson-dc_RegionOne",
+ 'cloud_owner': 'lenovo-hudson-dc',
+ 'cloud_region_id': 'RegionOne',
+ 'cloud_extra_info': '',
+ 'insecure': 'True',
+}
+
+MOCK_GET_FLAVOR_RESPONSE = {
+ "flavors": [
+ {
+ "id": "1", "name": "micro", "vcpus": 1, "ram": "1MB",
+ "disk": "1G", "OS-FLV-EXT-DATA:ephemeral": False,
+ "swap": True, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True, "link": [{"href": 1}]
+ },
+ {
+ "id": "2", "name": "mini", "vcpus": 2, "ram": "2MB",
+ "disk": "2G", "OS-FLV-EXT-DATA:ephemeral": True,
+ "swap": False, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True
+ },
+ ]
+}
+
+MOCK_GET_FLAVOR_RESPONSE_w_hpa_numa = {
+ "flavors": [
+ {
+ "id": "1", "name": "onap.big", "vcpus": 6, "ram": "8192",
+ "disk": "10", "OS-FLV-EXT-DATA:ephemeral": False,
+ "swap": True, "os-flavor-access:is_public": True,
+ "OS-FLV-DISABLED:disabled": True, "link": [{"href": 1}]
+ }
+ ]
+}
+MOCK_GET_FLAVOR_EXTRASPECS_RESPONSE_w_hpa_numa = {
+ "hw:numa_nodes": 2
+}
+
+
+class TestRegistration2(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+ self.view = registration.Registry()
+
+ def tearDown(self):
+ pass
+
+ def test_discover_flavors(self):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_session = test_base.get_mock_session(
+ ["get"],
+ {
+ "get": {
+ "content": MOCK_GET_FLAVOR_RESPONSE
+ }
+ }
+ )
+
+ resp = self.view._discover_flavors(vimid="lenovo-hudson-dc_RegionOne",
+ session=mock_session, viminfo=MOCK_VIM_INFO)
+
+ self.assertIsNone(resp)
+
+ def test_discover_flavors_w_hpa_numa(self):
+ restcall.req_to_aai = mock.Mock()
+ restcall.req_to_aai.return_value = (0, {}, status.HTTP_200_OK)
+ mock_session = test_base.get_mock_session(
+ ["get"],
+ {
+ "side_effect": [
+ {"content": MOCK_GET_FLAVOR_RESPONSE_w_hpa_numa},
+ {"content": MOCK_GET_FLAVOR_EXTRASPECS_RESPONSE_w_hpa_numa}
+ ]
+ }
+ ),
+
+ resp = self.view._discover_flavors(vimid="lenovo-hudson-dc_RegionOne",
+ session=mock_session, viminfo=MOCK_VIM_INFO)
+
+ self.assertIsNone(resp)
diff --git a/lenovo/thinkcloud/registration/views/__init__.py b/lenovo/thinkcloud/registration/views/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/registration/views/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/registration/views/registration.py b/lenovo/thinkcloud/registration/views/registration.py
new file mode 100644
index 00000000..0fe4a80e
--- /dev/null
+++ b/lenovo/thinkcloud/registration/views/registration.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from django.conf import settings
+from newton_base.registration import registration as newton_registration
+from common.msapi import extsys
+
+logger = logging.getLogger(__name__)
+
+# DEBUG=True
+
+
+class Registry(newton_registration.Registry):
+
+ def __init__(self):
+ self.proxy_prefix = settings.MULTICLOUD_PREFIX
+ self.aai_base_url = settings.AAI_BASE_URL
+ self._logger = logger
+
+
+class RegistryV1(Registry):
+ def __init__(self):
+ self.proxy_prefix = settings.MULTICLOUD_API_V1_PREFIX
+ self.aai_base_url = settings.AAI_BASE_URL
+ self._logger = logger
+
+ def post(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("registration with : %s, %s" % (cloud_owner, cloud_region_id))
+ self._logger.debug("with data: %s" % request.data)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(RegistryV1, self).post(request, vimid)
+
+ def delete(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.debug("unregister cloud region: %s, %s" % (cloud_owner, cloud_region_id))
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(RegistryV1, self).delete(request, vimid)
diff --git a/lenovo/thinkcloud/requests/__init__.py b/lenovo/thinkcloud/requests/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/requests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/requests/urls.py b/lenovo/thinkcloud/requests/urls.py
new file mode 100644
index 00000000..8b1ebbc0
--- /dev/null
+++ b/lenovo/thinkcloud/requests/urls.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from newton_base.openoapi import network
+from newton_base.openoapi import subnet
+from newton_base.openoapi import image
+from newton_base.openoapi import volume
+from newton_base.openoapi import server
+from newton_base.openoapi import vport
+from newton_base.openoapi import limits
+from newton_base.openoapi import hosts
+from newton_base.openoapi import flavor
+
+urlpatterns = [
+ url(r'^networks(/(?P<networkid>[0-9a-zA-Z_-]+))?',
+ network.Networks.as_view()),
+ url(r'^subnets(/(?P<subnetid>[0-9a-zA-Z_-]+))?',
+ subnet.Subnets.as_view()),
+ url(r'^images(/(?P<imageid>[0-9a-zA-Z_-]+))?',
+ image.Images.as_view()),
+ url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
+ volume.Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.ServerAction.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
+ server.Servers.as_view()),
+ url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',
+ vport.Vports.as_view()),
+ url(r'^flavors(/(?P<flavorid>[0-9a-zA-Z_-]+))?',
+ flavor.Flavors.as_view()),
+ url(r'^limits$', limits.Limits.as_view()),
+ url(r'^hosts(/(?P<hostname>[0-9a-zA-Z_-]+))?', hosts.Hosts.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/requests/urlsV1.py b/lenovo/thinkcloud/requests/urlsV1.py
new file mode 100644
index 00000000..80b0a7e9
--- /dev/null
+++ b/lenovo/thinkcloud/requests/urlsV1.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from newton_base.openoapi import network
+from newton_base.openoapi import subnet
+from newton_base.openoapi import image
+from newton_base.openoapi import volume
+from newton_base.openoapi import server
+from newton_base.openoapi import vport
+from newton_base.openoapi import limits
+from newton_base.openoapi import hosts
+from newton_base.openoapi import flavor
+
+urlpatterns = [
+ url(r'^networks(/(?P<networkid>[0-9a-zA-Z_-]+))?',
+ network.APIv1Networks.as_view()),
+ url(r'^subnets(/(?P<subnetid>[0-9a-zA-Z_-]+))?',
+ subnet.APIv1Subnets.as_view()),
+ url(r'^images(/(?P<imageid>[0-9a-zA-Z_-]+))?',
+ image.APIv1Images.as_view()),
+ url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
+ volume.APIv1Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.APIv1ServerAction.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
+ server.APIv1Servers.as_view()),
+ url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',
+ vport.APIv1Vports.as_view()),
+ url(r'^flavors(/(?P<flavorid>[0-9a-zA-Z_-]+))?',
+ flavor.APIv1Flavors.as_view()),
+ url(r'^limits$', limits.APIv1Limits.as_view()),
+ url(r'^hosts(/(?P<hostname>[0-9a-zA-Z_-]+))?', hosts.APIv1Hosts.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/requests/views/__init__.py b/lenovo/thinkcloud/requests/views/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/requests/views/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/resource/__init__.py b/lenovo/thinkcloud/resource/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/resource/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/resource/tests/__init__.py b/lenovo/thinkcloud/resource/tests/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/resource/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/resource/tests/test_capacity.py b/lenovo/thinkcloud/resource/tests/test_capacity.py
new file mode 100644
index 00000000..c592d5ef
--- /dev/null
+++ b/lenovo/thinkcloud/resource/tests/test_capacity.py
@@ -0,0 +1,277 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+import json
+
+from rest_framework import status
+
+from newton_base.tests import mock_info
+from newton_base.tests import test_base
+from newton_base.util import VimDriverUtils
+
+MOCK_GET_TENANT_LIMIT_RESPONSE = {
+ "limits": {
+ "rate": [],
+ "absolute": {
+ "maxTotalRAMSize": 128 * 1024,
+ "totalRAMUsed": 8 * 1024,
+ "totalCoresUsed": 4,
+ "maxTotalCores": 20,
+ }
+ }
+}
+
+MOCK_GET_HYPER_STATATICS_RESPONSE = {
+ "hypervisor_statistics": {
+ "vcpus_used": 4,
+ "free_ram_mb": 120 * 1024,
+ "vcpus": 10,
+ "free_disk_gb": 300
+ }
+}
+
+MOCK_GET_STORAGE_RESPONSE_OOS = {
+ "limits": {
+ "rate": [],
+ "absolute": {
+ "totalGigabytesUsed": 498,
+ "maxTotalVolumeGigabytes": 500,
+ }
+ }
+}
+
+MOCK_GET_TENANT_LIMIT_RESPONSE_OUTOFRAM = {
+ "limits": {
+ "rate": [],
+ "absolute": {
+ "maxTotalRAMSize": 128 * 1024,
+ "totalRAMUsed": 1 * 1024,
+ "totalCoresUsed": 4,
+ "maxTotalCores": 20,
+ }
+ }
+}
+
+MOCK_GET_HYPER_STATATICS_RESPONSE_OUTOFVCPU = {
+ "hypervisor_statistics": {
+ "vcpus_used": 9,
+ "free_ram_mb": 120 * 1024,
+ "vcpus": 10,
+ "free_disk_gb": 300
+ }
+}
+
+MOCK_GET_HYPER_STATATICS_RESPONSE_OUTOFSTORAGE = {
+ "hypervisor_statistics": {
+ "vcpus_used": 4,
+ "free_ram_mb": 120 * 1024,
+ "vcpus": 10,
+ "free_disk_gb": 3
+ }
+}
+
+MOCK_GET_HYPER_STATATICS_RESPONSE_OUTOFRAM = {
+ "hypervisor_statistics": {
+ "vcpus_used": 4,
+ "free_ram_mb": 1 * 1024,
+ "vcpus": 10,
+ "free_disk_gb": 300
+ }
+}
+
+MOCK_GET_STORAGE_RESPONSE = {
+ "limits": {
+ "rate": [],
+ "absolute": {
+ "totalGigabytesUsed": 200,
+ "maxTotalVolumeGigabytes": 500,
+ }
+ }
+}
+
+TEST_REQ_SUCCESS_SOURCE = {
+ "vCPU": "4",
+ "Memory": "4096",
+ "Storage": "200"
+}
+
+TEST_REQ_FAILED_SOURCE = {
+ "vCPU": "17",
+ "Memory": "4096",
+ "Storage": "200"
+}
+
+
+class TestCapacity(test_base.TestRequest):
+ def setUp(self):
+ super(TestCapacity, self).setUp()
+
+ def _get_mock_response(self, return_value=None):
+ mock_response = mock.Mock(spec=test_base.MockResponse)
+ mock_response.status_code = status.HTTP_200_OK
+ mock_response.json.return_value = return_value
+ return mock_response
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_success(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ TEST_REQ_SUCCESS_SOURCE,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": True}, response.data)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_nova_limits_failed(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ TEST_REQ_FAILED_SOURCE,
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": False}, response.data)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_nova_hypervisor_outofram(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE_OUTOFRAM),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ data=json.dumps(TEST_REQ_SUCCESS_SOURCE),
+ content_type='application/json',
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": False}, response.data)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_nova_hypervisor_outofstorage(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE_OUTOFSTORAGE),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ data=json.dumps(TEST_REQ_SUCCESS_SOURCE),
+ content_type='application/json',
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": False}, response.data)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_nova_hypervisor_outofvcpu(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE_OUTOFVCPU),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ data=json.dumps(TEST_REQ_SUCCESS_SOURCE),
+ content_type='application/json',
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": False}, response.data)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_nova_limits_outofram(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE_OUTOFRAM),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ data=json.dumps(TEST_REQ_SUCCESS_SOURCE),
+ content_type='application/json',
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": True}, response.data)
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_capacity_check_volume_limits_outofstorage(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_TENANT_LIMIT_RESPONSE),
+ self._get_mock_response(MOCK_GET_HYPER_STATATICS_RESPONSE),
+ self._get_mock_response(MOCK_GET_STORAGE_RESPONSE_OOS),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/capacity_check",
+ data=json.dumps(TEST_REQ_SUCCESS_SOURCE),
+ content_type='application/json',
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual({"result": False}, response.data)
diff --git a/lenovo/thinkcloud/resource/tests/test_events.py b/lenovo/thinkcloud/resource/tests/test_events.py
new file mode 100644
index 00000000..5a363a4d
--- /dev/null
+++ b/lenovo/thinkcloud/resource/tests/test_events.py
@@ -0,0 +1,350 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+
+from rest_framework import status
+
+from newton_base.tests import mock_info
+from newton_base.tests import test_base
+from newton_base.util import VimDriverUtils
+
+MOCK_GET_SERVERS_DETAIL_RESPONSE = {
+ "servers": [
+ {
+ "accessIPv4": "",
+ "OS-EXT-SRV-ATTR:instance_name": "instance-0000000a",
+ "OS-SRV-USG:terminated_at": "",
+ "accessIPv6": "",
+ "config_drive": "",
+ "OS-DCF:diskConfig": "AUTO",
+ "updated": "2018-03-27T02:17:12Z",
+ "metadata": {},
+ "id": "12f5b1d0-fe5c-469f-a7d4-b62a91134bf8",
+ "flavor": {
+ "id": "60edb520-5826-4ae7-9e07-709b19ba6f39",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/flavors/60edb520-5826-4ae7-9e07-709b19ba6f39"
+ }
+ ]
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://192.168.100.100:8774/v2.1/ad979139d5ea4a84b21b3620c0e4761e/servers/12f5b1d0-fe5c-469f-a7d4-b62a91134bf8"
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/servers/12f5b1d0-fe5c-469f-a7d4-b62a91134bf8"
+ }
+ ],
+ "OS-EXT-SRV-ATTR:host": "compute-0",
+ "OS-EXT-AZ:availability_zone": "nova",
+ "name": "test1",
+ "wrs-res:pci_devices": "",
+ "hostId": "b3479a460f5effda10c6fdb860e824be631026c1d09f551479180577",
+ "user_id": "777155411f3042c9b7e3194188d6f85d",
+ "status": "PAUSED",
+ "OS-EXT-STS:power_state": 3,
+ "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute-0",
+ "tenant_id": "ad979139d5ea4a84b21b3620c0e4761e",
+ "OS-SRV-USG:launched_at": "2018-03-27T02:16:40.000000",
+ "OS-EXT-STS:vm_state": "paused",
+ "wrs-if:nics": [
+ {
+ "nic1": {
+ "mac_address": "fa:16:3e:5f:1a:76",
+ "network": "mgmt",
+ "port_id": "6c225c23-abe3-42a8-8909-83471503d5d4",
+ "vif_model": "virtio",
+ "vif_pci_address": "",
+ "mtu": 9216
+ }
+ },
+ {
+ "nic2": {
+ "mac_address": "fa:16:3e:7c:7b:d7",
+ "network": "data0",
+ "port_id": "cbea2fec-c9b8-48ec-a964-0e3e255841bc",
+ "vif_model": "virtio",
+ "vif_pci_address": "",
+ "mtu": 9216
+ }
+ }
+ ],
+ "wrs-sg:server_group": "",
+ "OS-EXT-STS:task_state": "",
+ "wrs-res:topology": "node:0, 1024MB, pgsize:2M, 1s,1c,2t, vcpus:0,1, pcpus:5,21, siblings:{0,1}, pol:ded, thr:pre\nnode:1, 1024MB, pgsize:2M, 1s,1c,2t, vcpus:2,3, pcpus:8,24, siblings:{2,3}, pol:ded, thr:pre",
+ "wrs-res:vcpus": [4, 4, 4],
+ "key_name": "",
+ "image": {
+ "id": "7ba636ef-5dfd-4e67-ad32-cd23ee74e1eb",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/images/7ba636ef-5dfd-4e67-ad32-cd23ee74e1eb"
+ }
+ ]
+ },
+ "created": "2018-03-27T02:16:32Z",
+ "addresses": {
+ "data0": [
+ {
+ "OS-EXT-IPS:type": "fixed",
+ "version": 4,
+ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:7b:d7",
+ "addr": "192.168.2.8"
+ }
+ ],
+ "mgmt": [
+ {
+ "OS-EXT-IPS:type": "fixed",
+ "version": 4,
+ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:5f:1a:76",
+ "addr": "192.168.1.6"
+ }
+ ]
+ },
+ "os-extended-volumes:volumes_attached": []
+ },
+ {
+ "accessIPv4": "",
+ "OS-EXT-SRV-ATTR:instance_name": "instance-00000009",
+ "OS-SRV-USG:terminated_at": "",
+ "accessIPv6": "",
+ "config_drive": "",
+ "OS-DCF:diskConfig": "AUTO",
+ "updated": "2018-03-27T02:12:21Z",
+ "metadata": {},
+ "id": "3f1b0375-a1db-4d94-b336-f32c82c0d7ec",
+ "flavor": {
+ "id": "0d3b1381-1626-4f6b-869b-4a4d5d42085e",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/flavors/0d3b1381-1626-4f6b-869b-4a4d5d42085e"
+ }
+ ]
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://192.168.100.100:8774/v2.1/ad979139d5ea4a84b21b3620c0e4761e/servers/3f1b0375-a1db-4d94-b336-f32c82c0d7ec"
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/servers/3f1b0375-a1db-4d94-b336-f32c82c0d7ec"
+ }
+ ],
+ "OS-EXT-SRV-ATTR:host": "compute-0",
+ "OS-EXT-AZ:availability_zone": "nova",
+ "name": "test2",
+ "wrs-res:pci_devices": "",
+ "hostId": "b3479a460f5effda10c6fdb860e824be631026c1d09f551479180577",
+ "user_id": "777155411f3042c9b7e3194188d6f85d",
+ "status": "ACTIVE",
+ "OS-EXT-STS:power_state": 1,
+ "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute-0",
+ "tenant_id": "ad979139d5ea4a84b21b3620c0e4761e",
+ "OS-SRV-USG:launched_at": "2018-03-27T02:12:21.000000",
+ "OS-EXT-STS:vm_state": "active",
+ "wrs-if:nics": [
+ {
+ "nic1": {
+ "mac_address": "fa:16:3e:54:f8:a6",
+ "network": "mgmt",
+ "port_id": "30e2f51c-4473-4650-9ae9-a35e5d7ad452",
+ "vif_model": "avp",
+ "vif_pci_address": "",
+ "mtu": 9216
+ }
+ }
+ ],
+ "wrs-sg:server_group": "",
+ "OS-EXT-STS:task_state": "",
+ "wrs-res:topology": "node:0, 4096MB, pgsize:2M, 1s,3c,1t, vcpus:0-2, pcpus:4,20,7, pol:ded, thr:pre",
+ "progress": 0,
+ "wrs-res:vcpus": [3, 3, 3],
+ "key_name": "",
+ "image": {
+ "id": "7ba636ef-5dfd-4e67-ad32-cd23ee74e1eb",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/images/7ba636ef-5dfd-4e67-ad32-cd23ee74e1eb"
+ }
+ ]
+ },
+ "created": "2018-03-27T02:10:26Z",
+ "addresses": {
+ "mgmt": [
+ {
+ "OS-EXT-IPS:type": "fixed",
+ "version": 4,
+ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:54:f8:a6",
+ "addr": "192.168.1.11"
+ }
+ ]
+ },
+ "os-extended-volumes:volumes_attached": []
+ },
+ {
+ "accessIPv4": "",
+ "OS-EXT-SRV-ATTR:instance_name": "instance-00000008",
+ "OS-SRV-USG:terminated_at": "",
+ "accessIPv6": "",
+ "config_drive": "",
+ "OS-DCF:diskConfig": "AUTO",
+ "updated": "2018-03-27T02:12:15Z",
+ "metadata": {},
+ "id": "1b6f6671-b680-42cd-89e9-fc4ddd5d2e02",
+ "flavor": {
+ "id": "0d3b1381-1626-4f6b-869b-4a4d5d42085e",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/flavors/0d3b1381-1626-4f6b-869b-4a4d5d42085e"
+ }
+ ]
+ },
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://192.168.100.100:8774/v2.1/ad979139d5ea4a84b21b3620c0e4761e/servers/1b6f6671-b680-42cd-89e9-fc4ddd5d2e02"
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/servers/1b6f6671-b680-42cd-89e9-fc4ddd5d2e02"
+ }
+ ],
+ "OS-EXT-SRV-ATTR:host": "compute-0",
+ "OS-EXT-AZ:availability_zone": "nova",
+ "name": "test3",
+ "wrs-res:pci_devices": "",
+ "hostId": "b3479a460f5effda10c6fdb860e824be631026c1d09f551479180577",
+ "user_id": "777155411f3042c9b7e3194188d6f85d",
+ "status": "ACTIVE",
+ "OS-EXT-STS:power_state": 1,
+ "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute-0",
+ "tenant_id": "ad979139d5ea4a84b21b3620c0e4761e",
+ "OS-SRV-USG:launched_at": "2018-03-27T02:12:15.000000",
+ "OS-EXT-STS:vm_state": "active",
+ "wrs-if:nics": [
+ {
+ "nic1": {
+ "mac_address": "fa:16:3e:4e:9b:75",
+ "network": "mgmt",
+ "port_id": "72d13987-1d94-4a64-aa1a-973869ae1cad",
+ "vif_model": "avp",
+ "vif_pci_address": "",
+ "mtu": 9216
+ }
+ }
+ ],
+ "wrs-sg:server_group": "",
+ "OS-EXT-STS:task_state": "",
+ "wrs-res:topology": "node:0, 4096MB, pgsize:2M, 1s,3c,1t, vcpus:0-2, pcpus:19,3,22, pol:ded, thr:pre",
+ "progress": 0,
+ "wrs-res:vcpus": [3, 3, 3],
+ "key_name": "",
+ "image": {
+ "id": "7ba636ef-5dfd-4e67-ad32-cd23ee74e1eb",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://192.168.100.100:8774/ad979139d5ea4a84b21b3620c0e4761e/images/7ba636ef-5dfd-4e67-ad32-cd23ee74e1eb"
+ }
+ ]
+ },
+ "created": "2018-03-27T02:10:01Z",
+ "addresses": {
+ "mgmt": [
+ {
+ "OS-EXT-IPS:type": "fixed",
+ "version": 4,
+ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:4e:9b:75",
+ "addr": "192.168.1.8"
+ }
+ ]
+ },
+ "os-extended-volumes:volumes_attached": []
+ }
+ ]
+}
+
+SUCCESS_VMSTATE_RESPONSE = {
+ 'result': [
+ {
+ 'name': 'test1',
+ 'power_state': 3,
+ 'id': '12f5b1d0-fe5c-469f-a7d4-b62a91134bf8',
+ 'state': 'paused',
+ 'tenant_id': 'ad979139d5ea4a84b21b3620c0e4761e',
+ 'host': 'compute-0',
+ 'availability_zone': 'nova',
+ 'launched_at': '2018-03-27T02:16:40.000000'
+ },
+ {
+ 'name': 'test2',
+ 'power_state': 1,
+ 'id': '3f1b0375-a1db-4d94-b336-f32c82c0d7ec',
+ 'state': 'active',
+ 'tenant_id': 'ad979139d5ea4a84b21b3620c0e4761e',
+ 'host': 'compute-0',
+ 'availability_zone': 'nova',
+ 'launched_at': '2018-03-27T02:12:21.000000'
+ },
+ {
+ 'name': 'test3',
+ 'power_state': 1,
+ 'id': '1b6f6671-b680-42cd-89e9-fc4ddd5d2e02',
+ 'state': 'active',
+ 'tenant_id': 'ad979139d5ea4a84b21b3620c0e4761e',
+ 'host': 'compute-0',
+ 'availability_zone': 'nova',
+ 'launched_at': '2018-03-27T02:12:15.000000'
+ }
+ ]
+}
+
+
+class TestEvents(test_base.TestRequest):
+ def setUp(self):
+ super(TestEvents, self).setUp()
+
+ def _get_mock_response(self, return_value=None):
+ mock_response = mock.Mock(spec=test_base.MockResponse)
+ mock_response.status_code = status.HTTP_200_OK
+ mock_response.json.return_value = return_value
+ return mock_response
+
+ @mock.patch.object(VimDriverUtils, 'get_session')
+ @mock.patch.object(VimDriverUtils, 'get_vim_info')
+ def test_events_check_success(self, mock_get_vim_info, mock_get_session):
+ mock_get_vim_info.return_value = mock_info.MOCK_VIM_INFO
+ mock_get_session.return_value = test_base.get_mock_session(
+ ["get"], {
+ "side_effect": [
+ self._get_mock_response(MOCK_GET_SERVERS_DETAIL_RESPONSE),
+ ]
+ })
+
+ response = self.client.post(
+ "/api/multicloud-thinkcloud/v0/lenovo-hudson-dc_RegionOne/events_check",
+ HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
+
+ self.assertEquals(status.HTTP_200_OK, response.status_code)
+ self.assertEqual(SUCCESS_VMSTATE_RESPONSE, response.data)
diff --git a/lenovo/thinkcloud/resource/tests/tests_infra_workload.py b/lenovo/thinkcloud/resource/tests/tests_infra_workload.py
new file mode 100644
index 00000000..9c263d61
--- /dev/null
+++ b/lenovo/thinkcloud/resource/tests/tests_infra_workload.py
@@ -0,0 +1,332 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import mock
+
+import unittest
+from rest_framework import status
+
+from common.msapi.helper import Helper as helper
+from thinkcloud.resource.views.infra_workload import InfraWorkload
+from thinkcloud.resource.views.infra_workload import APIv1InfraWorkload
+
+MOCK_TOKEN_RESPONSE = {
+ "access": {
+ "token": {
+ "issued_at": "2018-05-10T16:56:56.000000Z",
+ "expires": "2018-05-10T17:56:56.000000Z",
+ "id": "4a832860dd744306b3f66452933f939e",
+ "tenant": {
+ "domain": {"id": "default", "name": "Default"},
+ "enabled": "true",
+ "id": "0e148b76ee8c42f78d37013bf6b7b1ae",
+ "name": "VIM"
+ }
+ },
+ "serviceCatalog": [],
+ "user": {
+ "domain": {"id": "default", "name": "Default"},
+ "id": "ba76c94eb5e94bb7bec6980e5507aae2",
+ "name": "demo"
+ }
+ }
+}
+
+MOCK_HEAT_CREATE_BODY1 = {
+ "generic-vnf-id": "MOCK_GENERIF_VNF_ID1",
+ "vf-module-id": "MOCK_VF_MODULE_ID1",
+ "oof_directives": {
+ "directives": [
+ {
+ "id": "MOCK_VNFC_ID1",
+ "type": "vnfc",
+ "directives": [
+ {
+ "type": "flavor_directives",
+ "attributes": [
+ {
+ "attribute_name": "flavor1",
+ "attribute_value": "m1.hpa.medium"
+ }
+ ]
+ },
+ {
+ "type": "sriovNetNetwork_directives",
+ "attributes": [
+ {
+ "attribute_name": "physnetwork_label",
+ "attribute_value": "physnet1"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "sdnc_directives": {},
+ "template_type": "HEAT",
+ "template_data": {
+ "files": {},
+ "disable_rollback": True,
+ "parameters": {
+ "flavor1": "m1.heat"
+ },
+ "stack_name": "teststack",
+ "template": {
+ "heat_template_version": "2013-05-23",
+ "description": "Simple template to test heat commands",
+ "parameters": {
+ "flavor": {
+ "default": "m1.tiny",
+ "type": "string"
+ }
+ },
+ "resources": {
+ "hello_world": {
+ "type": "OS::Nova::Server",
+ "properties": {
+ "key_name": "heat_key",
+ "flavor": {
+ "get_param": "flavor"
+ },
+ "image": "40be8d1a-3eb9-40de-8abd-43237517384f",
+ "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
+ }
+ }
+ }
+ },
+ "timeout_mins": 60
+ }
+}
+
+MOCK_HEAT_CREATE_RESPONSE1 = {
+ 'stack': {
+ 'id': "MOCKED_HEAT_STACK_ID1"
+ }
+}
+
+MOCK_HEAT_LIST_RESPONSE1 = {
+ 'stacks': [
+ {
+ 'stack_status': "CREATE_IN_PROCESS"
+ }
+ ]
+}
+
+
+MOCK_HEAT_CREATE_BODY2 = {
+ "generic-vnf-id": "MOCK_GENERIF_VNF_ID1",
+ "vf-module-id": "MOCK_VF_MODULE_ID1",
+ "template_type": "HEAT",
+ "template_data": {
+ "files": {},
+ "disable_rollback": True,
+ "parameters": {
+ "flavor1": "m1.heat"
+ },
+ "stack_name": "teststack",
+ "template": {
+ "heat_template_version": "2013-05-23",
+ "description": "Simple template to test heat commands",
+ "parameters": {
+ "flavor": {
+ "default": "m1.tiny",
+ "type": "string"
+ }
+ },
+ "resources": {
+ "hello_world": {
+ "type": "OS::Nova::Server",
+ "properties": {
+ "key_name": "heat_key",
+ "flavor": {
+ "get_param": "flavor"
+ },
+ "image": "40be8d1a-3eb9-40de-8abd-43237517384f",
+ "user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
+ }
+ }
+ }
+ },
+ "timeout_mins": 60
+ }
+}
+
+
+class InfraWorkloadTest(unittest.TestCase):
+ def setUp(self):
+ self._InfraWorkload = InfraWorkload()
+ pass
+
+ def tearDown(self):
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_post(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+ mock_request.data = MOCK_HEAT_CREATE_BODY1
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_CREATE_RESPONSE1, status.HTTP_201_CREATED)
+ ]
+
+ vimid = "CloudOwner_Region1"
+
+ response = self._InfraWorkload.post(mock_request, vimid)
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_post_wo_oof_directive(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+ mock_request.data = MOCK_HEAT_CREATE_BODY2
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_CREATE_RESPONSE1, status.HTTP_201_CREATED)
+ ]
+
+ vimid = "CloudOwner_Region1"
+
+ response = self._InfraWorkload.post(mock_request, vimid)
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_get(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_LIST_RESPONSE1, status.HTTP_200_OK)
+ ]
+
+ vimid = "CloudOwner_Region1"
+ mock_stack_id = "MOCKED_HEAT_STACK_ID1"
+
+ response = self._InfraWorkload.get(mock_request, vimid, mock_stack_id)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_delete(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_LIST_RESPONSE1, status.HTTP_200_OK)
+ ]
+
+ vimid = "CloudOwner_Region1"
+ mock_stack_id = "MOCKED_HEAT_STACK_ID1"
+
+ response = self._InfraWorkload.delete(mock_request, vimid, mock_stack_id)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ pass
+
+
+class APIv1InfraWorkloadTest(unittest.TestCase):
+ def setUp(self):
+ self._APIv1InfraWorkload = APIv1InfraWorkload()
+ pass
+
+ def tearDown(self):
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_post(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+ mock_request.data = MOCK_HEAT_CREATE_BODY1
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_CREATE_RESPONSE1, status.HTTP_201_CREATED)
+ ]
+
+ cloud_owner = "CloudOwner"
+ cloud_region_id = "Region1"
+
+ response = self._APIv1InfraWorkload.post(mock_request, cloud_owner, cloud_region_id)
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_get(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_LIST_RESPONSE1, status.HTTP_200_OK)
+ ]
+
+ cloud_owner = "CloudOwner"
+ cloud_region_id = "Region1"
+ mock_stack_id = "MOCKED_HEAT_STACK_ID1"
+
+ response = self._APIv1InfraWorkload.get(mock_request, cloud_owner, cloud_region_id, mock_stack_id)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ pass
+
+ @mock.patch.object(helper, 'MultiCloudServiceHelper')
+ @mock.patch.object(helper, 'MultiCloudIdentityHelper')
+ def test_delete(self, mock_MultiCloudIdentityHelper, mock_MultiCloudServiceHelper):
+ mock_request = mock.Mock()
+ mock_request.META = {"testkey": "testvalue"}
+
+ mock_MultiCloudIdentityHelper.side_effect = [
+ (0, MOCK_TOKEN_RESPONSE, status.HTTP_201_CREATED)
+ ]
+
+ mock_MultiCloudServiceHelper.side_effect = [
+ (0, MOCK_HEAT_LIST_RESPONSE1, status.HTTP_200_OK)
+ ]
+
+ cloud_owner = "CloudOwner"
+ cloud_region_id = "Region1"
+ mock_stack_id = "MOCKED_HEAT_STACK_ID1"
+
+ response = self._APIv1InfraWorkload.delete(mock_request, cloud_owner, cloud_region_id, mock_stack_id)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ pass
diff --git a/lenovo/thinkcloud/resource/views/__init__.py b/lenovo/thinkcloud/resource/views/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/resource/views/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/resource/views/capacity.py b/lenovo/thinkcloud/resource/views/capacity.py
new file mode 100644
index 00000000..e76c93a3
--- /dev/null
+++ b/lenovo/thinkcloud/resource/views/capacity.py
@@ -0,0 +1,144 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import traceback
+
+from common.exceptions import VimDriverNewtonException
+from newton_base.util import VimDriverUtils
+
+from keystoneauth1.exceptions import HttpError
+from rest_framework import status
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from common.msapi import extsys
+
+
+logger = logging.getLogger(__name__)
+
+
+class CapacityCheck(APIView):
+
+ def __init__(self):
+ self._logger = logger
+
+ def post(self, request, vimid=""):
+ self._logger.info("CapacityCheck--post::vimid, data> %s, %s" % (vimid, request.data))
+ self._logger.debug("CapacityCheck--post::META> %s" % request.META)
+
+ hasEnoughResource = False
+ try:
+ resource_demand = request.data
+
+ tenant_name = None
+ vim = VimDriverUtils.get_vim_info(vimid)
+ sess = VimDriverUtils.get_session(vim, tenant_name)
+
+ # get token:
+ cloud_owner, regionid = extsys.decode_vim_id(vimid)
+ interface = 'public'
+ service = {'service_type': 'compute',
+ 'interface': interface,
+ 'region_name': vim['openstack_region_id']
+ if vim.get('openstack_region_id')
+ else vim['cloud_region_id']}
+
+ # get limit for this tenant
+ req_resouce = "/limits"
+ resp = sess.get(req_resouce, endpoint_filter=service)
+ content = resp.json()
+ compute_limits = content['limits']['absolute']
+
+ # get total resource of this cloud region
+ try:
+ req_resouce = "/os-hypervisors/statistics"
+ self._logger.info("check os-hypervisors statistics> URI:%s" % req_resouce)
+ resp = sess.get(req_resouce, endpoint_filter=service)
+ self._logger.info("check os-hypervisors statistics> status:%s" % resp.status_code)
+ content = resp.json()
+ hypervisor_statistics = content['hypervisor_statistics']
+ self._logger.debug("check os-hypervisors statistics> resp data:%s" % content)
+ except HttpError as e:
+ if e.http_status == status.HTTP_403_FORBIDDEN:
+ # Due to non administrator account cannot get hypervisor data,
+ # so construct enough resource data
+ conVCPUS = int(resource_demand['vCPU'])
+ conFreeRamMB = int(resource_demand['Memory'])
+ conFreeDiskGB = int(resource_demand['Storage'])
+ self._logger.info("Non administator forbidden to access hypervisor statistics data")
+ hypervisor_statistics = {'vcpus_used': 0, 'vcpus': conVCPUS, 'free_ram_mb': conFreeRamMB, 'free_disk_gb': conFreeDiskGB}
+ else:
+ # non forbiden exeption will be redirected
+ raise e
+
+ # get storage limit for this tenant
+ service['service_type'] = 'volumev2'
+ req_resouce = "/limits"
+ resp = sess.get(req_resouce, endpoint_filter=service)
+ content = resp.json()
+ storage_limits = content['limits']['absolute']
+
+ # compute actual available resource for this tenant
+ remainVCPU = compute_limits['maxTotalCores'] - compute_limits['totalCoresUsed']
+ remainHypervisorVCPU = hypervisor_statistics['vcpus'] - hypervisor_statistics['vcpus_used']
+
+ if (remainVCPU > remainHypervisorVCPU):
+ remainVCPU = remainHypervisorVCPU
+
+ remainMEM = compute_limits['maxTotalRAMSize'] - compute_limits['totalRAMUsed']
+ remainHypervisorMEM = hypervisor_statistics['free_ram_mb']
+ if remainMEM > remainHypervisorMEM:
+ remainMEM = remainHypervisorMEM
+
+ remainStorage = storage_limits['maxTotalVolumeGigabytes'] - storage_limits['totalGigabytesUsed']
+ remainHypervisorStorage = hypervisor_statistics['free_disk_gb']
+ if (remainStorage > remainHypervisorStorage):
+ remainStorage = remainHypervisorStorage
+
+ # compare resource demanded with available
+ if (int(resource_demand['vCPU']) > remainVCPU):
+ hasEnoughResource = False
+ elif (int(resource_demand['Memory']) > remainMEM):
+ hasEnoughResource = False
+ elif (int(resource_demand['Storage']) > remainStorage):
+ hasEnoughResource = False
+ else:
+ hasEnoughResource = True
+
+ return Response(data={'result': hasEnoughResource}, status=status.HTTP_200_OK)
+ except VimDriverNewtonException as e:
+ return Response(data={'result': hasEnoughResource, 'error': e.content}, status=e.status_code)
+ except HttpError as e:
+ self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
+ resp = e.response.json()
+ resp.update({'result': hasEnoughResource})
+ return Response(data=e.response.json(), status=e.http_status)
+ except Exception as e:
+ self._logger.error(traceback.format_exc())
+ return Response(data={'result': hasEnoughResource, 'error': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+
+class APIv1CapacityCheck(CapacityCheck):
+
+ def __init__(self):
+ super(APIv1CapacityCheck, self).__init__()
+ # self._logger = logger
+
+ def post(self, request, cloud_owner="", cloud_region_id=""):
+ self._logger.info("vimid, data> %s,%s, %s" % (cloud_owner, cloud_region_id, request.data))
+ self._logger.debug("META> %s" % request.META)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1CapacityCheck, self).post(request, vimid)
diff --git a/lenovo/thinkcloud/resource/views/events.py b/lenovo/thinkcloud/resource/views/events.py
new file mode 100644
index 00000000..ab706889
--- /dev/null
+++ b/lenovo/thinkcloud/resource/views/events.py
@@ -0,0 +1,99 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import traceback
+
+from common.exceptions import VimDriverNewtonException
+from newton_base.util import VimDriverUtils
+
+from keystoneauth1.exceptions import HttpError
+from rest_framework import status
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from common.msapi import extsys
+
+
+logger = logging.getLogger(__name__)
+
+
+class EventsCheck(APIView):
+
+ def __init__(self):
+ self._logger = logger
+
+ def post(self, request, vimid=""):
+ self._logger.info("vimid, data> %s, %s" % (vimid, request.data))
+ self._logger.debug("META> %s" % request.META)
+
+ try:
+ tenant_name = None
+ vim = VimDriverUtils.get_vim_info(vimid)
+ sess = VimDriverUtils.get_session(vim, tenant_name)
+
+ # get token:
+ cloud_owner, regionid = extsys.decode_vim_id(vimid)
+ interface = 'public'
+ service = {
+ 'service_type': 'compute',
+ 'interface': interface,
+ 'region_name': vim['openstack_region_id']
+ if vim.get('openstack_region_id')
+ else vim['cloud_region_id']
+ }
+
+ # get servers detail info
+ req_resouce = "/servers/detail"
+ self._logger.info("check servers detail> URI:%s" % req_resouce)
+ resp = sess.get(req_resouce, endpoint_filter=service)
+ self._logger.info("check servers detail> status:%s" % resp.status_code)
+ content = resp.json()
+ self._logger.debug("check servers detail> resp data:%s" % content)
+
+ # extract server status info
+ if len(content['servers']):
+ servers = content['servers']
+ resp_vmstate = []
+ for num in range(0, len(servers)):
+ vmstate = {
+ 'name': servers[num]['name'],
+ 'state': servers[num]['OS-EXT-STS:vm_state'],
+ 'power_state': servers[num]['OS-EXT-STS:power_state'],
+ 'launched_at': servers[num]['OS-SRV-USG:launched_at'],
+ 'id': servers[num]['id'],
+ 'host': servers[num]['OS-EXT-SRV-ATTR:host'],
+ 'availability_zone': servers[num]['OS-EXT-AZ:availability_zone'],
+ 'tenant_id': servers[num]['tenant_id']
+ }
+
+ resp_vmstate.append(vmstate)
+
+ self._logger.info("RESP with data> result:%s" % resp_vmstate)
+ return Response(data={'result': resp_vmstate}, status=status.HTTP_200_OK)
+
+ except VimDriverNewtonException as e:
+ self._logger.error("Plugin exception> status:%s,error:%s" %
+ (e.status_code, e.content))
+ return Response(data={'result': resp_vmstate, 'error': e.content}, status=e.status_code)
+
+ except HttpError as e:
+ self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
+ resp = e.response.json()
+ resp.update({'result': resp_vmstate})
+ return Response(data=e.response.json(), status=e.http_status)
+
+ except Exception as e:
+ self._logger.error(traceback.format_exc())
+ return Response(data={'result': resp_vmstate, 'error': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
diff --git a/lenovo/thinkcloud/resource/views/infra_workload.py b/lenovo/thinkcloud/resource/views/infra_workload.py
new file mode 100644
index 00000000..ab464da8
--- /dev/null
+++ b/lenovo/thinkcloud/resource/views/infra_workload.py
@@ -0,0 +1,420 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import logging
+import traceback
+
+from django.conf import settings
+from keystoneauth1.exceptions import HttpError
+from rest_framework import status
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+from common.msapi import extsys
+from common.msapi.helper import Helper as helper
+from common.utils import restcall
+from common.exceptions import VimDriverNewtonException
+
+logger = logging.getLogger(__name__)
+
+
+class InfraWorkload(APIView):
+
+ def __init__(self):
+ self._logger = logger
+
+ def post(self, request, vimid="", requri=""):
+ self._logger.info("vimid, requri: %s, %s" % (vimid, requri))
+ self._logger.info("data: %s" % request.data)
+ self._logger.debug("META: %s" % request.META)
+
+ try:
+ data = request.data
+ oof_directive = data.get("oof_directives", {})
+ template_type = data.get("template_type", None)
+ template_data = data.get("template_data", {})
+
+ resp_template = None
+ if template_type and "heat" == template_type.lower():
+ # update heat parameters from oof_directive
+ parameters = template_data.get("parameters", {})
+
+ for directive in oof_directive.get("directives", []):
+ if directive["type"] == "vnfc":
+ for directive2 in directive.get("directives", []):
+ if directive2["type"] in ["flavor_directives", "sriovNICNetwork_directives"]:
+ for attr in directive2.get("attributes", []):
+ label_name = attr["attribute_name"]
+ label_value = attr["attribute_value"]
+ if label_name in parameters:
+ template_data["parameters"][label_name] = label_value
+ else:
+ self._logger.warn("There is no parameter exist: %s" % label_name)
+
+ # update parameters
+ template_data["parameters"] = parameters
+
+ # reset to make sure "files" are empty
+ template_data["file"] = {}
+
+ # authenticate
+ cloud_owner, regionid = extsys.decode_vim_id(vimid)
+ # should go via multicloud proxy so that the selflink is updated by multicloud
+ retcode, v2_token_resp_json, os_status = helper.MultiCloudIdentityHelper(
+ settings.MULTICLOUD_API_V1_PREFIX,
+ cloud_owner, regionid, "/v2.0/tokens")
+ if retcode > 0 or not v2_token_resp_json:
+ logger.error("authenticate fails:%s,%s, %s" %
+ (cloud_owner, regionid, v2_token_resp_json))
+ return
+
+ service_type = "orchestration"
+ resource_uri = "/stacks"
+ self._logger.info("retrieve stack resources, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner,
+ regionid,
+ v2_token_resp_json,
+ service_type,
+ resource_uri,
+ template_data,
+ "POST")
+ stack1 = content.get('stack', None) if retcode == 0 and content else None
+ resp_template = {
+ "template_type": template_type,
+ "workload_id": stack1["id"] if stack1 else "",
+ "template_response": content
+ }
+ self._logger.info("RESP with data> result:%s" % resp_template)
+
+ return Response(data=resp_template, status=os_status)
+
+ else:
+ msg = "The template type %s is not supported" % (template_type)
+ self._logger.warn(msg)
+ return Response(data={"error": msg}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+ except VimDriverNewtonException as e:
+ self._logger.error("Plugin exception> status:%s,error:%s"
+ % (e.status_code, e.content))
+ return Response(data={'error': e.content}, status=e.status_code)
+ except HttpError as e:
+ self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
+ return Response(data=e.response.json(), status=e.http_status)
+ except Exception as e:
+ self._logger.error(traceback.format_exc())
+ return Response(data={'error': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+ def get(self, request, vimid="", requri=""):
+ self._logger.info("vimid, requri: %s, %s" % (vimid, requri))
+ self._logger.debug("META: %s" % request.META)
+
+ try:
+ # assume the workload_type is heat
+ template_type = "heat"
+ stack_id = requri
+ cloud_owner, regionid = extsys.decode_vim_id(vimid)
+ # should go via multicloud proxy so that the selflink is updated by multicloud
+ retcode, v2_token_resp_json, os_status = helper.MultiCloudIdentityHelper(
+ settings.MULTICLOUD_API_V1_PREFIX,
+ cloud_owner, regionid, "/v2.0/tokens")
+ if retcode > 0 or not v2_token_resp_json:
+ logger.error("authenticate fails:%s, %s, %s" % (cloud_owner, regionid, v2_token_resp_json))
+ return
+
+ # get stack status
+ service_type = "orchestration"
+ resource_uri = "/stacks?id=%s" % stack_id if stack_id else "/stacks"
+ self._logger.info("retrieve stack resources, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner, regionid, v2_token_resp_json,
+ service_type, resource_uri, None, "GET")
+ stacks = content.get('stacks', []) if retcode == 0 and content else []
+ stack_status = stacks[0]["stack_status"] if len(stacks) > 0 else ""
+
+ resp_template = {
+ "template_type": template_type,
+ "workload_id": stack_id,
+ "workload_status": stack_status
+ }
+
+ if retcode > 0:
+ resp_template['workload_response'] = content
+
+ if ('CREATE_COMPLETE' == stack_status):
+ self.heatbridge_update(request, vimid, stack_id)
+
+ self._logger.info("RESP with data> result:%s" % resp_template)
+ return Response(data=resp_template, status=status.HTTP_200_OK)
+ except VimDriverNewtonException as e:
+ self._logger.error("Plugin exception> status:%s,error:%s"
+ % (e.status_code, e.content))
+ return Response(data={'error': e.content}, status=e.status_code)
+ except HttpError as e:
+ self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
+ return Response(data=e.response.json(), status=e.http_status)
+ except Exception as e:
+ self._logger.error(traceback.format_exc())
+ return Response(data={'error': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+ def heatbridge_update(self, request, vimid, stack_id):
+ '''
+ update heat resource to AAI for the specified cloud region and tenant
+ The resources includes: vserver, vserver/l-interface,
+ :param request:
+ :param vimid:
+ :param stack_id:
+ :return:
+ '''
+
+ cloud_owner, regionid = extsys.decode_vim_id(vimid)
+ # should go via multicloud proxy so that the selflink is updated by multicloud
+ retcode, v2_token_resp_json, os_status = helper.MultiCloudIdentityHelper(settings.MULTICLOUD_API_V1_PREFIX,
+ cloud_owner, regionid, "/v2.0/tokens")
+ if retcode > 0:
+ logger.error("authenticate fails:%s, %s, %s" % (cloud_owner, regionid, v2_token_resp_json))
+ return None
+ tenant_id = v2_token_resp_json["access"]["token"]["tenant"]["id"]
+ # tenant_name = v2_token_resp_json["access"]["token"]["tenant"]["name"]
+
+ # common prefix
+ aai_cloud_region = "/cloud-infrastructure/cloud-regions/cloud-region/%s/%s/tenants/tenant/%s" \
+ % (cloud_owner, regionid, tenant_id)
+
+ # get stack resource
+ service_type = "orchestration"
+ resource_uri = "/stacks/%s/resources" % (stack_id)
+ self._logger.info("retrieve stack resources, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner, regionid, v2_token_resp_json, service_type, resource_uri, None, "GET")
+ resources = content.get('resources', []) if retcode == 0 and content else []
+
+ # find and update resources
+ transactions = []
+ for resource in resources:
+ if resource.get('resource_status', None) != "CREATE_COMPLETE":
+ continue
+ if resource.get('resource_type', None) == 'OS::Nova::Server':
+ # retrieve vserver details
+ service_type = "compute"
+ resource_uri = "/servers/%s" % (resource['physical_resource_id'])
+ self._logger.info("retrieve vserver detail, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner, regionid, v2_token_resp_json, service_type,
+ resource_uri, None, "GET")
+ self._logger.debug(" resp data:%s" % content)
+ vserver_detail = content.get('server', None) if retcode == 0 and content else None
+ if vserver_detail:
+ # compose inventory entry for vserver
+ vserver_link = ""
+ for link in vserver_detail['links']:
+ if link['rel'] == 'self':
+ vserver_link = link['href']
+ break
+ pass
+
+ # note: relationship-list to flavor/image is not be update yet
+ # note: volumes is not updated yet
+ # note: relationship-list to vnf will be handled somewhere else
+ aai_resource = {
+ 'body': {
+ 'vserver-name': vserver_detail['name'],
+ 'vserver-name2': vserver_detail['name'],
+ "vserver-id": vserver_detail['id'],
+ "vserver-selflink": vserver_link,
+ "prov-status": vserver_detail['status']
+ },
+ "uri": aai_cloud_region + "/vservers/vserver/%s"
+ % (vserver_detail['id'])
+ }
+
+ try:
+ # then update the resource
+ retcode, content, status_code = \
+ restcall.req_to_aai(aai_resource['uri'], "PUT", content=aai_resource['body'])
+
+ if retcode == 0 and content:
+ content = json.JSONDecoder().decode(content)
+ self._logger.debug("AAI update %s response: %s" % (aai_resource['uri'], content))
+ except Exception as e:
+ self._logger.error(traceback.format_exc(e))
+ pass
+
+ aai_resource_transactions = {"put": [aai_resource]}
+ transactions.append(aai_resource_transactions)
+ # self._logger.debug("aai_resource :%s" % aai_resource_transactions)
+ pass
+
+ for resource in resources:
+ if resource.get('resource_status', None) != "CREATE_COMPLETE":
+ continue
+ if resource.get('resource_type', None) == 'OS::Neutron::Port':
+ # retrieve vserver details
+ service_type = "network"
+ resource_uri = "/v2.0/ports/%s" % (resource['physical_resource_id'])
+ self._logger.info("retrieve vserver detail, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner,
+ regionid,
+ v2_token_resp_json,
+ service_type,
+ resource_uri,
+ None,
+ "GET")
+ self._logger.debug(" resp data:%s" % content)
+
+ vport_detail = content.get('port', None) if retcode == 0 and content else None
+ if vport_detail:
+ # compose inventory entry for vport
+ # note: l3-interface-ipv4-address-list, l3-interface-ipv6-address-list are not updated yet
+ # note: network-name is not update yet since the detail coming with network-id
+ aai_resource = {
+ "body": {
+ "interface-name": vport_detail['name'],
+ "interface-id": vport_detail['id'],
+ "macaddr": vport_detail['mac_address']
+ },
+ 'uri': aai_cloud_region + "/vservers/vserver/%s/l-interfaces/l-interface/%s"
+ % (vport_detail['device_id'], vport_detail['name'])
+ }
+ try:
+ # then update the resource
+ retcode, content, status_code = \
+ restcall.req_to_aai(aai_resource['uri'], "PUT", content=aai_resource['body'])
+
+ if retcode == 0 and content:
+ content = json.JSONDecoder().decode(content)
+ self._logger.debug("AAI update %s response: %s" % (aai_resource['uri'], content))
+ except Exception as e:
+ self._logger.error(traceback.format_exc(e))
+ pass
+
+ aai_resource_transactions = {"put": [aai_resource]}
+ transactions.append(aai_resource_transactions)
+ # self._logger.debug("aai_resource :%s" % aai_resource_transactions)
+
+ pass
+
+ aai_transactions = {"transactions": transactions}
+ self._logger.debug("aai_transactions :%s" % aai_transactions)
+
+ return aai_transactions
+
+ def delete(self, request, vimid="", requri=""):
+ self._logger.info("vimid, requri: %s, %s" % (vimid, requri))
+ self._logger.debug("META: %s" % request.META)
+
+ try:
+ if requri == "":
+ raise VimDriverNewtonException(
+ message="workload_id is not specified",
+ content="workload_id must be specified to delete the workload",
+ status_code=400)
+
+ # assume the workload_type is heat
+ template_type = "heat"
+ stack_id = requri
+ cloud_owner, regionid = extsys.decode_vim_id(vimid)
+ # should go via multicloud proxy so that the selflink is updated by multicloud
+ retcode, v2_token_resp_json, os_status = helper.MultiCloudIdentityHelper(
+ settings.MULTICLOUD_API_V1_PREFIX,
+ cloud_owner, regionid, "/v2.0/tokens")
+ if retcode > 0 or not v2_token_resp_json:
+ logger.error("authenticate fails:%s, %s, %s" % (cloud_owner, regionid, v2_token_resp_json))
+ return
+ # tenant_id = v2_token_resp_json["access"]["token"]["tenant"]["id"]
+ # tenant_name = v2_token_resp_json["access"]["token"]["tenant"]["name"]
+
+ # get stack status
+ service_type = "orchestration"
+ resource_uri = "/stacks?id=%s" % stack_id if stack_id else "/stacks"
+ self._logger.info("retrieve stack resources, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner, regionid, v2_token_resp_json,
+ service_type, resource_uri, None, "GET")
+ stacks = content.get('stacks', []) if retcode == 0 and content else []
+ # assume there is at most 1 stack returned since it was filtered by id
+ stack1 = stacks[0] if stacks else None
+ stack_status = ""
+
+ if stack1 and 'CREATE_COMPLETE' == stack1['stack_status']:
+ # delete the stack
+ resource_uri = "/stacks/%s/%s" % (stack1['stack_name'], stack1['id'])
+ self._logger.info("delete stack, URI:%s" % resource_uri)
+ retcode, content, os_status = helper.MultiCloudServiceHelper(cloud_owner, regionid, v2_token_resp_json,
+ service_type, resource_uri, None, "DELETE")
+ if retcode == 0:
+ stack_status = "DELETE_IN_PROCESS"
+ # and update AAI inventory by heatbridge-delete
+ self.heatbridge_delete(request, vimid, stack1['id'])
+
+ resp_template = {
+ "template_type": template_type,
+ "workload_id": stack_id,
+ "workload_status": stack_status
+ }
+
+ if retcode > 0:
+ resp_template["workload_response"] = content
+
+ self._logger.info("RESP with data> result:%s" % resp_template)
+ return Response(status=os_status)
+ except VimDriverNewtonException as e:
+ self._logger.error("Plugin exception> status:%s,error:%s"
+ % (e.status_code, e.content))
+ return Response(data={'error': e.content}, status=e.status_code)
+ except HttpError as e:
+ self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json()))
+ return Response(data=e.response.json(), status=e.http_status)
+ except Exception as e:
+ self._logger.error(traceback.format_exc())
+ return Response(data={'error': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+ def heatbridge_delete(self, request, stack_id, vimid):
+ '''
+ remove heat resource from AAI for the specified cloud region and tenant
+ The resources includes: vserver, vserver/l-interface,
+ :param request:
+ :param stack_id:
+ :param vimid:
+ :param tenant_id:
+ :return:
+ '''
+ pass
+
+
+class APIv1InfraWorkload(InfraWorkload):
+
+ def __init__(self):
+ super(APIv1InfraWorkload, self).__init__()
+ # self._logger = logger
+
+ def post(self, request, cloud_owner="", cloud_region_id="", requri=""):
+ # self._logger.info("cloud owner, cloud region id, data: %s,%s, %s" % (cloud_owner, cloud_region_id, request.data))
+ # self._logger.debug("META: %s" % request.META)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1InfraWorkload, self).post(request, vimid, requri)
+
+ def get(self, request, cloud_owner="", cloud_region_id="", requri=""):
+ # self._logger.info("cloud owner, cloud region id, data: %s,%s, %s" % (cloud_owner, cloud_region_id, request.data))
+ # self._logger.debug("META: %s" % request.META)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1InfraWorkload, self).get(request, vimid, requri)
+
+ def delete(self, request, cloud_owner="", cloud_region_id="", requri=""):
+ # self._logger.info("cloud owner, cloud region id, data: %s,%s, %s" % (cloud_owner, cloud_region_id, request.data))
+ # self._logger.debug("META: %s" % request.META)
+
+ vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
+ return super(APIv1InfraWorkload, self).delete(request, vimid, requri)
diff --git a/lenovo/thinkcloud/samples/__init__.py b/lenovo/thinkcloud/samples/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/samples/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/samples/tests.py b/lenovo/thinkcloud/samples/tests.py
new file mode 100644
index 00000000..1419027c
--- /dev/null
+++ b/lenovo/thinkcloud/samples/tests.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from django.test import Client
+from rest_framework import status
+
+
+class SampleViewTest(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ def tearDown(self):
+ pass
+
+ def test_sample(self):
+ response = self.client.get("/samples/")
+ self.assertEqual(status.HTTP_200_OK, response.status_code, response.content)
+ resp_data = response.json()
+ self.assertEqual({"status": "active"}, resp_data)
diff --git a/lenovo/thinkcloud/samples/urls.py b/lenovo/thinkcloud/samples/urls.py
new file mode 100644
index 00000000..0871944f
--- /dev/null
+++ b/lenovo/thinkcloud/samples/urls.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from thinkcloud.samples import views
+
+urlpatterns = [
+ url(r'^samples/$', views.SampleList.as_view()), ]
diff --git a/lenovo/thinkcloud/samples/views.py b/lenovo/thinkcloud/samples/views.py
new file mode 100644
index 00000000..714ad911
--- /dev/null
+++ b/lenovo/thinkcloud/samples/views.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from rest_framework.views import APIView
+from rest_framework.response import Response
+
+logger = logging.getLogger(__name__)
+
+
+class SampleList(APIView):
+ """
+ List all samples.
+ """
+ def get(self, request, format=None):
+ logger.debug("get")
+ return Response({"status": "active"})
diff --git a/lenovo/thinkcloud/settings.py b/lenovo/thinkcloud/settings.py
new file mode 100644
index 00000000..22f6eade
--- /dev/null
+++ b/lenovo/thinkcloud/settings.py
@@ -0,0 +1,141 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+from logging import config
+from onaplogging import monkey
+monkey.patch_all()
+
+CACHE_EXPIRATION_TIME = 3600
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = '3o-wney!99y)^h3v)0$j16l9=fdjxcb+a8g+q3tfbahcnu2b0o'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+# DEBUG = True
+
+ALLOWED_HOSTS = ['*']
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'rest_framework',
+]
+
+MIDDLEWARE_CLASSES = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'thinkcloud.middleware.LogContextMiddleware',
+]
+
+ROOT_URLCONF = 'thinkcloud.urls'
+
+WSGI_APPLICATION = 'thinkcloud.wsgi.application'
+
+REST_FRAMEWORK = {
+ 'DEFAULT_RENDERER_CLASSES': (
+ 'rest_framework.renderers.JSONRenderer',
+ ),
+
+ 'DEFAULT_PARSER_CLASSES': (
+ 'rest_framework.parsers.JSONParser',
+ 'rest_framework.parsers.MultiPartParser',
+ # 'rest_framework.parsers.FormParser',
+ # 'rest_framework.parsers.FileUploadParser',
+ )
+}
+
+TIME_ZONE = 'UTC'
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.6/howto/static-files/
+
+STATIC_URL = '/static/'
+
+CACHES = {
+ 'default': {
+ 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
+ 'LOCATION': '127.0.0.1:11211',
+ }
+}
+
+# [MSB]
+MSB_SERVICE_ADDR = os.environ.get('MSB_ADDR', "127.0.0.1")
+MSB_SERVICE_PORT = os.environ.get('MSB_PORT', "80")
+
+# [Multicloud]
+MULTICLOUD_PREFIX = "http://%s:%s/api/multicloud-thinkcloud/v0" % (
+ MSB_SERVICE_ADDR, MSB_SERVICE_PORT)
+
+MULTICLOUD_API_V1_PREFIX = "http://%s:%s/api/multicloud-thinkcloud/v1" % (
+ MSB_SERVICE_ADDR, MSB_SERVICE_PORT)
+
+# [A&AI]
+AAI_ADDR = os.environ.get('AAI_ADDR', "aai.api.simpledemo.openecomp.org")
+AAI_PORT = os.environ.get('AAI_PORT', "8443")
+
+AAI_SERVICE_URL = os.environ.get('AAI_SERVICE_URL', "")
+if AAI_SERVICE_URL == "":
+ AAI_SERVICE_URL = 'https://%s:%s/aai' % (AAI_ADDR, AAI_PORT)
+
+AAI_SCHEMA_VERSION = os.environ.get('AAI_SCHEMA_VERSION', "v13")
+AAI_USERNAME = os.environ.get('AAI_USERNAME', "AAI")
+AAI_PASSWORD = os.environ.get('AAI_PASSWORD', "AAI")
+
+AAI_BASE_URL = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
+
+MULTICLOUD_APP_ID = 'MultiCloud-Thinkcloud'
+
+# [IMAGE LOCAL PATH]
+ROOT_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+OPENSTACK_VERSION = "thinkcloud"
+MULTIVIM_VERSION = "multicloud-" + OPENSTACK_VERSION
+
+LOGGING_CONFIG = None
+# yaml configuration of logging
+LOGGING_FILE = os.path.join(BASE_DIR, 'thinkcloud/pub/config/log.yml')
+config.yamlConfig(filepath=LOGGING_FILE, watchDog=True)
+
+if 'test' in sys.argv:
+
+ # LOGGING['handlers']['thinkcloud_handler']['filename'] = 'logs/thinkcloud.log'
+
+ REST_FRAMEWORK = {}
+ import platform
+
+ if platform.system() == 'Linux':
+ TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
+ TEST_OUTPUT_VERBOSE = True
+ TEST_OUTPUT_DESCRIPTIONS = True
+ TEST_OUTPUT_DIR = 'test-reports'
diff --git a/lenovo/thinkcloud/swagger/__init__.py b/lenovo/thinkcloud/swagger/__init__.py
new file mode 100644
index 00000000..5b09b2fd
--- /dev/null
+++ b/lenovo/thinkcloud/swagger/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/lenovo/thinkcloud/swagger/tests.py b/lenovo/thinkcloud/swagger/tests.py
new file mode 100644
index 00000000..400292a3
--- /dev/null
+++ b/lenovo/thinkcloud/swagger/tests.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from django.test import Client
+from rest_framework import status
+
+
+class SampleViewTest(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ def tearDown(self):
+ pass
+
+ def test_sample(self):
+ response = self.client.get("/api/multicloud-thinkcloud/v0/swagger.json")
+ self.assertEqual(status.HTTP_200_OK, response.status_code, response.content)
+# resp_data = response.json()
+# self.assertEqual({"status": "active"}, resp_data)
diff --git a/lenovo/thinkcloud/swagger/urls.py b/lenovo/thinkcloud/swagger/urls.py
new file mode 100644
index 00000000..7061cc41
--- /dev/null
+++ b/lenovo/thinkcloud/swagger/urls.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from rest_framework.urlpatterns import format_suffix_patterns
+
+from thinkcloud.swagger.views import SwaggerJsonView
+from thinkcloud.swagger.views import APIv1SwaggerJsonView
+
+urlpatterns = [
+ url(r'^api/multicloud-thinkcloud/v0/swagger.json$', SwaggerJsonView.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/swagger.json$', APIv1SwaggerJsonView.as_view()),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/lenovo/thinkcloud/swagger/views.py b/lenovo/thinkcloud/swagger/views.py
new file mode 100644
index 00000000..e3c14c81
--- /dev/null
+++ b/lenovo/thinkcloud/swagger/views.py
@@ -0,0 +1,59 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from rest_framework.response import Response
+
+from newton_base.swagger import views as newton_json_view
+
+logger = logging.getLogger(__name__)
+
+
+class SwaggerJsonView(newton_json_view.SwaggerJsonView):
+
+ def get(self, request):
+ '''
+ reuse newton code and update the basePath
+ :param request:
+ :return:
+ '''
+
+ resp = super(SwaggerJsonView, self).get(request)
+ json_data = resp.data if resp else None
+ if json_data:
+ json_data["basePath"] = "/api/multicloud-thinkcloud/v0/"
+ json_data["info"]["title"] = "Service NBI of MultiCloud plugin for OpenStack Thinkcloud"
+ return Response(data=json_data, status=200)
+ else:
+ return Response(data={'error': 'internal error'}, status=500)
+
+
+class APIv1SwaggerJsonView(newton_json_view.SwaggerJsonView):
+
+ def get(self, request):
+ '''
+ reuse newton code and update the basePath
+ :param request:
+ :return:
+ '''
+
+ resp = super(APIv1SwaggerJsonView, self).get(request)
+ json_data = resp.data if resp else None
+ if json_data:
+ json_data["basePath"] = "/api/multicloud-thinkcloud/v1/"
+ json_data["info"]["title"] = "Service NBI v1 of MultiCloud plugin for Thinkcloud"
+ return Response(data=json_data, status=200)
+ else:
+ return Response(data={'error': 'internal error'}, status=500)
diff --git a/lenovo/thinkcloud/urls.py b/lenovo/thinkcloud/urls.py
new file mode 100644
index 00000000..1828bb33
--- /dev/null
+++ b/lenovo/thinkcloud/urls.py
@@ -0,0 +1,68 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import include, url
+
+from thinkcloud.registration.views import registration
+from newton_base.openoapi import tenants
+from thinkcloud.resource.views import capacity
+from thinkcloud.resource.views import events
+from thinkcloud.resource.views import infra_workload
+
+urlpatterns = [
+ url(r'^', include('thinkcloud.swagger.urls')),
+ url(r'^', include('thinkcloud.samples.urls')),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/registry$',
+ registration.Registry.as_view()),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)$',
+ registration.Registry.as_view()),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/exten',
+ include('thinkcloud.extensions.urls')),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/infra_workload/?$',
+ infra_workload.InfraWorkload.as_view()),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/infra_workload/(?P<requri>[0-9a-zA-Z_-]*)/?$',
+ infra_workload.InfraWorkload.as_view()),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/',
+ include('thinkcloud.proxy.urls')),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/tenants$',
+ tenants.Tenants.as_view()),
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/'
+ '(?P<tenantid>[0-9a-zA-Z_-]{20,})/', include('thinkcloud.requests.urls')),
+ # CapacityCheck
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/capacity_check/?$',
+ capacity.CapacityCheck.as_view()),
+ # events
+ url(r'^api/multicloud-thinkcloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/events_check/?$',
+ events.EventsCheck.as_view()),
+
+ # API upgrading
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/registry$',
+ registration.RegistryV1.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)$',
+ registration.RegistryV1.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/exten',
+ include('thinkcloud.extensions.urlsV1')),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/tenants/?$',
+ tenants.APIv1Tenants.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/'
+ '(?P<tenantid>[0-9a-zA-Z_-]{20,})/', include('thinkcloud.requests.urlsV1')),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/capacity_check/?$',
+ capacity.APIv1CapacityCheck.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/infra_workload/?$',
+ infra_workload.APIv1InfraWorkload.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/infra_workload/(?P<requri>[0-9a-zA-Z_-]*)/?$',
+ infra_workload.APIv1InfraWorkload.as_view()),
+ url(r'^api/multicloud-thinkcloud/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/',
+ include('thinkcloud.proxy.urlsV1')),
+]
diff --git a/lenovo/thinkcloud/wsgi.py b/lenovo/thinkcloud/wsgi.py
new file mode 100644
index 00000000..3abd9d4f
--- /dev/null
+++ b/lenovo/thinkcloud/wsgi.py
@@ -0,0 +1,21 @@
+# Copyright (c) 2017-2018 Lenovo Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "thinkcloud.settings")
+
+application = get_wsgi_application()
diff --git a/lenovo/tox.ini b/lenovo/tox.ini
new file mode 100644
index 00000000..975ed784
--- /dev/null
+++ b/lenovo/tox.ini
@@ -0,0 +1,48 @@
+# Copyright (c) 2018 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[tox]
+envlist = py27,pep8,cov,pylint
+skipsdist = true
+
+[tox:jenkins]
+downloadcache = ~/cache/pip
+
+[flake8]
+ignore = E501,E722
+exclude = ./venv-tox,./.tox
+max-complexity = 27
+
+[testenv]
+setenv =
+ PYTHONPATH = {toxinidir}/../share
+deps =
+ -r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
+commands =
+ coverage run --branch manage.py test thinkcloud
+ coverage report --omit="./venv-tox/*,./.tox/*,*tests*,*__init__.py,*newton_base*,*common*" --fail-under=30
+
+[testenv:pep8]
+deps=flake8
+commands=flake8
+
+[testenv:cov]
+commands = coverage xml --omit="./venv-tox/*,./.tox/*,*tests*,*__init__.py,*newton_base*,*common*, *site-packages*"
+
+[testenv:pylint]
+whitelist_externals = bash
+commands =
+ bash -c "\
+ pylint -f parseable --reports=y thinkcloud | tee pylint.out"
diff --git a/ocata/docker/Dockerfile b/ocata/docker/Dockerfile
index bff5706a..985b6442 100644
--- a/ocata/docker/Dockerfile
+++ b/ocata/docker/Dockerfile
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FROM python:2.7
+FROM python:2-slim
ARG HTTP_PROXY=${HTTP_PROXY}
ARG HTTPS_PROXY=${HTTPS_PROXY}
@@ -33,16 +33,19 @@ EXPOSE 9006
RUN groupadd -r onap && useradd -r -g onap onap
-WORKDIR /opt/ocata
-RUN apt-get update && apt-get install -y memcached unzip
-RUN wget -O /opt/multicloud-openstack-ocata.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-ocata&e=zip&v=1.3.0-SNAPSHOT" && \
+RUN apt-get update && \
+ apt-get install -y memcached wget unzip gcc && \
+ cd /opt/ && \
+ wget -O /opt/multicloud-openstack-ocata.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-ocata&e=zip&v=1.3.0-SNAPSHOT" && \
unzip -q -o -B /opt/multicloud-openstack-ocata.zip -d /opt/ && \
- rm -f /opt/multicloud-openstack-ocata.zip
+ rm -f /opt/multicloud-openstack-ocata.zip && \
+ pip install -r /opt/ocata/requirements.txt && \
+ apt-get --purge remove -y wget unzip gcc && \
+ apt-get -y autoremove && \
+ chown onap:onap /opt/ocata -R
+
RUN mkdir -p /var/log/onap/multicloud/openstack/ocata/
-#COPY ./ .
-RUN pip install -r requirements.txt
-RUN chown onap:onap /opt/ocata -R
USER onap
-
-CMD "/opt/ocata/run.sh"
+WORKDIR /opt/ocata
+CMD /bin/sh -c "/opt/ocata/run.sh"
diff --git a/ocata/ocata/__init__.py b/ocata/ocata/__init__.py
index f7fd66a2..94817625 100644
--- a/ocata/ocata/__init__.py
+++ b/ocata/ocata/__init__.py
@@ -16,6 +16,6 @@ from __future__ import absolute_import, unicode_literals
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
-from .celery import app as celery_app
-
-__all__ = ['celery_app']
+# from .celery import app as celery_app
+#
+# __all__ = ['celery_app']
diff --git a/ocata/ocata/celery.py b/ocata/ocata/celery.py
deleted file mode 100644
index 3dd5d1e5..00000000
--- a/ocata/ocata/celery.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from __future__ import absolute_import, unicode_literals
-import os
-from celery import Celery
-import logging
-
-# set the default Django settings module for the 'celery' program.
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ocata.settings')
-
-app = Celery('ocata')
-
-# Using a string here means the worker doesn't have to serialize
-# the configuration object to child processes.
-# - namespace='CELERY' means all celery-related configuration keys
-# should have a `CELERY_` prefix.
-app.config_from_object('django.conf:settings', namespace='CELERY')
-
-# Load task modules from all registered Django app configs.
-app.autodiscover_tasks()
-
-logger = logging.getLogger(__name__)
-
-
-@app.task(bind=True)
-def debug_task(self):
- logger.debug("self.request")
diff --git a/ocata/ocata/urls.py b/ocata/ocata/urls.py
index ddabfb84..3e825c84 100644
--- a/ocata/ocata/urls.py
+++ b/ocata/ocata/urls.py
@@ -18,7 +18,6 @@ from ocata.registration.views import registration
from newton_base.openoapi import tenants
from ocata.resource.views import capacity
from ocata.resource.views import events
-from ocata.vesagent import vesagent_ctrl
from ocata.resource.views import infra_workload
urlpatterns = [
@@ -46,8 +45,6 @@ urlpatterns = [
# events
url(r'^api/multicloud-ocata/v0/(?P<vimid>[0-9a-zA-Z_-]+)/events_check/?$',
events.EventsCheck.as_view()),
- url(r'^api/multicloud-ocata/v0/(?P<vimid>[0-9a-zA-Z_-]+)/vesagent/?$',
- vesagent_ctrl.VesAgentCtrl.as_view()),
# API upgrading
url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/registry$',
@@ -62,8 +59,6 @@ urlpatterns = [
'(?P<tenantid>[0-9a-zA-Z_-]{20,})/', include('ocata.requests.urlsV1')),
url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/capacity_check/?$',
capacity.APIv1CapacityCheck.as_view()),
- url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/vesagent/?$',
- vesagent_ctrl.APIv1VesAgentCtrl.as_view()),
url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/infra_workload/?$',
infra_workload.APIv1InfraWorkload.as_view()),
url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/infra_workload/(?P<requri>[0-9a-zA-Z_-]*)/?$',
diff --git a/ocata/ocata/vesagent/event_domain/fault_vm.py b/ocata/ocata/vesagent/event_domain/fault_vm.py
deleted file mode 100644
index c3387114..00000000
--- a/ocata/ocata/vesagent/event_domain/fault_vm.py
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import logging
-import json
-import uuid
-import time
-import datetime
-
-from django.conf import settings
-from ocata.vesagent.vespublish import publishAnyEventToVES
-from common.utils import restcall
-
-logger = logging.getLogger(__name__)
-
-
-def get_epoch_now_usecond():
- '''
- get epoch timestamp of this moment in usecond
- :return:
- '''
- now_time = datetime.datetime.now()
- epoch_time_sec = time.mktime(now_time.timetuple())
- return int(epoch_time_sec * 1e6 + now_time.microsecond)
-
-
-# build backlog with domain:"fault", type:"vm"
-def buildBacklog_fault_vm(vimid, backlog_input):
-
- logger.info("vimid: %s" % vimid)
- logger.debug("with input: %s" % backlog_input)
-
- try:
-
- # must resolve the tenant id and server id while building the backlog
- tenant_id = backlog_input.get("tenantid", None)
- server_id = backlog_input.get("sourceid", None)
- server_name = backlog_input.get("source", None)
-
- # should resolve the name to id later
- if tenant_id is None:
- tenant_name = backlog_input["tenant"]
-
- # get token
- # resolve tenant_name to tenant_id
- auth_api_url_format = "/{f_vim_id}/identity/v2.0/tokens"
- auth_api_url = auth_api_url_format.format(f_vim_id=vimid)
- auth_api_data = {"auth": {"tenantName": tenant_name}}
- base_url = settings.MULTICLOUD_PREFIX
- extra_headers = ''
- ret = restcall._call_req(base_url, "", "", 0, auth_api_url, "POST", extra_headers, json.dumps(auth_api_data))
- if ret[0] > 0 or ret[1] is None:
- logger.critical("call url %s failed with status %s" % (auth_api_url, ret[0]))
- return None
-
- token_resp = json.JSONDecoder().decode(ret[1])
- token = token_resp["access"]["token"]["id"]
- tenant_id = token_resp["access"]["token"]["tenant"]["id"]
-
- if server_id is None and server_name:
- # resolve server_name to server_id in case no wildcast in server_name
- vserver_api_url_format \
- = "/{f_vim_id}/compute/v2.1/{f_tenant_id}/servers?name={f_server_name}"
- vserver_api_url = vserver_api_url_format.format(f_vim_id=vimid,
- f_tenant_id=tenant_id,
- f_server_name=server_name)
- base_url = settings.MULTICLOUD_PREFIX
- extra_headers = {'X-Auth-Token': token}
- ret = restcall._call_req(base_url, "", "", 0, vserver_api_url, "GET", extra_headers, "")
- if ret[0] > 0 or ret[1] is None:
- logger.critical("call url %s failed with status %s" % (vserver_api_url, ret[0]))
- return None
-
- server_resp = json.JSONDecoder().decode(ret[1])
- # find out the server wanted
- for s in server_resp.get("servers", []):
- if s["name"] == server_name:
- server_id = s["id"]
- break
- if server_id is None:
- logger.warn("source %s cannot be found under tenant id %s "
- % (server_name, tenant_id))
- return None
-
- # m.c. proxied OpenStack API
- if server_id is None and server_name is None:
- # monitor all VMs of the specified VIMs since no server_id can be resolved
- api_url_fmt = "/{f_vim_id}/compute/v2.1/{f_tenant_id}/servers/detail"
- api_url = api_url_fmt.format(
- f_vim_id=vimid, f_tenant_id=tenant_id)
- else:
- api_url_fmt = "/{f_vim_id}/compute/v2.1/{f_tenant_id}/servers/{f_server_id}"
- api_url = api_url_fmt.format(f_vim_id=vimid, f_tenant_id=tenant_id, f_server_id=server_id)
-
- backlog = {
- "backlog_uuid": str(uuid.uuid3(uuid.NAMESPACE_URL,
- str("%s-%s-%s" % (vimid, tenant_id, server_id)))),
- "tenant_id": tenant_id,
- "server_id": server_id,
- "api_method": "GET",
- "api_link": api_url,
- }
- backlog.update(backlog_input)
- except Exception as e:
- logger.error("exception:%s" % str(e))
- return None
-
- logger.info("return")
- logger.debug("with backlog: %s" % backlog)
- return backlog
-
-
-# process backlog with domain:"fault", type:"vm"
-def processBacklog_fault_vm(vesAgentConfig, vesAgentState, oneBacklog):
- logger.debug("vesAgentConfig:%s, vesAgentState:%s, oneBacklog: %s"
- % (vesAgentConfig, vesAgentState, oneBacklog))
-
- try:
- vimid = vesAgentConfig["vimid"]
- tenant_name = oneBacklog["tenant"]
-
- # get token
- auth_api_url_format = "/{f_vim_id}/identity/v2.0/tokens"
- auth_api_url = auth_api_url_format.format(f_vim_id=vimid)
- auth_api_data = {"auth": {"tenantName": tenant_name}}
- base_url = settings.MULTICLOUD_PREFIX
- extra_headers = ''
- logger.debug("authenticate with url:%s" % auth_api_url)
- ret = restcall._call_req(base_url, "", "", 0, auth_api_url, "POST", extra_headers, json.dumps(auth_api_data))
- if ret[0] > 0 or ret[1] is None:
- logger.critical("call url %s failed with status %s" % (auth_api_url, ret[0]))
-
- token_resp = json.JSONDecoder().decode(ret[1])
- logger.debug("authenticate resp: %s" % token_resp)
- token = token_resp["access"]["token"]["id"]
-
- # collect data by issue API
- api_link = oneBacklog["api_link"]
- method = oneBacklog["api_method"]
- base_url = settings.MULTICLOUD_PREFIX
- data = ''
- extra_headers = {'X-Auth-Token': token}
- # which one is correct? extra_headers = {'HTTP_X_AUTH_TOKEN': token}
- logger.debug("authenticate with url:%s, header:%s" % (auth_api_url, extra_headers))
- ret = restcall._call_req(base_url, "", "", 0, api_link, method, extra_headers, data)
- if ret[0] > 0 or ret[1] is None:
- logger.critical("call url %s failed with status %s" % (api_link, ret[0]))
-
- server_resp = json.JSONDecoder().decode(ret[1])
- logger.debug("collected data: %s" % server_resp)
-
- # encode data
- backlog_uuid = oneBacklog.get("backlog_uuid", None)
- backlogState = vesAgentState.get("%s" % (backlog_uuid), None)
-
- # iterate all VMs
- all_events = []
- server_1 = server_resp.get("server", None) # in case querying single server
- for s in server_resp.get("servers", [server_1] if server_1 else []):
- server_id = s.get("id", None)
- server_name = s.get("name", None)
- if not server_id:
- continue
-
- last_event = backlogState.get("last_event_%s" % (server_id), None)
- logger.debug("last event for server name %s: %s" % (server_name, last_event))
-
- this_event = data2event_fault_vm(vimid, oneBacklog, last_event, s)
- if this_event is not None:
- logger.debug("this event: %s" % this_event)
- all_events.append(this_event.get("event", None))
- backlogState["last_event_%s" % (server_id)] = this_event
-
- # report data to VES
- if len(all_events) > 0:
- ves_subscription = vesAgentConfig.get("subscription", None)
- publishAnyEventToVES(ves_subscription, all_events)
- # store the latest data into cache, never expire
-
- except Exception as e:
- logger.error("exception:%s" % str(e))
- return
-
- logger.info("return")
- return
-
-
-def data2event_fault_vm(vimid, oneBacklog, last_event, vm_data):
-
- VES_EVENT_VERSION = 3.0
- VES_EVENT_FAULT_VERSION = 2.0
- VES_EVENT_FAULT_DOMAIN = "fault"
-
- try:
-
- if vm_status_is_fault(vm_data["status"]):
- if last_event is not None \
- and last_event['event']['commonEventHeader']['eventName'] == 'Fault_MultiCloud_VMFailure':
- # asserted alarm already, so no need to assert it again
- return None
-
- eventName = "Fault_MultiCloud_VMFailure"
- priority = "High"
- eventSeverity = "CRITICAL"
- alarmCondition = "Guest_Os_Failure"
- specificProblem = "Fault_MultiCloud_VMFailure"
- eventType = ''
- reportingEntityId = vimid
- reportingEntityName = vimid
- sequence = 0
-
- startEpochMicrosec = get_epoch_now_usecond()
- lastEpochMicrosec = startEpochMicrosec
-
- eventId = str(uuid.uuid4())
- pass
- else:
- if last_event is None \
- or last_event['event']['commonEventHeader']['eventName'] != 'Fault_MultiCloud_VMFailure':
- # not assert alarm yet, so no need to clear it
- return None
-
- eventName = "Fault_MultiCloud_VMFailureCleared"
- priority = "Normal"
- eventSeverity = "NORMAL"
- alarmCondition = "Vm_Restart"
- specificProblem = "Fault_MultiCloud_VMFailure"
- eventType = ''
- reportingEntityId = vimid
- reportingEntityName = vimid
- sequence = 1 # last_event['event']['commonEventHeader']['sequence'] + 1
-
- startEpochMicrosec = last_event['event']['commonEventHeader']['startEpochMicrosec']
- lastEpochMicrosec = get_epoch_now_usecond()
- eventId = last_event['event']['commonEventHeader']['eventId']
-
- pass
-
- # now populate the event structure
- this_event = {
- 'event': {
- 'commonEventHeader': {
- 'version': VES_EVENT_VERSION,
- 'eventName': eventName,
- 'domain': VES_EVENT_FAULT_DOMAIN,
- 'eventId': eventId,
- 'eventType': eventType,
- 'sourceId': vm_data['id'],
- 'sourceName': vm_data['name'],
- 'reportingEntityId': reportingEntityId,
- 'reportingEntityName': reportingEntityName,
- 'priority': priority,
- 'startEpochMicrosec': startEpochMicrosec,
- 'lastEpochMicrosec': lastEpochMicrosec,
- 'sequence': sequence
- },
- 'faultFields': {
- 'faultFieldsVersion': VES_EVENT_FAULT_VERSION,
- 'eventSeverity': eventSeverity,
- 'eventSourceType': 'virtualMachine',
- 'alarmCondition': alarmCondition,
- 'specificProblem': specificProblem,
- 'vfStatus': 'Active',
- "alarmInterfaceA": "aaaa",
- "alarmAdditionalInformation": [
- {
- "name": "objectType",
- "value": "VIM"
- },
- {
- "name": "eventTime",
- "value": str(datetime.datetime.now())
- }
- ],
- }
-
- }
-
- }
-
- return this_event
-
- except Exception as e:
- logger.error("exception:%s" % str(e))
- return None
-
-
-def vm_status_is_fault(status):
- '''
- report VM fault when status falls into one of following state
- ['ERROR', 'DELETED', 'PAUSED', 'REBUILD', 'RESCUE',
- 'RESIZE','REVERT_RESIZE', 'SHELVED', 'SHELVED_OFFLOADED',
- 'SHUTOFF', 'SOFT_DELETED','SUSPENDED', 'UNKNOWN', 'VERIFY_RESIZE']
- :param status:
- :return:
- '''
- if status in ['BUILD', 'ACTIVE', 'HARD_REBOOT', 'REBOOT', 'MIGRATING', 'PASSWORD']:
- return False
- else:
- return True
diff --git a/ocata/ocata/vesagent/event_domain/tests_fault_vm.py b/ocata/ocata/vesagent/event_domain/tests_fault_vm.py
deleted file mode 100644
index 7798ae52..00000000
--- a/ocata/ocata/vesagent/event_domain/tests_fault_vm.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# Copyright (c) Intel Corporation, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import mock
-
-import unittest
-import json
-
-from ocata.vesagent import vespublish
-from common.utils import restcall
-from ocata.vesagent.event_domain import fault_vm
-
-MOCK_TOKEN_RESPONSE = {
- "access": {
- "token": {
- "issued_at": "2018-05-10T16:56:56.000000Z",
- "expires": "2018-05-10T17:56:56.000000Z",
- "id": "4a832860dd744306b3f66452933f939e",
- "tenant": {
- "domain": {"id": "default", "name": "Default"},
- "enabled": "true",
- "id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "name": "VIM"
- }
- },
- "serviceCatalog": [],
- "user": {
- "domain": {"id": "default", "name": "Default"},
- "id": "ba76c94eb5e94bb7bec6980e5507aae2",
- "name": "demo"
- }
- }
-}
-MOCK_SERVERS_GET_RESPONSE = {
- "servers": [
- {
- "id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "links": [
- {
- "href": "http://10.12.25.2:8774/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "rel": "self"
- },
- {
- "href": "http://10.12.25.2:8774/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "rel": "bookmark"
- }
- ],
- "name": "onap-aaf"
- }
- ]
-}
-MOCK_BACKLOG_INPUT = {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae", "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
-}
-MOCK_BACKLOG_INPUT_wo_tenant_id = {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
-}
-MOCK_BACKLOG_INPUT_wo_tenant = {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "source": "onap-aaf",
- "domain": "fault", "type": "vm"
-}
-MOCK_BACKLOG_INPUT_wo_server_id = {
- "source": "onap-aaf",
- "domain": "fault",
- "type": "vm",
- "tenant": "VIM"
-}
-MOCK_BACKLOG_INPUT_wo_server = {"domain": "fault", "type": "vm", "tenant": "VIM"}
-
-MOCK_SERVER_GET_RESPONSE = {
- "server": {
- "wrs-res:topology": "node:0, 4096MB, pgsize:2M, vcpus:0,1, pol:sha",
- "OS-EXT-STS:task_state": None,
- "addresses": {
- "oam_onap_BTHY": [
- {
- "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:6c:0d:6b",
- "version": 4,
- "addr": "10.0.13.1",
- "OS-EXT-IPS:type": "fixed"
- },
- {
- "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:6c:0d:6b",
- "version": 4,
- "addr": "10.12.5.185",
- "OS-EXT-IPS:type": "floating"
- }
- ]
- },
- "links": [],
- "image": {"id": "6e219e86-cd94-4989-9119-def29aa10b12", "links": []},
- "wrs-if:nics": [],
- "wrs-sg:server_group": "",
- "OS-EXT-STS:vm_state": "active",
- "OS-SRV-USG:launched_at": "2018-04-26T08:01:28.000000",
- "flavor": {},
- "id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "security_groups": [{"name": "onap_sg_BTHY"}],
- "user_id": "ba76c94eb5e94bb7bec6980e5507aae2",
- "OS-DCF:diskConfig": "MANUAL",
- "accessIPv4": "",
- "accessIPv6": "",
- "progress": 0,
- "OS-EXT-STS:power_state": 1,
- "OS-EXT-AZ:availability_zone": "nova",
- "metadata": {},
- "status": "ACTIVE",
- "updated": "2018-04-26T08:01:28Z",
- "hostId": "17acc9f2ae4f618c314e4cdf0c206585b895bc72a9ec57e57b254133",
- "OS-SRV-USG:terminated_at": None,
- "wrs-res:pci_devices": "",
- "wrs-res:vcpus": [2, 2, 2],
- "key_name": "onap_key_BTHY",
- "name": "onap-aaf",
- "created": "2018-04-26T08:01:20Z",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "os-extended-volumes:volumes_attached": [],
- "config_drive": ""
- }
-}
-
-MOCK_SERVER_GET_RESPONSE_empty = {}
-
-MOCK_vesAgentConfig = {
- "backlogs": [
- {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae", "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault",
- "type": "vm",
- "tenant": "VIM"
- }
- ],
- "poll_interval_default": 10, "vimid": "windriver-hudson-dc_RegionOne",
- "ves_subscription": {
- "username": "user", "password": "password",
- "endpoint": "http://127.0.0.1:9005/sample"
- }
-}
-
-MOCK_vesAgentState = {
- "ce2d7597-22e1-4239-890f-bc303bd67076": {"timestamp": 1525975400}
-}
-MOCK_oneBacklog = {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault",
- "type": "vm",
- "tenant": "VIM"
-}
-
-
-class FaultVMTest(unittest.TestCase):
- def setUp(self):
- pass
-
- def tearDown(self):
- pass
-
- def test_get_epoch_now_usecond(self):
- epoch = fault_vm.get_epoch_now_usecond()
- self.assertGreater(epoch, 1)
- pass
-
- @mock.patch.object(restcall, '_call_req')
- def test_buildBacklog_fault_vm(self, mock_call_req):
-
- mock_call_req.side_effect = [
- (0, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body"),
- (0, json.dumps(MOCK_SERVERS_GET_RESPONSE), "MOCKED response body")
- ]
- backlog = fault_vm.buildBacklog_fault_vm(vimid="windriver-hudson-dc_RegionOne",
- backlog_input=MOCK_BACKLOG_INPUT)
- self.assertIsNotNone(backlog)
- pass
-
- @mock.patch.object(restcall, '_call_req')
- def test_buildBacklog_fault_vm_wo_tenant_id(self, mock_call_req):
-
- mock_call_req.side_effect = [
- (0, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body"),
- (0, json.dumps(MOCK_SERVERS_GET_RESPONSE), "MOCKED response body")
- ]
- backlog = fault_vm.buildBacklog_fault_vm(vimid="windriver-hudson-dc_RegionOne",
- backlog_input=MOCK_BACKLOG_INPUT_wo_tenant_id)
- self.assertIsNotNone(backlog)
- pass
-
- @mock.patch.object(restcall, '_call_req')
- def test_buildBacklog_fault_vm_wo_tenant(self, mock_call_req):
-
- mock_call_req.side_effect = [
- (1, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body: failed"),
- (0, json.dumps(MOCK_SERVERS_GET_RESPONSE), "MOCKED response body")
- ]
- backlog = fault_vm.buildBacklog_fault_vm(vimid="windriver-hudson-dc_RegionOne",
- backlog_input=MOCK_BACKLOG_INPUT_wo_tenant)
- self.assertIsNone(backlog)
- pass
-
- @mock.patch.object(restcall, '_call_req')
- def test_buildBacklog_fault_vm_wo_server_id(self, mock_call_req):
-
- mock_call_req.side_effect = [
- (0, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body"),
- (0, json.dumps(MOCK_SERVERS_GET_RESPONSE), "MOCKED response body")
- ]
- backlog = fault_vm.buildBacklog_fault_vm(vimid="windriver-hudson-dc_RegionOne",
- backlog_input=MOCK_BACKLOG_INPUT_wo_server_id)
- self.assertIsNotNone(backlog)
- pass
-
- @mock.patch.object(restcall, '_call_req')
- def test_buildBacklog_fault_vm_wo_server(self, mock_call_req):
-
- mock_call_req.side_effect = [
- (0, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body"),
- (0, json.dumps(MOCK_SERVERS_GET_RESPONSE), "MOCKED response body")
- ]
- backlog = fault_vm.buildBacklog_fault_vm(vimid="windriver-hudson-dc_RegionOne",
- backlog_input=MOCK_BACKLOG_INPUT_wo_server)
- self.assertIsNotNone(backlog)
- pass
-
- @mock.patch.object(vespublish, 'publishAnyEventToVES')
- @mock.patch.object(restcall, '_call_req')
- def test_processBacklog_fault_vm(self, mock_call_req, mock_publishAnyEventToVES):
-
- mock_call_req.side_effect = [
- (0, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body"),
- (0, json.dumps(MOCK_SERVER_GET_RESPONSE), "MOCKED response body")
- ]
- mock_publishAnyEventToVES.return_value = "mocked return value"
-
- result = fault_vm.processBacklog_fault_vm(vesAgentConfig=MOCK_vesAgentConfig,
- vesAgentState=MOCK_vesAgentState,
- oneBacklog=MOCK_oneBacklog)
- self.assertIsNone(result)
- pass
-
- @mock.patch.object(vespublish, 'publishAnyEventToVES')
- @mock.patch.object(restcall, '_call_req')
- def test_processBacklog_fault_vm_wo_server(self, mock_call_req, mock_publishAnyEventToVES):
-
- mock_call_req.side_effect = [
- (0, json.dumps(MOCK_TOKEN_RESPONSE), "MOCKED response body"),
- (0, json.dumps(MOCK_SERVER_GET_RESPONSE_empty), "MOCKED response body")
- ]
- mock_publishAnyEventToVES.return_value = "mocked return value"
-
- result = fault_vm.processBacklog_fault_vm(vesAgentConfig=MOCK_vesAgentConfig,
- vesAgentState=MOCK_vesAgentState,
- oneBacklog=MOCK_oneBacklog)
- self.assertIsNone(result)
- pass
diff --git a/ocata/ocata/vesagent/tasks.py b/ocata/ocata/vesagent/tasks.py
deleted file mode 100644
index 5b63be36..00000000
--- a/ocata/ocata/vesagent/tasks.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# VES agent workers
-from __future__ import absolute_import, unicode_literals
-from ocata.celery import app
-import logging
-import json
-import time
-
-from django.core.cache import cache
-
-from ocata.vesagent.event_domain.fault_vm import processBacklog_fault_vm
-
-logger = logging.getLogger(__name__)
-
-
-@app.task(bind=True)
-def scheduleBacklogs(self, vimid):
- # make sure only one task runs here
- # cannot get vimid ? logger.info("schedule with vimid:%" % (vimid))
-
- logger.debug("scheduleBacklogs starts")
- backlog_count, next_time_slot = processBacklogs()
- logger.debug("processBacklogs return with %s, %s" % (backlog_count, next_time_slot))
-
- # sleep for next_time_slot
- while backlog_count > 0:
- time.sleep(next_time_slot)
- backlog_count, next_time_slot = processBacklogs()
-
- logger.debug("scheduleBacklogs stops")
-
-
-def processBacklogs():
- # find out count of valid backlog and the next time slot
- backlog_count = 0
- next_time_slot = 10
- try:
- # get the whole list of backlog
- VesAgentBacklogsVimListStr = cache.get("VesAgentBacklogs.vimlist")
- if VesAgentBacklogsVimListStr is None:
- logger.warn("VesAgentBacklogs.vimlist cannot be found in cache")
- return 0, next_time_slot
-
- logger.debug("VesAgentBacklogs.vimlist: %s" % (VesAgentBacklogsVimListStr))
-
- backlogsAllVims = json.loads(VesAgentBacklogsVimListStr)
- if backlogsAllVims is None:
- logger.warn("VesAgentBacklogs.vimlist is empty")
- return 0, next_time_slot
-
- for vimid in backlogsAllVims:
- # iterate each backlogs
- backlog_count_tmp, next_time_slot_tmp = processBacklogsOfOneVIM(vimid)
- logger.debug("vimid:%s, backlog_count,next_time_slot:%s,%s" % (vimid, backlog_count_tmp, next_time_slot_tmp))
- backlog_count += backlog_count_tmp
- next_time_slot = next_time_slot_tmp if next_time_slot > next_time_slot_tmp else next_time_slot
- pass
-
- except Exception as e:
- logger.error("exception:%s" % str(e))
-
- return backlog_count, next_time_slot
-
- pass
-
-
-def processBacklogsOfOneVIM(vimid):
- '''
- process all backlogs for a VIM, return count of valid backlogs
- :param vimid:
- :return:
- '''
- backlog_count = 0
- next_time_slot = 10
-
- try:
- vesAgentConfigStr = cache.get("VesAgentBacklogs.config.%s" % (vimid))
- if vesAgentConfigStr is None:
- logger.warn("VesAgentBacklogs.config.%s cannot be found in cache" % (vimid))
- return 0, next_time_slot
-
- logger.debug("VesAgentBacklogs.config.%s: %s" % (vimid, vesAgentConfigStr))
-
- vesAgentConfig = json.loads(vesAgentConfigStr)
- if vesAgentConfig is None:
- logger.warn("VesAgentBacklogs.config.%s corrupts" % (vimid))
- return 0, next_time_slot
-
- vesAgentStateStr = cache.get("VesAgentBacklogs.state.%s" % (vimid))
- vesAgentState = json.loads(vesAgentStateStr) if vesAgentStateStr is not None else {}
-
- ves_info = vesAgentConfig.get("subscription", None)
- if ves_info is None:
- logger.warn("VesAgentBacklogs.config.%s: ves subscription corrupts:%s" % (vimid, vesAgentConfigStr))
- return 0, next_time_slot
-
- poll_interval_default = vesAgentConfig.get("poll_interval_default", None)
- if poll_interval_default is None:
- logger.warn("VesAgentBacklogs.config.%s: poll_interval_default corrupts:%s" % (vimid, vesAgentConfigStr))
- return 0, next_time_slot
-
- if poll_interval_default == 0:
- # invalid interval value
- logger.warn("VesAgentBacklogs.config.%s: poll_interval_default invalid:%s" % (vimid, vesAgentConfigStr))
- return 0, next_time_slot
-
- backlogs_list = vesAgentConfig.get("backlogs", None)
- if backlogs_list is None:
- logger.warn("VesAgentBacklogs.config.%s: backlogs corrupts:%s" % (vimid, vesAgentConfigStr))
- return 0, next_time_slot
-
- for backlog in backlogs_list:
- backlog_count_tmp, next_time_slot_tmp = processOneBacklog(vesAgentConfig,
- vesAgentState,
- poll_interval_default,
- backlog)
- logger.debug("processOneBacklog return with %s,%s" % (backlog_count_tmp, next_time_slot_tmp))
- backlog_count += backlog_count_tmp
- next_time_slot = next_time_slot_tmp if next_time_slot > next_time_slot_tmp else next_time_slot
-
- pass
-
- # save back the updated backlogs state
- vesAgentStateStr = json.dumps(vesAgentState)
- cache.set("VesAgentBacklogs.state.%s" % vimid, vesAgentStateStr, None)
-
- except Exception as e:
- logger.error("exception:%s" % str(e))
-
- return backlog_count, next_time_slot
-
-
-def processOneBacklog(vesAgentConfig, vesAgentState, poll_interval_default, oneBacklog):
- logger.info("Process one backlog")
- # logger.debug("vesAgentConfig:%s, vesAgentState:%s, poll_interval_default:%s, oneBacklog: %s"
- # % (vesAgentConfig, vesAgentState, poll_interval_default, oneBacklog))
-
- backlog_count = 1
- next_time_slot = 10
- try:
- timestamp_now = int(time.time())
- backlog_uuid = oneBacklog.get("backlog_uuid", None)
- if backlog_uuid is None:
- # warning: uuid is None, omit this backlog
- logger.warn("backlog without uuid: %s" % oneBacklog)
- return 0, next_time_slot
-
- backlogState = vesAgentState.get("%s" % (backlog_uuid), None)
- if backlogState is None:
- initialBacklogState = {
- "timestamp": timestamp_now
- }
- vesAgentState["%s" % (backlog_uuid)] = initialBacklogState
- backlogState = initialBacklogState
-
- time_expiration = backlogState["timestamp"] + oneBacklog.get("poll_interval", poll_interval_default)
- # check if poll interval expires
- if timestamp_now < time_expiration:
- # not expired yet
- logger.info("return without dispatching, not expired yet")
- return backlog_count, next_time_slot
-
- logger.info("Dispatching backlog")
-
- # collect data in case of expiration
- if oneBacklog["domain"] == "fault" and oneBacklog["type"] == "vm":
- processBacklog_fault_vm(vesAgentConfig, vesAgentState, oneBacklog)
- else:
- logger.warn("Dispatching backlog fails due to unsupported backlog domain %s,type:%s"
- % (oneBacklog["domain"], oneBacklog["type"]))
- backlog_count = 0
- pass
-
- # update timestamp and internal state
- backlogState["timestamp"] = timestamp_now
- except Exception as e:
- logger.error("exception:%s" % str(e))
-
- logger.info("return")
- return backlog_count, next_time_slot
diff --git a/ocata/ocata/vesagent/tests.py b/ocata/ocata/vesagent/tests.py
deleted file mode 100644
index a730ba87..00000000
--- a/ocata/ocata/vesagent/tests.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import mock
-
-import unittest
-import json
-from django.test import Client
-from rest_framework import status
-
-from django.core.cache import cache
-from common.msapi import extsys
-from ocata.vesagent import vesagent_ctrl
-from ocata.vesagent.tasks import scheduleBacklogs
-
-
-MOCK_VIM_INFO = {
- "createTime": "2017-04-01 02:22:27",
- "domain": "Default",
- "name": "TiS_R4",
- "password": "admin",
- "tenant": "admin",
- "type": "openstack",
- "url": "http://128.224.180.14:5000/v3",
- "userName": "admin",
- "vendor": "WindRiver",
- "version": "newton",
- "vimId": "windriver-hudson-dc_RegionOne",
- 'cloud_owner': 'windriver-hudson-dc',
- 'cloud_region_id': 'RegionOne',
- 'cloud_extra_info': '{"vesagent_config":{"backlogs":[{"source":"onap-aaf","domain":"fault","type":"vm","tenant":"VIM"}],"poll_interval_default":10,"ves_subscription":{"username":"user","password":"password","endpoint":"http://127.0.0.1:9005/sample"}}}',
- 'insecure': 'True',
-}
-
-
-class VesAgentCtrlTest(unittest.TestCase):
- def setUp(self):
- self.client = Client()
- self.view = vesagent_ctrl.VesAgentCtrl()
-
- def tearDown(self):
- pass
-
- @mock.patch.object(cache, 'get')
- @mock.patch.object(extsys, 'get_vim_by_id')
- def test_get(self, mock_get_vim_by_id, mock_get):
- mock_get_vim_by_id.return_value = MOCK_VIM_INFO
- mock_get.return_value = '{"backlogs": [{"backlog_uuid": "2b8f6ff8-bc64-339b-a714-155909db937f", "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721", "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae", "api_method": "GET", "source": "onap-aaf", "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721", "domain": "fault", "type": "vm", "tenant": "VIM"}], "poll_interval_default": 10, "vimid": "onaplab_RegionOne", "subscription": {"username": "user", "password": "password", "endpoint": "http://127.0.0.1:9005/sample"}}'
-
- response = self.client.get("/api/multicloud-ocata/v0/windriver-hudson-dc_RegionOne/vesagent")
- self.assertEqual(status.HTTP_200_OK, response.status_code, response.content)
-
- @mock.patch.object(vesagent_ctrl.VesAgentCtrl, 'buildBacklogsOneVIM')
- @mock.patch.object(extsys, 'get_vim_by_id')
- def test_post(self, mock_get_vim_by_id, mock_buildBacklogsOneVIM):
- mock_get_vim_by_id.return_value = MOCK_VIM_INFO
- mock_buildBacklogsOneVIM.return_value = "mocked vesagent_backlogs"
- mock_request = mock.Mock()
- mock_request.META = {"testkey": "testvalue"}
- mock_request.data = {"testdatakey": "testdatavalue"}
-
- response = self.view.post(request=mock_request, vimid="windriver-hudson-dc_RegionOne")
- self.assertEquals(status.HTTP_201_CREATED, response.status_code)
-
- pass
-
- @mock.patch.object(vesagent_ctrl.VesAgentCtrl, 'clearBacklogsOneVIM')
- @mock.patch.object(extsys, 'get_vim_by_id')
- def test_delete(self, mock_get_vim_by_id, mock_clearBacklogsOneVIM):
- mock_get_vim_by_id.return_value = MOCK_VIM_INFO
- mock_clearBacklogsOneVIM.return_value = "mocked vesagent_backlogs"
- mock_request = mock.Mock()
- mock_request.META = {"testkey": "testvalue"}
-
- response = self.view.delete(request=mock_request, vimid="windriver-hudson-dc_RegionOne")
- self.assertEquals(status.HTTP_200_OK, response.status_code)
-
- pass
-
- @mock.patch.object(cache, 'get')
- def test_getBacklogsOneVIM(self, mock_get):
- mock_vesagent_config = {
- "backlogs": [
- {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
- }
- ],
- "poll_interval_default": 10,
- "vimid": "onaplab_RegionOne",
- "subscription": {
- "username": "user",
- "password": "password",
- "endpoint": "http://127.0.0.1:9005/sample"
- }
- }
- mock_get.return_value = json.dumps(mock_vesagent_config)
-
- vesAgentConfig = self.view.getBacklogsOneVIM(vimid="windriver-hudson-dc_RegionOne")
- self.assertEquals(vesAgentConfig, mock_vesagent_config)
-
- pass
-
- @mock.patch.object(cache, 'set')
- @mock.patch.object(cache, 'get')
- def test_clearBacklogsOneVIM(self, mock_get, mock_set):
- mock_VesAgentBacklogs_vimlist = ["windriver-hudson-dc_RegionOne"]
- mock_vesagent_config = {
- "backlogs": [
- {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
- }
- ],
- "poll_interval_default": 10, "vimid": "onaplab_RegionOne",
- "subscription": {
- "username": "user",
- "password": "password",
- "endpoint": "http://127.0.0.1:9005/sample"
- }
- }
-
- mock_get.side_effect = [
- json.dumps(mock_VesAgentBacklogs_vimlist),
- json.dumps(mock_vesagent_config)
- ]
-
- mock_set.return_value = "mocked cache set"
-
- result = self.view.clearBacklogsOneVIM(vimid="windriver-hudson-dc_RegionOne")
- self.assertEquals(0, result)
-
- pass
-
- @mock.patch.object(scheduleBacklogs, 'delay')
- @mock.patch.object(cache, 'set')
- @mock.patch.object(cache, 'get')
- def test_buildBacklogsOneVIM(self, mock_get, mock_set, mock_scheduleBacklogs_delay):
- mock_VesAgentBacklogs_vimlist = ["windriver-hudson-dc_RegionOne"]
- mock_vesagent_config = {
- "backlogs": [
- {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
- }
- ],
- "poll_interval_default": 10,
- "vimid": "windriver-hudson-dc_RegionOne",
- "ves_subscription": {
- "username": "user",
- "password": "password",
- "endpoint": "http://127.0.0.1:9005/sample"
- }
- }
-
- mock_get.side_effect = [
- json.dumps(mock_VesAgentBacklogs_vimlist),
- ]
-
- mock_set.return_value = "mocked cache set"
- mock_scheduleBacklogs_delay.return_value = "mocked delay"
-
- VesAgentBacklogsConfig = self.view.buildBacklogsOneVIM(vimid="windriver-hudson-dc_RegionOne",
- vesagent_config=mock_vesagent_config)
- self.assertIsNotNone(VesAgentBacklogsConfig)
-
- pass
diff --git a/ocata/ocata/vesagent/tests_tasks.py b/ocata/ocata/vesagent/tests_tasks.py
deleted file mode 100644
index a4618f54..00000000
--- a/ocata/ocata/vesagent/tests_tasks.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import mock
-
-import unittest
-import json
-from django.test import Client
-from django.core.cache import cache
-
-from common.msapi import extsys
-from ocata.vesagent import tasks
-from ocata.vesagent.event_domain import fault_vm
-
-
-MOCK_VIM_INFO = {
- "createTime": "2017-04-01 02:22:27",
- "domain": "Default",
- "name": "TiS_R4",
- "password": "admin",
- "tenant": "admin",
- "type": "openstack",
- "url": "http://128.224.180.14:5000/v3",
- "userName": "admin",
- "vendor": "WindRiver",
- "version": "newton",
- "vimId": "windriver-hudson-dc_RegionOne",
- 'cloud_owner': 'windriver-hudson-dc',
- 'cloud_region_id': 'RegionOne',
- 'cloud_extra_info': '{"vesagent_config":{"backlogs":[{"source":"onap-aaf","domain":"fault","type":"vm","tenant":"VIM"}],"poll_interval_default":10,"ves_subscription":{"username":"user","password":"password","endpoint":"http://127.0.0.1:9005/sample"}}}',
- 'insecure': 'True',
-}
-
-COUNT_TIME_SLOT1 = (1, 1)
-COUNT_TIME_SLOT2 = (0, 1)
-
-
-class VesTaskTest(unittest.TestCase):
- def setUp(self):
- self.client = Client()
-
- def tearDown(self):
- pass
-
- @mock.patch.object(tasks, 'processBacklogs')
- @mock.patch.object(extsys, 'get_vim_by_id')
- def test_tasks_scheduleBacklogs(self, mock_get_vim_by_id, mock_processBacklogs):
- mock_get_vim_by_id.return_value = MOCK_VIM_INFO
- mock_processBacklogs.side_effect = [
- COUNT_TIME_SLOT1,
- COUNT_TIME_SLOT2
- ]
- result = tasks.scheduleBacklogs(vimid="windriver-hudson-dc_RegionOne")
- self.assertEquals(None, result)
- pass
-
- @mock.patch.object(tasks, 'processBacklogsOfOneVIM')
- @mock.patch.object(cache, 'get')
- def test_tasks_processBacklogs(self, mock_cache_get, mock_tasks_processBacklogsOfOneVIM):
- mock_VesAgentBacklogs_vimlist = ["windriver-hudson-dc_RegionOne"]
- COUNT_TIME_SLOT_ONE_VIM = (1, 1)
- mock_tasks_processBacklogsOfOneVIM.return_value = COUNT_TIME_SLOT_ONE_VIM
- mock_cache_get.side_effect = [
- json.dumps(mock_VesAgentBacklogs_vimlist),
- ]
- result = tasks.processBacklogs()
- self.assertEquals(COUNT_TIME_SLOT_ONE_VIM, result)
- pass
-
- @mock.patch.object(tasks, 'processOneBacklog')
- @mock.patch.object(cache, 'set')
- @mock.patch.object(cache, 'get')
- def test_tasks_processBacklogsOfOneVIM(self, mock_cache_get, mock_cache_set, mock_tasks_processOneBacklog):
- mock_vesagent_config = {
- "backlogs": [
- {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
- }
- ],
- "poll_interval_default": 10, "vimid": "onaplab_RegionOne",
- "subscription": {
- "username": "user",
- "password": "password",
- "endpoint": "http://127.0.0.1:9005/sample"
- }
- }
- mock_cache_get.side_effect = [
- json.dumps(mock_vesagent_config),
- json.dumps({})
- ]
- mock_tasks_processOneBacklog.return_value = (1, 11)
- mock_cache_set.return_value = "mocked cache set"
- result = tasks.processBacklogsOfOneVIM(vimid="windriver-hudson-dc_RegionOne")
- COUNT_TIME_SLOT = (1, 10)
- self.assertEquals(COUNT_TIME_SLOT, result)
- pass
-
- @mock.patch.object(fault_vm, 'processBacklog_fault_vm')
- def test_tasks_processOneBacklog(self, mock_fault_vm_processBacklog_fault_vm):
- mock_fault_vm_processBacklog_fault_vm.return_value = None
- vesagent_config = {
- "backlogs": [
- {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae",
- "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
- }
- ],
- "poll_interval_default": 10, "vimid": "onaplab_RegionOne",
- "subscription": {
- "username": "user",
- "password": "password",
- "endpoint": "http://127.0.0.1:9005/sample"
- }
- }
- vesagent_onebacklog = {
- "backlog_uuid": "ce2d7597-22e1-4239-890f-bc303bd67076",
- "poll_interval": 10,
- "server_id": "c4b575fa-ed85-4642-ab4b-335cb5744721",
- "tenant_id": "0e148b76ee8c42f78d37013bf6b7b1ae", "api_method": "GET",
- "source": "onap-aaf",
- "api_link": "/onaplab_RegionOne/compute/v2.1/0e148b76ee8c42f78d37013bf6b7b1ae/servers/c4b575fa-ed85-4642-ab4b-335cb5744721",
- "domain": "fault", "type": "vm", "tenant": "VIM"
- }
-
- result = tasks.processOneBacklog(vesAgentConfig=vesagent_config,
- vesAgentState={},
- poll_interval_default=10,
- oneBacklog=vesagent_onebacklog)
- COUNT_TIME_SLOT = (1, 10)
- self.assertEquals(COUNT_TIME_SLOT, result)
- pass
diff --git a/ocata/ocata/vesagent/tests_vespublish.py b/ocata/ocata/vesagent/tests_vespublish.py
deleted file mode 100644
index 976f01b5..00000000
--- a/ocata/ocata/vesagent/tests_vespublish.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import mock
-
-import unittest
-# import json
-import urllib2
-
-from ocata.vesagent import vespublish
-
-MOCK_VESENDPOINT = {
- "endpoint": "MOCKED_VES_COLLECTOR_EP1",
- "username": "MOCKED_VES_COLLECTOR_USER1",
- "password": "MOCKED_VES_COLLECTOR_PASSWD1",
-}
-
-MOCK_VESPUBLISH_EVENT1 = [{"name": "event1"}]
-
-
-class VespublishTest(unittest.TestCase):
- def setUp(self):
- pass
-
- def tearDown(self):
- pass
-
- @mock.patch.object(urllib2, 'urlopen')
- @mock.patch.object(urllib2, 'Request')
- def test_publishAnyEventToVES(self, mock_Request, mock_urlopen):
- mock_request = mock.Mock()
-
- mock_Request.side_effect = [
- mock_request
- ]
-
- mock_response = mock.Mock(["read"])
- mock_response.read.return_value = "MOCKED_VESPUBLISH_RESPONSE_MESSAGE"
- mock_urlopen.side_effect = [
- mock_response
- ]
-
- vespublish.publishAnyEventToVES(MOCK_VESENDPOINT, MOCK_VESPUBLISH_EVENT1)
diff --git a/ocata/ocata/vesagent/vesagent_ctrl.py b/ocata/ocata/vesagent/vesagent_ctrl.py
deleted file mode 100644
index fcf222b7..00000000
--- a/ocata/ocata/vesagent/vesagent_ctrl.py
+++ /dev/null
@@ -1,448 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import logging
-import json
-
-from rest_framework import status
-from rest_framework.response import Response
-from rest_framework.views import APIView
-
-from django.conf import settings
-from common.msapi import extsys
-from ocata.vesagent.tasks import scheduleBacklogs
-from ocata.vesagent.event_domain.fault_vm import buildBacklog_fault_vm
-
-from django.core.cache import cache
-
-logger = logging.getLogger(__name__)
-
-
-class VesAgentCtrl(APIView):
- '''
- control plane of VesAgent
- Design tips:
- 1, vesagent are multiple processing workers
- 2, the runtime logic is simple: a vesagent worker polls the data source (vm/hypervisor/host/vim/etc.)
- and then feeds the encoded data to VES.
- 3, the vesagent workers can be distributed to different clouds while latency/throughput is concerned,
- this distributed deployment usually comes along with the distributed VES deployment.
- So it is very likely that the collected data from different VIM/Cloud instance will be fed into
- different VES endpoint, however, assuming that there will be at most one VES endpoint serving
- any single VIM/Cloud instance.
- 4, According to VES specs, the collected data can be cataloged by domain:
- domain : fault, heartbeat, measurementsForVfScaling, other, stateChange, syslog, thresholdCrossingAlert
- As far as VIM/Cloud concerned, fault, heartbeat, measurementsForVfScaling, TCAalert are relevant.
- 5, the source of the collected data can be cataloged by eventSourceType:
- eventSourceType: VNF/VNFC/VM
- As far as VIM/Cloud concerned, only VM is relevant. This eventSourceType should be extended to cover
- the data source of hypervisor, VIM, Host,Controller, PIM, etc.
-
- 6, the source of collected data should be specified explicitly,so is the domain of the collected data.
- To specify the source: eventSourceType, uuid or name of the source
- To specify the domain: domain
- the specifications above will be provisioned as a vesagent backlog entry to a VIM/Cloud instance
- to tell a vesagent worker that :
- with regarding to that VIM/Cloud instance, what kind of data to be collected from which source .
-
- 7,the VES endpoint will be also specified for a VIM/Cloud instance, so that all collected data
- will be fed into this VES endpoint
-
- 8, the vesagent backlog are stored into the respective cloud_region's property "cloud-extra-info",
- which implies that those specifications can be CRUD either by ESR portal or the RestAPIs in this view, e.g.
- "cloud-extra-info": {
- ...,
- "vesagent_config":
- {
- "ves_subscription":{
- "endpoint":"http://{VES IP}:{VES port}/{URI}",
- "username":"{VES username}",
- "password":"{VES password}",
- },
- "poll_interval_default" : "{default interval for polling}",
- "backlogs":[
- {
- "domain":"fault"
- "type":"vm",
- "tenant":"{tenant name1}",
- "source":"{VM name1}",
- "poll_interval" : "{optional, interval for polling}",
- },
- {
- "domain":"fault"
- "type":"vm",
- "tenant":"{tenant name2}",
- "source":"{VM name2}",
- "poll_interval" : "{optional, interval for polling}",
- }
- ]
- }
- }
-
- Idea: API dispatching to distributed M.C. service can be determined by Complex Object in AAI:
- cloud-region has been assoicated to a Complex Object
- M.C. plugin service instance should refer to the same Complex Object (by physical_locaton_id ?)
- So the M.C. broker/API distributor/other approach will correlate the cloud-region with
- corresponding M.C. plugin service instance.
-
-
- Backlog built in cache:
-
- maintain backlog in cache and VES agent workers
- cache objects:
- "VesAgentBacklogs.vimlist": [ list of vimid] ### will not expire forever
- "VesAgentBacklogs.state.{vimdid}":
- ### will expire eventually to eliminate the garbage, expiration duration: 1hour?
- {
- "{backlog_uuid}": {
- "timestamp": "{timestamp for last time of data collecting}",
- "api_data": [list of data to populate the format string of the API link]
- "last_event": {object, event reported to ves last time}"
- }
- }
- "VesAgentBacklogs.config.{vimdid}": ### will not expire forever
- {
- "vimid": "{vim id}",
- "subscription": {
- "endpoint": "{ves endpoint, e.g. http://ves_ip:ves_port/eventListener/v5}",
- "username": "{username}",
- "password": "{password}"
- }
- "poll_interval_default" : "{default interval for polling}",
- "backlogs":[
- {
- "backlog_uuid": "{uuid to identify the backlog}"
- "domain":"fault"
- "type":"vm",
- "tenant":"{tenant name1}",
- "source":"{VM name1}",
- "poll_interval" : "{optional, interval in second for polling}",
- "api_method": "{GET/POST/PUT/etc.}",
- "api_link":"{API link to collect data, could be format string}",
- "tenant_id": tenant_id,
- "server_id": server_id,
- },
- {
- "domain":"fault"
- "type":"vm",
- "tenant":"{tenant name2}",
- "source":"{VM name2}",
- "poll_interval" : "{optional, interval in second for polling}",
- "api_method": "{GET/POST/PUT/etc.}",
- "api_link":"{API link to collect data, could be format string}",
- "tenant_id": tenant_id,
- "server_id": server_id,
- }
- ]
- }
- '''
-
- def __init__(self):
- self._logger = logger
- self.proxy_prefix = settings.MULTICLOUD_PREFIX
-
- def get(self, request, vimid=""):
- '''
- get blob of vesagent-config
- :param request:
- :param vimid:
- :return:
- '''
- self._logger.info("vimid: %s" % vimid)
- self._logger.debug("with META: %s" % request.META)
- try:
- # get vesagent_config from cloud region
- try:
- viminfo = extsys.get_vim_by_id(vimid)
- cloud_extra_info_str = viminfo.get('cloud_extra_info', '')
- cloud_extra_info = json.loads(cloud_extra_info_str) if cloud_extra_info_str != '' else None
- vesagent_config = cloud_extra_info.get("vesagent_config", None) if cloud_extra_info is not None else None
- except Exception as e:
- # ignore this error
- self._logger.warn("cloud extra info is provided with data in bad format: %s" % str(e))
- pass
-
- vesagent_backlogs = self.getBacklogsOneVIM(vimid)
-
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
- return Response(data={'error': str(e)},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-
- self._logger.info("return with %s" % status.HTTP_200_OK)
- return Response(data={"vesagent_config": vesagent_config,
- "vesagent_backlogs": vesagent_backlogs},
- status=status.HTTP_200_OK)
-
- def post(self, request, vimid=""):
- '''
- update the blob of vesagent-config, rebuild the backlog for the vesagent workers,
- and start the vesagent workers if not started yet
- Implication: the request to this API endpoint will build the backlog locally, hence only local VES agent workers
- will process these backlogs, which conforms to distributed deployment of M.C. services which includes VES agents
- :param request:{"vesagent_config":
- {"ves_subscription":
- {"endpoint":"http://127.0.0.1:9005/sample",
- "username":"user","password":"password"},
- "poll_interval_default":10,
- "backlogs":
- [
- {"domain":"fault","type":"vm","tenant":"VIM","source":"onap-aaf"}
- ]
- }
- }
- :param vimid:
- :return:
- '''
- self._logger.info("vimid: %s" % vimid)
- self._logger.debug("with META: %s, with data: %s" % (request.META, request.data))
- try:
- vesagent_config = None
- if request.data is None or request.data.get("vesagent_config", None) is None:
- # Try to load the vesagent_config out of cloud_region["cloud_extra_info"]
- viminfo = extsys.get_vim_by_id(vimid)
- cloud_extra_info_str = viminfo.get('cloud_extra_info', None)
- cloud_extra_info = json.loads(cloud_extra_info_str) if cloud_extra_info_str is not None else None
- vesagent_config = cloud_extra_info.get("vesagent_config", None) if cloud_extra_info is not None else None
- else:
- vesagent_config = request.data.get("vesagent_config", None)
-
- if vesagent_config is None:
- return Response(data={'vesagent_config is not provided'},
- status=status.HTTP_400_BAD_REQUEST)
-
- vesagent_backlogs = self.buildBacklogsOneVIM(vimid, vesagent_config)
-
- # store back to cloud_extra_info
- # tbd
-
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
- return Response(data={'error': str(e)},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-
- self._logger.info("return with %s" % status.HTTP_201_CREATED)
- return Response(data={"vesagent_config": vesagent_config,
- "vesagent_backlogs": vesagent_backlogs},
- status=status.HTTP_201_CREATED)
-
- def delete(self, request, vimid=""):
- '''
- delete the blob of vesagent-config, remove it from backlog and stop the vesagent worker if no backlog
- :param request:
- :param vimid:
- :return:
- '''
- self._logger.info("vimid: %s" % vimid)
- self._logger.debug("with META: %s" % request.META)
- try:
- # tbd
- self.clearBacklogsOneVIM(vimid)
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
- return Response(data={'error': str(e)},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-
- self._logger.info("return with %s" % status.HTTP_200_OK)
- return Response(status=status.HTTP_200_OK)
-
- def getBacklogsOneVIM(self, vimid):
- '''
- remove the specified backlogs for a VIM
- :param vimid:
- :return:
- '''
- self._logger.debug("vimid: %s" % vimid)
-
- vesAgentConfig = None
- try:
- # retrive the backlogs
- vesAgentConfigStr = cache.get("VesAgentBacklogs.config.%s" % (vimid))
- if vesAgentConfigStr is None:
- logger.warn("VesAgentBacklogs.config.%s cannot be found in cache" % (vimid))
- return None
-
- logger.debug("VesAgentBacklogs.config.%s: %s" % (vimid, vesAgentConfigStr))
-
- vesAgentConfig = json.loads(vesAgentConfigStr)
- if vesAgentConfig is None:
- logger.warn("VesAgentBacklogs.config.%s corrupts" % (vimid))
- return None
-
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
- vesAgentConfig = {"error": "exception occurs"}
-
- self._logger.debug("return")
- return vesAgentConfig
-
- def clearBacklogsOneVIM(self, vimid):
- '''
- remove the specified backlogs for a VIM
- :param vimid:
- :param vesagent_config:
- :return:
- '''
- self._logger.debug("vimid: %s" % vimid)
-
- try:
- # remove vimid from "VesAgentBacklogs.vimlist"
- VesAgentBacklogsVimListStr = cache.get("VesAgentBacklogs.vimlist")
- VesAgentBacklogsVimList = []
- if VesAgentBacklogsVimListStr is not None:
- VesAgentBacklogsVimList = json.loads(VesAgentBacklogsVimListStr)
- VesAgentBacklogsVimList = [v for v in VesAgentBacklogsVimList if v != vimid]
-
- logger.debug("VesAgentBacklogs.vimlist is %s" % VesAgentBacklogsVimList)
-
- # cache forever
- cache.set("VesAgentBacklogs.vimlist", json.dumps(VesAgentBacklogsVimList), None)
-
- # retrieve the backlogs
- vesAgentConfigStr = cache.get("VesAgentBacklogs.config.%s" % (vimid))
- if vesAgentConfigStr is None:
- logger.warn("VesAgentBacklogs.config.%s cannot be found in cache" % (vimid))
- return 0
-
- logger.debug("VesAgentBacklogs.config.%s: %s" % (vimid, vesAgentConfigStr))
-
- vesAgentConfig = json.loads(vesAgentConfigStr)
- if vesAgentConfig is None:
- logger.warn("VesAgentBacklogs.config.%s corrupts" % (vimid))
- return 0
-
- # iterate all backlog and remove the associate state!
- # tbd
-
- # clear the whole backlogs for a VIM
- cache.set("VesAgentBacklogs.config.%s" % vimid, "deleting the backlogs", 1)
-
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
-
- self._logger.debug("return")
- return 0
-
- def buildBacklogsOneVIM(self, vimid, vesagent_config=None):
- '''
- build and cache backlog for specific cloud region,spawn vesagent workers if needed
- :param vimid:
- :param vesagent_config: vesagent_config data in json object
- :return:
- '''
- self._logger.debug("vimid: %s" % vimid)
- self._logger.debug("config data: %s" % vesagent_config)
-
- VesAgentBacklogsConfig = None
- try:
- if vesagent_config:
- # now rebuild the backlog
- VesAgentBacklogsConfig = {
- "vimid": vimid,
- "poll_interval_default": vesagent_config.get("poll_interval_default", 0),
- "subscription": vesagent_config.get("ves_subscription", None),
- "backlogs": [self.buildBacklog(vimid, b) for b in vesagent_config.get("backlogs", [])]
- }
-
- # add/update the backlog into cache
- VesAgentBacklogsConfigStr = json.dumps(VesAgentBacklogsConfig)
- # cache forever
- cache.set("VesAgentBacklogs.config.%s" % vimid, VesAgentBacklogsConfigStr, None)
-
- # update list of vimid for vesagent
- # get the whole list of backlog
- VesAgentBacklogsVimListStr = cache.get("VesAgentBacklogs.vimlist")
- VesAgentBacklogsVimList = [vimid]
- if VesAgentBacklogsVimListStr is not None:
- VesAgentBacklogsVimList = json.loads(VesAgentBacklogsVimListStr)
- VesAgentBacklogsVimList = [v for v in VesAgentBacklogsVimList if v != vimid]
- VesAgentBacklogsVimList.append(vimid)
-
- logger.debug("VesAgentBacklogs.vimlist is %s" % VesAgentBacklogsVimList)
-
- # cache forever
- cache.set("VesAgentBacklogs.vimlist", json.dumps(VesAgentBacklogsVimList), None)
-
- # notify schduler
- scheduleBacklogs.delay(vimid)
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
- VesAgentBacklogsConfig = {"error": "exception occurs during build backlogs"}
-
- self._logger.debug("return")
- return VesAgentBacklogsConfig
-
- def buildBacklog(self, vimid, backlog_input):
- self._logger.debug("build backlog for: %s" % vimid)
- self._logger.debug("with input: %s" % backlog_input)
-
- try:
- if backlog_input["domain"] == "fault" and backlog_input["type"] == "vm":
- return buildBacklog_fault_vm(vimid, backlog_input)
- else:
- self._logger.warn("return with failure: unsupported backlog domain:%s, type:%s"
- % (backlog_input["domain"], backlog_input["type"] == "vm"))
- return None
- except Exception as e:
- self._logger.error("exception:%s" % str(e))
- return None
-
- self._logger.debug("return without backlog")
- return None
-
-
-class APIv1VesAgentCtrl(VesAgentCtrl):
-
- def __init__(self):
- super(APIv1VesAgentCtrl, self).__init__()
- # self._logger = logger
- self.proxy_prefix = settings.MULTICLOUD_API_V1_PREFIX
-
- def get(self, request, cloud_owner="", cloud_region_id=""):
- '''
- :param request:
- :param cloud_owner:
- :param cloud_region_id:
- :return:
- '''
- self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
-
- vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
- return super(APIv1VesAgentCtrl, self).get(request, vimid)
-
- def post(self, request, cloud_owner="", cloud_region_id=""):
- '''
- wrapper for inherited API with VIM ID
- :param request:
- :param cloud_owner:
- :param cloud_region_id:
- :return:
- '''
- self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
-
- vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
- return super(APIv1VesAgentCtrl, self).post(request, vimid)
-
- def delete(self, request, cloud_owner="", cloud_region_id=""):
- '''
- wrapper of inherited API with VIM ID
- :param request:
- :param cloud_owner:
- :param cloud_region_id:
- :return:
- '''
- self._logger.info("cloud_owner,cloud_region_id: %s,%s" % (cloud_owner, cloud_region_id))
-
- vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id)
- return super(APIv1VesAgentCtrl, self).delete(request, vimid)
diff --git a/ocata/ocata/vesagent/vespublish.py b/ocata/ocata/vesagent/vespublish.py
deleted file mode 100644
index 358bd9a4..00000000
--- a/ocata/ocata/vesagent/vespublish.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) 2017-2018 Wind River Systems, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from __future__ import absolute_import, unicode_literals
-
-import time
-import logging
-import json
-import urllib2
-
-logger = logging.getLogger(__name__)
-
-
-def publishAnyEventToVES(ves_subscription, events):
- if not events or len(events) == 0:
- return
-
- logger.info("Start to send single event to VES collector.")
- endpoint = ves_subscription.get("endpoint", None)
-
- if endpoint:
- try:
- if len(events) > 1:
- endpoint = "%s/eventBatch" % endpoint
- events = {"eventList": events}
- elif len(events) == 1:
- events = {"event": events[0]}
-
- logger.info("publish event to VES: %s" % endpoint)
- headers = {'Content-Type': 'application/json'}
- request = urllib2.Request(url=endpoint, headers=headers, data=json.dumps(events))
- time.sleep(1)
- response = urllib2.urlopen(request)
- logger.info("VES response is: %s" % response.read())
- except urllib2.URLError as e:
- logger.critical("Failed to publish to %s: %s" % (endpoint, e.reason))
- except Exception as e:
- logger.error("exception:%s" % str(e))
- else:
- logger.info("Missing VES info.")
diff --git a/ocata/requirements.txt b/ocata/requirements.txt
index 6bb2ef04..c28da163 100644
--- a/ocata/requirements.txt
+++ b/ocata/requirements.txt
@@ -29,13 +29,11 @@ python-memcached
uwsgi
# for unit test
-coverage==4.2
-mock==2.0.0
-unittest_xml_reporting==1.12.0
+#coverage==4.2
+#mock==2.0.0
+#unittest_xml_reporting==1.12.0
# for onap logging
onappylog>=1.0.8
# for background tasks
-celery >= 4.0
-
diff --git a/ocata/run.sh b/ocata/run.sh
index a66a1e98..5cf7e748 100755
--- a/ocata/run.sh
+++ b/ocata/run.sh
@@ -16,8 +16,4 @@
memcached -d -m 2048 -u root -c 1024 -p 11211 -P /tmp/memcached1.pid
export PYTHONPATH=lib/share
-#service rabbitmq-server restart
-# make sure only 1 worker due to missing the synchronization between workers now
-#nohup celery -A ocata worker --concurrency=1 --loglevel=info &
-
uwsgi --http :9006 --module ocata.wsgi --master --processes 4
diff --git a/ocata/test-requirements.txt b/ocata/test-requirements.txt
index 97044b5c..cc3059e2 100644
--- a/ocata/test-requirements.txt
+++ b/ocata/test-requirements.txt
@@ -1 +1,6 @@
+# for unit test
+coverage==4.2
+mock==2.0.0
+unittest_xml_reporting==1.12.0
+
pylint # GPLv2
diff --git a/pike/docker/Dockerfile b/pike/docker/Dockerfile
index 9a3f3231..4e861b79 100644
--- a/pike/docker/Dockerfile
+++ b/pike/docker/Dockerfile
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FROM python:2.7
+FROM python:2-slim
ARG HTTP_PROXY=${HTTP_PROXY}
ARG HTTPS_PROXY=${HTTPS_PROXY}
@@ -32,16 +32,20 @@ ENV AAI_PASSWORD "AAI"
EXPOSE 9007
RUN groupadd -r onap && useradd -r -g onap onap
-WORKDIR /opt/pike
-RUN apt-get update && apt-get install -y memcached unzip
-RUN wget -O /opt/multicloud-openstack-pike.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-pike&e=zip&v=1.3.0-SNAPSHOT" && \
+
+RUN apt-get update && \
+ apt-get install -y memcached wget unzip gcc && \
+ cd /opt/ && \
+ wget -O /opt/multicloud-openstack-pike.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-pike&e=zip&v=1.3.0-SNAPSHOT" && \
unzip -q -o -B /opt/multicloud-openstack-pike.zip -d /opt/ && \
- rm -f /opt/multicloud-openstack-pike.zip
+ rm -f /opt/multicloud-openstack-pike.zip && \
+ pip install -r /opt/pike/requirements.txt && \
+ apt-get --purge remove -y wget unzip gcc && \
+ apt-get -y autoremove && \
+ chown onap:onap /opt/pike -R
+
RUN mkdir -p /var/log/onap/multicloud/openstack/pike/
-#COPY ./ .
-RUN pip install -r requirements.txt
-RUN chown onap:onap /opt/pike -R
USER onap
-
-CMD "/opt/pike/run.sh"
+WORKDIR /opt/pike
+CMD /bin/sh -c "/opt/pike/run.sh"
diff --git a/pike/requirements.txt b/pike/requirements.txt
index 04c1b47a..ea5aa649 100644
--- a/pike/requirements.txt
+++ b/pike/requirements.txt
@@ -29,9 +29,9 @@ python-memcached
uwsgi
# for unit test
-coverage==4.2
-mock==2.0.0
-unittest_xml_reporting==1.12.0
+# coverage==4.2
+# mock==2.0.0
+# unittest_xml_reporting==1.12.0
# for onap logging
onappylog>=1.0.8
diff --git a/pike/test-requirements.txt b/pike/test-requirements.txt
index 97044b5c..cc3059e2 100644
--- a/pike/test-requirements.txt
+++ b/pike/test-requirements.txt
@@ -1 +1,6 @@
+# for unit test
+coverage==4.2
+mock==2.0.0
+unittest_xml_reporting==1.12.0
+
pylint # GPLv2
diff --git a/pom.xml b/pom.xml
index 5c6fc9c1..542fa7b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
<module>windriver</module>
<module>pike</module>
<module>starlingx</module>
+ <module>lenovo</module>
</modules>
<build>
diff --git a/starlingx/docker/Dockerfile b/starlingx/docker/Dockerfile
index b676e065..a987f4dd 100644
--- a/starlingx/docker/Dockerfile
+++ b/starlingx/docker/Dockerfile
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FROM python:2.7
+FROM python:2-slim
ARG HTTP_PROXY=${HTTP_PROXY}
ARG HTTPS_PROXY=${HTTPS_PROXY}
@@ -32,16 +32,20 @@ ENV AAI_PASSWORD "AAI"
EXPOSE 9009
RUN groupadd -r onap && useradd -r -g onap onap
-WORKDIR /opt/starlingx
-RUN apt-get update && apt-get install -y memcached unzip
-RUN wget -O /opt/multicloud-openstack-starlingx.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-starlingx&e=zip&v=1.3.0-SNAPSHOT" && \
+
+RUN apt-get update && \
+ apt-get install -y memcached wget unzip gcc && \
+ cd /opt/ && \
+ wget -O /opt/multicloud-openstack-starlingx.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=snapshots&g=org.onap.multicloud.openstack&a=multicloud-openstack-starlingx&e=zip&v=1.3.0-SNAPSHOT" && \
unzip -q -o -B /opt/multicloud-openstack-starlingx.zip -d /opt/ && \
- rm -f /opt/multicloud-openstack-starlingx.zip
+ rm -f /opt/multicloud-openstack-starlingx.zip && \
+ pip install -r /opt/starlingx/requirements.txt && \
+ apt-get --purge remove -y wget unzip gcc && \
+ apt-get -y autoremove && \
+ chown onap:onap /opt/starlingx -R
+
RUN mkdir -p /var/log/onap/multicloud/openstack/starlingx/
-#COPY ./ .
-RUN pip install -r requirements.txt
-RUN chown onap:onap /opt/starlingx -R
USER onap
-
-CMD "/opt/starlingx/run.sh"
+WORKDIR /opt/starlingx
+CMD /bin/sh -c "/opt/starlingx/run.sh"
diff --git a/starlingx/requirements.txt b/starlingx/requirements.txt
index f37d41fe..c016f77f 100644
--- a/starlingx/requirements.txt
+++ b/starlingx/requirements.txt
@@ -29,9 +29,9 @@ python-memcached
uwsgi
# for unit test
-coverage==4.2
-mock==2.0.0
-unittest_xml_reporting==1.12.0
+# coverage==4.2
+# mock==2.0.0
+# unittest_xml_reporting==1.12.0
# for onap logging
onappylog>=1.0.8
diff --git a/starlingx/test-requirements.txt b/starlingx/test-requirements.txt
new file mode 100644
index 00000000..cc3059e2
--- /dev/null
+++ b/starlingx/test-requirements.txt
@@ -0,0 +1,6 @@
+# for unit test
+coverage==4.2
+mock==2.0.0
+unittest_xml_reporting==1.12.0
+
+pylint # GPLv2
diff --git a/starlingx/tox.ini b/starlingx/tox.ini
index cd5b6573..09d3a50f 100644
--- a/starlingx/tox.ini
+++ b/starlingx/tox.ini
@@ -27,7 +27,9 @@ max-complexity = 27
[testenv]
setenv =
PYTHONPATH = {toxinidir}/../share
-deps = -r{toxinidir}/requirements.txt
+deps =
+ -r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
commands =
coverage run --branch manage.py test starlingx
coverage report --omit="./venv-tox/*,./.tox/*,*tests*,*__init__.py,*newton_base*,*common*" --fail-under=30