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