Add Lenovo ThinkCLoud Driver 84/79584/8
authorChangjun Wang <310397125@qq.com>
Tue, 5 Mar 2019 10:22:47 +0000 (10:22 +0000)
committerChangjun Wang <310397125@qq.com>
Tue, 5 Mar 2019 10:23:37 +0000 (10:23 +0000)
add a new branch lenovo
modify pom.xml under the root

Change-Id: I5edd57bf3e9cc1e32c5a94b9494daf645b220eaa
Issue-ID: MULTICLOUD-506
Signed-off-by: Changjun Wang <310397125@qq.com>
68 files changed:
lenovo/.gitignore [new file with mode: 0644]
lenovo/README.md [new file with mode: 0644]
lenovo/assembly.xml [new file with mode: 0644]
lenovo/docker/Dockerfile [new file with mode: 0644]
lenovo/docker/build_image.sh [new file with mode: 0755]
lenovo/initialize.sh [new file with mode: 0755]
lenovo/logs/empty.txt [new file with mode: 0644]
lenovo/manage.py [new file with mode: 0644]
lenovo/mvn-phase-script.sh [new file with mode: 0755]
lenovo/pom.xml [new file with mode: 0644]
lenovo/requirements.txt [new file with mode: 0644]
lenovo/run.sh [new file with mode: 0755]
lenovo/stop.sh [new file with mode: 0755]
lenovo/test-requirements.txt [new file with mode: 0644]
lenovo/thinkcloud/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/tests/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/tests/test_extensions.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/urls.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/urlsV1.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/views/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/extensions/views/extensions.py [new file with mode: 0644]
lenovo/thinkcloud/middleware.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/tests/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/tests/test_identity_proxy.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/tests/test_service_proxy.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/urls.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/urlsV1.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/views/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/views/identityV3.py [new file with mode: 0644]
lenovo/thinkcloud/proxy/views/services.py [new file with mode: 0644]
lenovo/thinkcloud/pub/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/pub/config/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/pub/config/config.py [new file with mode: 0644]
lenovo/thinkcloud/pub/config/log.yml [new file with mode: 0644]
lenovo/thinkcloud/registration/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/registration/tests/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/registration/tests/test_registration.py [new file with mode: 0644]
lenovo/thinkcloud/registration/tests/test_registration2.py [new file with mode: 0644]
lenovo/thinkcloud/registration/views/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/registration/views/registration.py [new file with mode: 0644]
lenovo/thinkcloud/requests/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/requests/urls.py [new file with mode: 0644]
lenovo/thinkcloud/requests/urlsV1.py [new file with mode: 0644]
lenovo/thinkcloud/requests/views/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/resource/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/resource/tests/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/resource/tests/test_capacity.py [new file with mode: 0644]
lenovo/thinkcloud/resource/tests/test_events.py [new file with mode: 0644]
lenovo/thinkcloud/resource/tests/tests_infra_workload.py [new file with mode: 0644]
lenovo/thinkcloud/resource/views/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/resource/views/capacity.py [new file with mode: 0644]
lenovo/thinkcloud/resource/views/events.py [new file with mode: 0644]
lenovo/thinkcloud/resource/views/infra_workload.py [new file with mode: 0644]
lenovo/thinkcloud/samples/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/samples/tests.py [new file with mode: 0644]
lenovo/thinkcloud/samples/urls.py [new file with mode: 0644]
lenovo/thinkcloud/samples/views.py [new file with mode: 0644]
lenovo/thinkcloud/settings.py [new file with mode: 0644]
lenovo/thinkcloud/swagger/__init__.py [new file with mode: 0644]
lenovo/thinkcloud/swagger/tests.py [new file with mode: 0644]
lenovo/thinkcloud/swagger/urls.py [new file with mode: 0644]
lenovo/thinkcloud/swagger/views.py [new file with mode: 0644]
lenovo/thinkcloud/urls.py [new file with mode: 0644]
lenovo/thinkcloud/wsgi.py [new file with mode: 0644]
lenovo/tox.ini [new file with mode: 0644]
pom.xml

diff --git a/lenovo/.gitignore b/lenovo/.gitignore
new file mode 100644 (file)
index 0000000..c3bfb45
--- /dev/null
@@ -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 (file)
index 0000000..3751a58
--- /dev/null
@@ -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 (file)
index 0000000..97ee741
--- /dev/null
@@ -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 (file)
index 0000000..5660e8e
--- /dev/null
@@ -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 python:2.7
+
+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 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-thinkcloud&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 remove -y unzip gcc && \
+       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 (executable)
index 0000000..eca8432
--- /dev/null
@@ -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 (executable)
index 0000000..7985985
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/lenovo/manage.py b/lenovo/manage.py
new file mode 100644 (file)
index 0000000..7415ab9
--- /dev/null
@@ -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 (executable)
index 0000000..7e8b3ee
--- /dev/null
@@ -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 (file)
index 0000000..2e586c4
--- /dev/null
@@ -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 (file)
index 0000000..ffeb982
--- /dev/null
@@ -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 (executable)
index 0000000..b3d98ef
--- /dev/null
@@ -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 (executable)
index 0000000..f43fab1
--- /dev/null
@@ -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 (file)
index 0000000..cc3059e
--- /dev/null
@@ -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 (file)
index 0000000..b4536b4
--- /dev/null
@@ -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/lenovo/thinkcloud/extensions/__init__.py b/lenovo/thinkcloud/extensions/__init__.py
new file mode 100644 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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/tests/__init__.py b/lenovo/thinkcloud/extensions/tests/__init__.py
new file mode 100644 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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/tests/test_extensions.py b/lenovo/thinkcloud/extensions/tests/test_extensions.py
new file mode 100644 (file)
index 0000000..4bc64aa
--- /dev/null
@@ -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 (file)
index 0000000..e88c607
--- /dev/null
@@ -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 (file)
index 0000000..f6c2377
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..056ad5a
--- /dev/null
@@ -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 (file)
index 0000000..33f0426
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..66d924a
--- /dev/null
@@ -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 (file)
index 0000000..36a797e
--- /dev/null
@@ -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 (file)
index 0000000..41e9b12
--- /dev/null
@@ -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 (file)
index 0000000..33fd686
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..85bc573
--- /dev/null
@@ -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 (file)
index 0000000..ba849f3
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..1dfd67f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..c8c8e6b
--- /dev/null
@@ -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 (file)
index 0000000..d799c90
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..0fe4a80
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..8b1ebbc
--- /dev/null
@@ -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 (file)
index 0000000..80b0a7e
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..c592d5e
--- /dev/null
@@ -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 (file)
index 0000000..5a363a4
--- /dev/null
@@ -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 (file)
index 0000000..9c263d6
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..e76c93a
--- /dev/null
@@ -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 (file)
index 0000000..ab70688
--- /dev/null
@@ -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 (file)
index 0000000..ab464da
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..1419027
--- /dev/null
@@ -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 (file)
index 0000000..0871944
--- /dev/null
@@ -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 (file)
index 0000000..714ad91
--- /dev/null
@@ -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 (file)
index 0000000..22f6ead
--- /dev/null
@@ -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 (file)
index 0000000..5b09b2f
--- /dev/null
@@ -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 (file)
index 0000000..400292a
--- /dev/null
@@ -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 (file)
index 0000000..7061cc4
--- /dev/null
@@ -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 (file)
index 0000000..e3c14c8
--- /dev/null
@@ -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 (file)
index 0000000..1828bb3
--- /dev/null
@@ -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 (file)
index 0000000..3abd9d4
--- /dev/null
@@ -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 (file)
index 0000000..975ed78
--- /dev/null
@@ -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/pom.xml b/pom.xml
index 5c6fc9c..542fa7b 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
         <module>windriver</module>
         <module>pike</module>
         <module>starlingx</module>
+        <module>lenovo</module>
     </modules>
 
     <build>