summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--conductor/conductor/setup.cfg71
-rw-r--r--conductor/conductor/setup.py34
-rw-r--r--conductor/conductor/tests/unit/data/__init__.py0
-rw-r--r--conductor/conductor/tests/unit/data/candidate_list.json43
-rw-r--r--conductor/conductor/tests/unit/data/constraints.json95
-rw-r--r--conductor/conductor/tests/unit/data/demands.json30
-rw-r--r--conductor/conductor/tests/unit/data/test_service.py154
-rw-r--r--conductor/pom.xml2
-rw-r--r--conductor/setup.cfg2
-rw-r--r--docs/api.rst133
-rw-r--r--docs/index.rst1
-rw-r--r--pom.xml15
12 files changed, 577 insertions, 3 deletions
diff --git a/conductor/conductor/setup.cfg b/conductor/conductor/setup.cfg
new file mode 100644
index 0000000..8e3fc56
--- /dev/null
+++ b/conductor/conductor/setup.cfg
@@ -0,0 +1,71 @@
+[metadata]
+name = of-has
+summary = ONAP Homing Service
+description-file = README.rst
+author = AT&T
+author-email = ikram@research.att.com
+home-page = https://wiki.onap.org/pages/viewpage.action?pageId=16005528
+classifier =
+ Development Status :: 4 - Beta
+ Environment :: ONAP
+ Intended Audience :: Information Technology
+ Intended Audience :: System Administrators
+ License :: OSI Approved :: Apache Software License
+ Operating System :: POSIX :: Linux
+ Programming Language :: Python
+ Programming Language :: Python :: 2
+ Programming Language :: Python :: 2.7
+ Programming Language :: Python :: 3
+ Programming Language :: Python :: 3.5
+keywords =
+ onap
+ homing
+ conductor
+
+[global]
+setup-hooks =
+ pbr.hooks.setup_hook
+
+[files]
+packages =
+ conductor
+data_files =
+ etc/conductor = etc/conductor/*
+# conductor_integrationtests
+#scripts =
+# bin/conductor-db-setup
+
+[entry_points]
+wsgi_scripts =
+ conductor-api = conductor.api.app:build_wsgi_app
+
+console_scripts =
+ conductor-controller = conductor.cmd.controller:main
+ conductor-data = conductor.cmd.data:main
+ conductor-solver = conductor.cmd.solver:main
+ conductor-reservation = conductor.cmd.reservation:main
+
+conductor.inventory_provider.plugin =
+ aai = conductor.data.plugins.inventory_provider.aai:AAI
+
+conductor.service_controller.plugin =
+ sdnc = conductor.data.plugins.service_controller.sdnc:SDNC
+
+oslo.config.opts =
+ conductor = conductor.opts:list_opts
+
+oslo.config.opts.defaults =
+ conductor = conductor.conf.defaults:set_cors_middleware_defaults
+
+#tempest.test_plugins =
+# conductor_tests = conductor_integrationtests.plugin:ConductorTempestPlugin
+
+#[build_sphinx]
+#all_files = 1
+#build-dir = doc/build
+#source-dir = doc/source
+
+[pbr]
+warnerrors = true
+autodoc_index_modules = true
+
diff --git a/conductor/conductor/setup.py b/conductor/conductor/setup.py
new file mode 100644
index 0000000..0c696ed
--- /dev/null
+++ b/conductor/conductor/setup.py
@@ -0,0 +1,34 @@
+# -*- encoding: utf-8 -*-
+# -------------------------------------------------------------------------
+# Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+# 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.
+#
+# -------------------------------------------------------------------------
+#
+
+'''Setup'''
+
+import setuptools
+
+# In python < 2.7.4, a lazy loading of package `pbr` will break
+# setuptools if some other modules registered functions in `atexit`.
+# solution from: http://bugs.python.org/issue15881#msg170215
+try:
+ import multiprocessing # noqa # pylint: disable=W0611,C0411
+except ImportError:
+ pass
+
+setuptools.setup(
+ setup_requires=['pbr>=1.8'],
+ pbr=True)
diff --git a/conductor/conductor/tests/unit/data/__init__.py b/conductor/conductor/tests/unit/data/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/__init__.py
diff --git a/conductor/conductor/tests/unit/data/candidate_list.json b/conductor/conductor/tests/unit/data/candidate_list.json
new file mode 100644
index 0000000..e29782c
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/candidate_list.json
@@ -0,0 +1,43 @@
+{
+ "candidate_list": [
+ {
+ "candidate_id": "1ac71fb8-ad43-4e16-9459-c3f372b8236d",
+ "candidate_type": "service",
+ "inventory_type": "service",
+ "inventory_provider": "aai",
+ "host_id": "vnf_123456",
+ "cost": "100",
+ "location_id": "DLLSTX55",
+ "location_type": "azure",
+ "latitude": "32.897480",
+ "longitude": "-97.040443",
+ "city": "Dallas",
+ "state": "TX",
+ "country": "USA",
+ "region": "US",
+ "complex_name": "dalls_one",
+ "cloud_owner": "att-aic",
+ "cloud_region_version": "1.1",
+ "physical_location_id": "DLLSTX55"
+ },
+ {
+ "candidate_id": "NYCNY55",
+ "candidate_type": "cloud",
+ "inventory_type": "cloud",
+ "inventory_provider": "aai",
+ "cost": "100",
+ "location_id": "NYCNY55",
+ "location_type": "azure",
+ "latitude": "40.7128",
+ "longitude": "-74.0060",
+ "city": "New York",
+ "state": "NY",
+ "country": "USA",
+ "region": "US",
+ "complex_name": "ny_one",
+ "cloud_owner": "att-aic",
+ "cloud_region_version": "1.1",
+ "physical_location_id": "NYCNY55"
+ }
+ ]
+} \ No newline at end of file
diff --git a/conductor/conductor/tests/unit/data/constraints.json b/conductor/conductor/tests/unit/data/constraints.json
new file mode 100644
index 0000000..f89cdaf
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/constraints.json
@@ -0,0 +1,95 @@
+{
+ "constraint_name": "check_for_availability",
+ "candidate_list": [
+ {
+ "candidate_id": "DLLSTX55",
+ "candidate_type": "cloud",
+ "inventory_type": "cloud",
+ "inventory_provider": "aai",
+ "cost": "100",
+ "location_id": "DLLSTX55",
+ "location_type": "azure",
+ "latitude": "32.897480",
+ "longitude": "-97.040443",
+ "city": "Dallas",
+ "state": "TX",
+ "country": "USA",
+ "region": "US",
+ "complex_name": "dalls_one",
+ "cloud_owner": "att-aic",
+ "cloud_region_version": "1.1",
+ "physical_location_id": "DLLSTX55"
+ },
+ {
+ "candidate_id": "NYCNY55",
+ "candidate_type": "cloud",
+ "inventory_type": "cloud",
+ "inventory_provider": "aai",
+ "cost": "100",
+ "location_id": "NYCNY55",
+ "location_type": "azure",
+ "latitude": "40.7128",
+ "longitude": "-74.0060",
+ "city": "New York",
+ "state": "NY",
+ "country": "USA",
+ "region": "US",
+ "complex_name": "ny_one",
+ "cloud_owner": "att-aic",
+ "cloud_region_version": "1.1",
+ "physical_location_id": "NYCNY55"
+ },
+ {
+ "candidate_id": "c3",
+ "candidate_type": "service",
+ "inventory_type": "service",
+ "inventory_provider": "aai",
+ "host_id": "vnf_333",
+ "cost": "100",
+ "location_id": "SFOCA55",
+ "location_type": "azure",
+ "latitude": "32.897480",
+ "longitude": "-97.040443",
+ "city": "San Francisco",
+ "state": "CA",
+ "country": "USA",
+ "region": "US",
+ "complex_name": "sfo_one",
+ "cloud_owner": "att-aic",
+ "cloud_region_version": "1.1",
+ "physical_location_id": "SFOCA55"
+ }
+ ],
+ "constraint_type": "instance_fit",
+ "controller": "SDN-C",
+ "request": {
+ "key1": "value1",
+ "key2": "value2",
+ "key3": "value3"
+ },
+ "properties": {
+ "evaluate": {
+ "network_roles": "",
+ "complex_name": {
+ "any": [
+ "dalls_one"
+ ]
+ },
+ "country": {
+ "any": [
+ "USA"
+ ]
+ },
+ "state": {
+ "any": [
+ "TX"
+ ]
+ },
+ "region": {
+ "all": [
+ "US"
+ ]
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/conductor/conductor/tests/unit/data/demands.json b/conductor/conductor/tests/unit/data/demands.json
new file mode 100644
index 0000000..459a013
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/demands.json
@@ -0,0 +1,30 @@
+{
+ "demands": {
+ "vGMuxInfra": [
+ {
+ "inventory_provider": "aai",
+ "inventory_type": "service",
+ "service_type": "vG_Mux",
+ "attributes": {
+ "customer-id": "some_company",
+ "orchestration-status": "Activated"
+ }
+ }
+ ],
+ "vG": [
+ {
+ "inventory_provider": "aai",
+ "inventory_type": "service",
+ "service_type": "vG",
+ "attributes": {
+ "customer-id": "some_company",
+ "provisioning-status": "provisioned"
+ }
+ },
+ {
+ "inventory_provider": "aai",
+ "inventory_type": "cloud"
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/conductor/conductor/tests/unit/data/test_service.py b/conductor/conductor/tests/unit/data/test_service.py
new file mode 100644
index 0000000..b2c47be
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/test_service.py
@@ -0,0 +1,154 @@
+#
+# -------------------------------------------------------------------------
+# Copyright (c) 2018 Intel Corporation Intellectual Property
+#
+# 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 unittest
+
+import conductor.data.service as service
+import mock
+import stevedore
+import yaml
+from conductor.data.plugins.inventory_provider import extensions as ip_ext
+from conductor.data.plugins.service_controller import extensions as sc_ext
+from conductor.data.service import DataEndpoint
+from oslo_config import cfg
+
+
+class TestDataEndpoint(unittest.TestCase):
+
+ def setUp(self):
+ ip_ext_manager = (
+ ip_ext.Manager(cfg.CONF, 'conductor.inventory_provider.plugin'))
+ sc_ext_manager = (
+ sc_ext.Manager(cfg.CONF, 'conductor.service_controller.plugin'))
+ self.data_ep = DataEndpoint(ip_ext_manager, sc_ext_manager)
+
+ def tearDown(self):
+ pass
+
+ def test_get_candidate_location(self):
+ req_json_file = './conductor/tests/unit/data/candidate_list.json'
+ req_json_candidate = json.loads(open(req_json_file).read())
+ req_json = dict()
+ req_json['candidate'] = req_json_candidate['candidate_list'][0]
+ location = (32.897480, -97.040443)
+ self.assertEqual({'response': location, 'error': False},
+ self.data_ep.get_candidate_location(None, req_json))
+ req_json['candidate']['latitude'] = None
+ req_json['candidate']['longitude'] = None
+ self.assertEqual({'response': None, 'error': True},
+ self.data_ep.get_candidate_location(None,
+ req_json))
+ req_json['candidate'] = req_json_candidate['candidate_list'][1]
+ location = (40.7128, -74.0060)
+ self.assertEqual({'response': location, 'error': False},
+ self.data_ep.get_candidate_location(None, req_json))
+
+ def test_get_candidate_zone(self):
+ req_json_file = './conductor/tests/unit/data/candidate_list.json'
+ req_json_candidate = json.loads(open(req_json_file).read())
+ req_json = dict()
+ req_json['candidate'] = req_json_candidate['candidate_list'][0]
+ req_json['category'] = None
+ self.assertEqual({'response': None, 'error': True},
+ self.data_ep.get_candidate_zone(None, req_json))
+ req_json['category'] = 'region'
+ self.assertEqual({'response': 'DLLSTX55', 'error': False},
+ self.data_ep.get_candidate_zone(None, req_json))
+ req_json['category'] = 'complex'
+ self.assertEqual({'response': 'dalls_one', 'error': False},
+ self.data_ep.get_candidate_zone(None, req_json))
+ req_json['candidate'] = req_json_candidate['candidate_list'][1]
+ req_json['category'] = 'region'
+ self.assertEqual({'response': 'NYCNY55', 'error': False},
+ self.data_ep.get_candidate_zone(None, req_json))
+
+
+ @mock.patch.object(service.LOG, 'error')
+ @mock.patch.object(service.LOG, 'debug')
+ @mock.patch.object(stevedore.ExtensionManager, 'map_method')
+ def test_get_candidates_from_service(self, ext_mock, debug_mock,
+ error_mock):
+ req_json_file = './conductor/tests/unit/data/constraints.json'
+ req_json = yaml.safe_load(open(req_json_file).read())
+ candidate_list = req_json['candidate_list']
+ ext_mock.return_value = [candidate_list]
+ self.maxDiff = None
+ self.assertEqual(2, len(
+ self.data_ep.get_candidates_from_service(None, req_json)))
+ req_json['controller'] = 'APP-C'
+ self.assertEqual({'response': [], 'error': False},
+ self.data_ep.get_candidates_from_service(None,
+ req_json))
+
+ def test_get_candidate_discard_set(self):
+ req_json_file = './conductor/tests/unit/data/constraints.json'
+ req_json = yaml.safe_load(open(req_json_file).read())
+ value_attrib = 'complex_name'
+ value = req_json['properties']['evaluate'][value_attrib]
+ candidate_list = req_json['candidate_list']
+ self.assertEqual(2, len(self.data_ep.get_candidate_discard_set(value,
+ candidate_list,
+ value_attrib)))
+ value_attrib = 'region'
+ value = req_json['properties']['evaluate'][value_attrib]
+ self.assertEqual(0, len(self.data_ep.get_candidate_discard_set(value,
+ candidate_list,
+ value_attrib)))
+
+ @mock.patch.object(service.LOG, 'error')
+ @mock.patch.object(service.LOG, 'debug')
+ @mock.patch.object(service.LOG, 'info')
+ @mock.patch.object(stevedore.ExtensionManager, 'map_method')
+ @mock.patch.object(stevedore.ExtensionManager, 'names')
+ def test_get_candidates_by_attributes(self, ext_mock2, ext_mock1,
+ info_mock, debug_mock, error_mock):
+ req_json_file = './conductor/tests/unit/data/constraints.json'
+ req_json = yaml.safe_load(open(req_json_file).read())
+ candidate_list = req_json['candidate_list']
+ ext_mock1.return_value = [candidate_list]
+ ext_mock2.return_value = [None]
+ self.maxDiff = None
+ expected_response = {'response': [candidate_list[0]], 'error': False}
+ self.assertEqual(expected_response,
+ self.data_ep.get_candidates_by_attributes(None,
+ req_json))
+
+ @mock.patch.object(service.LOG, 'error')
+ @mock.patch.object(service.LOG, 'debug')
+ @mock.patch.object(service.LOG, 'info')
+ @mock.patch.object(stevedore.ExtensionManager, 'map_method')
+ def test_reslove_demands(self, ext_mock, info_mock, debug_mock,
+ error_mock):
+ req_json_file = './conductor/tests/unit/data/demands.json'
+ req_json = yaml.safe_load(open(req_json_file).read())
+ ext_mock.return_value = []
+ expected_response = {'response': {'resolved_demands': None},
+ 'error': True}
+ self.assertEqual(expected_response,
+ self.data_ep.resolve_demands(None, req_json))
+ return_value = req_json['demands']['vG']
+ ext_mock.return_value = [return_value]
+ expected_response = {'response': {'resolved_demands': return_value},
+ 'error': False}
+ self.assertEqual(expected_response,
+ self.data_ep.resolve_demands(None, req_json))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/conductor/pom.xml b/conductor/pom.xml
index f12574a..db0f047 100644
--- a/conductor/pom.xml
+++ b/conductor/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.onap.optf.has</groupId>
<version>1.1.0-SNAPSHOT</version>
- <artifactId>optf-has-root</artifactId>
+ <artifactId>optf-has</artifactId>
</parent>
<groupId>org.onap.optf.has</groupId>
diff --git a/conductor/setup.cfg b/conductor/setup.cfg
index b62c365..8e3fc56 100644
--- a/conductor/setup.cfg
+++ b/conductor/setup.cfg
@@ -3,7 +3,7 @@ name = of-has
summary = ONAP Homing Service
description-file = README.rst
author = AT&T
-author-email = jdandrea@research.att.com
+author-email = ikram@research.att.com
home-page = https://wiki.onap.org/pages/viewpage.action?pageId=16005528
classifier =
Development Status :: 4 - Beta
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 0000000..dba217f
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,133 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+
+Homing API v1
+=============
+
+*Updated: 28 Feb 2018*
+
+This document describes the Homing API, provided by the Homing and Allocation service (Conductor).
+It is a work in progress and subject to frequent revision.
+
+General API Information
+=======================
+
+Authenticated calls that target a known URI but that use an HTTP method
+the implementation does not support return a 405 Method Not Allowed
+status. In addition, the HTTP OPTIONS method is supported for each known
+URI. In both cases, the Allow response header indicates the supported
+HTTP methods. See the API Errors section for more information about the
+error response structure.
+
+API versions
+============
+
+List all Homing API versions
+----------------------------
+
+**GET** ``/``\ F
+
+**Normal response codes:** 200
+
+.. code:: json
+
+ {
+ "versions": [
+ {
+ "status": "EXPERIMENTAL",
+ "id": "v1",
+ "updated": "2016-11-01T00:00:00Z",
+ "media-types": [
+ {
+ "base": "application/json",
+ "type": "application/vnd.onap.homing-v1+json"
+ }
+ ],
+ "links": [
+ {
+ "href": "http://has.ip/v1",
+ "rel": "self"
+ },
+ {
+ "href": "http://has.url/",
+ "type": "text/html",
+ "rel": "describedby"
+ }
+ ]
+ }
+ ]
+ }
+
+This operation does not accept a request body.
+
+Plans
+=====
+
+Create a plan
+-------------
+
+**POST** ``/v1/plans``
+
+- **Normal response codes:** 201
+- **Error response codes:** badRequest (400), unauthorized (401),
+ internalServerError (500)
+
+Request an inventory plan for one or more related service demands.
+
+The request includes or references a declarative **template**,
+consisting of:
+
+- **Parameters** that can be referenced like macros
+- **Demands** for service made against inventory
+- **Locations** that are common to the overall plan
+- **Constraints** made against demands, resulting in a set of inventory
+ candidates
+- **Optimizations** to further narrow down the remaining candidates
+
+The response contains an inventory **plan**, consisting of one or more
+sets of recommended pairings of demands with an inventory candidate’s
+attributes and region.
+
+Request Parameters
+~~~~~~~~~~~~~~~~~~
+
++--------------------+------------+----------+------------------------+
+| Parameter | Style | Type | Description |
++====================+============+==========+========================+
+| ``name`` | plain | xsd:stri | A name for the new |
+| (Optional) | | ng | plan. If a name is not |
+| | | | provided, it will be |
+| | | | auto-generated based |
+| | | | on the homing |
+| | | | template. This name |
+| | | | must be unique within |
+| | | | a given Conductor |
+| | | | environment. When |
+| | | | deleting a plan, its |
+| | | | name will not become |
+| | | | available for reuse |
+| | | | until the deletion |
+| | | | completes |
+| | | | successfully. Must |
+| | | | only contain letters, |
+| | | | numbers, hypens, full |
+| | | | stops, underscores, |
+| | | | and tildes (RFC 3986, |
+| | | | Section 2.3). This |
+| | | | parameter is |
+| | | | immutable. |
++--------------------+------------+----------+------------------------+
+| ``id`` (Optional) | plain | csapi:UU | The UUID of the plan. |
+| | | ID | UUID is assigned by |
+| | | | Conductor if no id is |
+| | | | provided in the |
+| | | | request. |
++--------------------+------------+----------+------------------------+
+| ``transaction_id`` | plain | csapi:UU | The transaction id |
+| | | ID | assigned by SO. The |
+| | | | logs should have this |
+| | | | transaction id for |
+| | | | tracking purposes. |
++--------------------+------------+----------+------------------------+
+| ``files`` | plain | xsd:dict | Supplies the contents |
+| (Optional) | | | of files referenced. |
++--------------------+------------+----------+------------------------+ \ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 636f71e..2ea9c0f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -6,4 +6,5 @@ Optimization Framework: Homing and Allocation
.. toctree::
:maxdepth: 4
+ api
release-notes/index
diff --git a/pom.xml b/pom.xml
index 37a62d3..505fac8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
</parent>
<groupId>org.onap.optf.has</groupId>
- <artifactId>optf-has-root</artifactId>
+ <artifactId>optf-has</artifactId>
<name>optf-has</name>
<version>1.1.0-SNAPSHOT</version>
@@ -37,6 +37,19 @@
<!--<module>docs</module>-->
</modules>
+ <properties>
+ <sonar.sourceEncoding>UTF-8</sonar.sourceEncoding>
+ <sonar.sources>conductor/conductor</sonar.sources>
+ <sonar.tests>conductor/conductor/tests</sonar.tests>
+ <sonar.python.coverage.reportPath>conductor/cover/coverage.xml</sonar.python.coverage.reportPath>
+ <sonar.language>py</sonar.language>
+ <sonar.pluginname>python</sonar.pluginname>
+ <sonar.inclusions>**/**.py</sonar.inclusions>
+ <sonar.exclusions>**/tests/**,setup.py,**/lib/**</sonar.exclusions>
+ <sonar.test.inclusions>**/tests/**.py</sonar.test.inclusions>
+ <sonar.test.exclusions>**/**.py,setup.py,**/lib/**</sonar.test.exclusions>
+ </properties>
+
<build>
<plugins>
<plugin>