From 0b855c08fd98fb8fa0f4bc40d8df430c897b4bad Mon Sep 17 00:00:00 2001 From: Ankitkumar Patel Date: Sun, 11 Feb 2018 17:51:13 -0500 Subject: Re-org folders, onboard test folder, test config Reorganized the folder structure. Onboarded testcases. Added test config. Issue-ID: OPTFRA-74 Change-Id: I97882a162a405a18ffd287495039e15ae9d0ad7b Signed-off-by: Ankitkumar Patel --- .gitignore | 110 ++++ LICENSE.txt | 26 + __init__.py | 45 -- adapters/__init__.py | 0 adapters/database/OracleDB.py | 32 - adapters/database/PostgresDB.py | 31 - adapters/database/VerticaDB.py | 55 -- adapters/database/__init__.py | 0 adapters/dcae/__init__.py | 0 adapters/dcae/message_router.py | 100 --- adapters/local_data/__init__.py | 0 adapters/local_data/local_policies.py | 40 -- adapters/policy/__init__.py | 0 adapters/policy/interface.py | 203 ------ adapters/policy/utils.py | 58 -- adapters/request_parsing/__init__.py | 0 adapters/request_parsing/placement.py | 33 - adapters/sdc/__init__.py | 0 adapters/sdc/asdc.py | 40 -- adapters/sdc/constraint_handler.py | 81 --- config/__init__.py | 32 - config/base.py | 36 - config/common_config.yaml | 43 ++ config/credentials.py | 60 -- config/loader.py | 51 -- config/osdf_config.yaml | 34 + models/api/common.py | 54 -- models/api/placementRequest.py | 124 ---- models/api/placementResponse.py | 57 -- models/policy/cmso/xacml/placementPolicies.xcore | 718 -------------------- .../policy/placement/xacml/placementPolicies.xcore | 728 --------------------- operation/__init__.py | 0 operation/error_handling.py | 93 --- operation/exceptions.py | 40 -- operation/responses.py | 39 -- optimizers/__init__.py | 0 optimizers/licenseopt/__init__.py | 0 optimizers/licenseopt/simple_license_allocation.py | 56 -- optimizers/placementopt/__init__.py | 0 optimizers/placementopt/conductor/__init__.py | 0 optimizers/placementopt/conductor/api_builder.py | 121 ---- optimizers/placementopt/conductor/conductor.py | 186 ------ .../placementopt/conductor/remote_opt_processor.py | 79 --- optimizers/placementopt/conductor/translation.py | 215 ------ osdf/__init__.py | 45 ++ osdf/adapters/__init__.py | 0 osdf/adapters/database/OracleDB.py | 32 + osdf/adapters/database/PostgresDB.py | 31 + osdf/adapters/database/VerticaDB.py | 55 ++ osdf/adapters/database/__init__.py | 0 osdf/adapters/dcae/__init__.py | 0 osdf/adapters/dcae/message_router.py | 100 +++ osdf/adapters/local_data/__init__.py | 0 osdf/adapters/local_data/local_policies.py | 40 ++ osdf/adapters/policy/__init__.py | 0 osdf/adapters/policy/interface.py | 204 ++++++ osdf/adapters/policy/utils.py | 58 ++ osdf/adapters/request_parsing/__init__.py | 0 osdf/adapters/request_parsing/placement.py | 33 + osdf/adapters/sdc/__init__.py | 0 osdf/adapters/sdc/asdc.py | 40 ++ osdf/adapters/sdc/constraint_handler.py | 81 +++ osdf/config/__init__.py | 32 + osdf/config/base.py | 36 + osdf/config/credentials.py | 60 ++ osdf/config/loader.py | 51 ++ osdf/logging/__init__.py | 0 osdf/logging/osdf_logging.py | 229 +++++++ osdf/models/api/common.py | 54 ++ osdf/models/api/placementRequest.py | 124 ++++ osdf/models/api/placementResponse.py | 57 ++ .../policy/cmso/xacml/placementPolicies.xcore | 718 ++++++++++++++++++++ .../policy/placement/xacml/placementPolicies.xcore | 728 +++++++++++++++++++++ osdf/operation/__init__.py | 0 osdf/operation/error_handling.py | 93 +++ osdf/operation/exceptions.py | 40 ++ osdf/operation/responses.py | 39 ++ osdf/optimizers/__init__.py | 0 osdf/optimizers/licenseopt/__init__.py | 0 .../licenseopt/simple_license_allocation.py | 56 ++ osdf/optimizers/placementopt/__init__.py | 0 osdf/optimizers/placementopt/conductor/__init__.py | 0 .../placementopt/conductor/api_builder.py | 123 ++++ .../optimizers/placementopt/conductor/conductor.py | 186 ++++++ .../placementopt/conductor/remote_opt_processor.py | 79 +++ .../placementopt/conductor/translation.py | 216 ++++++ osdf/templates/cms_opt_request.jsont | 35 + osdf/templates/cms_opt_request.jsont_1707_v1 | 67 ++ osdf/templates/cms_opt_request_1702.jsont | 63 ++ osdf/templates/cms_opt_response.jsont | 8 + osdf/templates/conductor_interface.json | 81 +++ osdf/templates/license_opt_request.jsont | 6 + osdf/templates/plc_opt_request.jsont | 142 ++++ osdf/templates/plc_opt_response.jsont | 14 + osdf/templates/policy_request.jsont | 3 + osdf/templates/test_cms_nb_req_from_client.jsont | 19 + osdf/templates/test_plc_nb_req_from_client.jsont | 52 ++ osdf/utils/__init__.py | 0 osdf/utils/data_conversion.py | 62 ++ osdf/utils/data_types.py | 30 + osdf/utils/interfaces.py | 90 +++ osdf/utils/local_processing.py | 43 ++ osdf/utils/programming_utils.py | 105 +++ osdf/webapp/__init__.py | 0 osdf/webapp/appcontroller.py | 47 ++ requirements.txt | 13 + test/bad_test_Utils.py | 21 + test/dummy_test_dummy.py | 14 + test/placement-tests/request.json | 87 +++ test/placement-tests/request_mso.json | 63 ++ .../request_mso_subs_name_blank.json | 63 ++ .../request_mso_subs_name_default.json | 63 ++ .../request_mso_subs_name_none.json | 62 ++ .../request_mso_subs_name_null.json | 63 ++ test/placement-tests/request_vCPE.json | 105 +++ test/placement-tests/scopePolicies.json | 21 + test/placement-tests/testScoperequest.json | 144 ++++ test/placement-tests/test_by_scope.yaml | 20 + test/placement-tests/vnfGroupPolicies.txt | 197 ++++++ .../CloudAttributePolicy_vGMuxInfra_1.json | 34 + .../CloudAttributePolicy_vG_1.json | 34 + .../DistanceToLocationPolicy_vGMuxInfra_1.json | 30 + .../DistanceToLocationPolicy_vG_1.json | 30 + .../InventoryGroup_vGMuxInfra_1.json | 22 + test/policy-local-files/InventoryGroup_vG_1.json | 22 + .../PlacementOptimizationPolicy.json | 41 ++ .../ResourceInstancePolicy_vG_1.json | 26 + .../policy-local-files/VNFPolicy_vGMuxInfra_1.json | 36 + test/policy-local-files/VNFPolicy_vG_1.json | 36 + .../ZonePolicy_vGMuxInfra_1.json | 26 + test/policy-local-files/ZonePolicy_vG_1.json | 26 + test/test-requirements.txt | 4 + test/test_ConductorApiBuilder.py | 39 ++ test/test_PolicyCalls.py | 115 ++++ tox.ini | 17 + utils/__init__.py | 0 utils/data_conversion.py | 62 -- utils/data_types.py | 30 - utils/interfaces.py | 90 --- utils/local_processing.py | 43 -- utils/programming_utils.py | 105 --- webapp/__init__.py | 0 webapp/appcontroller.py | 47 -- 143 files changed, 6194 insertions(+), 3784 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE.txt delete mode 100755 __init__.py delete mode 100644 adapters/__init__.py delete mode 100644 adapters/database/OracleDB.py delete mode 100644 adapters/database/PostgresDB.py delete mode 100644 adapters/database/VerticaDB.py delete mode 100644 adapters/database/__init__.py delete mode 100644 adapters/dcae/__init__.py delete mode 100755 adapters/dcae/message_router.py delete mode 100644 adapters/local_data/__init__.py delete mode 100644 adapters/local_data/local_policies.py delete mode 100644 adapters/policy/__init__.py delete mode 100644 adapters/policy/interface.py delete mode 100644 adapters/policy/utils.py delete mode 100644 adapters/request_parsing/__init__.py delete mode 100644 adapters/request_parsing/placement.py delete mode 100644 adapters/sdc/__init__.py delete mode 100755 adapters/sdc/asdc.py delete mode 100644 adapters/sdc/constraint_handler.py delete mode 100644 config/__init__.py delete mode 100644 config/base.py create mode 100644 config/common_config.yaml delete mode 100644 config/credentials.py delete mode 100644 config/loader.py create mode 100755 config/osdf_config.yaml delete mode 100755 models/api/common.py delete mode 100644 models/api/placementRequest.py delete mode 100644 models/api/placementResponse.py delete mode 100644 models/policy/cmso/xacml/placementPolicies.xcore delete mode 100644 models/policy/placement/xacml/placementPolicies.xcore delete mode 100644 operation/__init__.py delete mode 100644 operation/error_handling.py delete mode 100644 operation/exceptions.py delete mode 100644 operation/responses.py delete mode 100644 optimizers/__init__.py delete mode 100644 optimizers/licenseopt/__init__.py delete mode 100644 optimizers/licenseopt/simple_license_allocation.py delete mode 100644 optimizers/placementopt/__init__.py delete mode 100644 optimizers/placementopt/conductor/__init__.py delete mode 100644 optimizers/placementopt/conductor/api_builder.py delete mode 100644 optimizers/placementopt/conductor/conductor.py delete mode 100644 optimizers/placementopt/conductor/remote_opt_processor.py delete mode 100644 optimizers/placementopt/conductor/translation.py create mode 100755 osdf/__init__.py create mode 100644 osdf/adapters/__init__.py create mode 100644 osdf/adapters/database/OracleDB.py create mode 100644 osdf/adapters/database/PostgresDB.py create mode 100644 osdf/adapters/database/VerticaDB.py create mode 100644 osdf/adapters/database/__init__.py create mode 100644 osdf/adapters/dcae/__init__.py create mode 100755 osdf/adapters/dcae/message_router.py create mode 100644 osdf/adapters/local_data/__init__.py create mode 100644 osdf/adapters/local_data/local_policies.py create mode 100644 osdf/adapters/policy/__init__.py create mode 100644 osdf/adapters/policy/interface.py create mode 100644 osdf/adapters/policy/utils.py create mode 100644 osdf/adapters/request_parsing/__init__.py create mode 100644 osdf/adapters/request_parsing/placement.py create mode 100644 osdf/adapters/sdc/__init__.py create mode 100755 osdf/adapters/sdc/asdc.py create mode 100644 osdf/adapters/sdc/constraint_handler.py create mode 100644 osdf/config/__init__.py create mode 100644 osdf/config/base.py create mode 100644 osdf/config/credentials.py create mode 100644 osdf/config/loader.py create mode 100644 osdf/logging/__init__.py create mode 100755 osdf/logging/osdf_logging.py create mode 100755 osdf/models/api/common.py create mode 100644 osdf/models/api/placementRequest.py create mode 100644 osdf/models/api/placementResponse.py create mode 100644 osdf/models/policy/cmso/xacml/placementPolicies.xcore create mode 100644 osdf/models/policy/placement/xacml/placementPolicies.xcore create mode 100644 osdf/operation/__init__.py create mode 100644 osdf/operation/error_handling.py create mode 100644 osdf/operation/exceptions.py create mode 100644 osdf/operation/responses.py create mode 100644 osdf/optimizers/__init__.py create mode 100644 osdf/optimizers/licenseopt/__init__.py create mode 100644 osdf/optimizers/licenseopt/simple_license_allocation.py create mode 100644 osdf/optimizers/placementopt/__init__.py create mode 100644 osdf/optimizers/placementopt/conductor/__init__.py create mode 100644 osdf/optimizers/placementopt/conductor/api_builder.py create mode 100644 osdf/optimizers/placementopt/conductor/conductor.py create mode 100644 osdf/optimizers/placementopt/conductor/remote_opt_processor.py create mode 100644 osdf/optimizers/placementopt/conductor/translation.py create mode 100755 osdf/templates/cms_opt_request.jsont create mode 100755 osdf/templates/cms_opt_request.jsont_1707_v1 create mode 100755 osdf/templates/cms_opt_request_1702.jsont create mode 100644 osdf/templates/cms_opt_response.jsont create mode 100755 osdf/templates/conductor_interface.json create mode 100644 osdf/templates/license_opt_request.jsont create mode 100755 osdf/templates/plc_opt_request.jsont create mode 100755 osdf/templates/plc_opt_response.jsont create mode 100755 osdf/templates/policy_request.jsont create mode 100755 osdf/templates/test_cms_nb_req_from_client.jsont create mode 100755 osdf/templates/test_plc_nb_req_from_client.jsont create mode 100644 osdf/utils/__init__.py create mode 100644 osdf/utils/data_conversion.py create mode 100644 osdf/utils/data_types.py create mode 100644 osdf/utils/interfaces.py create mode 100644 osdf/utils/local_processing.py create mode 100644 osdf/utils/programming_utils.py create mode 100644 osdf/webapp/__init__.py create mode 100644 osdf/webapp/appcontroller.py create mode 100644 requirements.txt create mode 100644 test/bad_test_Utils.py create mode 100644 test/dummy_test_dummy.py create mode 100644 test/placement-tests/request.json create mode 100644 test/placement-tests/request_mso.json create mode 100644 test/placement-tests/request_mso_subs_name_blank.json create mode 100644 test/placement-tests/request_mso_subs_name_default.json create mode 100644 test/placement-tests/request_mso_subs_name_none.json create mode 100644 test/placement-tests/request_mso_subs_name_null.json create mode 100644 test/placement-tests/request_vCPE.json create mode 100644 test/placement-tests/scopePolicies.json create mode 100644 test/placement-tests/testScoperequest.json create mode 100644 test/placement-tests/test_by_scope.yaml create mode 100644 test/placement-tests/vnfGroupPolicies.txt create mode 100644 test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json create mode 100644 test/policy-local-files/CloudAttributePolicy_vG_1.json create mode 100644 test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json create mode 100644 test/policy-local-files/DistanceToLocationPolicy_vG_1.json create mode 100644 test/policy-local-files/InventoryGroup_vGMuxInfra_1.json create mode 100644 test/policy-local-files/InventoryGroup_vG_1.json create mode 100644 test/policy-local-files/PlacementOptimizationPolicy.json create mode 100644 test/policy-local-files/ResourceInstancePolicy_vG_1.json create mode 100644 test/policy-local-files/VNFPolicy_vGMuxInfra_1.json create mode 100644 test/policy-local-files/VNFPolicy_vG_1.json create mode 100644 test/policy-local-files/ZonePolicy_vGMuxInfra_1.json create mode 100644 test/policy-local-files/ZonePolicy_vG_1.json create mode 100644 test/test-requirements.txt create mode 100644 test/test_ConductorApiBuilder.py create mode 100644 test/test_PolicyCalls.py create mode 100644 tox.ini delete mode 100644 utils/__init__.py delete mode 100644 utils/data_conversion.py delete mode 100644 utils/data_types.py delete mode 100644 utils/interfaces.py delete mode 100644 utils/local_processing.py delete mode 100644 utils/programming_utils.py delete mode 100644 webapp/__init__.py delete mode 100644 webapp/appcontroller.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..864f9ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,110 @@ +# Copied from https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore +# Copy as of 2018-02-08 +# github/gitignore is licensed under the +# Creative Commons Zero v1.0 Universal + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..fffadb0 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,26 @@ + +The following licence applies to all files in this and subdirectories. Licences +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have licence text +defaults to being covered by this text; not all files support the addition of +licenses. + +# +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + diff --git a/__init__.py b/__init__.py deleted file mode 100755 index d0993ae..0000000 --- a/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -"""Core functions for OSDF Application, including flask app""" - -from jinja2 import Template - - -end_point_auth_mapping = { # map a URL endpoint to auth group - "cmscheduler": "CMScheduler", - "placement": "Placement", -} - -userid_suffix, passwd_suffix = "Username", "Password" -auth_groups = set(end_point_auth_mapping.values()) - -ERROR_TEMPLATE = Template(""" -{ - "serviceException": { - "text": "{{ description }}" - } -} -""") - -ACCEPTED_MESSAGE_TEMPLATE = Template(""" -{ - "requestId": "{{ request_id }}", - "text": "{{ description }}" -} -""") diff --git a/adapters/__init__.py b/adapters/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/database/OracleDB.py b/adapters/database/OracleDB.py deleted file mode 100644 index 655dd27..0000000 --- a/adapters/database/OracleDB.py +++ /dev/null @@ -1,32 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import cx_Oracle - -from osdf.utils.programming_utils import MetaSingleton - - -class OracleDB(metaclass=MetaSingleton): - conn, cur = None, None - - def connect(self, host=None, sid=None, user=None, passwd=None, port=5432): - if self.conn is None: - tns_info = cx_Oracle.makedsn(host=host, port=port, sid=sid) - self.conn = cx_Oracle.connect(user=user, password=passwd, dsn=tns_info, threaded=True) - self.cur = self.conn.cursor() - return self.conn, self.cur diff --git a/adapters/database/PostgresDB.py b/adapters/database/PostgresDB.py deleted file mode 100644 index 6689566..0000000 --- a/adapters/database/PostgresDB.py +++ /dev/null @@ -1,31 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import psycopg2 - -from osdf.utils.programming_utils import MetaSingleton - - -class PostgresDB(metaclass=MetaSingleton): - conn, cur = None, None - - def connect(self, host=None, db=None, user=None, passwd=None, port=5432): - if self.conn is None: - self.conn = psycopg2.connect(host=host, port=port, user=user, password=passwd, database=db) - self.cur = self.conn.cursor() - return self.conn, self.cur diff --git a/adapters/database/VerticaDB.py b/adapters/database/VerticaDB.py deleted file mode 100644 index ad961d7..0000000 --- a/adapters/database/VerticaDB.py +++ /dev/null @@ -1,55 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import jaydebeapi -import sqlalchemy.pool as pool - -from jaydebeapi import _DEFAULT_CONVERTERS, _java_to_py -from osdf.utils.programming_utils import MetaSingleton -from osdf.config.base import osdf_config - -_DEFAULT_CONVERTERS.update({'BIGINT': _java_to_py('longValue')}) - - -class VerticaDB(metaclass=MetaSingleton): - connection_pool = None - - def get_connection(self): - p = self.get_config_params() - c = jaydebeapi.connect( - 'com.vertica.jdbc.Driver', - 'jdbc:vertica://{}:{}/{}'.format(p['host'], p['port'], p['db']), - {'user': p['user'], 'password': p['passwd'], 'CHARSET': 'UTF8'}, - jars=[p['db_driver']] - ) - return c - - def get_config_params(self): - config = osdf_config["deployment"] - host, port, db = config["verticaHost"], config["verticaPort"], config.get("verticaDB") - user, passwd = config["verticaUsername"], config["verticaPassword"] - jar_path = osdf_config['core']['osdf_system']['vertica_jar'] - params = dict(host=host, db=db, user=user, passwd=passwd, port=port, db_driver=jar_path) - return params - - def connect(self): - if self.connection_pool is None: - self.connection_pool = pool.QueuePool(self.get_connection, max_overflow=10, pool_size=5, recycle=600) - conn = self.connection_pool.connect() - cursor = conn.cursor() - return conn, cursor diff --git a/adapters/database/__init__.py b/adapters/database/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/dcae/__init__.py b/adapters/dcae/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/dcae/message_router.py b/adapters/dcae/message_router.py deleted file mode 100755 index e495331..0000000 --- a/adapters/dcae/message_router.py +++ /dev/null @@ -1,100 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import requests -from osdf.utils.data_types import list_like -from osdf.operation.exceptions import MessageBusConfigurationException - - -class MessageRouterClient(object): - def __init__(self, - dmaap_url=None, - mr_host_base_urls=None, - topic=None, - consumer_group=None, consumer_id=None, - timeout_ms=15000, fetch_limit=1000, - userid=None, passwd=None): - """ - :param dmaap_url: protocol, host and port; mostly for UEB - (e.g. https://dcae-msrt-ftl.homer.att.com:3905/) - :param mr_host_base_urls: for DMaaP, we get a topic URL (base_url + events/topic_name) - (e.g. https://dcae-msrt-ftl.homer.att.com:3905/events/com.att.dcae.dmaap.FTL.SNIRO-CM-SCHEDULER-RESPONSE) - :param consumer_group: DMaaP/UEB consumer group (unique for each subscriber; required for GET) - :param consumer_id: DMaaP/UEB consumer ID (unique for each thread/process for a subscriber; required for GET) - :param timeout_ms: (optional, default 15 seconds or 15,000 ms) server-side timeout for GET request - :param fetch_limit: (optional, default 1000 messages per request for GET), ignored for "POST" - :param userid: (optional, userid for HTTP basic authentication) - :param passwd: (optional, password for HTTP basic authentication) - """ - mr_error = MessageBusConfigurationException - if dmaap_url is None: # definitely not DMaaP, so use UEB mode - self.is_dmaap = False - if not (mr_host_base_urls and list_like(mr_host_base_urls)): - raise mr_error("Not a DMaaP or UEB configuration") - if not topic: - raise mr_error("Invalid topic: '{}'",format(topic)) - self.topic_urls = ["{}/events/{}".format(base_url, topic) for base_url in mr_host_base_urls] - else: - self.is_dmaap = True - self.topic_urls = [dmaap_url] - - self.timeout_ms = timeout_ms - self.fetch_limit = fetch_limit - self.auth = (userid, passwd) if userid and passwd else None - self.consumer_group = consumer_group - self.consumer_id = consumer_id - - def get(self, outputjson=True): - """Fetch messages from message router (DMaaP or UEB) - :param outputjson: (optional, specifies if response is expected to be in json format), ignored for "POST" - :return: response as a json object (if outputjson is True) or as a string - """ - url_fmt = "{topic_url}/{cgroup}/{cid}?timeout={timeout_ms}&limit={limit}" - urls = [url_fmt.format(topic_url=x, timeout_ms=self.timeout_ms, limit=self.fetch_limit, - cgroup=self.consumer_group, cid=self.consumer_id) for x in self.topic_urls] - for url in urls[:-1]: - try: - return self.http_request(method='GET', url=url, outputjson=outputjson) - except: - pass - return self.http_request(method='GET', url=urls[-1], outputjson=outputjson) - - def post(self, msg, inputjson=True): - for url in self.topic_urls[:-1]: - try: - return self.http_request(method='POST', url=url, inputjson=inputjson, msg=msg) - except: - pass - return self.http_request(method='POST', url=self.topic_urls[-1], inputjson=inputjson, msg=msg) - - def http_request(self, url, method, inputjson=True, outputjson=True, msg=None, **kwargs): - """ - Perform the actual URL request (GET or POST), and do error handling - :param url: full URL (including topic, limit, timeout, etc.) - :param method: GET or POST - :param inputjson: Specify whether input is in json format (valid only for POST) - :param outputjson: Is response expected in a json format - :param msg: content to be posted (valid only for POST) - :return: response as a json object (if outputjson or POST) or as a string; None if error - """ - res = requests.request(url=url, method=method, auth=self.auth, **kwargs) - if res.status_code == requests.codes.ok: - return res.json() if outputjson or method == "POST" else res.content - else: - raise Exception("HTTP Response Error: code {}; headers:{}, content: {}".format( - res.status_code, res.headers, res.content)) diff --git a/adapters/local_data/__init__.py b/adapters/local_data/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/local_data/local_policies.py b/adapters/local_data/local_policies.py deleted file mode 100644 index c63ae5a..0000000 --- a/adapters/local_data/local_policies.py +++ /dev/null @@ -1,40 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import json -import os - - -def get_local_policies(local_policy_folder, local_policy_list, policy_id_list=None): - """ - Get policies from a local file system. - Required for the following scenarios: - (a) doing work-arounds (e.g. if we are asked to drop some policies for testing purposes) - (b) work-arounds when policy platform is giving issues (e.g. if dev/IST policies are wiped out in an upgrade) - :param local_policy_folder: where the policy files are present - :param local_policy_list: list of local policies - :param policy_id_list: list of policies to get (if unspecified or None, get all) - :return: get policies - """ - policies = [] - for fname in local_policy_list: # ugly removal of .json from file name - if policy_id_list and fname[:-5] not in policy_id_list: - continue - with open(os.path.join(local_policy_folder, fname)) as fid: - policies.append(json.load(fid)) - return policies diff --git a/adapters/policy/__init__.py b/adapters/policy/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/policy/interface.py b/adapters/policy/interface.py deleted file mode 100644 index ee45051..0000000 --- a/adapters/policy/interface.py +++ /dev/null @@ -1,203 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import base64 -import itertools -import json - - -from requests import RequestException -from osdf.operation.exceptions import BusinessException -from osdf.adapters.local_data.local_policies import get_local_policies -from osdf.adapters.policy.utils import _regex_policy_name -from osdf.config.base import osdf_config -from osdf.logging.osdf_logging import audit_log, MH, metrics_log, error_log, debug_log -from osdf.utils.interfaces import RestClient -from osdf.optimizers.placementopt.conductor.api_builder import retrieve_node -from osdf.utils import data_mapping - - -def get_by_name(rest_client, policy_name_list, wildcards=True): - policy_list = [] - for policy_name in policy_name_list: - try: - query_name = policy_name - if wildcards: - query_name = _regex_policy_name(query_name) - policy_list.append(rest_client.request(json={"policyName": query_name})) - except RequestException as err: - audit_log.warn("Error in fetching policy: " + policy_name) - raise BusinessException("Cannot fetch policy {}: ".format(policy_name), err) - return policy_list - - -def get_subscriber_name(req, pmain): - subs_name = retrieve_node(req, pmain['subscriber_name']) - if subs_name is None: - return "DEFAULT" - else: - subs_name_uc = subs_name.upper() - if subs_name_uc in ("DEFAULT", "NULL", ""): - subs_name = "DEFAULT" - return subs_name - - -def get_subscriber_role(rest_client, req, pmain, service_name, scope): - """Make a request to policy and return subscriberRole - :param rest_client: rest client to make call - :param req: request object from MSO - :param pmain: main config that will have policy path information - :param service_name: the type of service to call: e.g. "vCPE - :param scope: the scope of policy to call: e.g. "OOF_HAS_vCPE". - :return: subscriberRole and provStatus retrieved from Subscriber policy - """ - subscriber_role = "DEFAULT" - prov_status = [] - subs_name = get_subscriber_name(req, pmain) - if subs_name == "DEFAULT": - return subscriber_role, prov_status - - policy_subs = pmain['policy_subscriber'] - policy_scope = {"policyName": "{}.*".format(scope), - "configAttributes": { - "serviceType": "{}".format(service_name), - "service": "{}".format(policy_subs)} - } - policy_list = [] - try: - policy_list.append(rest_client.request(json=policy_scope)) - except RequestException as err: - audit_log.warn("Error in fetching policy for {}: ".format(policy_subs)) - return subscriber_role, prov_status - - formatted_policies = [] - for x in itertools.chain(*policy_list): - if x['config'] is None: - raise BusinessException("Config not found for policy with name %s" % x['policyName']) - else: - formatted_policies.append(json.loads(x['config'])) - - for policy in formatted_policies: - property_list = policy['content']['property'] - for prop in property_list: - if subs_name in prop['subscriberName']: - subs_role_list = prop['subscriberRole'] - prov_status = prop['provStatus'] - if isinstance(subs_role_list, list): # as list is returned - return subs_role_list[0], prov_status - return subscriber_role, prov_status - - -def get_by_scope(rest_client, req, config_local, type_service): - policy_list = [] - pmain = config_local['policy_info'][type_service] - pscope = pmain['policy_scope'] - - model_name = retrieve_node(req, pscope['service_name']) - service_name = data_mapping.get_request_service_type(req) - if service_name is None: - service_name = data_mapping.get_service_type(model_name) - scope = pscope['scope_{}'.format(service_name.lower())] - subscriber_role, prov_status = get_subscriber_role(rest_client, req, pmain, service_name, scope) - policy_type_list = pmain['policy_type_{}'.format(service_name.lower())] - for policy_type in policy_type_list: - policy_scope = {"policyName": "{}.*".format(scope), - "configAttributes": { - "serviceType": "{}".format(service_name), - "service": "{}".format(policy_type), - "subscriberRole": "{}".format(subscriber_role)} - } - policy_list.append(rest_client.request(json=policy_scope)) - return policy_list, prov_status - - -def remote_api(req_json, osdf_config, service_type="placement"): - """Make a request to policy and return response -- it accounts for multiple requests that be needed - :param req_json: policy request object (can have multiple policy names) - :param osdf_config: main config that will have credential information - :param service_type: the type of service to call: "placement", "scheduling" - :return: all related policies and provStatus retrieved from Subscriber policy - """ -# if not req_json[service_type + "Info"]['policyId']: -# return [] - - config = osdf_config.deployment - uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword'] - pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword'] - headers = {"ClientAuth": base64.b64encode(bytes("{}:{}".format(pcuid, pcpasswd), "ascii"))} - headers.update({'Environment': config['policyPlatformEnv']}) - url = config['policyPlatformUrl'] - rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug) - - if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name": - policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=True) - elif osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name_no_wildcards": - policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=False) - else: # Get policy by scope - policies, prov_status = get_by_scope(rc, req_json, osdf_config.core, service_type) - - # policies in res are list of lists, so flatten them; also only keep config part - formatted_policies = [] - for x in itertools.chain(*policies): - if x['config'] is None: - raise BusinessException("Config not found for policy with name %s" % x['policyName']) - else: - formatted_policies.append(json.loads(x['config'])) - return formatted_policies, prov_status - - -def local_policies_location(req_json, osdf_config, service_type): - """ - Get folder and list of policy_files if "local policies" option is enabled - :param service_type: placement supported for now, but can be any other service - :return: a tuple (folder, file_list) or None - """ - lp = osdf_config.core.get('osdf_hacks', {}).get('local_policies', {}) - if lp.get('global_disabled'): - return None # short-circuit to disable all local policies - if lp.get('local_{}_policies_enabled'.format(service_type)): - if service_type == "scheduling": - return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type)) - else: - model_name = retrieve_node(req_json, osdf_config.core['policy_info'][service_type]['policy_scope']['service_name']) - service_name = data_mapping.get_service_type(model_name) - return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), lp.get('{}_policy_files_{}'.format(service_type, service_name.lower())) - return None - - -def get_policies(request_json, service_type): - """Validate the request and get relevant policies - :param request_json: Request object - :param service_type: the type of service to call: "placement", "scheduling" - :return: policies associated with this request and provStatus retrieved from Subscriber policy - """ - prov_status = [] - req_info = request_json['requestInfo'] - req_id = req_info['requestId'] - metrics_log.info(MH.requesting("policy", req_id)) - local_info = local_policies_location(request_json, osdf_config, service_type) - - if local_info: # tuple containing location and list of files - to_filter = None - if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name": - to_filter = request_json[service_type + "Info"]['policyId'] - policies = get_local_policies(local_info[0], local_info[1], to_filter) - else: - policies, prov_status= remote_api(request_json, osdf_config, service_type) - - return policies, prov_status diff --git a/adapters/policy/utils.py b/adapters/policy/utils.py deleted file mode 100644 index a006f12..0000000 --- a/adapters/policy/utils.py +++ /dev/null @@ -1,58 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from collections import defaultdict - - -def group_policies(flat_policies): - """Filter policies using the following steps: - 1. Apply prioritization among the policies that are sharing the same policy type and resource type - 2. Remove redundant policies that may applicable across different types of resource - 3. Filter policies based on type and return - :param flat_policies: list of flat policies - :return: Filtered policies - """ - aggregated_policies = {} - filter_policies = defaultdict(list) - policy_name = [] - for policy in flat_policies: - policy_type = policy['content']['type'] - if policy_type not in aggregated_policies: - aggregated_policies[policy_type] = defaultdict(list) - for resource in policy['content']['policyScope']['resourceInstanceType']: - aggregated_policies[policy_type][resource].append(policy) - for policy_type in aggregated_policies: - for resource in aggregated_policies[policy_type]: - if len(aggregated_policies[policy_type][resource]) > 0: - aggregated_policies[policy_type][resource].sort(key=lambda x: x['priority'], reverse=True) - policy = aggregated_policies[policy_type][resource][0] - if policy['policyName'] not in policy_name: - filter_policies[policy['content']['type']].append(policy) - policy_name.append(policy['policyName']) - return filter_policies - - -def _regex_policy_name(policy_name): - """Get the correct policy name as a regex - (e.g. OOF_HAS_vCPE.cloudAttributePolicy ends up in policy as OOF_HAS_vCPE.Config_MS_cloudAttributePolicy.1.xml - So, for now, we query it as OOF_HAS_vCPE..*aicAttributePolicy.*) - :param policy_name: Example: OOF_HAS_vCPE.aicAttributePolicy - :return: regexp for policy: Example: OOF_HAS_vCPE..*aicAttributePolicy.* - """ - p = policy_name.partition('.') - return p[0] + p[1] + ".*" + p[2] + ".*" diff --git a/adapters/request_parsing/__init__.py b/adapters/request_parsing/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/request_parsing/placement.py b/adapters/request_parsing/placement.py deleted file mode 100644 index d7a6575..0000000 --- a/adapters/request_parsing/placement.py +++ /dev/null @@ -1,33 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import copy -import json -from osdf.utils.programming_utils import list_flatten, dot_notation - - -def json_path_after_expansion(req_json, reference): - """ - Get the child node(s) from the dot-notation [reference] and parent [req_json]. - For placement and other requests, there are encoded JSONs inside the request or policy, - so we need to expand it and then do a search over the parent plus expanded JSON. - """ - req_json_copy = copy.deepcopy(req_json) # since we expand the JSON in place, we work on a copy - req_json_copy['placementInfo']['orderInfo'] = json.loads(req_json_copy['placementInfo']['orderInfo']) - info = dot_notation(req_json_copy, reference) - return list_flatten(info) if isinstance(info, list) else info diff --git a/adapters/sdc/__init__.py b/adapters/sdc/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/adapters/sdc/asdc.py b/adapters/sdc/asdc.py deleted file mode 100755 index 43932ba..0000000 --- a/adapters/sdc/asdc.py +++ /dev/null @@ -1,40 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from osdf.utils.interfaces import RestClient -import xml.etree.ElementTree as ET - -def request(model_version_id, request_id, config): - """Get all of the license artifacts from SDC using service_resource_id and model_version_id - :param model_version_id: model_version_id - :param request_id: request_id - :return: license artifacts from SDC - """ - base_url = config['sdcUrl'] - uid, passwd = config['sdcUsername'], config['sdcPassword'] - headers = {"CSP_UID": config['sdcMechId'], "X-ONAP-InstanceID": "osdf"} - rc = RestClient(userid=uid, passwd=passwd, headers=headers, method="GET", req_id=request_id) - resource_data = rc.request(base_url + '/resources/{}/metadata'.format(model_version_id)) - - artifact_ids = [x['artifactURL'].split("/resources/")[-1] # get the part after /resources/ - for x in resource_data.get('artifacts', []) if x.get('artifactType') == "VF_LICENSE"] - artifact_urls = [base_url + '/resources/' + str(artifact_id) for artifact_id in artifact_ids] - licenses = [] - for x in artifact_urls: - licenses.append(ET.fromstring(rc.request(x, asjson=False))) - return licenses diff --git a/adapters/sdc/constraint_handler.py b/adapters/sdc/constraint_handler.py deleted file mode 100644 index 2aae9a0..0000000 --- a/adapters/sdc/constraint_handler.py +++ /dev/null @@ -1,81 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from osdf.config.base import osdf_config -from osdf.utils.programming_utils import dot_notation - -ns = {'p': 'http://xmlns.onap.org/sdc/license-model/1.0'} -config_local = osdf_config.core - - -def choose_license(license_artifacts, order_info, service_type): - entitlement_pool_uuids = [] - license_key_group_uuids = [] - - for license_artifact in license_artifacts: - for feature in license_artifact.findall('./p:feature-group-list/', ns): - for entitlement in feature.findall('./p:entitlement-pool-list/', ns): - if is_valid(entitlement, order_info, service_type): - entitlement_pool_uuid = entitlement.find('p:entitlement-pool-uuid', ns).text - entitlement_pool_uuids.append(entitlement_pool_uuid) - for license_key_group in feature.findall('./p:license-key-group-list/', ns): - if is_valid(license_key_group, order_info, service_type): - license_key_group_uuid = license_key_group.find('p:license-key-group-uuid', ns).text - license_key_group_uuids.append(license_key_group_uuid) - return entitlement_pool_uuids, license_key_group_uuids - - -# element is expected to be a license-key-group or entitlement-pool -# if these elements diverge at a later date this method should be refactored -def is_valid(element, order_info, service_type): - for limit in element.findall('./p:sp-limits/p:limit', ns): - # description = limit.find('p:description', ns).text - metric_value = limit.find('p:values', ns).text - metric = limit.find('p:metric', ns).text - try: - order_value = dot_notation(order_info, config_local['service_info'][service_type][metric]) - # print("The order has the value %s for the metric %s and the limit specifies the value %s. The limit has the description %s." % (order_value, metric, metric_value, description)) - if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE - for arr_value in order_value: - if str(metric_value) != str(arr_value): - return False - else: - if str(metric_value) != str(order_value): - return False - except KeyError: - return False - # vendor limits - for limit in element.findall('./p:vendor-limits/p:limit', ns): - # description = limit.find('p:description', ns).text - metric_value = limit.find('p:values', ns).text - metric = limit.find('p:metric', ns).text - try: - order_value = dot_notation(order_info, config_local['service_info'][service_type][metric]) - if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE - for arr_value in order_value: - if str(metric_value) != str(arr_value): - return False - else: - if str(metric_value) != str(order_value): - return False - # print("The order has the value %s for the metric %s and the limit specifies the value %s. The limit has the description %s." % (order_value, metric, metric_value, description)) - - except KeyError: - return False - return True - diff --git a/config/__init__.py b/config/__init__.py deleted file mode 100644 index 303a8ce..0000000 --- a/config/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- - - -import yaml -import json - -from osdf.utils.programming_utils import MetaSingleton - - -class CoreConfig(metaclass=MetaSingleton): - core_config = None - - def get_core_config(self, config_file=None): - if self.core_config is None: - self.core_config = yaml.load(open(config_file)) - return self.core_config - diff --git a/config/base.py b/config/base.py deleted file mode 100644 index b8aacff..0000000 --- a/config/base.py +++ /dev/null @@ -1,36 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import os - -import osdf.config.loader as config_loader -import osdf.config.credentials as creds -from osdf.utils.programming_utils import DotDict - -config_spec = { - "deployment": os.environ.get("OSDF_MANAGER_CONFIG_FILE", "config/osdf_config.yaml"), - "core": "config/common_config.yaml" - } - -osdf_config = DotDict(config_loader.all_configs(**config_spec)) - -http_basic_auth_credentials = creds.load_credentials(osdf_config) - -dmaap_creds = creds.dmaap_creds() - -creds_prefixes = {"so": "so", "cm": "cmPortal"} diff --git a/config/common_config.yaml b/config/common_config.yaml new file mode 100644 index 0000000..d8f467b --- /dev/null +++ b/config/common_config.yaml @@ -0,0 +1,43 @@ +osdf_system: + libpath: /opt/app/osdf/libs + sniro_ports: + internal: 24699 # inside the Docker container, the app listens to this port + external: 14699 # clients use this port on DockerHost + # Important Note: At deployment time, we need to ensure the port mapping is done + ssl_context: ['./../etc/sniromanager.crt', './../etc/sniromanager.key'] + +osdf_temp: # hacks required for "workarounds" or testing + local_policies: + global_disabled: True + local_placement_policies_enabled: True + placement_policy_files_vcpe: # workaroud for policy platform glitches (or "work-arounds" for other components) + - CloudAttributePolicy_vGMuxInfra_1.json + - CloudAttributePolicy_vG_1.json + - DistanceToLocationPolicy_vGMuxInfra_1.json + - DistanceToLocationPolicy_vG_1.json + - InventoryGroup_vGMuxInfra_1.json + - InventoryGroup_vG_1.json + - PlacementOptimizationPolicy.json + - ResourceInstancePolicy_vG_1.json + - VNFPolicy_vGMuxInfra_1.json + - VNFPolicy_vG_1.json + - ZonePolicy_vGMuxInfra_1.json + - ZonePolicy_vG_1.json + +service_info: + vCPE: + vcpeHostName: requestParameters.vcpeHostName + e2eVpnKey: requestParameters.e2eVpnKey + +policy_info: + placement: + policy_fetch: by_scope + policy_scope: + default_scope: OSDF_R2 + scope_vcpe: OSDF_R2 + service_name: placementInfo.serviceModelInfo.modelName + policy_subscriber: SubscriberPolicy + subscriber_name: placementInfo.subscriberInfo.subscriberName + default: # if no explicit service related information is needed + policy_fetch: by_name + policy_scope: none diff --git a/config/credentials.py b/config/credentials.py deleted file mode 100644 index e5a6399..0000000 --- a/config/credentials.py +++ /dev/null @@ -1,60 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import json - -from osdf import auth_groups, userid_suffix, passwd_suffix - - -def dmaap_creds(dmaap_file="/etc/dcae/dmaap.conf"): - """Get DMaaP credentials from DCAE for publish and subscribe""" - try: - dmaap_creds = _get_dmaap_creds(dmaap_file) - except: - dmaap_creds = {} - return dmaap_creds - - -def _get_dmaap_creds(dmaap_file): - """Get DMaaP credentials from DCAE for publish and subscribe""" - streams = json.load(open(dmaap_file, 'r')) - pubs = [x for x in streams - if x['dmaapStreamId'] == 'requests' and x['dmaapAction'] == 'publish'] - subs = [x for x in streams - if x['dmaapStreamId'] == 'responses' and x['dmaapAction'] == 'subscribe'] - - def get_dmaap_info(x): - """Get DMaaP credentials from dmaap_object 'x'""" - return dict(url=x.get('dmaapUrl'), userid=x.get('dmaapUserName'), passwd=x.get('dmaapPassword')) - - return {'pub': get_dmaap_info(pubs[0]), 'sub': get_dmaap_info(subs[0])} - - -def load_credentials(osdf_config): - """Get credentials as dictionaries grouped by auth_group (e.g. creds["Placement"]["user1"] = "pass1")""" - creds = dict((x, dict()) for x in auth_groups) # each auth group has userid, passwd dict - suffix_start = len(userid_suffix) - - config = osdf_config.deployment - - for element, username in config.items(): - for x in auth_groups: - if element.startswith("osdf" + x) and element.endswith(userid_suffix): - passwd = config[element[:-suffix_start] + passwd_suffix] - creds[x][username] = passwd - return creds diff --git a/config/loader.py b/config/loader.py deleted file mode 100644 index 7cb363a..0000000 --- a/config/loader.py +++ /dev/null @@ -1,51 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import json - -import yaml - - -def load_config_file(config_file: str, child_name="dockerConfiguration") -> dict: - """ - Load OSDF configuration from a file -- currently only yaml/json are supported - :param config_file: path to config file (.yaml or .json). - :param child_name: if present, return only that child node - :return: config (all or specific child node) - """ - with open(config_file, 'r') as fid: - res = {} - if config_file.endswith(".yaml"): - res = yaml.load(fid) - elif config_file.endswith(".json") or config_file.endswith("json"): - res = json.load(fid) - return res.get(child_name, res) if child_name else res - - -def dcae_config(config_file: str) -> dict: - return load_config_file(config_file, child_name="dockerConfiguration") - - -def all_configs(**kwargs: dict) -> dict: - """ - Load all specified configurations - :param config_file_spec: key-value pairs - (e.g. { "core": "common_config.yaml", "deployment": "/tmp/1452523532json" }) - :return: merged config as a nested dictionary - """ - return {k: load_config_file(fname) for k, fname in kwargs.items()} diff --git a/config/osdf_config.yaml b/config/osdf_config.yaml new file mode 100755 index 0000000..69ebdf0 --- /dev/null +++ b/config/osdf_config.yaml @@ -0,0 +1,34 @@ +osdfUserNameForSO: "" # The OSDF Manager username for MSO. +odfPasswordForSO: "" # The OSDF Manager password for MSO. + +# msoUrl: "" # The SO url for call back. This will be part of the request, so no need +soUsername: "" # SO username for call back. +soPassword: "" # SO password for call back. + +conductorUrl: "https://OOF-HAS-CONDUCTOR-HOST:8091" +conductorUsername: "CONDUCTOR-USER" +conductorPassword: "CONDUCTOR-PASSWD" +conductorPingWaitTime: 60 # seconds to wait before calling the conductor retry URL +conductorMaxRetries: 30 # if we don't get something in 30 minutes, give up + +# Policy Platform -- requires ClientAuth, Authorization, and Environment +policyPlatformUrl: https://POLICY-URL:8081/pdp/getConfig # Policy Dev platform URL +policyPlatformEnv: TEST # Environment for policy platform +policyPlatformUsername: POLICY-USER # Policy platform username. +policyPlatformPassword: POLICY-PASSWD # Policy platform password. +policyClientUsername: POLICY-CLIENT-USER # For use with ClientAuth +policyClientPassword: POLICY-CLIENT-PASSWD # For use with ClientAuth + +messageReaderHosts: https://DMAAP-HOST1:3905,https://DMAAP-HOST2:3905,https://DMAAP-HOST3:3905 +messageReaderTopic: org.onap.oof.osdf.multicloud +messageReaderAafUserId: DMAAP-OSDF-MC-USER +messageReaderAafPassword: DMAAP-OSDF-MC-PASSWD + +sdcUrl: https://SDC-HOST:8443/sdc/v1/catalog +sdcUsername: SDC-OSDF-USER +sdcPassword: SDC-OSDF-PASSWD +sdcONAPInstanceID: ONAP-OSDF + +osdfPlacementUrl: "http://127.0.0.1:24699/osdf/api/v2/placement" +osdfPlacementUsername: "test" +osdfPlacementPassword: "testpwd" diff --git a/models/api/common.py b/models/api/common.py deleted file mode 100755 index 0d2d0eb..0000000 --- a/models/api/common.py +++ /dev/null @@ -1,54 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import datetime -from pprint import pformat - -from dateutil.parser import parse -from schematics.exceptions import ConversionError -from schematics.models import Model -from schematics.types import DateTimeType - - -class OSDFModel(Model): - """Extends generic model with a couple of extra methods""" - def __str__(self): - """Return values of object's attributes -- excluding hidden or callable ones""" - def _str_format(x): - """Coerce as string for some special objects""" - return str(x) if isinstance(x, datetime.datetime) else x - - z1 = dict((x, getattr(self, x)) for x in dir(self) - if not x.startswith("_") and not callable(getattr(self, x))) - z1 = dict((x, _str_format(y)) for x, y in z1.items()) - return pformat(z1, depth=4, indent=2, width=1000) - - def __repr__(self): - """Return values of object's attributes -- excluding hidden or callable ones""" - return self.__str__() - - -class CustomISODateType(DateTimeType): - """Schematics doesn't support full ISO, so we use custom one""" - def to_native(self, value, context=None): - if isinstance(value, datetime.datetime): - return value - try: - return parse(value) - except: - raise ConversionError(u'Invalid timestamp {}'.format(value)) diff --git a/models/api/placementRequest.py b/models/api/placementRequest.py deleted file mode 100644 index 73eac75..0000000 --- a/models/api/placementRequest.py +++ /dev/null @@ -1,124 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from .common import OSDFModel -from schematics.types import StringType, URLType, IntType, FloatType -from schematics.types.compound import ModelType, ListType - - -class RequestInfo(OSDFModel): - """Info for northbound request from client such as SO""" - transactionId = StringType(required=True) - requestId = StringType(required=True) - callbackUrl = URLType(required=True) - sourceId = StringType(required=True) - optimizer = ListType(StringType()) - numSolutions = IntType() - timeout = IntType() - requestType = StringType() - - -class CandidateInfo(OSDFModel): - """Preferred candidate for a resource (sent as part of a request from client)""" - candidateType = StringType(required=True) - candidates = ListType(StringType(required=True)) - - -class ResourceModelInfo(OSDFModel): - """Model information for a specific resource""" - modelCustomizationId = StringType(required=True) - modelInvariantId = StringType(required=True) - modelName = StringType() - modelVersion = StringType() - modelVersionId = StringType() - modelType = StringType() - operationalStatus = StringType() - - -class ExistingLicenseInfo(OSDFModel): - entitlementPoolUUID = ListType(StringType()) - licenseKeyGroupUUID = ListType(StringType()) - - -class LicenseDemand(OSDFModel): - resourceInstanceType = StringType(required=True) - serviceResourceId = StringType(required=True) - resourceModuleName = StringType(required=True) - resourceModelInfo = ModelType(ResourceModelInfo) - existingLicense = ModelType(ExistingLicenseInfo) - - -class PlacementDemand(OSDFModel): - resourceInstanceType = StringType(required=True) - serviceResourceId = StringType(required=True) - resourceModuleName = StringType(required=True) - exclusionCandidateInfo = ListType(ModelType(CandidateInfo)) - requiredCandidateInfo = ListType(ModelType(CandidateInfo)) - resourceModelInfo = ModelType(ResourceModelInfo) - tenantId = StringType() - tenantName = StringType() - - -class ExistingPlacementInfo(OSDFModel): - serviceInstanceId = StringType(required=True) - - -class DemandInfo(OSDFModel): - """Requested resources (sent as part of a request from client)""" - placementDemand = ListType(ModelType(PlacementDemand)) - licenseDemand = ListType(ModelType(LicenseDemand)) - - -class SubscriberInfo(OSDFModel): - """Details on the customer that subscribes to the VNFs""" - globalSubscriberId = StringType(required=True) - subscriberName = StringType() - subscriberCommonSiteId = StringType() - - -class ServiceModelInfo(OSDFModel): - """ASDC Service model information""" - modelType = StringType(required=True) - modelInvariantId = StringType(required=True) - modelVersionId = StringType(required=True) - modelName = StringType(required=True) - modelVersion = StringType(required=True) - - -class Location(OSDFModel): - latitude = FloatType(required=True) - longitude = FloatType(required=True) - - -class PlacementInfo(OSDFModel): - """Information specific to placement optimization""" - serviceModelInfo = ModelType(ServiceModelInfo) - subscriberInfo = ModelType(SubscriberInfo) - demandInfo = ModelType(DemandInfo, required=True) - orderInfo = StringType() - policyId = ListType(StringType()) - serviceInstanceId = StringType() - existingPlacement = ModelType(ExistingPlacementInfo) - location = ModelType(Location) - serviceType = StringType() - - -class PlacementAPI(OSDFModel): - """Request for placement optimization (specific to optimization and additional metadata""" - requestInfo = ModelType(RequestInfo, required=True) - placementInfo = ModelType(PlacementInfo, required=True) diff --git a/models/api/placementResponse.py b/models/api/placementResponse.py deleted file mode 100644 index e9746d6..0000000 --- a/models/api/placementResponse.py +++ /dev/null @@ -1,57 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from .common import OSDFModel -from schematics.types import StringType -from schematics.types.compound import ModelType, ListType - - -# TODO: update osdf.models - -class LicenseSolution(OSDFModel): - serviceResourceId = StringType(required=True) - resourceModuleName = StringType(required=True) - entitlementPoolList = ListType(StringType(required=True)) - licenseKeyGroupList = ListType(StringType(required=True)) - - -class AssignmentInfo(OSDFModel): - variableName = StringType(required=True) - variableValue = StringType(required=True) - - -class PlacementSolution(OSDFModel): - serviceResourceId = StringType(required=True) - resourceModuleName = StringType(required=True) - inventoryType = StringType(required=True) - serviceInstanceId = StringType() - cloudRegionId = StringType() - assignmentInfo = ListType(ModelType(AssignmentInfo)) - - -class SolutionInfo(OSDFModel): - placement = ListType(ModelType(PlacementSolution), min_size=1) - license = ListType(ModelType(LicenseSolution), min_size=1) - - -class PlacementResponse(OSDFModel): - transactionId = StringType(required=True) - requestId = StringType(required=True) - requestState = StringType(required=True) - statusMessage = StringType(required=True) - solutionInfo = ModelType(SolutionInfo) diff --git a/models/policy/cmso/xacml/placementPolicies.xcore b/models/policy/cmso/xacml/placementPolicies.xcore deleted file mode 100644 index 3348cb0..0000000 --- a/models/policy/cmso/xacml/placementPolicies.xcore +++ /dev/null @@ -1,718 +0,0 @@ -/* - * This XCORE file contains models for the placement optimization policies in SNIRO. - * @author Ankitkumar Patel - * @version 0.0.1 - * @since 2017-04-13 - */ - -package com.att.ecomp.sniro.policies.placement - -import java.util.UUID - -annotation "http://ecomp.att.com" as ecomp -annotation "http://ecomp.att.com/policy" as policy - -type UUID wraps UUID - -/* - * Comparison operators - */ -enum ComparisonOperator{ - less=1, grater=2, lessEqual=3, greaterEqual=4, equal=5, notEqual=6 -} - -enum TenantType{ - GW_TENANT_ID=1, PORTAL_TENANT_ID=2 -} - -/* - * Computational operators - */ -enum ComputationalOperator{ - sum=1, product=2 -} - - -/* - * Qualifier types - */ -enum Qualifier{ - same=1, different=2 -} - -/* - * Zone category - */ -enum ZoneCategory{ - disaster=1, region=2, complex=3, time=4, maintenance=5 -} - -/* - * Geographical region types - */ -enum GeoRegion{ - US=1, EMEA=2, AP=3, CALA=4, CA=5, INTERNATIONAL=6 -} - -/* - * Parameters - */ -enum Parameter{ - distance=0, latency=1, aic_version=2 - //thoughput=1, geoDistance=1, airDistance=2, latency=3, bandwidth=4 -} - - -/* - * The type of inventory defined in AIC - */ -enum InventoryType{ - service=1, cloud=2 -} - -/* - * The type of AT&T network - */ - enum NetworkType{ - ip=1 - } - -/* - * Objective functions. - */ - -enum ObjectiveFunction{ - minimize=1, maximize=2 -} - -/* - * This is a model of a condition. - * @param parameter This is the parameter of interest. - * @param operator This is a comparison operator. - * @param value This is a value of a parameter - */ -class ConditionalInfo{ - @ecomp(^type = "configuration") - Parameter parameter - @ecomp(^type = "configuration") - ComparisonOperator operator - @ecomp(^type = "configuration") - String value -} - -enum LocationInfo{ - customer_loc=1, none=2, customer_pref_loc=3 -} - - -/* - * Model for distance to location property. - * @param distanceCondition This is a distance condition. - * @param locationInfo This is a location with respect to which distance condition is applied. - */ -class DistanceToLocationProperty{ - //distanceCondition.parameter must be distance. - @ecomp(^type = "configuration") - contains ConditionalInfo distanceCondition - @ecomp(^type = "configuration") - LocationInfo locationInfo -} - -enum DistanceToLocationPolicyType{ - distance_to_location=1 -} - -/* - * Model for distance to location policy. - * @param identity This is an identity created by a user. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resource instances over which this policy is applied. - * @param distanceToLocationProperty This is the distance properties of the policy. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class DistanceToLocationPolicy extends SniroPolicyMetaInfo{ - @ecomp(^type = "configuration") - @policy (matching = "true") - DistanceToLocationPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains DistanceToLocationProperty distanceToLocationProperty -} - -/* - * The property associated with the NetworkBetweenDemandsPolicy. - * @param latencyCondition The latency property associated with the policy. - * @param locationInfo The customer location information. - */ -class NetworkToLocationProperty{ - //latencyCondition.parameter must be latency. - @ecomp(^type = "configuration") - contains ConditionalInfo latencyCondition - @ecomp(^type = "configuration") - LocationInfo locationInfo -} - -enum NetworkToLocationPolicyType{ - network_to_location=1 -} - -/* - * Network between demands policy. - * @param type The type of a policy. - * @param resourceInstanceType The resources associated with a policy. - * @param networkToLocationProperty The property associated with the policy. - */ - -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class NetworkToLocationPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - NetworkToLocationPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains NetworkToLocationProperty networkToLocationProperty -} - -/* - * The property associated with the NetworkBetweenDemandsPolicy. - * @param latencyCondition The latency property associated with the policy. - */ -class NetworkBetweenDemandsProperty{ - //latencyCondition.parameter must be latency. - @ecomp(^type = "configuration") - contains ConditionalInfo latencyCondition -} - -enum NetworkBetweenDemandsPolicyType{ - network_between_demands=1 -} - -/* - * Network between demands policy. - * @param type The type of a policy. - * @param resourceInstanceType The resources associated with a policy. - * @param networkBetweenDemandsProperty The property associated with the policy. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class NetworkBetweenDemandsPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - NetworkBetweenDemandsPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains NetworkBetweenDemandsProperty networkBetweenDemandsProperty -} - - -/* - * Network roles supported a VNF placement - * @param all A list of network roles. - */ -class NetworkRoles{ - @ecomp(^type = "configuration") - String[] all -} - -/* - * Complex names supported by a VNF placement - * @param any A list of complex names. - */ -class Complex{ - @ecomp(^type = "configuration") - String[] any -} -/* - * This are the AIC properties. - * @param aicVersion This is the version of AIC. - * @param aicType This is the type of AIC. - * @param dataPlace This is the type of data plane. - * @param hypervisor This is the type of hypervisor. - * @param networkRoles This is a list of connected networks. - * @param exclusivityGroups This is an exclusivity group Id - * @param state State in which a VNF should be located. - * @param country Country in which a VNF should be located. - * @param getRegion Geographical region in which a VNF should be located. - */ -class AicAttributeProperty{ - //aicVersionCondition.parameter must be aicVersion. - @ecomp(^type = "configuration") - String aicVersion - @ecomp(^type = "configuration") - String aicType - @ecomp(^type = "configuration") - String dataPlane - @ecomp(^type = "configuration") - String hypervisor - @ecomp(^type = "configuration") - contains NetworkRoles networkRoles - @ecomp(^type = "configuration") - contains Complex complex - @ecomp(^type = "configuration") - String exclusivityGroups - @ecomp(^type = "configuration") - String state - @ecomp(^type = "configuration") - String country - @ecomp(^type = "configuration") - GeoRegion geoRegion - @ecomp(^type = "configuration") - String replicationRole - -} - -enum AicAttributePolicyType{ - attribute=1 -} - -/* - * Model for the AIC attribute policy. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources over which the policy is applied. - * @param aicAttributeProperty This is the properties associated with the policy. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class AicAttributePolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - AicAttributePolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains AicAttributeProperty aicAttributeProperty -} - -/* - * The property associated with the capacity policy. - * @param tenant The tenant whose capacity needs to be checked. - * @param description The location of a heat template. - */ -class CapacityProperty{ - @ecomp(^type = "configuration") - TenantType tenant - @ecomp(^type = "configuration") - String description -} - -enum CapacityPolicyType{ - cloud_capacity=1 -} - -/* - * Capacity policy - * @param type The type of a policy. - * @param resourceInstanceType The type of resources associated with a policy. - * @param capacityProperty The property associated with a policy. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class CapacityPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - CapacityPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains CapacityProperty capacityProperty -} - -enum InventoryGroupPolicyType{ - inventory_group = 1 -} - -/* - * Model for the inventory group policy. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources that must be grouped/paired - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class InventoryGroupPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - InventoryGroupPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType -} - -/* - * This is the property associated with this policy. - * @param controller ECOMP controller. - * @param request This should be key-value pairs to be sent in a request. - */ -class ResourceInstanceProperty{ - @ecomp(^type = "configuration") - String controller - @ecomp(^type = "configuration") - String request -} - -enum ResourceInstancePolicyType{ - instance_fit=1 -} -/* - * Model for the resource instance policy. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources. - * @param resourceInstanceProperty This is a property associated with each resource in the list. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class ResourceInstancePolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - ResourceInstancePolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains ResourceInstanceProperty resourceInstanceProperty - -} - -/* - * This is the property associated with this policy. - * @param controller ECOMP controller - * @param request This should be key-value pairs to be sent in a request. - */ -class ResourceRegionProperty{ - @ecomp(^type = "configuration") - String controller - @ecomp(^type = "configuration") - String request -} - -enum ResourceRegionPolicyType{ - region_fit=1 -} - -/* - * Model for the resource region policy - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources. - * @param resourceRegionProperty This is a property associated with this policy. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class ResourceRegionPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - ResourceRegionPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains ResourceRegionProperty resourceRegionProperty -} - -/* - * This is the property associated with zone policy. - * @param qualifier This is the qualifier. - * @param category This is the category of a zone. - */ -class ZoneProperty{ - @ecomp(^type = "configuration") - Qualifier qualifier - @ecomp(^type = "configuration") - ZoneCategory category -} - -enum ZonePolicyType{ - zone=1 -} - -/* - * Model of the zone policy. - * @param type This is the type of a policy. - * @param resourceInstanceType This is a list of resources. - * @param zoneProperty This is the property associated with the policy. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) - -class ZonePolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - ZonePolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains ZoneProperty zoneProperty -} - -/* - * The property associated with a VNF type. - * @param inventoryProvider The ECOMP entity providing inventory information. - * @param inventoryType The type of an inventory. - * @param serviceId The id of a service. - */ -class VNFPolicyProperty{ - @ecomp(^type = "configuration") - String inventoryProvider - @ecomp(^type = "configuration") - InventoryType inventoryType - @ecomp(^type = "configuration") - contains Attributes attributes -} - -/* - * The property associated with a Subscriber type. - * @param subscriberName The name of a subscriber. - * @param subscriberRole The role of a subscriber. - * @param provStatus The provisioning status of a subscriber. - */ -class SubscriberPolicyProperty{ - @ecomp(^type = "configuration") - String[] subscriberName - @ecomp(^type = "configuration") - String[] subscriberRole - @ecomp(^type = "configuration") - String[] provStatus -} - -enum VNFPolicyType{ - vnfPolicy=1 -} - -enum SubscriberPolicyType{ - subscriberPolicy=1 -} - -class Attributes{ - @ecomp(^type = "configuration") - String globalCustomerId - @ecomp(^type = "configuration") - String operationalStatus - @ecomp(^type = "configuration") - String[] orchestrationStatus - @ecomp(^type = "configuration") - String modelInvariantId - @ecomp(^type = "configuration") - String modelVersionId - @ecomp(^type = "configuration") - String equipmentRole -} - -/* - * Policy associated with a VNF. - * @param resourceInstance This parameter identifies a specific VNF. - * @param inventoryProvider This is the provider of inventory. - * @param inventoryType This is the type of inventory. - * @param serviceType The service associated with a VNF. - * @param serviceId The Id associated with a service. - * @param globalCustomerId The global id of a customer. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class VNFPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - VNFPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains VNFPolicyProperty[] property -} - -/* - * Policy associated with a Subscriber. - * @param subscriberName The name of a subscriber. - * @param subscriberRole The role of a subscriber. - * @param provStatus The provisioning status of a subscriber. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class SubscriberPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - SubscriberPolicyType ^type - @ecomp(^type = "configuration") - contains SubscriberPolicyProperty[] property -} - - -/* - * This is the property associated with this policy. - * @param providerUrl This is the url of provider to check the capacity. - * @param request This should be key-value pairs to be sent in a request. - */ -class InstanceReservationProperty{ - @ecomp(^type = "configuration") - String controller - @ecomp(^type = "configuration") - String request -} - -enum InstanceReservationPolicyType{ - instance_reservation=1 -} -/* - * Model for the resource instance policy. - * @param identity This is an identity created by a user. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources. - * @param resourceInstanceProperty This is a property associated with each resource in the list. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) -class instanceReservationPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - InstanceReservationPolicyType ^type - @ecomp(^type = "configuration") - String[] resourceInstanceType - @ecomp(^type = "configuration") - contains InstanceReservationProperty instanceReservationProperty - -} - -/* - * This is a model of an operand. - * @param parameter This is a parameter. - * @param associativity This is a list of entities with which a parameter is associated. - */ - /* -class Operand{ - @ecomp(^type = "configuration") - Parameter parameter - @ecomp(^type = "configuration") - Entity associativity -} -*/ - -/* - * This is the optimization function. - * @param identity This is an identity of a function. - * @param operation This is a computational operator. - * @param leftOperand This is a left operand of a function. - * @param rightOperand This is a right operand of a function. - */ -/* -class OptimizationFunction{ - @ecomp(^type = "configuration") - ExpressionIdentity identity - @ecomp(^type = "configuration") - ComputationalOperator operation - @ecomp(^type = "configuration") - contains Operand[] operands -} -*/ - -/* - * Properties associated with a sub-expression. - * @param weight The weight of an expression. - * @param parameter The parameter involved in an expression. - * @param entity The entities involved in an expression. - * @param operator The operator of an expression. - * @param customerLocationInfo The location of a customer. - */ -class AttributeProperty{ - @ecomp(^type = "configuration") - double weight - @ecomp(^type = "configuration") - Parameter parameter - @ecomp(^type = "configuration") - String[] resource - @ecomp(^type = "configuration") - ComputationalOperator operator - @ecomp(^type = "configuration") - LocationInfo customerLocationInfo -} - -enum PlacementOptimizationPolicyType{ - placementOptimization=1 -} - -/* - * @param operator An operator in an expression. - * @param parameterAttributes Represents sub-expression - */ -class ObjectiveParameter{ - @ecomp(^type = "configuration") - ComputationalOperator operator - @ecomp(^type = "configuration") - contains AttributeProperty[] parameterAttributes -} - -/* - * Model of the placement optimization policy. - * @param type This is the type of a policy. - * @param objective This is an objective function. - * @param objectiveParameter The parameter/expression to be optimized. - */ -@policy ( - policyTemplate = "SNIRO-PLACEMENT" -) - -class PlacementOptimizationPolicy extends SniroPolicyMetaInfo{ - @policy (matching = "true") - @ecomp(^type = "configuration") - PlacementOptimizationPolicyType ^type - @ecomp(^type = "configuration") - ObjectiveFunction objective - @ecomp(^type = "configuration") - contains ObjectiveParameter objectiveParameter -} - - -/* - * Meta information required for SNIRO policies. - * @param identity This is a user-defined identity. - * @param policyScope The scope of a policy - */ - -@policy ( - policyTemplate = "SNIRO" -) -class SniroPolicyMetaInfo{ - @ecomp(^type = "configuration") - String identity - @ecomp(^type = "configuration") - @policy (matching = "true") - contains Scope policyScope -} - -/* - * Scopes in which a policy is applicable. - * @param serviceType The type of a service. - * @param networkType The type of a network - * @param geoRigion The geographical region. - * @param resourceInstanceType The resources associated with a policy/ - * @param subscriberRole - */ -class Scope{ - @ecomp(^type = "configuration") - @policy (matching = "true") - String[] serviceType - @ecomp(^type = "configuration") - @policy (matching = "true") - String[] networkType - @ecomp(^type = "configuration") - @policy (matching = "true") - String[] geoRegion - @ecomp(^type = "configuration") - @policy (matching = "true") - String[] resourceInstanceType - @ecomp(^type = "configuration") - @policy (matching = "true") - String[] modelInvariantId - @ecomp(^type = "configuration") - @policy (matching = "true") - String[] subscriberRole -} \ No newline at end of file diff --git a/models/policy/placement/xacml/placementPolicies.xcore b/models/policy/placement/xacml/placementPolicies.xcore deleted file mode 100644 index 866488e..0000000 --- a/models/policy/placement/xacml/placementPolicies.xcore +++ /dev/null @@ -1,728 +0,0 @@ -/* - * ================================================================================ - * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. -*/ - -package org.onap.oof.osdf.policies.placement - -import java.util.UUID - -annotation "http://org.onap" as onap -annotation "http://org.onap.policy" as policy - -type UUID wraps UUID - -/* - * Comparison operators - */ -enum ComparisonOperator{ - less=1, grater=2, lessEqual=3, greaterEqual=4, equal=5, notEqual=6 -} - -enum TenantType{ - GW_TENANT_ID=1, PORTAL_TENANT_ID=2 -} - -/* - * Computational operators - */ -enum ComputationalOperator{ - sum=1, product=2 -} - - -/* - * Qualifier types - */ -enum Qualifier{ - same=1, different=2 -} - -/* - * Zone category - */ -enum ZoneCategory{ - disaster=1, region=2, complex=3, time=4, maintenance=5 -} - -/* - * Geographical region types - */ -enum GeoRegion{ - US=1, EMEA=2, AP=3, CALA=4, CA=5, INTERNATIONAL=6 -} - -/* - * Parameters - */ -enum Parameter{ - distance=0, latency=1, cloud_version=2 - //thoughput=1, geoDistance=1, airDistance=2, latency=3, bandwidth=4 -} - - -/* - * The type of inventory defined in cloud - */ -enum InventoryType{ - service=1, cloud=2 -} - -/* - * The type of network - */ - enum NetworkType{ - ip=1 - } - -/* - * Objective functions. - */ - -enum ObjectiveFunction{ - minimize=1, maximize=2 -} - -/* - * This is a model of a condition. - * @param parameter This is the parameter of interest. - * @param operator This is a comparison operator. - * @param value This is a value of a parameter - */ -class ConditionalInfo{ - @onap(^type = "configuration") - Parameter parameter - @onap(^type = "configuration") - ComparisonOperator operator - @onap(^type = "configuration") - String value -} - -enum LocationInfo{ - customer_loc=1, none=2, customer_pref_loc=3 -} - - -/* - * Model for distance to location property. - * @param distanceCondition This is a distance condition. - * @param locationInfo This is a location with respect to which distance condition is applied. - */ -class DistanceToLocationProperty{ - //distanceCondition.parameter must be distance. - @onap(^type = "configuration") - contains ConditionalInfo distanceCondition - @onap(^type = "configuration") - LocationInfo locationInfo -} - -enum DistanceToLocationPolicyType{ - distance_to_location=1 -} - -/* - * Model for distance to location policy. - * @param identity This is an identity created by a user. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resource instances over which this policy is applied. - * @param distanceToLocationProperty This is the distance properties of the policy. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class DistanceToLocationPolicy extends OOFPolicyMetaInfo{ - @onap(^type = "configuration") - @policy (matching = "true") - DistanceToLocationPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains DistanceToLocationProperty distanceToLocationProperty -} - -/* - * The property associated with the NetworkBetweenDemandsPolicy. - * @param latencyCondition The latency property associated with the policy. - * @param locationInfo The customer location information. - */ -class NetworkToLocationProperty{ - //latencyCondition.parameter must be latency. - @onap(^type = "configuration") - contains ConditionalInfo latencyCondition - @onap(^type = "configuration") - LocationInfo locationInfo -} - -enum NetworkToLocationPolicyType{ - network_to_location=1 -} - -/* - * Network between demands policy. - * @param type The type of a policy. - * @param resourceInstanceType The resources associated with a policy. - * @param networkToLocationProperty The property associated with the policy. - */ - -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class NetworkToLocationPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - NetworkToLocationPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains NetworkToLocationProperty networkToLocationProperty -} - -/* - * The property associated with the NetworkBetweenDemandsPolicy. - * @param latencyCondition The latency property associated with the policy. - */ -class NetworkBetweenDemandsProperty{ - //latencyCondition.parameter must be latency. - @onap(^type = "configuration") - contains ConditionalInfo latencyCondition -} - -enum NetworkBetweenDemandsPolicyType{ - network_between_demands=1 -} - -/* - * Network between demands policy. - * @param type The type of a policy. - * @param resourceInstanceType The resources associated with a policy. - * @param networkBetweenDemandsProperty The property associated with the policy. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class NetworkBetweenDemandsPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - NetworkBetweenDemandsPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains NetworkBetweenDemandsProperty networkBetweenDemandsProperty -} - - -/* - * Network roles supported a VNF placement - * @param all A list of network roles. - */ -class NetworkRoles{ - @onap(^type = "configuration") - String[] all -} - -/* - * Complex names supported by a VNF placement - * @param any A list of complex names. - */ -class Complex{ - @onap(^type = "configuration") - String[] any -} -/* - * This are the cloud properties. - * @param cloudVersion This is the version of cloud. - * @param cloudType This is the type of cloud. - * @param dataPlace This is the type of data plane. - * @param hypervisor This is the type of hypervisor. - * @param networkRoles This is a list of connected networks. - * @param exclusivityGroups This is an exclusivity group Id - * @param state State in which a VNF should be located. - * @param country Country in which a VNF should be located. - * @param getRegion Geographical region in which a VNF should be located. - */ -class cloudAttributeProperty{ - //cloudVersionCondition.parameter must be cloudVersion. - @onap(^type = "configuration") - String cloudVersion - @onap(^type = "configuration") - String cloudType - @onap(^type = "configuration") - String dataPlane - @onap(^type = "configuration") - String hypervisor - @onap(^type = "configuration") - contains NetworkRoles networkRoles - @onap(^type = "configuration") - contains Complex complex - @onap(^type = "configuration") - String exclusivityGroups - @onap(^type = "configuration") - String state - @onap(^type = "configuration") - String country - @onap(^type = "configuration") - GeoRegion geoRegion - @onap(^type = "configuration") - String replicationRole - -} - -enum cloudAttributePolicyType{ - attribute=1 -} - -/* - * Model for the cloud attribute policy. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources over which the policy is applied. - * @param cloudAttributeProperty This is the properties associated with the policy. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class cloudAttributePolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - cloudAttributePolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains cloudAttributeProperty cloudAttributeProperty -} - -/* - * The property associated with the capacity policy. - * @param tenant The tenant whose capacity needs to be checked. - * @param description The location of a heat template. - */ -class CapacityProperty{ - @onap(^type = "configuration") - TenantType tenant - @onap(^type = "configuration") - String description -} - -enum CapacityPolicyType{ - cloud_capacity=1 -} - -/* - * Capacity policy - * @param type The type of a policy. - * @param resourceInstanceType The type of resources associated with a policy. - * @param capacityProperty The property associated with a policy. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class CapacityPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - CapacityPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains CapacityProperty capacityProperty -} - -enum InventoryGroupPolicyType{ - inventory_group = 1 -} - -/* - * Model for the inventory group policy. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources that must be grouped/paired - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class InventoryGroupPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - InventoryGroupPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType -} - -/* - * This is the property associated with this policy. - * @param controller onap controller. - * @param request This should be key-value pairs to be sent in a request. - */ -class ResourceInstanceProperty{ - @onap(^type = "configuration") - String controller - @onap(^type = "configuration") - String request -} - -enum ResourceInstancePolicyType{ - instance_fit=1 -} -/* - * Model for the resource instance policy. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources. - * @param resourceInstanceProperty This is a property associated with each resource in the list. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class ResourceInstancePolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - ResourceInstancePolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains ResourceInstanceProperty resourceInstanceProperty - -} - -/* - * This is the property associated with this policy. - * @param controller onap controller - * @param request This should be key-value pairs to be sent in a request. - */ -class ResourceRegionProperty{ - @onap(^type = "configuration") - String controller - @onap(^type = "configuration") - String request -} - -enum ResourceRegionPolicyType{ - region_fit=1 -} - -/* - * Model for the resource region policy - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources. - * @param resourceRegionProperty This is a property associated with this policy. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class ResourceRegionPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - ResourceRegionPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains ResourceRegionProperty resourceRegionProperty -} - -/* - * This is the property associated with zone policy. - * @param qualifier This is the qualifier. - * @param category This is the category of a zone. - */ -class ZoneProperty{ - @onap(^type = "configuration") - Qualifier qualifier - @onap(^type = "configuration") - ZoneCategory category -} - -enum ZonePolicyType{ - zone=1 -} - -/* - * Model of the zone policy. - * @param type This is the type of a policy. - * @param resourceInstanceType This is a list of resources. - * @param zoneProperty This is the property associated with the policy. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) - -class ZonePolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - ZonePolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains ZoneProperty zoneProperty -} - -/* - * The property associated with a VNF type. - * @param inventoryProvider The onap entity providing inventory information. - * @param inventoryType The type of an inventory. - * @param serviceId The id of a service. - */ -class VNFPolicyProperty{ - @onap(^type = "configuration") - String inventoryProvider - @onap(^type = "configuration") - InventoryType inventoryType - @onap(^type = "configuration") - contains Attributes attributes -} - -/* - * The property associated with a Subscriber type. - * @param subscriberName The name of a subscriber. - * @param subscriberRole The role of a subscriber. - * @param provStatus The provisioning status of a subscriber. - */ -class SubscriberPolicyProperty{ - @onap(^type = "configuration") - String[] subscriberName - @onap(^type = "configuration") - String[] subscriberRole - @onap(^type = "configuration") - String[] provStatus -} - -enum VNFPolicyType{ - vnfPolicy=1 -} - -enum SubscriberPolicyType{ - subscriberPolicy=1 -} - -class Attributes{ - @onap(^type = "configuration") - String globalCustomerId - @onap(^type = "configuration") - String operationalStatus - @onap(^type = "configuration") - String[] orchestrationStatus - @onap(^type = "configuration") - String modelInvariantId - @onap(^type = "configuration") - String modelVersionId - @onap(^type = "configuration") - String equipmentRole -} - -/* - * Policy associated with a VNF. - * @param resourceInstance This parameter identifies a specific VNF. - * @param inventoryProvider This is the provider of inventory. - * @param inventoryType This is the type of inventory. - * @param serviceType The service associated with a VNF. - * @param serviceId The Id associated with a service. - * @param globalCustomerId The global id of a customer. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class VNFPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - VNFPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains VNFPolicyProperty[] property -} - -/* - * Policy associated with a Subscriber. - * @param subscriberName The name of a subscriber. - * @param subscriberRole The role of a subscriber. - * @param provStatus The provisioning status of a subscriber. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class SubscriberPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - SubscriberPolicyType ^type - @onap(^type = "configuration") - contains SubscriberPolicyProperty[] property -} - - -/* - * This is the property associated with this policy. - * @param providerUrl This is the url of provider to check the capacity. - * @param request This should be key-value pairs to be sent in a request. - */ -class InstanceReservationProperty{ - @onap(^type = "configuration") - String controller - @onap(^type = "configuration") - String request -} - -enum InstanceReservationPolicyType{ - instance_reservation=1 -} -/* - * Model for the resource instance policy. - * @param identity This is an identity created by a user. - * @param type This is the type of a policy. - * @param resourceInstance This is a list of resources. - * @param resourceInstanceProperty This is a property associated with each resource in the list. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class InstanceReservationPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - InstanceReservationPolicyType ^type - @onap(^type = "configuration") - String[] resourceInstanceType - @onap(^type = "configuration") - contains InstanceReservationProperty instanceReservationProperty - -} - -/* - * This is a model of an operand. - * @param parameter This is a parameter. - * @param associativity This is a list of entities with which a parameter is associated. - */ - /* -class Operand{ - @onap(^type = "configuration") - Parameter parameter - @onap(^type = "configuration") - Entity associativity -} -*/ - -/* - * This is the optimization function. - * @param identity This is an identity of a function. - * @param operation This is a computational operator. - * @param leftOperand This is a left operand of a function. - * @param rightOperand This is a right operand of a function. - */ -/* -class OptimizationFunction{ - @onap(^type = "configuration") - ExpressionIdentity identity - @onap(^type = "configuration") - ComputationalOperator operation - @onap(^type = "configuration") - contains Operand[] operands -} -*/ - -/* - * Properties associated with a sub-expression. - * @param weight The weight of an expression. - * @param parameter The parameter involved in an expression. - * @param entity The entities involved in an expression. - * @param operator The operator of an expression. - * @param customerLocationInfo The location of a customer. - */ -class AttributeProperty{ - @onap(^type = "configuration") - double weight - @onap(^type = "configuration") - Parameter parameter - @onap(^type = "configuration") - String[] resource - @onap(^type = "configuration") - ComputationalOperator operator - @onap(^type = "configuration") - LocationInfo customerLocationInfo -} - -enum PlacementOptimizationPolicyType{ - placementOptimization=1 -} - -/* - * @param operator An operator in an expression. - * @param parameterAttributes Represents sub-expression - */ -class ObjectiveParameter{ - @onap(^type = "configuration") - ComputationalOperator operator - @onap(^type = "configuration") - contains AttributeProperty[] parameterAttributes -} - -/* - * Model of the placement optimization policy. - * @param type This is the type of a policy. - * @param objective This is an objective function. - * @param objectiveParameter The parameter/expression to be optimized. - */ -@policy ( - policyTemplate = "OOF-PLACEMENT" -) - -class PlacementOptimizationPolicy extends OOFPolicyMetaInfo{ - @policy (matching = "true") - @onap(^type = "configuration") - PlacementOptimizationPolicyType ^type - @onap(^type = "configuration") - ObjectiveFunction objective - @onap(^type = "configuration") - contains ObjectiveParameter objectiveParameter -} - - -/* - * Meta information required for oof policies. - * @param identity This is a user-defined identity. - * @param policyScope The scope of a policy - */ - -@policy ( - policyTemplate = "OOF-PLACEMENT" -) -class OOFPolicyMetaInfo{ - @onap(^type = "configuration") - String identity - @onap(^type = "configuration") - @policy (matching = "true") - contains Scope policyScope -} - -/* - * Scopes in which a policy is applicable. - * @param serviceType The type of a service. - * @param networkType The type of a network - * @param geoRigion The geographical region. - * @param resourceInstanceType The resources associated with a policy/ - * @param subscriberRole - */ -class Scope{ - @onap(^type = "configuration") - @policy (matching = "true") - String[] serviceType - @onap(^type = "configuration") - @policy (matching = "true") - String[] networkType - @onap(^type = "configuration") - @policy (matching = "true") - String[] geoRegion - @onap(^type = "configuration") - @policy (matching = "true") - String[] resourceInstanceType - @onap(^type = "configuration") - @policy (matching = "true") - String[] modelInvariantId - @onap(^type = "configuration") - @policy (matching = "true") - String[] subscriberRole -} diff --git a/operation/__init__.py b/operation/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/operation/error_handling.py b/operation/error_handling.py deleted file mode 100644 index dfb0848..0000000 --- a/operation/error_handling.py +++ /dev/null @@ -1,93 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import json - -from schematics.exceptions import DataError - -from requests import RequestException -from requests import ConnectionError, HTTPError, Timeout -from osdf.operation.exceptions import BusinessException - -import osdf - -ERROR_TEMPLATE = osdf.ERROR_TEMPLATE - -MESSAGE_BASE = "A solution couldn't be determined because an external application" -HTTP_ERROR_MESSAGE = MESSAGE_BASE + " returned a HTTP error" -TIMEOUT_ERROR_MESSAGE = MESSAGE_BASE + " could not respond in time, please check the external application" -CONNECTION_ERROR_MESSAGE = MESSAGE_BASE + " could not be reached" - -internal_error_body = { - "serviceException": { - "text": "Unhandled internal exception, request could not be processed" - } -} - -internal_error_message = json.dumps(internal_error_body) - - -def build_json_error_body(error): - if isinstance(error,RequestException): - return request_exception_to_json_body(error) - elif isinstance(error, DataError): - return data_error_to_json_body(error) - elif type(error) is BusinessException: # return the error message, because it is well formatted - return ERROR_TEMPLATE.render(description=str(error)) - else: - return internal_error_message - - -def data_error_to_json_body(error): - description = str(error).replace('"', '\\"') - error_message = ERROR_TEMPLATE.render(description=description) - return error_message - - -def request_exception_to_json_body(error): - friendly_message = "A request exception has occurred when contacting an external system" - if type(error) is HTTPError: - friendly_message = HTTP_ERROR_MESSAGE - if type(error) is ConnectionError: - friendly_message = CONNECTION_ERROR_MESSAGE - if type(error) is Timeout: - friendly_message = TIMEOUT_ERROR_MESSAGE - - eie_body = { - "serviceException": { - "text": friendly_message, - "errorType": "InterfaceError" - }, - "externalApplicationDetails": { - "httpMethod": error.request.method, - "url": error.request.url - } - } - - response = error.response - - if response is not None: - eie_body['externalApplicationDetails']['httpStatusCode'] = response.status_code - content_type = response.headers.get('content-type') - if content_type is not None: - if 'application/json' in content_type: - eie_body['externalApplicationDetails']['responseMessage'] = response.json() - elif 'text/html' in content_type: - eie_body['externalApplicationDetails']['responseMessage'] = response.text - error_message = json.dumps(eie_body) - return error_message diff --git a/operation/exceptions.py b/operation/exceptions.py deleted file mode 100644 index 5277b01..0000000 --- a/operation/exceptions.py +++ /dev/null @@ -1,40 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -class BusinessException(Exception): - pass - - -class MessageBusConfigurationException(Exception): - pass - - -class CMDataError(Exception): - pass - - -class CMSOExecutionError(Exception): - pass - - -class CMSOCallBackError(Exception): - pass - - -class CMSOInvalidRequestException(Exception): - pass diff --git a/operation/responses.py b/operation/responses.py deleted file mode 100644 index 22a94f7..0000000 --- a/operation/responses.py +++ /dev/null @@ -1,39 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from flask import Response - -from osdf import ACCEPTED_MESSAGE_TEMPLATE - - -def osdf_response_for_request_accept(req_id="", text="", response_code=202, as_http=True): - """Helper method to create a response object for request acceptance, so that the object can be sent to a client - :param req_id: request ID provided by the caller - :param text: extra text description about accepting the request (e.g. "Request accepted") - :param response_code: the HTTP status code to send -- default is 202 (accepted) - :param as_http: whether to send response as HTTP response object or as a string - :return: if as_http is True, return a HTTP Response object. Otherwise, return json-encoded-message - """ - response_message = ACCEPTED_MESSAGE_TEMPLATE.render(description=text, request_id=req_id) - if not as_http: - return response_message - - response = Response(response_message, content_type='application/json; charset=utf-8') - response.headers.add('content-length', len(response_message)) - response.status_code = response_code - return response diff --git a/optimizers/__init__.py b/optimizers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/optimizers/licenseopt/__init__.py b/optimizers/licenseopt/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/optimizers/licenseopt/simple_license_allocation.py b/optimizers/licenseopt/simple_license_allocation.py deleted file mode 100644 index 1b5b670..0000000 --- a/optimizers/licenseopt/simple_license_allocation.py +++ /dev/null @@ -1,56 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import json - -from requests import RequestException -from osdf.datasources.sdc import sdc, constraint_handler -from osdf.logging.osdf_logging import audit_log, metrics_log, MH -from osdf.config.base import osdf_config -from osdf.utils import data_mapping - - -def license_optim(request_json): - """ - Fetch license artifacts associated with the service model and search licensekey-group-UUID and entitlement-pool-uuid - associated with the given att part number and nominal throughput in a request - :param request_json: Request in a JSON format - :return: A tuple of licensekey-group-uuid-list and entitlement-group-uuid-list - """ - req_id = request_json["requestInfo"]["requestId"] - config = osdf_config.deployment - - model_name = request_json['placementInfo']['serviceModelInfo']['modelName'] - service_name = data_mapping.get_service_type(model_name) - - license_info = [] - - order_info = json.loads(request_json["placementInfo"]["orderInfo"]) - if service_name == 'VPE': - data_mapping.normalize_user_params(order_info) - for licenseDemand in request_json['placementInfo']['demandInfo']['licenseDemand']: - metrics_log.info(MH.requesting("sdc", req_id)) - license_artifacts = sdc.request(licenseDemand['resourceModelInfo']['modelVersionId'],request_json["requestInfo"]["requestId"], config) - entitlement_pool_uuids, license_key_group_uuids = constraint_handler.choose_license(license_artifacts,order_info, service_name) - license_info.append( - {'serviceResourceId': licenseDemand['serviceResourceId'], - 'resourceModuleName': licenseDemand['resourceModuleName'], - 'entitlementPoolList': entitlement_pool_uuids, - 'licenseKeyGroupList': license_key_group_uuids - }) - return license_info diff --git a/optimizers/placementopt/__init__.py b/optimizers/placementopt/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/optimizers/placementopt/conductor/__init__.py b/optimizers/placementopt/conductor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/optimizers/placementopt/conductor/api_builder.py b/optimizers/placementopt/conductor/api_builder.py deleted file mode 100644 index c0281fe..0000000 --- a/optimizers/placementopt/conductor/api_builder.py +++ /dev/null @@ -1,121 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import copy -import json -from osdf.utils import data_mapping -from jinja2 import Template -from osdf.utils.programming_utils import list_flatten, dot_notation -import osdf.optimizers.placementopt.conductor.translation as tr -from osdf.adapters.policy.utils import group_policies - - -def conductor_api_builder(request_json, flat_policies: list, local_config, prov_status, - template="templates/conductor_interface.json"): - """Build a SNIRO southbound API call for Conductor/Placement optimization - :param request_json: parameter data received from a client - :param flat_policies: policy data received from the policy platform (flat policies) - :param template: template to generate southbound API call to conductor - :param local_config: local configuration file with pointers for the service specific information - :param prov_status: provStatus retrieved from Subscriber policy - :return: json to be sent to Conductor/placement optimization - """ - templ = Template(open(template).read()) - gp = group_policies(flat_policies) - demand_vnf_name_list = [] - - for placementDemand in request_json['placementInfo']['demandInfo']['placementDemand']: - demand_vnf_name_list.append(placementDemand['resourceModuleName']) - - demand_list = tr.gen_demands(request_json['placementInfo']['demandInfo'], gp['vnfPolicy']) - attribute_policy_list = tr.gen_attribute_policy(demand_vnf_name_list, gp['attribute']) - distance_to_location_policy_list = tr.gen_distance_to_location_policy( - demand_vnf_name_list, gp['distance_to_location']) - inventory_policy_list = tr.gen_inventory_group_policy(demand_vnf_name_list, gp['inventory_group']) - resource_instance_policy_list = tr.gen_resource_instance_policy( - demand_vnf_name_list, gp['instance_fit']) - resource_region_policy_list = tr.gen_resource_region_policy(demand_vnf_name_list, gp['region_fit']) - zone_policy_list = tr.gen_zone_policy(demand_vnf_name_list, gp['zone']) - optimization_policy_list = tr.gen_optimization_policy(demand_vnf_name_list, gp['placementOptimization']) - reservation_policy_list = tr.gen_reservation_policy(demand_vnf_name_list, gp['instance_reservation']) - conductor_policies = [attribute_policy_list, distance_to_location_policy_list, inventory_policy_list, - resource_instance_policy_list, resource_region_policy_list, zone_policy_list] - filtered_policies = [x for x in conductor_policies if len(x) > 0] - policy_groups = list_flatten(filtered_policies) - reservation_policies = [x for x in reservation_policy_list if len(x) > 0] - reservation_groups = list_flatten(reservation_policies) - req_info = request_json['requestInfo'] - model_name = request_json['placementInfo']['serviceModelInfo']['modelName'] - service_type = data_mapping.get_service_type(model_name) - service_info = local_config.get('service_info', {}).get(service_type, {}) - if 'orderInfo' in request_json["placementInfo"]: - order_info = json.loads(request_json["placementInfo"]["orderInfo"]) - request_type = req_info.get('requestType', None) - subs_com_site_id = "" - if 'subscriberInfo' in request_json['placementInfo']: - subs_com_site_id = request_json['placementInfo']['subscriberInfo'].get('subscriberCommonSiteId', "") - if service_type == 'vCPE': - data_mapping.normalize_user_params(order_info) - rendered_req = templ.render( - requestType=request_type, - chosenComplex=subs_com_site_id, - demand_list=demand_list, - policy_groups=policy_groups, - optimization_policies=optimization_policy_list, - name=req_info['requestId'], - timeout=req_info['timeout'], - limit=req_info['numSolutions'], - serviceType=service_type, - serviceInstance=request_json['placementInfo']['serviceInstanceId'], - provStatus = prov_status, - chosenRegion=order_info['requestParameters']['lcpCloudRegionId'], - json=json) - elif service_type == 'UNKNOWN': - rendered_req = templ.render( - requestType=request_type, - chosenComplex=subs_com_site_id, - demand_list=demand_list, - policy_groups=policy_groups, - reservation_groups=reservation_groups, - optimization_policies=optimization_policy_list, - name=req_info['requestId'], - timeout=req_info['timeout'], - limit=req_info['numSolutions'], - serviceType=service_type, - serviceInstance=request_json['placementInfo']['serviceInstanceId'], - provStatus = prov_status, - # process order data - bandwidth=dot_notation(order_info, service_info['bandwidth']), - bandwidth_unit=dot_notation(order_info, service_info['bandwidth_units']), - json=json) - json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! - return json_payload - - -def retrieve_node(req_json, reference): - """ - Get the child node(s) from the dot-notation [reference] and parent [req_json]. - For placement and other requests, there are encoded JSONs inside the request or policy, - so we need to expand it and then do a search over the parent plus expanded JSON. - """ - req_json_copy = copy.deepcopy(req_json) # since we expand the JSON in place, we work on a copy - if 'orderInfo' in req_json_copy['placementInfo']: - req_json_copy['placementInfo']['orderInfo'] = json.loads(req_json_copy['placementInfo']['orderInfo']) - info = dot_notation(req_json_copy, reference) - return list_flatten(info) if isinstance(info, list) else info - diff --git a/optimizers/placementopt/conductor/conductor.py b/optimizers/placementopt/conductor/conductor.py deleted file mode 100644 index bdc7f17..0000000 --- a/optimizers/placementopt/conductor/conductor.py +++ /dev/null @@ -1,186 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -""" -This application generates conductor API calls using the information received from SO and Policy platform. -""" - -import json -import time - -from jinja2 import Template -from requests import RequestException - -from osdf.logging.osdf_logging import debug_log -from osdf.optimizers.placementopt.conductor.api_builder import conductor_api_builder -from osdf.utils.interfaces import RestClient -from osdf.operation.exceptions import BusinessException - - -def request(req_object, osdf_config, grouped_policies, prov_status): - """ - Process a placement request from a Client (build Conductor API call, make the call, return result) - :param req_object: Request parameters from the client - :param osdf_config: Configuration specific to SNIRO application (core + deployment) - :param grouped_policies: policies related to placement (fetched based on request, and grouped by policy type) - :param prov_status: provStatus retrieved from Subscriber policy - :return: response from Conductor (accounting for redirects from Conductor service - """ - config = osdf_config.deployment - local_config = osdf_config.core - uid, passwd = config['conductorUsername'], config['conductorPassword'] - conductor_url = config['conductorUrl'] - req_id = req_object['requestInfo']['requestId'] - transaction_id = req_object['requestInfo']['transactionId'] - headers = dict(transaction_id=transaction_id) - - max_retries = config.get('conductorMaxRetries', 30) - ping_wait_time = config.get('conductorPingWaitTime', 60) - - rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug, headers=headers) - conductor_req_json_str = conductor_api_builder(req_object, grouped_policies, local_config, prov_status) - conductor_req_json = json.loads(conductor_req_json_str) - - debug_log.debug("Sending first Conductor request for request_id {}".format(req_id)) - resp, raw_resp = initial_request_to_conductor(rc, conductor_url, conductor_req_json) - # Very crude way of keeping track of time. - # We are not counting initial request time, first call back, or time for HTTP request - total_time, ctr = 0, 2 - client_timeout = req_object['requestInfo']['timeout'] - configured_timeout = max_retries * ping_wait_time - max_timeout = min(client_timeout, configured_timeout) - - while True: # keep requesting conductor till we get a result or we run out of time - if resp is not None: - if resp["plans"][0].get("status") in ["error"]: - raise RequestException(response=raw_resp, request=raw_resp.request) - - if resp["plans"][0].get("status") in ["done", "not found"]: - if resp["plans"][0].get("recommendations"): - return conductor_response_processor(resp, raw_resp, req_id) - else: # "solved" but no solutions found - return conductor_no_solution_processor(resp, raw_resp, req_id) - new_url = resp['plans'][0]['links'][0][0]['href'] # TODO: check why a list of lists - - if total_time >= max_timeout: - raise BusinessException("Conductor could not provide a solution within {} seconds, this transaction is timing out".format(max_timeout)) - time.sleep(ping_wait_time) - ctr += 1 - debug_log.debug("Attempt number {} url {}; prior status={}".format(ctr, new_url, resp['plans'][0]['status'])) - total_time += ping_wait_time - - try: - raw_resp = rc.request(new_url, raw_response=True) - resp = raw_resp.json() - except RequestException as e: - debug_log.debug("Conductor attempt {} for request_id {} has failed because {}".format(ctr, req_id, str(e))) - - -def initial_request_to_conductor(rc, conductor_url, conductor_req_json): - """First steps in the request-redirect chain in making a call to Conductor - :param rc: REST client object for calling conductor - :param conductor_url: conductor's base URL to submit a placement request - :param conductor_req_json: request json object to send to Conductor - :return: URL to check for follow up (similar to redirects); we keep checking these till we get a result/error - """ - debug_log.debug("Payload to Conductor: {}".format(json.dumps(conductor_req_json))) - raw_resp = rc.request(url=conductor_url, raw_response=True, method="POST", json=conductor_req_json) - resp = raw_resp.json() - if resp["status"] != "template": - raise RequestException(response=raw_resp, request=raw_resp.request) - time.sleep(10) # 10 seconds wait time to avoid being too quick! - plan_url = resp["links"][0][0]["href"] - debug_log.debug("Attemping to read the plan from the conductor provided url {}".format(plan_url)) - raw_resp = rc.request(raw_response=True, url=plan_url) # TODO: check why a list of lists for links - resp = raw_resp.json() - - if resp["plans"][0]["status"] in ["error"]: - raise RequestException(response=raw_resp, request=raw_resp.request) - return resp, raw_resp # now the caller of this will handle further follow-ups - - -def conductor_response_processor(conductor_response, raw_response, req_id): - """Build a response object to be sent to client's callback URL from Conductor's response - This includes Conductor's placement optimization response, and required ASDC license artifacts - - :param conductor_response: JSON response from Conductor - :param raw_response: Raw HTTP response corresponding to above - :param req_id: Id of a request - :return: JSON object that can be sent to the client's callback URL - """ - composite_solutions = [] - name_map = {"physical-location-id": "cloudClli", "host_id": "vnfHostName", - "cloud_version": "cloudVersion", "cloud_owner": "cloudOwner"} - for reco in conductor_response['plans'][0]['recommendations']: - for resource in reco.keys(): - c = reco[resource]['candidate'] - solution = { - 'resourceModuleName': resource, - 'serviceResourceId': reco[resource]['service_resource_id'], - 'inventoryType': c['inventory_type'], - 'serviceInstanceId': c['candidate_id'] if c['inventory_type'] == "service" else "", - 'cloudRegionId': c['location_id'], - 'assignmentInfo': [] - } - - for key, value in reco[resource]['attributes'].items(): - try: - solution['assignmentInfo'].append({"variableName": name_map[key], "variableValue": value}) - except KeyError: - debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info".format(key)) - - if c.get('host_id'): - solution['assignmentInfo'].append({'variableName': name_map['host_id'], 'variableValue': c['host_id']}) - composite_solutions.append(solution) - - request_state = conductor_response['plans'][0]['status'] - transaction_id = raw_response.headers.get('transaction_id', "") - status_message = conductor_response.get('plans')[0].get('message', "") - - solution_info = {} - if composite_solutions: - solution_info['placementInfo'] = composite_solutions - - resp = { - "transactionId": transaction_id, - "requestId": req_id, - "requestState": request_state, - "statusMessage": status_message, - "solutionInfo": solution_info - } - return resp - - -def conductor_no_solution_processor(conductor_response, raw_response, request_id, - template_placement_response="templates/plc_opt_response.jsont"): - """Build a response object to be sent to client's callback URL from Conductor's response - This is for case where no solution is found - - :param conductor_response: JSON response from Conductor - :param raw_response: Raw HTTP response corresponding to above - :param request_id: request Id associated with the client request (same as conductor response's "name") - :param template_placement_response: the template for generating response to client (plc_opt_response.jsont) - :return: JSON object that can be sent to the client's callback URL - """ - status_message = conductor_response["plans"][0].get("message") - templ = Template(open(template_placement_response).read()) - return json.loads(templ.render(composite_solutions=[], requestId=request_id, - transactionId=raw_response.headers.get('transaction_id', ""), - statusMessage=status_message, json=json)) - - diff --git a/optimizers/placementopt/conductor/remote_opt_processor.py b/optimizers/placementopt/conductor/remote_opt_processor.py deleted file mode 100644 index f753a70..0000000 --- a/optimizers/placementopt/conductor/remote_opt_processor.py +++ /dev/null @@ -1,79 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from requests import RequestException - -import traceback -from osdf.operation.error_handling import build_json_error_body -from osdf.logging.osdf_logging import metrics_log, MH, error_log -from osdf.optimizers.placementopt.conductor import conductor -from osdf.optimizers.licenseopt.simple_license_allocation import license_optim -from osdf.utils.interfaces import get_rest_client - - -def process_placement_opt(request_json, policies, osdf_config, prov_status): - """Perform the work for placement optimization (e.g. call SDC artifact and make conductor request) - NOTE: there is scope to make the requests to policy asynchronous to speed up overall performance - :param request_json: json content from original request - :param policies: flattened policies corresponding to this request - :param osdf_config: configuration specific to OSDF app - :param prov_status: provStatus retrieved from Subscriber policy - :return: None, but make a POST to callback URL - """ - - try: - rc = get_rest_client(request_json, service="so") - req_id = request_json["requestInfo"]["requestId"] - transaction_id = request_json['requestInfo']['transactionId'] - - metrics_log.info(MH.inside_worker_thread(req_id)) - license_info = None - if 'licenseDemand' in request_json['placementInfo']['demandInfo']: - license_info = license_optim(request_json) - - # Conductor only handles placement, only call Conductor if placementDemands exist - if 'placementDemand' in request_json['placementInfo']['demandInfo']: - metrics_log.info(MH.requesting("placement/conductor", req_id)) - placement_response = conductor.request(request_json, osdf_config, policies, prov_status) - if license_info: # Attach license solution if it exists - placement_response['solutionInfo']['licenseInfo'] = license_info - else: # License selection only scenario - placement_response = { - "transactionId": transaction_id, - "requestId": req_id, - "requestState": "complete", - "statusMessage": "License selection completed successfully", - "solutionInfo": {"licenseInfo": license_info} - } - except Exception as err: - error_log.error("Error for {} {}".format(req_id, traceback.format_exc())) - - try: - body = build_json_error_body(err) - metrics_log.info(MH.sending_response(req_id, "ERROR")) - rc.request(json=body, noresponse=True) - except RequestException: - error_log.error("Error sending asynchronous notification for {} {}".format(req_id, traceback.format_exc())) - return - - try: - metrics_log.info(MH.calling_back_with_body(req_id, rc.url,placement_response)) - rc.request(json=placement_response, noresponse=True) - except RequestException : # can't do much here but log it and move on - error_log.error("Error sending asynchronous notification for {} {}".format(req_id, traceback.format_exc())) - diff --git a/optimizers/placementopt/conductor/translation.py b/optimizers/placementopt/conductor/translation.py deleted file mode 100644 index 036398a..0000000 --- a/optimizers/placementopt/conductor/translation.py +++ /dev/null @@ -1,215 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import json -from osdf.utils.data_conversion import text_to_symbol -from osdf.utils import data_mapping - -def gen_optimization_policy(vnf_list, optimization_policy): - """Generate optimization policy details to pass to Conductor - :param vnf_list: List of vnf's to used in placement request - :param optimization_policy: optimization policy information provided in the incoming request - :return: List of optimization policies in a format required by Conductor - """ - optimization_policy_list = [] - for policy in optimization_policy: - content = policy['content'] - parameter_list = [] - - for attr in content['objectiveParameter']['parameterAttributes']: - parameter = attr['parameter'] if attr['parameter'] == "cloud_version" else attr['parameter']+"_between" - for res in attr['resource']: - vnf = get_matching_vnf(res, vnf_list) - value = [vnf] if attr['parameter'] == "cloud_version" else [attr['customerLocationInfo'], vnf] - parameter_list.append({ - attr['operator']: [attr['weight'], {parameter: value}] - }) - - optimization_policy_list.append({ - content['objective']: {content['objectiveParameter']['operator']: parameter_list } - }) - return optimization_policy_list - - -def get_matching_vnf(resource, vnf_list): - - for vnf in vnf_list: - if resource in vnf: - return vnf - return resource - - -def get_matching_vnfs(resources, vnf_list, match_type="intersection"): - """Get a list of matching VNFs from the list of resources - :param resources: - :param vnf_list: List of vnf's to used in placement request - :param match_type: "intersection" or "all" or "any" (any => send all_vnfs if there is any intersection) - :return: List of matching VNFs - """ - common_vnfs = [] - for vnf in vnf_list: - for resource in resources: - if resource in vnf: - common_vnfs.append(vnf) - if match_type == "intersection": # specifically requested intersection - return common_vnfs - elif common_vnfs or match_type == "all": # ("any" and common) OR "all" - return resources - return None - - -def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rtype=None): - """Generate a list of policies - :param vnf_list: List of vnf's to used in placement request - :param resource_policy: policy for this specific resource - :param match_type: How to match the vnf_names with the vnf_list (intersection or "any") - intersection => return intersection; "any" implies return all vnf_names if intersection is not null - :param rtype: resource type (e.g. resourceRegionProperty or resourceInstanceProperty) - None => no controller information added to the policy specification to Conductor - :return: resource policy list in a format required by Conductor - """ - resource_policy_list = [] - related_policies = [] - for policy in resource_policy: - pc = policy['content'] - demands = get_matching_vnfs(pc['resourceInstanceType'], vnf_list, match_type=match_type) - resource = {pc['identity']: {'type': pc['type'], 'demands': demands}} - - if rtype: - resource[pc['identity']]['properties'] = {'controller': pc[rtype]['controller'], - 'request': json.loads(pc[rtype]['request'])} - if demands and len(demands) != 0: - resource_policy_list.append(resource) - related_policies.append(policy) - return resource_policy_list, related_policies - - -def gen_resource_instance_policy(vnf_list, resource_instance_policy): - """Get policies governing resource instances in order to populate the Conductor API call""" - cur_policies, _ = gen_policy_instance(vnf_list, resource_instance_policy, rtype='resourceInstanceProperty') - return cur_policies - - -def gen_resource_region_policy(vnf_list, resource_region_policy): - """Get policies governing resource region in order to populate the Conductor API call""" - cur_policies, _ = gen_policy_instance(vnf_list, resource_region_policy, rtype='resourceRegionProperty') - return cur_policies - - -def gen_inventory_group_policy(vnf_list, inventory_group_policy): - """Get policies governing inventory group in order to populate the Conductor API call""" - cur_policies, _ = gen_policy_instance(vnf_list, inventory_group_policy, rtype=None) - return cur_policies - - -def gen_reservation_policy(vnf_list, reservation_policy): - """Get policies governing resource instances in order to populate the Conductor API call""" - cur_policies, _ = gen_policy_instance(vnf_list, reservation_policy, rtype='instanceReservationProperty') - return cur_policies - - -def gen_distance_to_location_policy(vnf_list, distance_to_location_policy): - """Get policies governing distance-to-location for VNFs in order to populate the Conductor API call""" - cur_policies, related_policies = gen_policy_instance(vnf_list, distance_to_location_policy, rtype=None) - for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy - properties = p_main['content']['distanceToLocationProperty'] - pcp_d = properties['distanceCondition'] - p_new[p_main['content']['identity']]['properties'] = { - 'distance': text_to_symbol[pcp_d['operator']] + " " + pcp_d['value'].lower(), - 'location': properties['locationInfo'] - } - return cur_policies - - -def gen_attribute_policy(vnf_list, attribute_policy): - """Get policies governing attributes of VNFs in order to populate the Conductor API call""" - cur_policies, related_policies = gen_policy_instance(vnf_list, attribute_policy, rtype=None) - for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy - properties = p_main['content']['cloudAttributeProperty'] - p_new[p_main['content']['identity']]['properties'] = { - 'evaluate': { - 'hypervisor': properties.get('hypervisor', ''), - 'cloud_version': properties.get('cloudVersion', ''), - 'cloud_type': properties.get('cloudType', ''), - 'dataplane': properties.get('dataPlane', ''), - 'network_roles': properties.get('networkRoles', ''), - 'complex': properties.get('complex', ''), - 'state': properties.get('state', ''), - 'country': properties.get('country', ''), - 'geo_region': properties.get('geoRegion', ''), - 'exclusivity_groups': properties.get('exclusivityGroups', ''), - 'replication_role': properties.get('replicationRole', '') - } - } - return cur_policies - - -def gen_zone_policy(vnf_list, zone_policy): - """Get zone policies in order to populate the Conductor API call""" - cur_policies, related_policies = gen_policy_instance(vnf_list, zone_policy, rtype=None) - for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy - pmz = p_main['content']['zoneProperty'] - p_new[p_main['content']['identity']]['properties'] = {'category': pmz['category'], 'qualifier': pmz['qualifier']} - return cur_policies - - -def get_demand_properties(demand, policies): - """Get list demand properties objects (named tuples) from policy""" - def _get_candidates(candidate_info): - return [dict(inventory_type=x['candidateType'], candidate_id=x['candidates']) for x in candidate_info] - properties = [] - for policy in policies: - for resourceInstanceType in policy['content']['resourceInstanceType']: - if resourceInstanceType in demand['resourceModuleName']: - for x in policy['content']['property']: - property = dict(inventory_provider=x['inventoryProvider'], - inventory_type=x['inventoryType'], - service_resource_id=demand['serviceResourceId']) - if 'attributes' in x: - attributes = {} - for k,v in x['attributes'].items(): - key=data_mapping.convert(k) - attributes[key] = v - if(key=="model-invariant-id"): - attributes[key]=demand['resourceModelInfo']['modelInvariantId'] - elif(key=="model-version-id"): - attributes[key]=demand['resourceModelInfo']['modelVersionId'] - property.update({"attributes": attributes}) - if x['inventoryType'] == "cloud": - property['region'] = {'get_param': "CHOSEN_REGION"} - if 'exclusionCandidateInfo' in demand: - property['excluded_candidates'] = _get_candidates(demand['exclusionCandidateInfo']) - if 'requiredCandidateInfo' in demand: - property['required_candidates'] = _get_candidates(demand['requiredCandidateInfo']) - properties.append(property) - if len(properties) == 0: - properties.append(dict(customer_id="", service_type="", inventory_provider="", inventory_type="")) - return properties - - -def gen_demands(req_json, vnf_policies): - """Generate list of demands based on request and VNF policies - :param req_json: Request object from the client (e.g. MSO) - :param vnf_policies: Policies associated with demand resources (e.g. from grouped_policies['vnfPolicy']) - :return: list of demand parameters to populate the Conductor API call - """ - demand_dictionary = {} - for placementDemand in req_json['placementDemand']: - demand_dictionary.update({placementDemand['resourceModuleName']: get_demand_properties(placementDemand, vnf_policies)}) - - return demand_dictionary diff --git a/osdf/__init__.py b/osdf/__init__.py new file mode 100755 index 0000000..d0993ae --- /dev/null +++ b/osdf/__init__.py @@ -0,0 +1,45 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +"""Core functions for OSDF Application, including flask app""" + +from jinja2 import Template + + +end_point_auth_mapping = { # map a URL endpoint to auth group + "cmscheduler": "CMScheduler", + "placement": "Placement", +} + +userid_suffix, passwd_suffix = "Username", "Password" +auth_groups = set(end_point_auth_mapping.values()) + +ERROR_TEMPLATE = Template(""" +{ + "serviceException": { + "text": "{{ description }}" + } +} +""") + +ACCEPTED_MESSAGE_TEMPLATE = Template(""" +{ + "requestId": "{{ request_id }}", + "text": "{{ description }}" +} +""") diff --git a/osdf/adapters/__init__.py b/osdf/adapters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/database/OracleDB.py b/osdf/adapters/database/OracleDB.py new file mode 100644 index 0000000..655dd27 --- /dev/null +++ b/osdf/adapters/database/OracleDB.py @@ -0,0 +1,32 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import cx_Oracle + +from osdf.utils.programming_utils import MetaSingleton + + +class OracleDB(metaclass=MetaSingleton): + conn, cur = None, None + + def connect(self, host=None, sid=None, user=None, passwd=None, port=5432): + if self.conn is None: + tns_info = cx_Oracle.makedsn(host=host, port=port, sid=sid) + self.conn = cx_Oracle.connect(user=user, password=passwd, dsn=tns_info, threaded=True) + self.cur = self.conn.cursor() + return self.conn, self.cur diff --git a/osdf/adapters/database/PostgresDB.py b/osdf/adapters/database/PostgresDB.py new file mode 100644 index 0000000..6689566 --- /dev/null +++ b/osdf/adapters/database/PostgresDB.py @@ -0,0 +1,31 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import psycopg2 + +from osdf.utils.programming_utils import MetaSingleton + + +class PostgresDB(metaclass=MetaSingleton): + conn, cur = None, None + + def connect(self, host=None, db=None, user=None, passwd=None, port=5432): + if self.conn is None: + self.conn = psycopg2.connect(host=host, port=port, user=user, password=passwd, database=db) + self.cur = self.conn.cursor() + return self.conn, self.cur diff --git a/osdf/adapters/database/VerticaDB.py b/osdf/adapters/database/VerticaDB.py new file mode 100644 index 0000000..ad961d7 --- /dev/null +++ b/osdf/adapters/database/VerticaDB.py @@ -0,0 +1,55 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import jaydebeapi +import sqlalchemy.pool as pool + +from jaydebeapi import _DEFAULT_CONVERTERS, _java_to_py +from osdf.utils.programming_utils import MetaSingleton +from osdf.config.base import osdf_config + +_DEFAULT_CONVERTERS.update({'BIGINT': _java_to_py('longValue')}) + + +class VerticaDB(metaclass=MetaSingleton): + connection_pool = None + + def get_connection(self): + p = self.get_config_params() + c = jaydebeapi.connect( + 'com.vertica.jdbc.Driver', + 'jdbc:vertica://{}:{}/{}'.format(p['host'], p['port'], p['db']), + {'user': p['user'], 'password': p['passwd'], 'CHARSET': 'UTF8'}, + jars=[p['db_driver']] + ) + return c + + def get_config_params(self): + config = osdf_config["deployment"] + host, port, db = config["verticaHost"], config["verticaPort"], config.get("verticaDB") + user, passwd = config["verticaUsername"], config["verticaPassword"] + jar_path = osdf_config['core']['osdf_system']['vertica_jar'] + params = dict(host=host, db=db, user=user, passwd=passwd, port=port, db_driver=jar_path) + return params + + def connect(self): + if self.connection_pool is None: + self.connection_pool = pool.QueuePool(self.get_connection, max_overflow=10, pool_size=5, recycle=600) + conn = self.connection_pool.connect() + cursor = conn.cursor() + return conn, cursor diff --git a/osdf/adapters/database/__init__.py b/osdf/adapters/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/dcae/__init__.py b/osdf/adapters/dcae/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/dcae/message_router.py b/osdf/adapters/dcae/message_router.py new file mode 100755 index 0000000..e495331 --- /dev/null +++ b/osdf/adapters/dcae/message_router.py @@ -0,0 +1,100 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import requests +from osdf.utils.data_types import list_like +from osdf.operation.exceptions import MessageBusConfigurationException + + +class MessageRouterClient(object): + def __init__(self, + dmaap_url=None, + mr_host_base_urls=None, + topic=None, + consumer_group=None, consumer_id=None, + timeout_ms=15000, fetch_limit=1000, + userid=None, passwd=None): + """ + :param dmaap_url: protocol, host and port; mostly for UEB + (e.g. https://dcae-msrt-ftl.homer.att.com:3905/) + :param mr_host_base_urls: for DMaaP, we get a topic URL (base_url + events/topic_name) + (e.g. https://dcae-msrt-ftl.homer.att.com:3905/events/com.att.dcae.dmaap.FTL.SNIRO-CM-SCHEDULER-RESPONSE) + :param consumer_group: DMaaP/UEB consumer group (unique for each subscriber; required for GET) + :param consumer_id: DMaaP/UEB consumer ID (unique for each thread/process for a subscriber; required for GET) + :param timeout_ms: (optional, default 15 seconds or 15,000 ms) server-side timeout for GET request + :param fetch_limit: (optional, default 1000 messages per request for GET), ignored for "POST" + :param userid: (optional, userid for HTTP basic authentication) + :param passwd: (optional, password for HTTP basic authentication) + """ + mr_error = MessageBusConfigurationException + if dmaap_url is None: # definitely not DMaaP, so use UEB mode + self.is_dmaap = False + if not (mr_host_base_urls and list_like(mr_host_base_urls)): + raise mr_error("Not a DMaaP or UEB configuration") + if not topic: + raise mr_error("Invalid topic: '{}'",format(topic)) + self.topic_urls = ["{}/events/{}".format(base_url, topic) for base_url in mr_host_base_urls] + else: + self.is_dmaap = True + self.topic_urls = [dmaap_url] + + self.timeout_ms = timeout_ms + self.fetch_limit = fetch_limit + self.auth = (userid, passwd) if userid and passwd else None + self.consumer_group = consumer_group + self.consumer_id = consumer_id + + def get(self, outputjson=True): + """Fetch messages from message router (DMaaP or UEB) + :param outputjson: (optional, specifies if response is expected to be in json format), ignored for "POST" + :return: response as a json object (if outputjson is True) or as a string + """ + url_fmt = "{topic_url}/{cgroup}/{cid}?timeout={timeout_ms}&limit={limit}" + urls = [url_fmt.format(topic_url=x, timeout_ms=self.timeout_ms, limit=self.fetch_limit, + cgroup=self.consumer_group, cid=self.consumer_id) for x in self.topic_urls] + for url in urls[:-1]: + try: + return self.http_request(method='GET', url=url, outputjson=outputjson) + except: + pass + return self.http_request(method='GET', url=urls[-1], outputjson=outputjson) + + def post(self, msg, inputjson=True): + for url in self.topic_urls[:-1]: + try: + return self.http_request(method='POST', url=url, inputjson=inputjson, msg=msg) + except: + pass + return self.http_request(method='POST', url=self.topic_urls[-1], inputjson=inputjson, msg=msg) + + def http_request(self, url, method, inputjson=True, outputjson=True, msg=None, **kwargs): + """ + Perform the actual URL request (GET or POST), and do error handling + :param url: full URL (including topic, limit, timeout, etc.) + :param method: GET or POST + :param inputjson: Specify whether input is in json format (valid only for POST) + :param outputjson: Is response expected in a json format + :param msg: content to be posted (valid only for POST) + :return: response as a json object (if outputjson or POST) or as a string; None if error + """ + res = requests.request(url=url, method=method, auth=self.auth, **kwargs) + if res.status_code == requests.codes.ok: + return res.json() if outputjson or method == "POST" else res.content + else: + raise Exception("HTTP Response Error: code {}; headers:{}, content: {}".format( + res.status_code, res.headers, res.content)) diff --git a/osdf/adapters/local_data/__init__.py b/osdf/adapters/local_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/local_data/local_policies.py b/osdf/adapters/local_data/local_policies.py new file mode 100644 index 0000000..c63ae5a --- /dev/null +++ b/osdf/adapters/local_data/local_policies.py @@ -0,0 +1,40 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import json +import os + + +def get_local_policies(local_policy_folder, local_policy_list, policy_id_list=None): + """ + Get policies from a local file system. + Required for the following scenarios: + (a) doing work-arounds (e.g. if we are asked to drop some policies for testing purposes) + (b) work-arounds when policy platform is giving issues (e.g. if dev/IST policies are wiped out in an upgrade) + :param local_policy_folder: where the policy files are present + :param local_policy_list: list of local policies + :param policy_id_list: list of policies to get (if unspecified or None, get all) + :return: get policies + """ + policies = [] + for fname in local_policy_list: # ugly removal of .json from file name + if policy_id_list and fname[:-5] not in policy_id_list: + continue + with open(os.path.join(local_policy_folder, fname)) as fid: + policies.append(json.load(fid)) + return policies diff --git a/osdf/adapters/policy/__init__.py b/osdf/adapters/policy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/policy/interface.py b/osdf/adapters/policy/interface.py new file mode 100644 index 0000000..4ddee15 --- /dev/null +++ b/osdf/adapters/policy/interface.py @@ -0,0 +1,204 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import base64 +import itertools +import json + + +from requests import RequestException +from osdf.operation.exceptions import BusinessException +from osdf.adapters.local_data.local_policies import get_local_policies +from osdf.adapters.policy.utils import _regex_policy_name +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import audit_log, MH, metrics_log, error_log, debug_log +from osdf.utils.interfaces import RestClient +from osdf.optimizers.placementopt.conductor.api_builder import retrieve_node +# from osdf.utils import data_mapping + + +def get_by_name(rest_client, policy_name_list, wildcards=True): + policy_list = [] + for policy_name in policy_name_list: + try: + query_name = policy_name + if wildcards: + query_name = _regex_policy_name(query_name) + policy_list.append(rest_client.request(json={"policyName": query_name})) + except RequestException as err: + audit_log.warn("Error in fetching policy: " + policy_name) + raise BusinessException("Cannot fetch policy {}: ".format(policy_name), err) + return policy_list + + +def get_subscriber_name(req, pmain): + subs_name = retrieve_node(req, pmain['subscriber_name']) + if subs_name is None: + return "DEFAULT" + else: + subs_name_uc = subs_name.upper() + if subs_name_uc in ("DEFAULT", "NULL", ""): + subs_name = "DEFAULT" + return subs_name + + +def get_subscriber_role(rest_client, req, pmain, service_name, scope): + """Make a request to policy and return subscriberRole + :param rest_client: rest client to make call + :param req: request object from MSO + :param pmain: main config that will have policy path information + :param service_name: the type of service to call: e.g. "vCPE + :param scope: the scope of policy to call: e.g. "OOF_HAS_vCPE". + :return: subscriberRole and provStatus retrieved from Subscriber policy + """ + subscriber_role = "DEFAULT" + prov_status = [] + subs_name = get_subscriber_name(req, pmain) + if subs_name == "DEFAULT": + return subscriber_role, prov_status + + policy_subs = pmain['policy_subscriber'] + policy_scope = {"policyName": "{}.*".format(scope), + "configAttributes": { + "serviceType": "{}".format(service_name), + "service": "{}".format(policy_subs)} + } + policy_list = [] + try: + policy_list.append(rest_client.request(json=policy_scope)) + except RequestException as err: + audit_log.warn("Error in fetching policy for {}: ".format(policy_subs)) + return subscriber_role, prov_status + + formatted_policies = [] + for x in itertools.chain(*policy_list): + if x['config'] is None: + raise BusinessException("Config not found for policy with name %s" % x['policyName']) + else: + formatted_policies.append(json.loads(x['config'])) + + for policy in formatted_policies: + property_list = policy['content']['property'] + for prop in property_list: + if subs_name in prop['subscriberName']: + subs_role_list = prop['subscriberRole'] + prov_status = prop['provStatus'] + if isinstance(subs_role_list, list): # as list is returned + return subs_role_list[0], prov_status + return subscriber_role, prov_status + + +def get_by_scope(rest_client, req, config_local, type_service): + policy_list = [] + pmain = config_local['policy_info'][type_service] + pscope = pmain['policy_scope'] + + model_name = retrieve_node(req, pscope['service_name']) + service_name = model_name + # service_name = data_mapping.get_request_service_type(req) + # if service_name is None: + # service_name = data_mapping.get_service_type(model_name) + scope = pscope['scope_{}'.format(service_name.lower())] + subscriber_role, prov_status = get_subscriber_role(rest_client, req, pmain, service_name, scope) + policy_type_list = pmain['policy_type_{}'.format(service_name.lower())] + for policy_type in policy_type_list: + policy_scope = {"policyName": "{}.*".format(scope), + "configAttributes": { + "serviceType": "{}".format(service_name), + "service": "{}".format(policy_type), + "subscriberRole": "{}".format(subscriber_role)} + } + policy_list.append(rest_client.request(json=policy_scope)) + return policy_list, prov_status + + +def remote_api(req_json, osdf_config, service_type="placement"): + """Make a request to policy and return response -- it accounts for multiple requests that be needed + :param req_json: policy request object (can have multiple policy names) + :param osdf_config: main config that will have credential information + :param service_type: the type of service to call: "placement", "scheduling" + :return: all related policies and provStatus retrieved from Subscriber policy + """ +# if not req_json[service_type + "Info"]['policyId']: +# return [] + + config = osdf_config.deployment + uid, passwd = config['policyPlatformUsername'], config['policyPlatformPassword'] + pcuid, pcpasswd = config['policyClientUsername'], config['policyClientPassword'] + headers = {"ClientAuth": base64.b64encode(bytes("{}:{}".format(pcuid, pcpasswd), "ascii"))} + headers.update({'Environment': config['policyPlatformEnv']}) + url = config['policyPlatformUrl'] + rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug) + + if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name": + policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=True) + elif osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name_no_wildcards": + policies = get_by_name(rc, req_json[service_type + "Info"]['policyId'], wildcards=False) + else: # Get policy by scope + policies, prov_status = get_by_scope(rc, req_json, osdf_config.core, service_type) + + # policies in res are list of lists, so flatten them; also only keep config part + formatted_policies = [] + for x in itertools.chain(*policies): + if x['config'] is None: + raise BusinessException("Config not found for policy with name %s" % x['policyName']) + else: + formatted_policies.append(json.loads(x['config'])) + return formatted_policies, prov_status + + +def local_policies_location(req_json, osdf_config, service_type): + """ + Get folder and list of policy_files if "local policies" option is enabled + :param service_type: placement supported for now, but can be any other service + :return: a tuple (folder, file_list) or None + """ + lp = osdf_config.core.get('osdf_hacks', {}).get('local_policies', {}) + if lp.get('global_disabled'): + return None # short-circuit to disable all local policies + if lp.get('local_{}_policies_enabled'.format(service_type)): + if service_type == "scheduling": + return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type)) + else: + model_name = retrieve_node(req_json, osdf_config.core['policy_info'][service_type]['policy_scope']['service_name']) + service_name = data_mapping.get_service_type(model_name) + return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), lp.get('{}_policy_files_{}'.format(service_type, service_name.lower())) + return None + + +def get_policies(request_json, service_type): + """Validate the request and get relevant policies + :param request_json: Request object + :param service_type: the type of service to call: "placement", "scheduling" + :return: policies associated with this request and provStatus retrieved from Subscriber policy + """ + prov_status = [] + req_info = request_json['requestInfo'] + req_id = req_info['requestId'] + metrics_log.info(MH.requesting("policy", req_id)) + local_info = local_policies_location(request_json, osdf_config, service_type) + + if local_info: # tuple containing location and list of files + to_filter = None + if osdf_config.core['policy_info'][service_type]['policy_fetch'] == "by_name": + to_filter = request_json[service_type + "Info"]['policyId'] + policies = get_local_policies(local_info[0], local_info[1], to_filter) + else: + policies, prov_status= remote_api(request_json, osdf_config, service_type) + + return policies, prov_status diff --git a/osdf/adapters/policy/utils.py b/osdf/adapters/policy/utils.py new file mode 100644 index 0000000..a006f12 --- /dev/null +++ b/osdf/adapters/policy/utils.py @@ -0,0 +1,58 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from collections import defaultdict + + +def group_policies(flat_policies): + """Filter policies using the following steps: + 1. Apply prioritization among the policies that are sharing the same policy type and resource type + 2. Remove redundant policies that may applicable across different types of resource + 3. Filter policies based on type and return + :param flat_policies: list of flat policies + :return: Filtered policies + """ + aggregated_policies = {} + filter_policies = defaultdict(list) + policy_name = [] + for policy in flat_policies: + policy_type = policy['content']['type'] + if policy_type not in aggregated_policies: + aggregated_policies[policy_type] = defaultdict(list) + for resource in policy['content']['policyScope']['resourceInstanceType']: + aggregated_policies[policy_type][resource].append(policy) + for policy_type in aggregated_policies: + for resource in aggregated_policies[policy_type]: + if len(aggregated_policies[policy_type][resource]) > 0: + aggregated_policies[policy_type][resource].sort(key=lambda x: x['priority'], reverse=True) + policy = aggregated_policies[policy_type][resource][0] + if policy['policyName'] not in policy_name: + filter_policies[policy['content']['type']].append(policy) + policy_name.append(policy['policyName']) + return filter_policies + + +def _regex_policy_name(policy_name): + """Get the correct policy name as a regex + (e.g. OOF_HAS_vCPE.cloudAttributePolicy ends up in policy as OOF_HAS_vCPE.Config_MS_cloudAttributePolicy.1.xml + So, for now, we query it as OOF_HAS_vCPE..*aicAttributePolicy.*) + :param policy_name: Example: OOF_HAS_vCPE.aicAttributePolicy + :return: regexp for policy: Example: OOF_HAS_vCPE..*aicAttributePolicy.* + """ + p = policy_name.partition('.') + return p[0] + p[1] + ".*" + p[2] + ".*" diff --git a/osdf/adapters/request_parsing/__init__.py b/osdf/adapters/request_parsing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/request_parsing/placement.py b/osdf/adapters/request_parsing/placement.py new file mode 100644 index 0000000..d7a6575 --- /dev/null +++ b/osdf/adapters/request_parsing/placement.py @@ -0,0 +1,33 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import copy +import json +from osdf.utils.programming_utils import list_flatten, dot_notation + + +def json_path_after_expansion(req_json, reference): + """ + Get the child node(s) from the dot-notation [reference] and parent [req_json]. + For placement and other requests, there are encoded JSONs inside the request or policy, + so we need to expand it and then do a search over the parent plus expanded JSON. + """ + req_json_copy = copy.deepcopy(req_json) # since we expand the JSON in place, we work on a copy + req_json_copy['placementInfo']['orderInfo'] = json.loads(req_json_copy['placementInfo']['orderInfo']) + info = dot_notation(req_json_copy, reference) + return list_flatten(info) if isinstance(info, list) else info diff --git a/osdf/adapters/sdc/__init__.py b/osdf/adapters/sdc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/adapters/sdc/asdc.py b/osdf/adapters/sdc/asdc.py new file mode 100755 index 0000000..43932ba --- /dev/null +++ b/osdf/adapters/sdc/asdc.py @@ -0,0 +1,40 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from osdf.utils.interfaces import RestClient +import xml.etree.ElementTree as ET + +def request(model_version_id, request_id, config): + """Get all of the license artifacts from SDC using service_resource_id and model_version_id + :param model_version_id: model_version_id + :param request_id: request_id + :return: license artifacts from SDC + """ + base_url = config['sdcUrl'] + uid, passwd = config['sdcUsername'], config['sdcPassword'] + headers = {"CSP_UID": config['sdcMechId'], "X-ONAP-InstanceID": "osdf"} + rc = RestClient(userid=uid, passwd=passwd, headers=headers, method="GET", req_id=request_id) + resource_data = rc.request(base_url + '/resources/{}/metadata'.format(model_version_id)) + + artifact_ids = [x['artifactURL'].split("/resources/")[-1] # get the part after /resources/ + for x in resource_data.get('artifacts', []) if x.get('artifactType') == "VF_LICENSE"] + artifact_urls = [base_url + '/resources/' + str(artifact_id) for artifact_id in artifact_ids] + licenses = [] + for x in artifact_urls: + licenses.append(ET.fromstring(rc.request(x, asjson=False))) + return licenses diff --git a/osdf/adapters/sdc/constraint_handler.py b/osdf/adapters/sdc/constraint_handler.py new file mode 100644 index 0000000..2aae9a0 --- /dev/null +++ b/osdf/adapters/sdc/constraint_handler.py @@ -0,0 +1,81 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from osdf.config.base import osdf_config +from osdf.utils.programming_utils import dot_notation + +ns = {'p': 'http://xmlns.onap.org/sdc/license-model/1.0'} +config_local = osdf_config.core + + +def choose_license(license_artifacts, order_info, service_type): + entitlement_pool_uuids = [] + license_key_group_uuids = [] + + for license_artifact in license_artifacts: + for feature in license_artifact.findall('./p:feature-group-list/', ns): + for entitlement in feature.findall('./p:entitlement-pool-list/', ns): + if is_valid(entitlement, order_info, service_type): + entitlement_pool_uuid = entitlement.find('p:entitlement-pool-uuid', ns).text + entitlement_pool_uuids.append(entitlement_pool_uuid) + for license_key_group in feature.findall('./p:license-key-group-list/', ns): + if is_valid(license_key_group, order_info, service_type): + license_key_group_uuid = license_key_group.find('p:license-key-group-uuid', ns).text + license_key_group_uuids.append(license_key_group_uuid) + return entitlement_pool_uuids, license_key_group_uuids + + +# element is expected to be a license-key-group or entitlement-pool +# if these elements diverge at a later date this method should be refactored +def is_valid(element, order_info, service_type): + for limit in element.findall('./p:sp-limits/p:limit', ns): + # description = limit.find('p:description', ns).text + metric_value = limit.find('p:values', ns).text + metric = limit.find('p:metric', ns).text + try: + order_value = dot_notation(order_info, config_local['service_info'][service_type][metric]) + # print("The order has the value %s for the metric %s and the limit specifies the value %s. The limit has the description %s." % (order_value, metric, metric_value, description)) + if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE + for arr_value in order_value: + if str(metric_value) != str(arr_value): + return False + else: + if str(metric_value) != str(order_value): + return False + except KeyError: + return False + # vendor limits + for limit in element.findall('./p:vendor-limits/p:limit', ns): + # description = limit.find('p:description', ns).text + metric_value = limit.find('p:values', ns).text + metric = limit.find('p:metric', ns).text + try: + order_value = dot_notation(order_info, config_local['service_info'][service_type][metric]) + if isinstance(order_value, list): # it is possible a list is returned, for example a list of vnfs for vCPE + for arr_value in order_value: + if str(metric_value) != str(arr_value): + return False + else: + if str(metric_value) != str(order_value): + return False + # print("The order has the value %s for the metric %s and the limit specifies the value %s. The limit has the description %s." % (order_value, metric, metric_value, description)) + + except KeyError: + return False + return True + diff --git a/osdf/config/__init__.py b/osdf/config/__init__.py new file mode 100644 index 0000000..303a8ce --- /dev/null +++ b/osdf/config/__init__.py @@ -0,0 +1,32 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- + + +import yaml +import json + +from osdf.utils.programming_utils import MetaSingleton + + +class CoreConfig(metaclass=MetaSingleton): + core_config = None + + def get_core_config(self, config_file=None): + if self.core_config is None: + self.core_config = yaml.load(open(config_file)) + return self.core_config + diff --git a/osdf/config/base.py b/osdf/config/base.py new file mode 100644 index 0000000..b8aacff --- /dev/null +++ b/osdf/config/base.py @@ -0,0 +1,36 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import os + +import osdf.config.loader as config_loader +import osdf.config.credentials as creds +from osdf.utils.programming_utils import DotDict + +config_spec = { + "deployment": os.environ.get("OSDF_MANAGER_CONFIG_FILE", "config/osdf_config.yaml"), + "core": "config/common_config.yaml" + } + +osdf_config = DotDict(config_loader.all_configs(**config_spec)) + +http_basic_auth_credentials = creds.load_credentials(osdf_config) + +dmaap_creds = creds.dmaap_creds() + +creds_prefixes = {"so": "so", "cm": "cmPortal"} diff --git a/osdf/config/credentials.py b/osdf/config/credentials.py new file mode 100644 index 0000000..e5a6399 --- /dev/null +++ b/osdf/config/credentials.py @@ -0,0 +1,60 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import json + +from osdf import auth_groups, userid_suffix, passwd_suffix + + +def dmaap_creds(dmaap_file="/etc/dcae/dmaap.conf"): + """Get DMaaP credentials from DCAE for publish and subscribe""" + try: + dmaap_creds = _get_dmaap_creds(dmaap_file) + except: + dmaap_creds = {} + return dmaap_creds + + +def _get_dmaap_creds(dmaap_file): + """Get DMaaP credentials from DCAE for publish and subscribe""" + streams = json.load(open(dmaap_file, 'r')) + pubs = [x for x in streams + if x['dmaapStreamId'] == 'requests' and x['dmaapAction'] == 'publish'] + subs = [x for x in streams + if x['dmaapStreamId'] == 'responses' and x['dmaapAction'] == 'subscribe'] + + def get_dmaap_info(x): + """Get DMaaP credentials from dmaap_object 'x'""" + return dict(url=x.get('dmaapUrl'), userid=x.get('dmaapUserName'), passwd=x.get('dmaapPassword')) + + return {'pub': get_dmaap_info(pubs[0]), 'sub': get_dmaap_info(subs[0])} + + +def load_credentials(osdf_config): + """Get credentials as dictionaries grouped by auth_group (e.g. creds["Placement"]["user1"] = "pass1")""" + creds = dict((x, dict()) for x in auth_groups) # each auth group has userid, passwd dict + suffix_start = len(userid_suffix) + + config = osdf_config.deployment + + for element, username in config.items(): + for x in auth_groups: + if element.startswith("osdf" + x) and element.endswith(userid_suffix): + passwd = config[element[:-suffix_start] + passwd_suffix] + creds[x][username] = passwd + return creds diff --git a/osdf/config/loader.py b/osdf/config/loader.py new file mode 100644 index 0000000..7cb363a --- /dev/null +++ b/osdf/config/loader.py @@ -0,0 +1,51 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import json + +import yaml + + +def load_config_file(config_file: str, child_name="dockerConfiguration") -> dict: + """ + Load OSDF configuration from a file -- currently only yaml/json are supported + :param config_file: path to config file (.yaml or .json). + :param child_name: if present, return only that child node + :return: config (all or specific child node) + """ + with open(config_file, 'r') as fid: + res = {} + if config_file.endswith(".yaml"): + res = yaml.load(fid) + elif config_file.endswith(".json") or config_file.endswith("json"): + res = json.load(fid) + return res.get(child_name, res) if child_name else res + + +def dcae_config(config_file: str) -> dict: + return load_config_file(config_file, child_name="dockerConfiguration") + + +def all_configs(**kwargs: dict) -> dict: + """ + Load all specified configurations + :param config_file_spec: key-value pairs + (e.g. { "core": "common_config.yaml", "deployment": "/tmp/1452523532json" }) + :return: merged config as a nested dictionary + """ + return {k: load_config_file(fname) for k, fname in kwargs.items()} diff --git a/osdf/logging/__init__.py b/osdf/logging/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/logging/osdf_logging.py b/osdf/logging/osdf_logging.py new file mode 100755 index 0000000..9a6ff4e --- /dev/null +++ b/osdf/logging/osdf_logging.py @@ -0,0 +1,229 @@ +import logging +import traceback +import uuid + +import logging +from logging.handlers import RotatingFileHandler +from osdf.utils.programming_utils import MetaSingleton + + +def log_handlers_pre_onap(config_file="config/pre_onap_logging_common_v1.config", + service_name="OOF_OSDF"): + """ + Convenience handlers for logging to different log files + + :param config_file: configuration file (properties file) that specifies log location, rotation, etc. + :param service_name: name for this service + :return: dictionary of log objects: "error", "metrics", "audit", "debug" + + We can use the returned values as follows: + X["error"].fatal("a FATAL message for the error log") + X["error"].error("an ERROR message for the error log") + X["error"].warn("a WARN message for the error log") + X["audit"].info("an INFO message for the audit log") + X["metrics"].info("an INFO message for the metrics log") + X["debug"].debug("a DEBUG message for the debug log") + """ + # Keeping main_params as a place-holder for ONAP related logging needs + # main_params = dict(instanceUUID=uuid.uuid1(), serviceName=service_name, configFile=config_file) + return dict((x, logging.getLogger(x)) # keep **main_params as a placeholder for ONAP fields + for x in ["error", "metrics", "audit", "debug"]) + + +def format_exception(err, prefix=None): + """Format operation for use with ecomp logging + :param err: exception object + :param prefix: prefix string message + :return: formatted exception (via repr(traceback.format_tb(err.__traceback__)) + """ + exception_lines = traceback.format_exception(err.__class__, err, err.__traceback__) + exception_desc = "".join(exception_lines) + return exception_desc if not prefix else prefix + ": " + exception_desc + + +class OOF_OSDFLogMessageHelper(metaclass=MetaSingleton): + """Provides loggers as a singleton (otherwise, we end up with duplicate messages). + Provides error_log, metric_log, audit_log, and debug_log (in that order) + Additionally can provide specific log handlers too + """ + log_handlers = None + default_levels = ["error", "metrics", "audit", "debug"] + + def _setup_handlers(self, log_version="pre_onap", config_file=None, service_name=None): + """return error_log, metrics_log, audit_log, debug_log""" + if self.log_handlers is None: + params = {} + params.update({"config_file": config_file} if config_file else {}) + params.update({"service_name": service_name} if service_name else {}) + + if log_version == "pre_onap": + self.log_handlers = log_handlers_pre_onap(**params) + + def get_handlers(self, levels=None, log_version="pre_onap", config_file=None, service_name=None): + """Return ONAP-compliant log handlers for different levels. Each "level" ends up in a different log file + with a prefix of that level. + + For example: error_log, metrics_log, audit_log, debug_log in that order + :param levels: None or list of levels subset of self.default_levels (["error", "metrics", "audit", "debug"]) + :param log_version: Currently only pre_onap is supported + :param config_file: Logging configuration file for ONAP compliant logging + :param service_name: Name of the service + :return: list of log_handlers in the order of levels requested. + if levels is None: we return handlers for self.default_levels + if levels is ["error", "audit"], we return log handlers for that. + """ + self._setup_handlers(log_version="pre_onap", config_file=config_file, service_name=service_name) + wanted_levels = self.default_levels if levels is None else levels + return [self.log_handlers.get(x) for x in wanted_levels] + + +class OOF_OSDFLogMessageFormatter(object): + + @staticmethod + def accepted_valid_request(req_id, request): + return "Accepted valid request for ID: {} for endpoint: {}".format( + req_id, request.url) + + @staticmethod + def invalid_request(req_id, err): + return "Invalid request for request ID: {}; cause: {}".format( + req_id, format_exception(err)) + + @staticmethod + def invalid_response(req_id, err): + return "Invalid response for request ID: {}; cause: {}".format( + req_id, format_exception(err)) + + @staticmethod + def malformed_request(request, err): + return "Malformed request for URL {}, from {}; cause: {}".format( + request.url, request.remote_address, format_exception(err)) + + @staticmethod + def malformed_response(response, client, err): + return "Malformed response {} for client {}; cause: {}".format( + response, client, format_exception(err)) + + @staticmethod + def need_policies(req_id): + return "Policies required but found no policies for request ID: {}".format(req_id) + + @staticmethod + def policy_service_error(url, req_id, err): + return "Unable to call policy for {} for request ID: {}; cause: {}".format( + url, req_id, format_exception(err)) + + @staticmethod + def requesting_url(url, req_id): + return "Making a call to URL {} for request ID: {}".format(url, req_id) + + @staticmethod + def requesting(service_name, req_id): + return "Making a call to service {} for request ID: {}".format(service_name, req_id) + + @staticmethod + def error_requesting(service_name, req_id, err): + return "Error while requesting service {} for request ID: {}; cause: {}".format( + service_name, req_id, format_exception(err)) + + @staticmethod + def calling_back(req_id, callback_url): + return "Posting result to callback URL for request ID: {}; callback URL={}".format( + req_id, callback_url) + + @staticmethod + def calling_back_with_body(req_id, callback_url, body): + return "Posting result to callback URL for request ID: {}; callback URL={} body={}".format( + req_id, callback_url, body) + + @staticmethod + def error_calling_back(req_id, callback_url, err): + return "Error while posting result to callback URL {} for request ID: {}; cause: {}".format( + req_id, callback_url, format_exception(err)) + + @staticmethod + def received_request(url, remote_addr, json_body): + return "Received a call to {} from {} {}".format(url, remote_addr, json_body) + + @staticmethod + def new_worker_thread(req_id, extra_msg=""): + res = "Initiating new worker thread for request ID: {}".format(req_id) + return res + extra_msg + + @staticmethod + def inside_worker_thread(req_id, extra_msg=""): + res = "Inside worker thread for request ID: {}".format(req_id) + return res + extra_msg + + @staticmethod + def processing(req_id, desc): + return "Processing request ID: {} -- {}".format(req_id, desc) + + @staticmethod + def processed(req_id, desc): + return "Processed request ID: {} -- {}".format(req_id, desc) + + @staticmethod + def error_while_processing(req_id, desc, err): + return "Error while processing request ID: {} -- {}; cause: {}".format( + req_id, desc, format_exception(err)) + + @staticmethod + def creating_local_env(req_id): + return "Creating local environment request ID: {}".format( + req_id) + + @staticmethod + def error_local_env(req_id, desc, err): + return "Error while creating local environment for request ID: {} -- {}; cause: {}".format( + req_id, desc, err.__traceback__) + + @staticmethod + def inside_new_thread(req_id, extra_msg=""): + res = "Spinning up a new thread for request ID: {}".format(req_id) + return res + " " + extra_msg + + @staticmethod + def error_response_posting(req_id, desc, err): + return "Error while posting a response for a request ID: {} -- {}; cause: {}".format( + req_id, desc, err.__traceback__) + + @staticmethod + def received_http_response(resp): + return "Received response [code: {}, headers: {}, data: {}]".format( + resp.status_code, resp.headers, resp.__dict__) + + @staticmethod + def sending_response(req_id, desc): + return "Response is sent for request ID: {} -- {}".format( + req_id, desc) + + @staticmethod + def listening_response(req_id, desc): + return "Response is sent for request ID: {} -- {}".format( + req_id, desc) + + @staticmethod + def items_received(item_num, item_type, desc="Received"): + return "{} {} {}".format(desc, item_num, item_type) + + @staticmethod + def items_sent(item_num, item_type, desc="Published"): + return "{} {} {}".format(desc, item_num, item_type) + + +MH = OOF_OSDFLogMessageFormatter +error_log, metrics_log, audit_log, debug_log = OOF_OSDFLogMessageHelper().get_handlers() + +def warn_audit_error(msg): + """Log the message to error_log.warn and audit_log.warn""" + log_message_multi(msg, audit_log.warn, error_log.warn) + + +def log_message_multi(msg, *logger_methods): + """Log the msg to multiple loggers + :param msg: message to log + :param logger_methods: e.g. error_log.warn, audit_log.warn, etc. + """ + for log_method in logger_methods: + log_method(msg) diff --git a/osdf/models/api/common.py b/osdf/models/api/common.py new file mode 100755 index 0000000..0d2d0eb --- /dev/null +++ b/osdf/models/api/common.py @@ -0,0 +1,54 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import datetime +from pprint import pformat + +from dateutil.parser import parse +from schematics.exceptions import ConversionError +from schematics.models import Model +from schematics.types import DateTimeType + + +class OSDFModel(Model): + """Extends generic model with a couple of extra methods""" + def __str__(self): + """Return values of object's attributes -- excluding hidden or callable ones""" + def _str_format(x): + """Coerce as string for some special objects""" + return str(x) if isinstance(x, datetime.datetime) else x + + z1 = dict((x, getattr(self, x)) for x in dir(self) + if not x.startswith("_") and not callable(getattr(self, x))) + z1 = dict((x, _str_format(y)) for x, y in z1.items()) + return pformat(z1, depth=4, indent=2, width=1000) + + def __repr__(self): + """Return values of object's attributes -- excluding hidden or callable ones""" + return self.__str__() + + +class CustomISODateType(DateTimeType): + """Schematics doesn't support full ISO, so we use custom one""" + def to_native(self, value, context=None): + if isinstance(value, datetime.datetime): + return value + try: + return parse(value) + except: + raise ConversionError(u'Invalid timestamp {}'.format(value)) diff --git a/osdf/models/api/placementRequest.py b/osdf/models/api/placementRequest.py new file mode 100644 index 0000000..73eac75 --- /dev/null +++ b/osdf/models/api/placementRequest.py @@ -0,0 +1,124 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from .common import OSDFModel +from schematics.types import StringType, URLType, IntType, FloatType +from schematics.types.compound import ModelType, ListType + + +class RequestInfo(OSDFModel): + """Info for northbound request from client such as SO""" + transactionId = StringType(required=True) + requestId = StringType(required=True) + callbackUrl = URLType(required=True) + sourceId = StringType(required=True) + optimizer = ListType(StringType()) + numSolutions = IntType() + timeout = IntType() + requestType = StringType() + + +class CandidateInfo(OSDFModel): + """Preferred candidate for a resource (sent as part of a request from client)""" + candidateType = StringType(required=True) + candidates = ListType(StringType(required=True)) + + +class ResourceModelInfo(OSDFModel): + """Model information for a specific resource""" + modelCustomizationId = StringType(required=True) + modelInvariantId = StringType(required=True) + modelName = StringType() + modelVersion = StringType() + modelVersionId = StringType() + modelType = StringType() + operationalStatus = StringType() + + +class ExistingLicenseInfo(OSDFModel): + entitlementPoolUUID = ListType(StringType()) + licenseKeyGroupUUID = ListType(StringType()) + + +class LicenseDemand(OSDFModel): + resourceInstanceType = StringType(required=True) + serviceResourceId = StringType(required=True) + resourceModuleName = StringType(required=True) + resourceModelInfo = ModelType(ResourceModelInfo) + existingLicense = ModelType(ExistingLicenseInfo) + + +class PlacementDemand(OSDFModel): + resourceInstanceType = StringType(required=True) + serviceResourceId = StringType(required=True) + resourceModuleName = StringType(required=True) + exclusionCandidateInfo = ListType(ModelType(CandidateInfo)) + requiredCandidateInfo = ListType(ModelType(CandidateInfo)) + resourceModelInfo = ModelType(ResourceModelInfo) + tenantId = StringType() + tenantName = StringType() + + +class ExistingPlacementInfo(OSDFModel): + serviceInstanceId = StringType(required=True) + + +class DemandInfo(OSDFModel): + """Requested resources (sent as part of a request from client)""" + placementDemand = ListType(ModelType(PlacementDemand)) + licenseDemand = ListType(ModelType(LicenseDemand)) + + +class SubscriberInfo(OSDFModel): + """Details on the customer that subscribes to the VNFs""" + globalSubscriberId = StringType(required=True) + subscriberName = StringType() + subscriberCommonSiteId = StringType() + + +class ServiceModelInfo(OSDFModel): + """ASDC Service model information""" + modelType = StringType(required=True) + modelInvariantId = StringType(required=True) + modelVersionId = StringType(required=True) + modelName = StringType(required=True) + modelVersion = StringType(required=True) + + +class Location(OSDFModel): + latitude = FloatType(required=True) + longitude = FloatType(required=True) + + +class PlacementInfo(OSDFModel): + """Information specific to placement optimization""" + serviceModelInfo = ModelType(ServiceModelInfo) + subscriberInfo = ModelType(SubscriberInfo) + demandInfo = ModelType(DemandInfo, required=True) + orderInfo = StringType() + policyId = ListType(StringType()) + serviceInstanceId = StringType() + existingPlacement = ModelType(ExistingPlacementInfo) + location = ModelType(Location) + serviceType = StringType() + + +class PlacementAPI(OSDFModel): + """Request for placement optimization (specific to optimization and additional metadata""" + requestInfo = ModelType(RequestInfo, required=True) + placementInfo = ModelType(PlacementInfo, required=True) diff --git a/osdf/models/api/placementResponse.py b/osdf/models/api/placementResponse.py new file mode 100644 index 0000000..e9746d6 --- /dev/null +++ b/osdf/models/api/placementResponse.py @@ -0,0 +1,57 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from .common import OSDFModel +from schematics.types import StringType +from schematics.types.compound import ModelType, ListType + + +# TODO: update osdf.models + +class LicenseSolution(OSDFModel): + serviceResourceId = StringType(required=True) + resourceModuleName = StringType(required=True) + entitlementPoolList = ListType(StringType(required=True)) + licenseKeyGroupList = ListType(StringType(required=True)) + + +class AssignmentInfo(OSDFModel): + variableName = StringType(required=True) + variableValue = StringType(required=True) + + +class PlacementSolution(OSDFModel): + serviceResourceId = StringType(required=True) + resourceModuleName = StringType(required=True) + inventoryType = StringType(required=True) + serviceInstanceId = StringType() + cloudRegionId = StringType() + assignmentInfo = ListType(ModelType(AssignmentInfo)) + + +class SolutionInfo(OSDFModel): + placement = ListType(ModelType(PlacementSolution), min_size=1) + license = ListType(ModelType(LicenseSolution), min_size=1) + + +class PlacementResponse(OSDFModel): + transactionId = StringType(required=True) + requestId = StringType(required=True) + requestState = StringType(required=True) + statusMessage = StringType(required=True) + solutionInfo = ModelType(SolutionInfo) diff --git a/osdf/models/policy/cmso/xacml/placementPolicies.xcore b/osdf/models/policy/cmso/xacml/placementPolicies.xcore new file mode 100644 index 0000000..3348cb0 --- /dev/null +++ b/osdf/models/policy/cmso/xacml/placementPolicies.xcore @@ -0,0 +1,718 @@ +/* + * This XCORE file contains models for the placement optimization policies in SNIRO. + * @author Ankitkumar Patel + * @version 0.0.1 + * @since 2017-04-13 + */ + +package com.att.ecomp.sniro.policies.placement + +import java.util.UUID + +annotation "http://ecomp.att.com" as ecomp +annotation "http://ecomp.att.com/policy" as policy + +type UUID wraps UUID + +/* + * Comparison operators + */ +enum ComparisonOperator{ + less=1, grater=2, lessEqual=3, greaterEqual=4, equal=5, notEqual=6 +} + +enum TenantType{ + GW_TENANT_ID=1, PORTAL_TENANT_ID=2 +} + +/* + * Computational operators + */ +enum ComputationalOperator{ + sum=1, product=2 +} + + +/* + * Qualifier types + */ +enum Qualifier{ + same=1, different=2 +} + +/* + * Zone category + */ +enum ZoneCategory{ + disaster=1, region=2, complex=3, time=4, maintenance=5 +} + +/* + * Geographical region types + */ +enum GeoRegion{ + US=1, EMEA=2, AP=3, CALA=4, CA=5, INTERNATIONAL=6 +} + +/* + * Parameters + */ +enum Parameter{ + distance=0, latency=1, aic_version=2 + //thoughput=1, geoDistance=1, airDistance=2, latency=3, bandwidth=4 +} + + +/* + * The type of inventory defined in AIC + */ +enum InventoryType{ + service=1, cloud=2 +} + +/* + * The type of AT&T network + */ + enum NetworkType{ + ip=1 + } + +/* + * Objective functions. + */ + +enum ObjectiveFunction{ + minimize=1, maximize=2 +} + +/* + * This is a model of a condition. + * @param parameter This is the parameter of interest. + * @param operator This is a comparison operator. + * @param value This is a value of a parameter + */ +class ConditionalInfo{ + @ecomp(^type = "configuration") + Parameter parameter + @ecomp(^type = "configuration") + ComparisonOperator operator + @ecomp(^type = "configuration") + String value +} + +enum LocationInfo{ + customer_loc=1, none=2, customer_pref_loc=3 +} + + +/* + * Model for distance to location property. + * @param distanceCondition This is a distance condition. + * @param locationInfo This is a location with respect to which distance condition is applied. + */ +class DistanceToLocationProperty{ + //distanceCondition.parameter must be distance. + @ecomp(^type = "configuration") + contains ConditionalInfo distanceCondition + @ecomp(^type = "configuration") + LocationInfo locationInfo +} + +enum DistanceToLocationPolicyType{ + distance_to_location=1 +} + +/* + * Model for distance to location policy. + * @param identity This is an identity created by a user. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resource instances over which this policy is applied. + * @param distanceToLocationProperty This is the distance properties of the policy. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class DistanceToLocationPolicy extends SniroPolicyMetaInfo{ + @ecomp(^type = "configuration") + @policy (matching = "true") + DistanceToLocationPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains DistanceToLocationProperty distanceToLocationProperty +} + +/* + * The property associated with the NetworkBetweenDemandsPolicy. + * @param latencyCondition The latency property associated with the policy. + * @param locationInfo The customer location information. + */ +class NetworkToLocationProperty{ + //latencyCondition.parameter must be latency. + @ecomp(^type = "configuration") + contains ConditionalInfo latencyCondition + @ecomp(^type = "configuration") + LocationInfo locationInfo +} + +enum NetworkToLocationPolicyType{ + network_to_location=1 +} + +/* + * Network between demands policy. + * @param type The type of a policy. + * @param resourceInstanceType The resources associated with a policy. + * @param networkToLocationProperty The property associated with the policy. + */ + +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class NetworkToLocationPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + NetworkToLocationPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains NetworkToLocationProperty networkToLocationProperty +} + +/* + * The property associated with the NetworkBetweenDemandsPolicy. + * @param latencyCondition The latency property associated with the policy. + */ +class NetworkBetweenDemandsProperty{ + //latencyCondition.parameter must be latency. + @ecomp(^type = "configuration") + contains ConditionalInfo latencyCondition +} + +enum NetworkBetweenDemandsPolicyType{ + network_between_demands=1 +} + +/* + * Network between demands policy. + * @param type The type of a policy. + * @param resourceInstanceType The resources associated with a policy. + * @param networkBetweenDemandsProperty The property associated with the policy. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class NetworkBetweenDemandsPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + NetworkBetweenDemandsPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains NetworkBetweenDemandsProperty networkBetweenDemandsProperty +} + + +/* + * Network roles supported a VNF placement + * @param all A list of network roles. + */ +class NetworkRoles{ + @ecomp(^type = "configuration") + String[] all +} + +/* + * Complex names supported by a VNF placement + * @param any A list of complex names. + */ +class Complex{ + @ecomp(^type = "configuration") + String[] any +} +/* + * This are the AIC properties. + * @param aicVersion This is the version of AIC. + * @param aicType This is the type of AIC. + * @param dataPlace This is the type of data plane. + * @param hypervisor This is the type of hypervisor. + * @param networkRoles This is a list of connected networks. + * @param exclusivityGroups This is an exclusivity group Id + * @param state State in which a VNF should be located. + * @param country Country in which a VNF should be located. + * @param getRegion Geographical region in which a VNF should be located. + */ +class AicAttributeProperty{ + //aicVersionCondition.parameter must be aicVersion. + @ecomp(^type = "configuration") + String aicVersion + @ecomp(^type = "configuration") + String aicType + @ecomp(^type = "configuration") + String dataPlane + @ecomp(^type = "configuration") + String hypervisor + @ecomp(^type = "configuration") + contains NetworkRoles networkRoles + @ecomp(^type = "configuration") + contains Complex complex + @ecomp(^type = "configuration") + String exclusivityGroups + @ecomp(^type = "configuration") + String state + @ecomp(^type = "configuration") + String country + @ecomp(^type = "configuration") + GeoRegion geoRegion + @ecomp(^type = "configuration") + String replicationRole + +} + +enum AicAttributePolicyType{ + attribute=1 +} + +/* + * Model for the AIC attribute policy. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources over which the policy is applied. + * @param aicAttributeProperty This is the properties associated with the policy. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class AicAttributePolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + AicAttributePolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains AicAttributeProperty aicAttributeProperty +} + +/* + * The property associated with the capacity policy. + * @param tenant The tenant whose capacity needs to be checked. + * @param description The location of a heat template. + */ +class CapacityProperty{ + @ecomp(^type = "configuration") + TenantType tenant + @ecomp(^type = "configuration") + String description +} + +enum CapacityPolicyType{ + cloud_capacity=1 +} + +/* + * Capacity policy + * @param type The type of a policy. + * @param resourceInstanceType The type of resources associated with a policy. + * @param capacityProperty The property associated with a policy. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class CapacityPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + CapacityPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains CapacityProperty capacityProperty +} + +enum InventoryGroupPolicyType{ + inventory_group = 1 +} + +/* + * Model for the inventory group policy. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources that must be grouped/paired + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class InventoryGroupPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + InventoryGroupPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType +} + +/* + * This is the property associated with this policy. + * @param controller ECOMP controller. + * @param request This should be key-value pairs to be sent in a request. + */ +class ResourceInstanceProperty{ + @ecomp(^type = "configuration") + String controller + @ecomp(^type = "configuration") + String request +} + +enum ResourceInstancePolicyType{ + instance_fit=1 +} +/* + * Model for the resource instance policy. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources. + * @param resourceInstanceProperty This is a property associated with each resource in the list. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class ResourceInstancePolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + ResourceInstancePolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains ResourceInstanceProperty resourceInstanceProperty + +} + +/* + * This is the property associated with this policy. + * @param controller ECOMP controller + * @param request This should be key-value pairs to be sent in a request. + */ +class ResourceRegionProperty{ + @ecomp(^type = "configuration") + String controller + @ecomp(^type = "configuration") + String request +} + +enum ResourceRegionPolicyType{ + region_fit=1 +} + +/* + * Model for the resource region policy + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources. + * @param resourceRegionProperty This is a property associated with this policy. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class ResourceRegionPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + ResourceRegionPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains ResourceRegionProperty resourceRegionProperty +} + +/* + * This is the property associated with zone policy. + * @param qualifier This is the qualifier. + * @param category This is the category of a zone. + */ +class ZoneProperty{ + @ecomp(^type = "configuration") + Qualifier qualifier + @ecomp(^type = "configuration") + ZoneCategory category +} + +enum ZonePolicyType{ + zone=1 +} + +/* + * Model of the zone policy. + * @param type This is the type of a policy. + * @param resourceInstanceType This is a list of resources. + * @param zoneProperty This is the property associated with the policy. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) + +class ZonePolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + ZonePolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains ZoneProperty zoneProperty +} + +/* + * The property associated with a VNF type. + * @param inventoryProvider The ECOMP entity providing inventory information. + * @param inventoryType The type of an inventory. + * @param serviceId The id of a service. + */ +class VNFPolicyProperty{ + @ecomp(^type = "configuration") + String inventoryProvider + @ecomp(^type = "configuration") + InventoryType inventoryType + @ecomp(^type = "configuration") + contains Attributes attributes +} + +/* + * The property associated with a Subscriber type. + * @param subscriberName The name of a subscriber. + * @param subscriberRole The role of a subscriber. + * @param provStatus The provisioning status of a subscriber. + */ +class SubscriberPolicyProperty{ + @ecomp(^type = "configuration") + String[] subscriberName + @ecomp(^type = "configuration") + String[] subscriberRole + @ecomp(^type = "configuration") + String[] provStatus +} + +enum VNFPolicyType{ + vnfPolicy=1 +} + +enum SubscriberPolicyType{ + subscriberPolicy=1 +} + +class Attributes{ + @ecomp(^type = "configuration") + String globalCustomerId + @ecomp(^type = "configuration") + String operationalStatus + @ecomp(^type = "configuration") + String[] orchestrationStatus + @ecomp(^type = "configuration") + String modelInvariantId + @ecomp(^type = "configuration") + String modelVersionId + @ecomp(^type = "configuration") + String equipmentRole +} + +/* + * Policy associated with a VNF. + * @param resourceInstance This parameter identifies a specific VNF. + * @param inventoryProvider This is the provider of inventory. + * @param inventoryType This is the type of inventory. + * @param serviceType The service associated with a VNF. + * @param serviceId The Id associated with a service. + * @param globalCustomerId The global id of a customer. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class VNFPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + VNFPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains VNFPolicyProperty[] property +} + +/* + * Policy associated with a Subscriber. + * @param subscriberName The name of a subscriber. + * @param subscriberRole The role of a subscriber. + * @param provStatus The provisioning status of a subscriber. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class SubscriberPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + SubscriberPolicyType ^type + @ecomp(^type = "configuration") + contains SubscriberPolicyProperty[] property +} + + +/* + * This is the property associated with this policy. + * @param providerUrl This is the url of provider to check the capacity. + * @param request This should be key-value pairs to be sent in a request. + */ +class InstanceReservationProperty{ + @ecomp(^type = "configuration") + String controller + @ecomp(^type = "configuration") + String request +} + +enum InstanceReservationPolicyType{ + instance_reservation=1 +} +/* + * Model for the resource instance policy. + * @param identity This is an identity created by a user. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources. + * @param resourceInstanceProperty This is a property associated with each resource in the list. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) +class instanceReservationPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + InstanceReservationPolicyType ^type + @ecomp(^type = "configuration") + String[] resourceInstanceType + @ecomp(^type = "configuration") + contains InstanceReservationProperty instanceReservationProperty + +} + +/* + * This is a model of an operand. + * @param parameter This is a parameter. + * @param associativity This is a list of entities with which a parameter is associated. + */ + /* +class Operand{ + @ecomp(^type = "configuration") + Parameter parameter + @ecomp(^type = "configuration") + Entity associativity +} +*/ + +/* + * This is the optimization function. + * @param identity This is an identity of a function. + * @param operation This is a computational operator. + * @param leftOperand This is a left operand of a function. + * @param rightOperand This is a right operand of a function. + */ +/* +class OptimizationFunction{ + @ecomp(^type = "configuration") + ExpressionIdentity identity + @ecomp(^type = "configuration") + ComputationalOperator operation + @ecomp(^type = "configuration") + contains Operand[] operands +} +*/ + +/* + * Properties associated with a sub-expression. + * @param weight The weight of an expression. + * @param parameter The parameter involved in an expression. + * @param entity The entities involved in an expression. + * @param operator The operator of an expression. + * @param customerLocationInfo The location of a customer. + */ +class AttributeProperty{ + @ecomp(^type = "configuration") + double weight + @ecomp(^type = "configuration") + Parameter parameter + @ecomp(^type = "configuration") + String[] resource + @ecomp(^type = "configuration") + ComputationalOperator operator + @ecomp(^type = "configuration") + LocationInfo customerLocationInfo +} + +enum PlacementOptimizationPolicyType{ + placementOptimization=1 +} + +/* + * @param operator An operator in an expression. + * @param parameterAttributes Represents sub-expression + */ +class ObjectiveParameter{ + @ecomp(^type = "configuration") + ComputationalOperator operator + @ecomp(^type = "configuration") + contains AttributeProperty[] parameterAttributes +} + +/* + * Model of the placement optimization policy. + * @param type This is the type of a policy. + * @param objective This is an objective function. + * @param objectiveParameter The parameter/expression to be optimized. + */ +@policy ( + policyTemplate = "SNIRO-PLACEMENT" +) + +class PlacementOptimizationPolicy extends SniroPolicyMetaInfo{ + @policy (matching = "true") + @ecomp(^type = "configuration") + PlacementOptimizationPolicyType ^type + @ecomp(^type = "configuration") + ObjectiveFunction objective + @ecomp(^type = "configuration") + contains ObjectiveParameter objectiveParameter +} + + +/* + * Meta information required for SNIRO policies. + * @param identity This is a user-defined identity. + * @param policyScope The scope of a policy + */ + +@policy ( + policyTemplate = "SNIRO" +) +class SniroPolicyMetaInfo{ + @ecomp(^type = "configuration") + String identity + @ecomp(^type = "configuration") + @policy (matching = "true") + contains Scope policyScope +} + +/* + * Scopes in which a policy is applicable. + * @param serviceType The type of a service. + * @param networkType The type of a network + * @param geoRigion The geographical region. + * @param resourceInstanceType The resources associated with a policy/ + * @param subscriberRole + */ +class Scope{ + @ecomp(^type = "configuration") + @policy (matching = "true") + String[] serviceType + @ecomp(^type = "configuration") + @policy (matching = "true") + String[] networkType + @ecomp(^type = "configuration") + @policy (matching = "true") + String[] geoRegion + @ecomp(^type = "configuration") + @policy (matching = "true") + String[] resourceInstanceType + @ecomp(^type = "configuration") + @policy (matching = "true") + String[] modelInvariantId + @ecomp(^type = "configuration") + @policy (matching = "true") + String[] subscriberRole +} \ No newline at end of file diff --git a/osdf/models/policy/placement/xacml/placementPolicies.xcore b/osdf/models/policy/placement/xacml/placementPolicies.xcore new file mode 100644 index 0000000..866488e --- /dev/null +++ b/osdf/models/policy/placement/xacml/placementPolicies.xcore @@ -0,0 +1,728 @@ +/* + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. +*/ + +package org.onap.oof.osdf.policies.placement + +import java.util.UUID + +annotation "http://org.onap" as onap +annotation "http://org.onap.policy" as policy + +type UUID wraps UUID + +/* + * Comparison operators + */ +enum ComparisonOperator{ + less=1, grater=2, lessEqual=3, greaterEqual=4, equal=5, notEqual=6 +} + +enum TenantType{ + GW_TENANT_ID=1, PORTAL_TENANT_ID=2 +} + +/* + * Computational operators + */ +enum ComputationalOperator{ + sum=1, product=2 +} + + +/* + * Qualifier types + */ +enum Qualifier{ + same=1, different=2 +} + +/* + * Zone category + */ +enum ZoneCategory{ + disaster=1, region=2, complex=3, time=4, maintenance=5 +} + +/* + * Geographical region types + */ +enum GeoRegion{ + US=1, EMEA=2, AP=3, CALA=4, CA=5, INTERNATIONAL=6 +} + +/* + * Parameters + */ +enum Parameter{ + distance=0, latency=1, cloud_version=2 + //thoughput=1, geoDistance=1, airDistance=2, latency=3, bandwidth=4 +} + + +/* + * The type of inventory defined in cloud + */ +enum InventoryType{ + service=1, cloud=2 +} + +/* + * The type of network + */ + enum NetworkType{ + ip=1 + } + +/* + * Objective functions. + */ + +enum ObjectiveFunction{ + minimize=1, maximize=2 +} + +/* + * This is a model of a condition. + * @param parameter This is the parameter of interest. + * @param operator This is a comparison operator. + * @param value This is a value of a parameter + */ +class ConditionalInfo{ + @onap(^type = "configuration") + Parameter parameter + @onap(^type = "configuration") + ComparisonOperator operator + @onap(^type = "configuration") + String value +} + +enum LocationInfo{ + customer_loc=1, none=2, customer_pref_loc=3 +} + + +/* + * Model for distance to location property. + * @param distanceCondition This is a distance condition. + * @param locationInfo This is a location with respect to which distance condition is applied. + */ +class DistanceToLocationProperty{ + //distanceCondition.parameter must be distance. + @onap(^type = "configuration") + contains ConditionalInfo distanceCondition + @onap(^type = "configuration") + LocationInfo locationInfo +} + +enum DistanceToLocationPolicyType{ + distance_to_location=1 +} + +/* + * Model for distance to location policy. + * @param identity This is an identity created by a user. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resource instances over which this policy is applied. + * @param distanceToLocationProperty This is the distance properties of the policy. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class DistanceToLocationPolicy extends OOFPolicyMetaInfo{ + @onap(^type = "configuration") + @policy (matching = "true") + DistanceToLocationPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains DistanceToLocationProperty distanceToLocationProperty +} + +/* + * The property associated with the NetworkBetweenDemandsPolicy. + * @param latencyCondition The latency property associated with the policy. + * @param locationInfo The customer location information. + */ +class NetworkToLocationProperty{ + //latencyCondition.parameter must be latency. + @onap(^type = "configuration") + contains ConditionalInfo latencyCondition + @onap(^type = "configuration") + LocationInfo locationInfo +} + +enum NetworkToLocationPolicyType{ + network_to_location=1 +} + +/* + * Network between demands policy. + * @param type The type of a policy. + * @param resourceInstanceType The resources associated with a policy. + * @param networkToLocationProperty The property associated with the policy. + */ + +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class NetworkToLocationPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + NetworkToLocationPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains NetworkToLocationProperty networkToLocationProperty +} + +/* + * The property associated with the NetworkBetweenDemandsPolicy. + * @param latencyCondition The latency property associated with the policy. + */ +class NetworkBetweenDemandsProperty{ + //latencyCondition.parameter must be latency. + @onap(^type = "configuration") + contains ConditionalInfo latencyCondition +} + +enum NetworkBetweenDemandsPolicyType{ + network_between_demands=1 +} + +/* + * Network between demands policy. + * @param type The type of a policy. + * @param resourceInstanceType The resources associated with a policy. + * @param networkBetweenDemandsProperty The property associated with the policy. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class NetworkBetweenDemandsPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + NetworkBetweenDemandsPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains NetworkBetweenDemandsProperty networkBetweenDemandsProperty +} + + +/* + * Network roles supported a VNF placement + * @param all A list of network roles. + */ +class NetworkRoles{ + @onap(^type = "configuration") + String[] all +} + +/* + * Complex names supported by a VNF placement + * @param any A list of complex names. + */ +class Complex{ + @onap(^type = "configuration") + String[] any +} +/* + * This are the cloud properties. + * @param cloudVersion This is the version of cloud. + * @param cloudType This is the type of cloud. + * @param dataPlace This is the type of data plane. + * @param hypervisor This is the type of hypervisor. + * @param networkRoles This is a list of connected networks. + * @param exclusivityGroups This is an exclusivity group Id + * @param state State in which a VNF should be located. + * @param country Country in which a VNF should be located. + * @param getRegion Geographical region in which a VNF should be located. + */ +class cloudAttributeProperty{ + //cloudVersionCondition.parameter must be cloudVersion. + @onap(^type = "configuration") + String cloudVersion + @onap(^type = "configuration") + String cloudType + @onap(^type = "configuration") + String dataPlane + @onap(^type = "configuration") + String hypervisor + @onap(^type = "configuration") + contains NetworkRoles networkRoles + @onap(^type = "configuration") + contains Complex complex + @onap(^type = "configuration") + String exclusivityGroups + @onap(^type = "configuration") + String state + @onap(^type = "configuration") + String country + @onap(^type = "configuration") + GeoRegion geoRegion + @onap(^type = "configuration") + String replicationRole + +} + +enum cloudAttributePolicyType{ + attribute=1 +} + +/* + * Model for the cloud attribute policy. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources over which the policy is applied. + * @param cloudAttributeProperty This is the properties associated with the policy. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class cloudAttributePolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + cloudAttributePolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains cloudAttributeProperty cloudAttributeProperty +} + +/* + * The property associated with the capacity policy. + * @param tenant The tenant whose capacity needs to be checked. + * @param description The location of a heat template. + */ +class CapacityProperty{ + @onap(^type = "configuration") + TenantType tenant + @onap(^type = "configuration") + String description +} + +enum CapacityPolicyType{ + cloud_capacity=1 +} + +/* + * Capacity policy + * @param type The type of a policy. + * @param resourceInstanceType The type of resources associated with a policy. + * @param capacityProperty The property associated with a policy. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class CapacityPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + CapacityPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains CapacityProperty capacityProperty +} + +enum InventoryGroupPolicyType{ + inventory_group = 1 +} + +/* + * Model for the inventory group policy. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources that must be grouped/paired + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class InventoryGroupPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + InventoryGroupPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType +} + +/* + * This is the property associated with this policy. + * @param controller onap controller. + * @param request This should be key-value pairs to be sent in a request. + */ +class ResourceInstanceProperty{ + @onap(^type = "configuration") + String controller + @onap(^type = "configuration") + String request +} + +enum ResourceInstancePolicyType{ + instance_fit=1 +} +/* + * Model for the resource instance policy. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources. + * @param resourceInstanceProperty This is a property associated with each resource in the list. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class ResourceInstancePolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + ResourceInstancePolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains ResourceInstanceProperty resourceInstanceProperty + +} + +/* + * This is the property associated with this policy. + * @param controller onap controller + * @param request This should be key-value pairs to be sent in a request. + */ +class ResourceRegionProperty{ + @onap(^type = "configuration") + String controller + @onap(^type = "configuration") + String request +} + +enum ResourceRegionPolicyType{ + region_fit=1 +} + +/* + * Model for the resource region policy + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources. + * @param resourceRegionProperty This is a property associated with this policy. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class ResourceRegionPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + ResourceRegionPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains ResourceRegionProperty resourceRegionProperty +} + +/* + * This is the property associated with zone policy. + * @param qualifier This is the qualifier. + * @param category This is the category of a zone. + */ +class ZoneProperty{ + @onap(^type = "configuration") + Qualifier qualifier + @onap(^type = "configuration") + ZoneCategory category +} + +enum ZonePolicyType{ + zone=1 +} + +/* + * Model of the zone policy. + * @param type This is the type of a policy. + * @param resourceInstanceType This is a list of resources. + * @param zoneProperty This is the property associated with the policy. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) + +class ZonePolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + ZonePolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains ZoneProperty zoneProperty +} + +/* + * The property associated with a VNF type. + * @param inventoryProvider The onap entity providing inventory information. + * @param inventoryType The type of an inventory. + * @param serviceId The id of a service. + */ +class VNFPolicyProperty{ + @onap(^type = "configuration") + String inventoryProvider + @onap(^type = "configuration") + InventoryType inventoryType + @onap(^type = "configuration") + contains Attributes attributes +} + +/* + * The property associated with a Subscriber type. + * @param subscriberName The name of a subscriber. + * @param subscriberRole The role of a subscriber. + * @param provStatus The provisioning status of a subscriber. + */ +class SubscriberPolicyProperty{ + @onap(^type = "configuration") + String[] subscriberName + @onap(^type = "configuration") + String[] subscriberRole + @onap(^type = "configuration") + String[] provStatus +} + +enum VNFPolicyType{ + vnfPolicy=1 +} + +enum SubscriberPolicyType{ + subscriberPolicy=1 +} + +class Attributes{ + @onap(^type = "configuration") + String globalCustomerId + @onap(^type = "configuration") + String operationalStatus + @onap(^type = "configuration") + String[] orchestrationStatus + @onap(^type = "configuration") + String modelInvariantId + @onap(^type = "configuration") + String modelVersionId + @onap(^type = "configuration") + String equipmentRole +} + +/* + * Policy associated with a VNF. + * @param resourceInstance This parameter identifies a specific VNF. + * @param inventoryProvider This is the provider of inventory. + * @param inventoryType This is the type of inventory. + * @param serviceType The service associated with a VNF. + * @param serviceId The Id associated with a service. + * @param globalCustomerId The global id of a customer. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class VNFPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + VNFPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains VNFPolicyProperty[] property +} + +/* + * Policy associated with a Subscriber. + * @param subscriberName The name of a subscriber. + * @param subscriberRole The role of a subscriber. + * @param provStatus The provisioning status of a subscriber. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class SubscriberPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + SubscriberPolicyType ^type + @onap(^type = "configuration") + contains SubscriberPolicyProperty[] property +} + + +/* + * This is the property associated with this policy. + * @param providerUrl This is the url of provider to check the capacity. + * @param request This should be key-value pairs to be sent in a request. + */ +class InstanceReservationProperty{ + @onap(^type = "configuration") + String controller + @onap(^type = "configuration") + String request +} + +enum InstanceReservationPolicyType{ + instance_reservation=1 +} +/* + * Model for the resource instance policy. + * @param identity This is an identity created by a user. + * @param type This is the type of a policy. + * @param resourceInstance This is a list of resources. + * @param resourceInstanceProperty This is a property associated with each resource in the list. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class InstanceReservationPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + InstanceReservationPolicyType ^type + @onap(^type = "configuration") + String[] resourceInstanceType + @onap(^type = "configuration") + contains InstanceReservationProperty instanceReservationProperty + +} + +/* + * This is a model of an operand. + * @param parameter This is a parameter. + * @param associativity This is a list of entities with which a parameter is associated. + */ + /* +class Operand{ + @onap(^type = "configuration") + Parameter parameter + @onap(^type = "configuration") + Entity associativity +} +*/ + +/* + * This is the optimization function. + * @param identity This is an identity of a function. + * @param operation This is a computational operator. + * @param leftOperand This is a left operand of a function. + * @param rightOperand This is a right operand of a function. + */ +/* +class OptimizationFunction{ + @onap(^type = "configuration") + ExpressionIdentity identity + @onap(^type = "configuration") + ComputationalOperator operation + @onap(^type = "configuration") + contains Operand[] operands +} +*/ + +/* + * Properties associated with a sub-expression. + * @param weight The weight of an expression. + * @param parameter The parameter involved in an expression. + * @param entity The entities involved in an expression. + * @param operator The operator of an expression. + * @param customerLocationInfo The location of a customer. + */ +class AttributeProperty{ + @onap(^type = "configuration") + double weight + @onap(^type = "configuration") + Parameter parameter + @onap(^type = "configuration") + String[] resource + @onap(^type = "configuration") + ComputationalOperator operator + @onap(^type = "configuration") + LocationInfo customerLocationInfo +} + +enum PlacementOptimizationPolicyType{ + placementOptimization=1 +} + +/* + * @param operator An operator in an expression. + * @param parameterAttributes Represents sub-expression + */ +class ObjectiveParameter{ + @onap(^type = "configuration") + ComputationalOperator operator + @onap(^type = "configuration") + contains AttributeProperty[] parameterAttributes +} + +/* + * Model of the placement optimization policy. + * @param type This is the type of a policy. + * @param objective This is an objective function. + * @param objectiveParameter The parameter/expression to be optimized. + */ +@policy ( + policyTemplate = "OOF-PLACEMENT" +) + +class PlacementOptimizationPolicy extends OOFPolicyMetaInfo{ + @policy (matching = "true") + @onap(^type = "configuration") + PlacementOptimizationPolicyType ^type + @onap(^type = "configuration") + ObjectiveFunction objective + @onap(^type = "configuration") + contains ObjectiveParameter objectiveParameter +} + + +/* + * Meta information required for oof policies. + * @param identity This is a user-defined identity. + * @param policyScope The scope of a policy + */ + +@policy ( + policyTemplate = "OOF-PLACEMENT" +) +class OOFPolicyMetaInfo{ + @onap(^type = "configuration") + String identity + @onap(^type = "configuration") + @policy (matching = "true") + contains Scope policyScope +} + +/* + * Scopes in which a policy is applicable. + * @param serviceType The type of a service. + * @param networkType The type of a network + * @param geoRigion The geographical region. + * @param resourceInstanceType The resources associated with a policy/ + * @param subscriberRole + */ +class Scope{ + @onap(^type = "configuration") + @policy (matching = "true") + String[] serviceType + @onap(^type = "configuration") + @policy (matching = "true") + String[] networkType + @onap(^type = "configuration") + @policy (matching = "true") + String[] geoRegion + @onap(^type = "configuration") + @policy (matching = "true") + String[] resourceInstanceType + @onap(^type = "configuration") + @policy (matching = "true") + String[] modelInvariantId + @onap(^type = "configuration") + @policy (matching = "true") + String[] subscriberRole +} diff --git a/osdf/operation/__init__.py b/osdf/operation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/operation/error_handling.py b/osdf/operation/error_handling.py new file mode 100644 index 0000000..dfb0848 --- /dev/null +++ b/osdf/operation/error_handling.py @@ -0,0 +1,93 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import json + +from schematics.exceptions import DataError + +from requests import RequestException +from requests import ConnectionError, HTTPError, Timeout +from osdf.operation.exceptions import BusinessException + +import osdf + +ERROR_TEMPLATE = osdf.ERROR_TEMPLATE + +MESSAGE_BASE = "A solution couldn't be determined because an external application" +HTTP_ERROR_MESSAGE = MESSAGE_BASE + " returned a HTTP error" +TIMEOUT_ERROR_MESSAGE = MESSAGE_BASE + " could not respond in time, please check the external application" +CONNECTION_ERROR_MESSAGE = MESSAGE_BASE + " could not be reached" + +internal_error_body = { + "serviceException": { + "text": "Unhandled internal exception, request could not be processed" + } +} + +internal_error_message = json.dumps(internal_error_body) + + +def build_json_error_body(error): + if isinstance(error,RequestException): + return request_exception_to_json_body(error) + elif isinstance(error, DataError): + return data_error_to_json_body(error) + elif type(error) is BusinessException: # return the error message, because it is well formatted + return ERROR_TEMPLATE.render(description=str(error)) + else: + return internal_error_message + + +def data_error_to_json_body(error): + description = str(error).replace('"', '\\"') + error_message = ERROR_TEMPLATE.render(description=description) + return error_message + + +def request_exception_to_json_body(error): + friendly_message = "A request exception has occurred when contacting an external system" + if type(error) is HTTPError: + friendly_message = HTTP_ERROR_MESSAGE + if type(error) is ConnectionError: + friendly_message = CONNECTION_ERROR_MESSAGE + if type(error) is Timeout: + friendly_message = TIMEOUT_ERROR_MESSAGE + + eie_body = { + "serviceException": { + "text": friendly_message, + "errorType": "InterfaceError" + }, + "externalApplicationDetails": { + "httpMethod": error.request.method, + "url": error.request.url + } + } + + response = error.response + + if response is not None: + eie_body['externalApplicationDetails']['httpStatusCode'] = response.status_code + content_type = response.headers.get('content-type') + if content_type is not None: + if 'application/json' in content_type: + eie_body['externalApplicationDetails']['responseMessage'] = response.json() + elif 'text/html' in content_type: + eie_body['externalApplicationDetails']['responseMessage'] = response.text + error_message = json.dumps(eie_body) + return error_message diff --git a/osdf/operation/exceptions.py b/osdf/operation/exceptions.py new file mode 100644 index 0000000..5277b01 --- /dev/null +++ b/osdf/operation/exceptions.py @@ -0,0 +1,40 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +class BusinessException(Exception): + pass + + +class MessageBusConfigurationException(Exception): + pass + + +class CMDataError(Exception): + pass + + +class CMSOExecutionError(Exception): + pass + + +class CMSOCallBackError(Exception): + pass + + +class CMSOInvalidRequestException(Exception): + pass diff --git a/osdf/operation/responses.py b/osdf/operation/responses.py new file mode 100644 index 0000000..22a94f7 --- /dev/null +++ b/osdf/operation/responses.py @@ -0,0 +1,39 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from flask import Response + +from osdf import ACCEPTED_MESSAGE_TEMPLATE + + +def osdf_response_for_request_accept(req_id="", text="", response_code=202, as_http=True): + """Helper method to create a response object for request acceptance, so that the object can be sent to a client + :param req_id: request ID provided by the caller + :param text: extra text description about accepting the request (e.g. "Request accepted") + :param response_code: the HTTP status code to send -- default is 202 (accepted) + :param as_http: whether to send response as HTTP response object or as a string + :return: if as_http is True, return a HTTP Response object. Otherwise, return json-encoded-message + """ + response_message = ACCEPTED_MESSAGE_TEMPLATE.render(description=text, request_id=req_id) + if not as_http: + return response_message + + response = Response(response_message, content_type='application/json; charset=utf-8') + response.headers.add('content-length', len(response_message)) + response.status_code = response_code + return response diff --git a/osdf/optimizers/__init__.py b/osdf/optimizers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/optimizers/licenseopt/__init__.py b/osdf/optimizers/licenseopt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/optimizers/licenseopt/simple_license_allocation.py b/osdf/optimizers/licenseopt/simple_license_allocation.py new file mode 100644 index 0000000..1b5b670 --- /dev/null +++ b/osdf/optimizers/licenseopt/simple_license_allocation.py @@ -0,0 +1,56 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import json + +from requests import RequestException +from osdf.datasources.sdc import sdc, constraint_handler +from osdf.logging.osdf_logging import audit_log, metrics_log, MH +from osdf.config.base import osdf_config +from osdf.utils import data_mapping + + +def license_optim(request_json): + """ + Fetch license artifacts associated with the service model and search licensekey-group-UUID and entitlement-pool-uuid + associated with the given att part number and nominal throughput in a request + :param request_json: Request in a JSON format + :return: A tuple of licensekey-group-uuid-list and entitlement-group-uuid-list + """ + req_id = request_json["requestInfo"]["requestId"] + config = osdf_config.deployment + + model_name = request_json['placementInfo']['serviceModelInfo']['modelName'] + service_name = data_mapping.get_service_type(model_name) + + license_info = [] + + order_info = json.loads(request_json["placementInfo"]["orderInfo"]) + if service_name == 'VPE': + data_mapping.normalize_user_params(order_info) + for licenseDemand in request_json['placementInfo']['demandInfo']['licenseDemand']: + metrics_log.info(MH.requesting("sdc", req_id)) + license_artifacts = sdc.request(licenseDemand['resourceModelInfo']['modelVersionId'],request_json["requestInfo"]["requestId"], config) + entitlement_pool_uuids, license_key_group_uuids = constraint_handler.choose_license(license_artifacts,order_info, service_name) + license_info.append( + {'serviceResourceId': licenseDemand['serviceResourceId'], + 'resourceModuleName': licenseDemand['resourceModuleName'], + 'entitlementPoolList': entitlement_pool_uuids, + 'licenseKeyGroupList': license_key_group_uuids + }) + return license_info diff --git a/osdf/optimizers/placementopt/__init__.py b/osdf/optimizers/placementopt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/optimizers/placementopt/conductor/__init__.py b/osdf/optimizers/placementopt/conductor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/optimizers/placementopt/conductor/api_builder.py b/osdf/optimizers/placementopt/conductor/api_builder.py new file mode 100644 index 0000000..0a874f7 --- /dev/null +++ b/osdf/optimizers/placementopt/conductor/api_builder.py @@ -0,0 +1,123 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import copy +import json +# from osdf.utils import data_mapping +from jinja2 import Template +from osdf.utils.programming_utils import list_flatten, dot_notation +import osdf.optimizers.placementopt.conductor.translation as tr +from osdf.adapters.policy.utils import group_policies + + +def conductor_api_builder(request_json, flat_policies: list, local_config, prov_status, + template="templates/conductor_interface.json"): + """Build a SNIRO southbound API call for Conductor/Placement optimization + :param request_json: parameter data received from a client + :param flat_policies: policy data received from the policy platform (flat policies) + :param template: template to generate southbound API call to conductor + :param local_config: local configuration file with pointers for the service specific information + :param prov_status: provStatus retrieved from Subscriber policy + :return: json to be sent to Conductor/placement optimization + """ + templ = Template(open(template).read()) + gp = group_policies(flat_policies) + demand_vnf_name_list = [] + + for placementDemand in request_json['placementInfo']['demandInfo']['placementDemand']: + demand_vnf_name_list.append(placementDemand['resourceModuleName']) + + demand_list = tr.gen_demands(request_json['placementInfo']['demandInfo'], gp['vnfPolicy']) + attribute_policy_list = tr.gen_attribute_policy(demand_vnf_name_list, gp['attribute']) + distance_to_location_policy_list = tr.gen_distance_to_location_policy( + demand_vnf_name_list, gp['distance_to_location']) + inventory_policy_list = tr.gen_inventory_group_policy(demand_vnf_name_list, gp['inventory_group']) + resource_instance_policy_list = tr.gen_resource_instance_policy( + demand_vnf_name_list, gp['instance_fit']) + resource_region_policy_list = tr.gen_resource_region_policy(demand_vnf_name_list, gp['region_fit']) + zone_policy_list = tr.gen_zone_policy(demand_vnf_name_list, gp['zone']) + optimization_policy_list = tr.gen_optimization_policy(demand_vnf_name_list, gp['placementOptimization']) + reservation_policy_list = tr.gen_reservation_policy(demand_vnf_name_list, gp['instance_reservation']) + conductor_policies = [attribute_policy_list, distance_to_location_policy_list, inventory_policy_list, + resource_instance_policy_list, resource_region_policy_list, zone_policy_list] + filtered_policies = [x for x in conductor_policies if len(x) > 0] + policy_groups = list_flatten(filtered_policies) + reservation_policies = [x for x in reservation_policy_list if len(x) > 0] + reservation_groups = list_flatten(reservation_policies) + req_info = request_json['requestInfo'] + model_name = request_json['placementInfo']['serviceModelInfo']['modelName'] + service_type = model_name + # service_type = data_mapping.get_service_type(model_name) + service_info = local_config.get('service_info', {}).get(service_type, {}) + order_info = {} + if 'orderInfo' in request_json["placementInfo"]: + order_info = json.loads(request_json["placementInfo"]["orderInfo"]) + request_type = req_info.get('requestType', None) + subs_com_site_id = "" + if 'subscriberInfo' in request_json['placementInfo']: + subs_com_site_id = request_json['placementInfo']['subscriberInfo'].get('subscriberCommonSiteId', "") + if service_type == 'vCPE': + # data_mapping.normalize_user_params(order_info) + rendered_req = templ.render( + requestType=request_type, + chosenComplex=subs_com_site_id, + demand_list=demand_list, + policy_groups=policy_groups, + optimization_policies=optimization_policy_list, + name=req_info['requestId'], + timeout=req_info['timeout'], + limit=req_info['numSolutions'], + serviceType=service_type, + serviceInstance=request_json['placementInfo']['serviceInstanceId'], + provStatus = prov_status, + chosenRegion=order_info.get('requestParameters',{}).get('lcpCloudRegionId'), + json=json) + elif service_type == 'UNKNOWN': + rendered_req = templ.render( + requestType=request_type, + chosenComplex=subs_com_site_id, + demand_list=demand_list, + policy_groups=policy_groups, + reservation_groups=reservation_groups, + optimization_policies=optimization_policy_list, + name=req_info['requestId'], + timeout=req_info['timeout'], + limit=req_info['numSolutions'], + serviceType=service_type, + serviceInstance=request_json['placementInfo']['serviceInstanceId'], + provStatus = prov_status, + # process order data + bandwidth=dot_notation(order_info, service_info['bandwidth']), + bandwidth_unit=dot_notation(order_info, service_info['bandwidth_units']), + json=json) + json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! + return json_payload + + +def retrieve_node(req_json, reference): + """ + Get the child node(s) from the dot-notation [reference] and parent [req_json]. + For placement and other requests, there are encoded JSONs inside the request or policy, + so we need to expand it and then do a search over the parent plus expanded JSON. + """ + req_json_copy = copy.deepcopy(req_json) # since we expand the JSON in place, we work on a copy + if 'orderInfo' in req_json_copy['placementInfo']: + req_json_copy['placementInfo']['orderInfo'] = json.loads(req_json_copy['placementInfo']['orderInfo']) + info = dot_notation(req_json_copy, reference) + return list_flatten(info) if isinstance(info, list) else info + diff --git a/osdf/optimizers/placementopt/conductor/conductor.py b/osdf/optimizers/placementopt/conductor/conductor.py new file mode 100644 index 0000000..bdc7f17 --- /dev/null +++ b/osdf/optimizers/placementopt/conductor/conductor.py @@ -0,0 +1,186 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +""" +This application generates conductor API calls using the information received from SO and Policy platform. +""" + +import json +import time + +from jinja2 import Template +from requests import RequestException + +from osdf.logging.osdf_logging import debug_log +from osdf.optimizers.placementopt.conductor.api_builder import conductor_api_builder +from osdf.utils.interfaces import RestClient +from osdf.operation.exceptions import BusinessException + + +def request(req_object, osdf_config, grouped_policies, prov_status): + """ + Process a placement request from a Client (build Conductor API call, make the call, return result) + :param req_object: Request parameters from the client + :param osdf_config: Configuration specific to SNIRO application (core + deployment) + :param grouped_policies: policies related to placement (fetched based on request, and grouped by policy type) + :param prov_status: provStatus retrieved from Subscriber policy + :return: response from Conductor (accounting for redirects from Conductor service + """ + config = osdf_config.deployment + local_config = osdf_config.core + uid, passwd = config['conductorUsername'], config['conductorPassword'] + conductor_url = config['conductorUrl'] + req_id = req_object['requestInfo']['requestId'] + transaction_id = req_object['requestInfo']['transactionId'] + headers = dict(transaction_id=transaction_id) + + max_retries = config.get('conductorMaxRetries', 30) + ping_wait_time = config.get('conductorPingWaitTime', 60) + + rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug, headers=headers) + conductor_req_json_str = conductor_api_builder(req_object, grouped_policies, local_config, prov_status) + conductor_req_json = json.loads(conductor_req_json_str) + + debug_log.debug("Sending first Conductor request for request_id {}".format(req_id)) + resp, raw_resp = initial_request_to_conductor(rc, conductor_url, conductor_req_json) + # Very crude way of keeping track of time. + # We are not counting initial request time, first call back, or time for HTTP request + total_time, ctr = 0, 2 + client_timeout = req_object['requestInfo']['timeout'] + configured_timeout = max_retries * ping_wait_time + max_timeout = min(client_timeout, configured_timeout) + + while True: # keep requesting conductor till we get a result or we run out of time + if resp is not None: + if resp["plans"][0].get("status") in ["error"]: + raise RequestException(response=raw_resp, request=raw_resp.request) + + if resp["plans"][0].get("status") in ["done", "not found"]: + if resp["plans"][0].get("recommendations"): + return conductor_response_processor(resp, raw_resp, req_id) + else: # "solved" but no solutions found + return conductor_no_solution_processor(resp, raw_resp, req_id) + new_url = resp['plans'][0]['links'][0][0]['href'] # TODO: check why a list of lists + + if total_time >= max_timeout: + raise BusinessException("Conductor could not provide a solution within {} seconds, this transaction is timing out".format(max_timeout)) + time.sleep(ping_wait_time) + ctr += 1 + debug_log.debug("Attempt number {} url {}; prior status={}".format(ctr, new_url, resp['plans'][0]['status'])) + total_time += ping_wait_time + + try: + raw_resp = rc.request(new_url, raw_response=True) + resp = raw_resp.json() + except RequestException as e: + debug_log.debug("Conductor attempt {} for request_id {} has failed because {}".format(ctr, req_id, str(e))) + + +def initial_request_to_conductor(rc, conductor_url, conductor_req_json): + """First steps in the request-redirect chain in making a call to Conductor + :param rc: REST client object for calling conductor + :param conductor_url: conductor's base URL to submit a placement request + :param conductor_req_json: request json object to send to Conductor + :return: URL to check for follow up (similar to redirects); we keep checking these till we get a result/error + """ + debug_log.debug("Payload to Conductor: {}".format(json.dumps(conductor_req_json))) + raw_resp = rc.request(url=conductor_url, raw_response=True, method="POST", json=conductor_req_json) + resp = raw_resp.json() + if resp["status"] != "template": + raise RequestException(response=raw_resp, request=raw_resp.request) + time.sleep(10) # 10 seconds wait time to avoid being too quick! + plan_url = resp["links"][0][0]["href"] + debug_log.debug("Attemping to read the plan from the conductor provided url {}".format(plan_url)) + raw_resp = rc.request(raw_response=True, url=plan_url) # TODO: check why a list of lists for links + resp = raw_resp.json() + + if resp["plans"][0]["status"] in ["error"]: + raise RequestException(response=raw_resp, request=raw_resp.request) + return resp, raw_resp # now the caller of this will handle further follow-ups + + +def conductor_response_processor(conductor_response, raw_response, req_id): + """Build a response object to be sent to client's callback URL from Conductor's response + This includes Conductor's placement optimization response, and required ASDC license artifacts + + :param conductor_response: JSON response from Conductor + :param raw_response: Raw HTTP response corresponding to above + :param req_id: Id of a request + :return: JSON object that can be sent to the client's callback URL + """ + composite_solutions = [] + name_map = {"physical-location-id": "cloudClli", "host_id": "vnfHostName", + "cloud_version": "cloudVersion", "cloud_owner": "cloudOwner"} + for reco in conductor_response['plans'][0]['recommendations']: + for resource in reco.keys(): + c = reco[resource]['candidate'] + solution = { + 'resourceModuleName': resource, + 'serviceResourceId': reco[resource]['service_resource_id'], + 'inventoryType': c['inventory_type'], + 'serviceInstanceId': c['candidate_id'] if c['inventory_type'] == "service" else "", + 'cloudRegionId': c['location_id'], + 'assignmentInfo': [] + } + + for key, value in reco[resource]['attributes'].items(): + try: + solution['assignmentInfo'].append({"variableName": name_map[key], "variableValue": value}) + except KeyError: + debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info".format(key)) + + if c.get('host_id'): + solution['assignmentInfo'].append({'variableName': name_map['host_id'], 'variableValue': c['host_id']}) + composite_solutions.append(solution) + + request_state = conductor_response['plans'][0]['status'] + transaction_id = raw_response.headers.get('transaction_id', "") + status_message = conductor_response.get('plans')[0].get('message', "") + + solution_info = {} + if composite_solutions: + solution_info['placementInfo'] = composite_solutions + + resp = { + "transactionId": transaction_id, + "requestId": req_id, + "requestState": request_state, + "statusMessage": status_message, + "solutionInfo": solution_info + } + return resp + + +def conductor_no_solution_processor(conductor_response, raw_response, request_id, + template_placement_response="templates/plc_opt_response.jsont"): + """Build a response object to be sent to client's callback URL from Conductor's response + This is for case where no solution is found + + :param conductor_response: JSON response from Conductor + :param raw_response: Raw HTTP response corresponding to above + :param request_id: request Id associated with the client request (same as conductor response's "name") + :param template_placement_response: the template for generating response to client (plc_opt_response.jsont) + :return: JSON object that can be sent to the client's callback URL + """ + status_message = conductor_response["plans"][0].get("message") + templ = Template(open(template_placement_response).read()) + return json.loads(templ.render(composite_solutions=[], requestId=request_id, + transactionId=raw_response.headers.get('transaction_id', ""), + statusMessage=status_message, json=json)) + + diff --git a/osdf/optimizers/placementopt/conductor/remote_opt_processor.py b/osdf/optimizers/placementopt/conductor/remote_opt_processor.py new file mode 100644 index 0000000..f753a70 --- /dev/null +++ b/osdf/optimizers/placementopt/conductor/remote_opt_processor.py @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from requests import RequestException + +import traceback +from osdf.operation.error_handling import build_json_error_body +from osdf.logging.osdf_logging import metrics_log, MH, error_log +from osdf.optimizers.placementopt.conductor import conductor +from osdf.optimizers.licenseopt.simple_license_allocation import license_optim +from osdf.utils.interfaces import get_rest_client + + +def process_placement_opt(request_json, policies, osdf_config, prov_status): + """Perform the work for placement optimization (e.g. call SDC artifact and make conductor request) + NOTE: there is scope to make the requests to policy asynchronous to speed up overall performance + :param request_json: json content from original request + :param policies: flattened policies corresponding to this request + :param osdf_config: configuration specific to OSDF app + :param prov_status: provStatus retrieved from Subscriber policy + :return: None, but make a POST to callback URL + """ + + try: + rc = get_rest_client(request_json, service="so") + req_id = request_json["requestInfo"]["requestId"] + transaction_id = request_json['requestInfo']['transactionId'] + + metrics_log.info(MH.inside_worker_thread(req_id)) + license_info = None + if 'licenseDemand' in request_json['placementInfo']['demandInfo']: + license_info = license_optim(request_json) + + # Conductor only handles placement, only call Conductor if placementDemands exist + if 'placementDemand' in request_json['placementInfo']['demandInfo']: + metrics_log.info(MH.requesting("placement/conductor", req_id)) + placement_response = conductor.request(request_json, osdf_config, policies, prov_status) + if license_info: # Attach license solution if it exists + placement_response['solutionInfo']['licenseInfo'] = license_info + else: # License selection only scenario + placement_response = { + "transactionId": transaction_id, + "requestId": req_id, + "requestState": "complete", + "statusMessage": "License selection completed successfully", + "solutionInfo": {"licenseInfo": license_info} + } + except Exception as err: + error_log.error("Error for {} {}".format(req_id, traceback.format_exc())) + + try: + body = build_json_error_body(err) + metrics_log.info(MH.sending_response(req_id, "ERROR")) + rc.request(json=body, noresponse=True) + except RequestException: + error_log.error("Error sending asynchronous notification for {} {}".format(req_id, traceback.format_exc())) + return + + try: + metrics_log.info(MH.calling_back_with_body(req_id, rc.url,placement_response)) + rc.request(json=placement_response, noresponse=True) + except RequestException : # can't do much here but log it and move on + error_log.error("Error sending asynchronous notification for {} {}".format(req_id, traceback.format_exc())) + diff --git a/osdf/optimizers/placementopt/conductor/translation.py b/osdf/optimizers/placementopt/conductor/translation.py new file mode 100644 index 0000000..262fa86 --- /dev/null +++ b/osdf/optimizers/placementopt/conductor/translation.py @@ -0,0 +1,216 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import json +from osdf.utils.data_conversion import text_to_symbol +# from osdf.utils import data_mapping + +def gen_optimization_policy(vnf_list, optimization_policy): + """Generate optimization policy details to pass to Conductor + :param vnf_list: List of vnf's to used in placement request + :param optimization_policy: optimization policy information provided in the incoming request + :return: List of optimization policies in a format required by Conductor + """ + optimization_policy_list = [] + for policy in optimization_policy: + content = policy['content'] + parameter_list = [] + + for attr in content['objectiveParameter']['parameterAttributes']: + parameter = attr['parameter'] if attr['parameter'] == "cloud_version" else attr['parameter']+"_between" + for res in attr['resource']: + vnf = get_matching_vnf(res, vnf_list) + value = [vnf] if attr['parameter'] == "cloud_version" else [attr['customerLocationInfo'], vnf] + parameter_list.append({ + attr['operator']: [attr['weight'], {parameter: value}] + }) + + optimization_policy_list.append({ + content['objective']: {content['objectiveParameter']['operator']: parameter_list } + }) + return optimization_policy_list + + +def get_matching_vnf(resource, vnf_list): + + for vnf in vnf_list: + if resource in vnf: + return vnf + return resource + + +def get_matching_vnfs(resources, vnf_list, match_type="intersection"): + """Get a list of matching VNFs from the list of resources + :param resources: + :param vnf_list: List of vnf's to used in placement request + :param match_type: "intersection" or "all" or "any" (any => send all_vnfs if there is any intersection) + :return: List of matching VNFs + """ + common_vnfs = [] + for vnf in vnf_list: + for resource in resources: + if resource in vnf: + common_vnfs.append(vnf) + if match_type == "intersection": # specifically requested intersection + return common_vnfs + elif common_vnfs or match_type == "all": # ("any" and common) OR "all" + return resources + return None + + +def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rtype=None): + """Generate a list of policies + :param vnf_list: List of vnf's to used in placement request + :param resource_policy: policy for this specific resource + :param match_type: How to match the vnf_names with the vnf_list (intersection or "any") + intersection => return intersection; "any" implies return all vnf_names if intersection is not null + :param rtype: resource type (e.g. resourceRegionProperty or resourceInstanceProperty) + None => no controller information added to the policy specification to Conductor + :return: resource policy list in a format required by Conductor + """ + resource_policy_list = [] + related_policies = [] + for policy in resource_policy: + pc = policy['content'] + demands = get_matching_vnfs(pc['resourceInstanceType'], vnf_list, match_type=match_type) + resource = {pc['identity']: {'type': pc['type'], 'demands': demands}} + + if rtype: + resource[pc['identity']]['properties'] = {'controller': pc[rtype]['controller'], + 'request': json.loads(pc[rtype]['request'])} + if demands and len(demands) != 0: + resource_policy_list.append(resource) + related_policies.append(policy) + return resource_policy_list, related_policies + + +def gen_resource_instance_policy(vnf_list, resource_instance_policy): + """Get policies governing resource instances in order to populate the Conductor API call""" + cur_policies, _ = gen_policy_instance(vnf_list, resource_instance_policy, rtype='resourceInstanceProperty') + return cur_policies + + +def gen_resource_region_policy(vnf_list, resource_region_policy): + """Get policies governing resource region in order to populate the Conductor API call""" + cur_policies, _ = gen_policy_instance(vnf_list, resource_region_policy, rtype='resourceRegionProperty') + return cur_policies + + +def gen_inventory_group_policy(vnf_list, inventory_group_policy): + """Get policies governing inventory group in order to populate the Conductor API call""" + cur_policies, _ = gen_policy_instance(vnf_list, inventory_group_policy, rtype=None) + return cur_policies + + +def gen_reservation_policy(vnf_list, reservation_policy): + """Get policies governing resource instances in order to populate the Conductor API call""" + cur_policies, _ = gen_policy_instance(vnf_list, reservation_policy, rtype='instanceReservationProperty') + return cur_policies + + +def gen_distance_to_location_policy(vnf_list, distance_to_location_policy): + """Get policies governing distance-to-location for VNFs in order to populate the Conductor API call""" + cur_policies, related_policies = gen_policy_instance(vnf_list, distance_to_location_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy + properties = p_main['content']['distanceToLocationProperty'] + pcp_d = properties['distanceCondition'] + p_new[p_main['content']['identity']]['properties'] = { + 'distance': text_to_symbol[pcp_d['operator']] + " " + pcp_d['value'].lower(), + 'location': properties['locationInfo'] + } + return cur_policies + + +def gen_attribute_policy(vnf_list, attribute_policy): + """Get policies governing attributes of VNFs in order to populate the Conductor API call""" + cur_policies, related_policies = gen_policy_instance(vnf_list, attribute_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy + properties = p_main['content']['cloudAttributeProperty'] + p_new[p_main['content']['identity']]['properties'] = { + 'evaluate': { + 'hypervisor': properties.get('hypervisor', ''), + 'cloud_version': properties.get('cloudVersion', ''), + 'cloud_type': properties.get('cloudType', ''), + 'dataplane': properties.get('dataPlane', ''), + 'network_roles': properties.get('networkRoles', ''), + 'complex': properties.get('complex', ''), + 'state': properties.get('state', ''), + 'country': properties.get('country', ''), + 'geo_region': properties.get('geoRegion', ''), + 'exclusivity_groups': properties.get('exclusivityGroups', ''), + 'replication_role': properties.get('replicationRole', '') + } + } + return cur_policies + + +def gen_zone_policy(vnf_list, zone_policy): + """Get zone policies in order to populate the Conductor API call""" + cur_policies, related_policies = gen_policy_instance(vnf_list, zone_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy + pmz = p_main['content']['zoneProperty'] + p_new[p_main['content']['identity']]['properties'] = {'category': pmz['category'], 'qualifier': pmz['qualifier']} + return cur_policies + + +def get_demand_properties(demand, policies): + """Get list demand properties objects (named tuples) from policy""" + def _get_candidates(candidate_info): + return [dict(inventory_type=x['candidateType'], candidate_id=x['candidates']) for x in candidate_info] + properties = [] + for policy in policies: + for resourceInstanceType in policy['content']['resourceInstanceType']: + if resourceInstanceType in demand['resourceModuleName']: + for x in policy['content']['property']: + property = dict(inventory_provider=x['inventoryProvider'], + inventory_type=x['inventoryType'], + service_resource_id=demand['serviceResourceId']) + if 'attributes' in x: + attributes = {} + for k,v in x['attributes'].items(): + # key=data_mapping.convert(k) + key = k + attributes[key] = v + if(key=="model-invariant-id"): + attributes[key]=demand['resourceModelInfo']['modelInvariantId'] + elif(key=="model-version-id"): + attributes[key]=demand['resourceModelInfo']['modelVersionId'] + property.update({"attributes": attributes}) + if x['inventoryType'] == "cloud": + property['region'] = {'get_param': "CHOSEN_REGION"} + if 'exclusionCandidateInfo' in demand: + property['excluded_candidates'] = _get_candidates(demand['exclusionCandidateInfo']) + if 'requiredCandidateInfo' in demand: + property['required_candidates'] = _get_candidates(demand['requiredCandidateInfo']) + properties.append(property) + if len(properties) == 0: + properties.append(dict(customer_id="", service_type="", inventory_provider="", inventory_type="")) + return properties + + +def gen_demands(req_json, vnf_policies): + """Generate list of demands based on request and VNF policies + :param req_json: Request object from the client (e.g. MSO) + :param vnf_policies: Policies associated with demand resources (e.g. from grouped_policies['vnfPolicy']) + :return: list of demand parameters to populate the Conductor API call + """ + demand_dictionary = {} + for placementDemand in req_json['placementDemand']: + demand_dictionary.update({placementDemand['resourceModuleName']: get_demand_properties(placementDemand, vnf_policies)}) + + return demand_dictionary diff --git a/osdf/templates/cms_opt_request.jsont b/osdf/templates/cms_opt_request.jsont new file mode 100755 index 0000000..006562b --- /dev/null +++ b/osdf/templates/cms_opt_request.jsont @@ -0,0 +1,35 @@ +{ + "transaction_id": "{{ transaction_id }}", + "request_id": "{{ request_id }}", + "start_date" : "{{ start_time }}", + "end_date" : "{{ end_time }}", + "change_elements" : {{ json.dumps(change_elements) }}, + "constraints" : [ + { + "type" : "general_concurrency_limit", + "parameters": [{{ concurrency_limit }}] + }, + + { + "type" : "allowed_forbidden_periods", + "parameters" : {{ json.dumps(allowed_periods) }} + } + + {% if spatial_conflicts is defined and spatial_conflicts|length > 0 %} + , + { + "type" : "spatial_conflict", + "parameters": {{ json.dumps(spatial_conflicts) }} + } + {% endif %} + + + {% if critical_periods is defined and spatial_conflicts|length > 0 %} + , + { + "type" : "critical_periods", + "parameters": {{ json.dumps(critical_periods) }} + } + {% endif %} + ] +} diff --git a/osdf/templates/cms_opt_request.jsont_1707_v1 b/osdf/templates/cms_opt_request.jsont_1707_v1 new file mode 100755 index 0000000..75ecbe5 --- /dev/null +++ b/osdf/templates/cms_opt_request.jsont_1707_v1 @@ -0,0 +1,67 @@ +{ + "transaction_id": "{{ transaction_id }}", + "request_id": "{{ request_id }}", + "start_date" : "{{ start_time }}", + "end_date" : "{{ end_time }}", + + "change_elements" : [ + {% set comma = joiner(",") -%} + {% for element in all_upgrades -%} {{ comma() }} + { + "id" : "{{ element.id }}", + "failback_duration": {{ element.failback_duration }}, + {% if element.group_id -%} + "group_id": "{{ element.group_id }}", + {% endif %} + {% if element.scheduled_on -%} + "scheduled_on": "{{ element.scheduled_on }}", + {% endif %} + "duration": {{ element.duration }} + } + {% endfor -%} + ], + + "constraints" : [ + { + "type" : "general_concurrency_limit", + "parameters": [{{ concurrency_limit }}] + }, + + { + "type" : "allowed_forbidden_periods", + "parameters" : [ + {% set comma = joiner(",") -%} + {% for idx in all_pending -%} {{ comma() }} + { "id" : "{{ idx.id }}", + "allowed_periods": [ {{ allowed_periods }}] + } + {% endfor -%} + ] + }, + { + "type" : "spatial_conflict", + "parameters": [ + {% set comma = joiner(",") -%} + {% for pserver, vce_list in vce_pserver_mapping.items() -%} {{ comma() }} + { + "spatial_entity": "{{ pserver }}", + "affected_entities": {{ vce_list }} + } + {% endfor -%} + ] + }, + + { + "type" : "critical_periods", + "parameters": [ + {% set comma = joiner(",") -%} + {% for element, conflict_period in conflict_interval.items() -%} {{ comma() }} + { + "id" : "{{ element }}", + "periods": [{{ conflict_period }}] + } + {% endfor -%} + ] + } + ] +} diff --git a/osdf/templates/cms_opt_request_1702.jsont b/osdf/templates/cms_opt_request_1702.jsont new file mode 100755 index 0000000..bcafa45 --- /dev/null +++ b/osdf/templates/cms_opt_request_1702.jsont @@ -0,0 +1,63 @@ +{ + "request_id": "{{ request_id }}", + "startdate" : "{{ start_time }}", + "enddate" : "{{ end_time }}", + + "change_elements" : [ +{% set comma = joiner(",") -%} +{% for element in all_upgrades -%} {{ comma() }} + { "id" : "{{ element.id }}", + {% if element.scheduled -%} "scheduled_on": "{{ element.scheduled }}", {% endif -%} + "duration": {{ element.duration }}, {# duration in seconds #} + "failback_duration": {{ element.failback_duration }}, {# duration in seconds #} + "group_id": {{ element.group_id }}, {# duration in seconds #} + }{% endfor -%} + ], + + "constraints" : [ + { + "type" : "general_concurrency_limit", + "parameters" : [ {{ general_concurrency_limit }} ] + }, + + { + "type" : "allowed_forbidden_periods", + "parameters" : [ +{% set comma = joiner(",") -%} +{% for idx in all_pending -%} {{ comma() }} + { "id" : "{{ idx.id }}", + "allowed_periods": [ {% set comma2 = joiner(",") -%} + {% for period in allowed_periods -%} {{ comma2() }} [{{ json.dumps(period[0]) }}, {{ json.dumps(period[1]) }}] + {% endfor -%} ] }{% endfor -%} + ] + } + +{% if p_v_conflict is defined and p_v_conflict|length > 0 %} + , + { + "type" : "critical_periods", + "description" : "Simultaneous upgrades", + "parameters" : [ +{% set comma2 = joiner(",") -%} +{% for element in p_v_conflict -%} {{ comma2() }} + { + "id" : "{{ element[0] }}", + "periods" : [{{ json.dumps(element[0]) }}, {{ json.dumps(element[1]) }}] + } +{% endfor -%} +{% endif %} + +{% for pserver, vce_group in grouped_vces.items() -%} {{ comma() }} + , + { + "id" : "{{ pserver }}", + "name" : "VCE's on pserver {{ pserver }}", + "description": "Only some VCEs on a pserver can be upgraded at a time", + "max_num_upgrades" : {{ max_num_upgrades(vce_group) }}, + "upgrades" : {{ json.dumps(vce_group) }} + } +{% endfor -%} + ] + } + ] +} diff --git a/osdf/templates/cms_opt_response.jsont b/osdf/templates/cms_opt_response.jsont new file mode 100644 index 0000000..a8817df --- /dev/null +++ b/osdf/templates/cms_opt_response.jsont @@ -0,0 +1,8 @@ +{ + "transactionId": "{{transaction_id}}", + "scheduleId":"{{schedule_id}}", + "requestState": "{{request_state}}", + "status": "{{status}}", + "description": "{{description}}", + "schedule": {{schedule}} +} \ No newline at end of file diff --git a/osdf/templates/conductor_interface.json b/osdf/templates/conductor_interface.json new file mode 100755 index 0000000..2b48647 --- /dev/null +++ b/osdf/templates/conductor_interface.json @@ -0,0 +1,81 @@ +{ + "name": "{{ name }}", + "files": {}, + "timeout": {{ timeout }}, + "limit": {{ limit }}, + "template": { + "conductor_template_version": "2018-02-01", + "parameters": { + "REQUEST_TYPE": "{{ requestType }}", + "CHOSEN_REGION": "{{ chosenRegion }}", + "LATITUDE": "{{ latitude }}", + "LONGITUDE": "{{ longitude }}", + {% if serviceType == 'DHV' %} + "E2EVPNKEY": "{{ e2eVpnKey }}", + "UCPEHOST": "{{ ucpeHostName }}", + "EFFECTIVE_BANDWIDTH": "{{ effectiveBandwidth }}", + "WAN_PORT1_UP": "{{ ipsec_bw_up }}", + "WAN_PORT1_DOWN": "{{ ipsec_bw_down }}", + "WAN_PORT2_UP": "{{ ipsec2_bw_up }}", + "WAN_PORT2_DOWN": "{{ ipsec2_bw_down }}", + {% endif %} + {% if serviceType != 'DHV' %} + "GW_TENANT_ID": "{{ gwTenantId }}", + "PORTAL_TENANT_ID": "{{ portalTenantId }}", + {% endif %} + "CHOSEN_COMPLEX": "{{ chosenComplex }}", + {% if serviceType == 'ADIOD' or serviceType == 'VPE' %} + "BANDWIDTH": "{{ bandwidth }}", + "UNIT": "{{ bandwidth_unit }}", + {% endif %} + "SERVICE_INST": "{{ serviceInstance }}", + "PROV_STATUS": {{ json.dumps(provStatus) }} + }, + "locations": { + {% if serviceType == 'DHV' %} + "customer_loc": { + "host_name": { "get_param": "UCPEHOST" } + } + {% elif serviceType == 'ADIOD' %} + "customer_pref_location": { + "clli_code": { "get_param": "CHOSEN_COMPLEX" } + } + {% elif serviceType == 'NETBOND' %} + "peering_point": { + "latitude": { "get_param": "LATITUDE" }, + "longitude": { "get_param": "LONGITUDE" } + } + {% else %} + "customer_pref_loc": { + "clli_code": { "get_param": "CHOSEN_COMPLEX" } + } + {% endif %} + }, + "demands": {{ json.dumps(demand_list) }}, + {% set comma_main = joiner(",") %} + "constraints": { + {% set comma=joiner(",") %} + {% for elem in policy_groups %} {{ comma() }} + {% for key, value in elem.items() %} + "{{key}}": {{ json.dumps(value) }} + {% endfor %} + {% endfor %} + }, + "reservation": { + {% set comma=joiner(",") %} + {% for elem in reservation_groups %} {{ comma() }} + {% for key, value in elem.items() %} + "{{key}}": {{ json.dumps(value) }} + {% endfor %} + {% endfor %} + }, + "optimization": { + {% set comma=joiner(",") %} + {% for elem in optimization_policies %} {{ comma() }} + {% for key, value in elem.items() %} + "{{key}}": {{ json.dumps(value) }} + {% endfor %} + {% endfor %} + } + } +} \ No newline at end of file diff --git a/osdf/templates/license_opt_request.jsont b/osdf/templates/license_opt_request.jsont new file mode 100644 index 0000000..7baa759 --- /dev/null +++ b/osdf/templates/license_opt_request.jsont @@ -0,0 +1,6 @@ +{ + "transactionId": "{{transaction_id}}", + "requestId": "{{request_id}}", + "partNumber": "{{part_number}}", + "licenseModel" : "{{artifact}}" +} \ No newline at end of file diff --git a/osdf/templates/plc_opt_request.jsont b/osdf/templates/plc_opt_request.jsont new file mode 100755 index 0000000..cd78b3e --- /dev/null +++ b/osdf/templates/plc_opt_request.jsont @@ -0,0 +1,142 @@ +{ + "name": "{{ name }}", + "files": "{{ files }}", + "timeout": "{{ timeout }}", + "limit": "{{ limit }}", + "template": { + "CUST_ID": "{{ cust_id }}", + "E2EVPNKEY": "{{ e2evpnkey }}", + "UCPEHOST": "{{ ucpehost }}", + "WAN_PORT1_UP": "{{ wan_port1_up }}", + "WAN_PORT1_DOWN": "{{ wan_port1_down }}", + "EFFECTIVE_BANDWIDTH": "{{ effective_bandwidth }}", + "SERVICE_INST": "{{ service_inst }}", + "locations": { + "customer_loc": { + "host_name": "{{ ucpehost }}" + } + }, + "demands": [ + {% set comma=joiner(",") %} + {% for demand in demand_list %} {{ comma() }} + { + "{{ demand.vnf_name }}": [ + {% set comma2=joiner(",") %} + {% for property in demand.property %} + "inventory_provider": {{ property.inventory_provider }}, + "inventory_type": {{ property.inventory_type }}, + "service_type": {{ property.service_type }}, + "customer_id": {{ property.customer_id }}, + "candidate_id": {{ property.candidate_id }} + {% endfor %} + ] + } + {% endfor %} + ], + "constraints": { + {% set comma_main=joiner(",") %} + + {% if attribute_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for attribute in attribute_policy_list %} {{ comma() }} + attribute['identity'] : { + "type": {{ attribute['type'] }}, + "demands": {{ attribute['demands'] }}, + "properties": { + "evaluate": { + "hypervisor": {{ attribute['property']['hypervisor'] }}, + "aic_version": {{ attribute['property']['aicVersion'] }}, + "aic_type": {{ attribute['property']['aicType'] }}, + "dataplane": {{ attribute['property']['datatype'] }}, + "network_roles": {{ attribute['property']['networkRoles'] }}, + "complex": {{ attribute['property']['complex'] }} + } + } + } + {% endfor %} + + {% if distance_to_location_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for distance_location in distance_to_location_policy_list %} {{ comma() }} + distance_location['identity'] : { + "type": {{ distance_location['type'] }}, + "demands": {{ distance_location['demands'] }}, + "properties": { + "distance": {{ distance_location['property']['distance'] }}, + "location": {{ distance_location['property']['location'] }} + } + } + {% endfor %} + + {% if inventory_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for inventory in inventory_policy_list %} {{ comma() }} + inventory['identity'] : { + "type": {{ inventory['type'] }}, + "demands": {{ inventory['demands'] }} + } + {% endfor %} + + {% if resource_instance_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for resource_instance in resource_instance_policy_list %} {{ comma() }} + resource_instance['identity'] : { + "type": {{ resource_instance['type'] }}, + "demands": {{ resource_instance['demands'] }}, + "properties": { + "controller": {{ resource_instance['property']['controller'] }}, + "request": {{ resource_instance['property']['request'] }} + } + } + {% endfor %} + + {% if resource_region_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for resource_region in resource_region_policy_list %} {{ comma() }} + resource_region['identity'] : { + "type": {{ resource_region['type'] }}, + "demands": {{ resource_region['demands'] }}, + "properties": { + "controller": {{ resource_region['property']['controller'] }}, + "request": {{ resource_region['property']['request'] }} + } + } + {% endfor %} + + {% if zone_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for zone in zone_policy_list %} {{ comma() }} + zone['identity'] : { + "type": {{ zone['type'] }}, + "demands": {{ zone['demands'] }}, + "properties": { + "qualifier": {{ resource_region['property']['qualifier'] }}, + "category": {{ resource_region['property']['category'] }} + } + } + {% endfor %} + + {% if optmization_policy_list %} {{ comma_main() }} {% endif %} + {% set comma=joiner(",") %} + {% for optimization in optimization_policy_list %} {{ comma() }} + "optimization" : { + {{ optimization['objective'] }}: { + "sum": [ + {% set comma2=joiner(",") %} + {% for parameter in optimization['parameter'] %} {{ comma() }} + { + "product": [ + {{ parameter['weight'] }}, + { + "distance_between": [{{ parameter['customerLocation'] }},{{ parameter['demand'] }}] + } + ] + } + {% endfor %} + ] + } + } + {% endfor %} + } + } +} \ No newline at end of file diff --git a/osdf/templates/plc_opt_response.jsont b/osdf/templates/plc_opt_response.jsont new file mode 100755 index 0000000..aa678b5 --- /dev/null +++ b/osdf/templates/plc_opt_response.jsont @@ -0,0 +1,14 @@ +{ + "requestId": "{{requestId}}", + "transactionId": "{{transacationId}}", + "startTime": "{{startTime}}", + "responseTime": "{{responseTime}}", + "requestType": "{{requestType}}", + "requestState": "{{requestState}}", + "statusMessage": "{{statusMessage}}", + "percentProgress": "{{percentProgress}}", + "solutionInfo": { + "placement": {{ json.dumps(composite_solutions) }}, + "licenseInfo":{ "featureGroupId": "{{featureGroupId}}" } + } +} diff --git a/osdf/templates/policy_request.jsont b/osdf/templates/policy_request.jsont new file mode 100755 index 0000000..3a9e201 --- /dev/null +++ b/osdf/templates/policy_request.jsont @@ -0,0 +1,3 @@ +{ + "policyName": "{{policy_name}}" {# we currently only support query by policy name only -- policyName #} +} diff --git a/osdf/templates/test_cms_nb_req_from_client.jsont b/osdf/templates/test_cms_nb_req_from_client.jsont new file mode 100755 index 0000000..a60c8ff --- /dev/null +++ b/osdf/templates/test_cms_nb_req_from_client.jsont @@ -0,0 +1,19 @@ +{ + "schedulingInfo": { + "change_management_id": "{{ change_management_id }}", + "start_time": "{{ start_time }}", + "end_time": "{{ end_time }}", + "policy_id": {{ json.dumps(policy_id) }}, {# a list of policy Ids #} + "service_type": "{{ service_type }}", + "workflow_type": "{{ workflow_type }}", + "upgrades": {{ json.dumps(upgrades) }} {# a list of node Ids #} + }, + "requestInfo": { + "requestId": "{{ requestId }}", + "sourceId": "{{ sourceId }}", + "optimizer": "{{ optimizer }}", + "numSolutions": "{{ numSolutions }}", + "callbackUrl" : "{{ callbackUrl }}" + } +} + diff --git a/osdf/templates/test_plc_nb_req_from_client.jsont b/osdf/templates/test_plc_nb_req_from_client.jsont new file mode 100755 index 0000000..998ffb3 --- /dev/null +++ b/osdf/templates/test_plc_nb_req_from_client.jsont @@ -0,0 +1,52 @@ +{ + "requestInfo": { + "requestId": "{{requestId}}", + "sourceId": "{{sourceId}}", + "optimizer": "{{optimizer}}", + "numSolutions": {{numSolutions}}, + "timeout": {{timeout}}, + "callbackUrl" : "{{callbackUrl}}" + }, + "placementInfo": { + "modelInfo": { + "modelType": "{{modelType}}", + "modelInvariant": "{{modelInvariantId}}", + "modelVersionId": "{{modelVersionId}}", + "modelName": "{{modelName}}", + "modelVersion": "{{modelVersion}}", + "modelCustomizationId": "{{modelCustomizationId}}" + }, + "subscriberInfo": { + "globalSubscriberId": "{{globalSubscriberId}}", + "subscriberName": "{{subscriberName}}", + "subscriberCommonSiteId": "{{subscriberCommonSiteId}}", + "ucpeHostName": "{{ucpeHostName}}" + }, + "policyId": {{json.dumps(policyId)}}, + "vnfInfo": { + "vnfType": "{{vnfType}}", + "vnfPartNumber": "{{vnfPartNumber}}", + "nominalThroughput": "{{nominalThroughput}}", + "vnfSoftwareVersion": "{{vnfSoftwareVersion}}", + "vnfManagementOption": "{{vnfManagementOption}}" + }, + "vpnInfo": { + "vpnId": "{{vpnId}}", + "pvcId": "{{pvcId}}" + }, + "serviceInfo": { + "dhvServiceInfo": { + "serviceInstanceId": "{{serviceInstanceId}}", + "serviceType": "{{serviceType}}", + "e2evpnkey": "{{e2evpnkey}}", + "dhvSiteEffectiveTransportBandwidth": {{dhvSiteEffectiveTransportBandwidth}}, + "dhvIPSecTransportBandwidthUp": {{dhvIPSecTransportBandwidthUp}}, + "dhvIPSecTransportBandwidthDown": {{dhvIPSecTransportBandwidthDown}}, + "dhvIPSec2TransportBandwidthUp": {{dhvIPSec2TransportBandwidthUp}}, + "dhvIPSec2TransportBandwidthDown": {{dhvIPSec2TransportBandwidthDown}}, + "dhvVendorName": "{{dhvVendorName}}" + } + }, + "demandInfo": {{json.dumps(demandInfo)}} + } +} diff --git a/osdf/utils/__init__.py b/osdf/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/utils/data_conversion.py b/osdf/utils/data_conversion.py new file mode 100644 index 0000000..2f678fa --- /dev/null +++ b/osdf/utils/data_conversion.py @@ -0,0 +1,62 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import itertools +from collections import defaultdict + +from dateutil import tz +from dateutil.parser import parse + + +def tuples_to_multi_val_dict(kvw_tuples, colnums=(0, 1)): + """Given a list of k,v tuples, get a dictionary of the form k -> [v1,v2,...,vn] + :param kvw_tuples: list of k,v,w tuples (e.g. [(k1,v1,a1), (k2,v2,a2), (k1,v3,a3), (k1,v4,a4)] + :param colnums: column numbers + :return: a dict of str:set, something like {k1: {v1, v3, v4}, k2: {v2}} or {k1: {a1, a3, a4}, k2: {a2}} + """ + res = defaultdict(set) + for x in kvw_tuples: + key, val = x[colnums[0]], x[colnums[1]] + res[key].add(val) + return dict((k, set(v)) for k, v in res.items()) + + +def tuples_to_dict(kvw_tuples, colnums=(0, 1)): + """Given a list of k,v tuples, get a dictionary of the form k -> v + :param kvw_tuples: list of k,v,w tuples (e.g. [(k1,v1,a1), (k2,v2,a2), (k3,v3,a3), (k1,v4,a4)] + :param colnums: column numbers + :return: a dict; something like {k1: v4, k2: v2, k3: v3} (note, k1 is repeated, so last val is retained) + """ + return dict((x[colnums[0]], x[colnums[1]]) for x in kvw_tuples) + + +def utc_time_from_ts(timestamp): + """Return corresponding UTC timestamp for a given ISO timestamp (or anything that parse accepts)""" + return parse(timestamp).astimezone(tz.tzutc()).strftime('%Y-%m-%d %H:%M:%S') + + +def list_flatten(l): + """Flatten a complex nested list of nested lists into a flat list""" + return itertools.chain(*[list_flatten(j) if isinstance(j, list) else [j] for j in l]) + + +text_to_symbol = { + 'greater': ">", + 'less': "<", + 'equal': "=" +} diff --git a/osdf/utils/data_types.py b/osdf/utils/data_types.py new file mode 100644 index 0000000..877d4a1 --- /dev/null +++ b/osdf/utils/data_types.py @@ -0,0 +1,30 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import collections + + +def list_like(obj): + """Check if an object is a list-like object, but not a string or dict""" + return isinstance(obj, collections.Sequence) and not isinstance(obj, (str, bytes)) + + +def dict_like(obj): + """Check if an object is a list-like object, but not a string or dict""" + return isinstance(obj, collections.Mapping) + diff --git a/osdf/utils/interfaces.py b/osdf/utils/interfaces.py new file mode 100644 index 0000000..7a0e3a9 --- /dev/null +++ b/osdf/utils/interfaces.py @@ -0,0 +1,90 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import requests + +from osdf.config.base import osdf_config, creds_prefixes +from osdf.logging.osdf_logging import MH, debug_log + + +def get_rest_client(request_json, service): + """Get a RestClient based on request_json's callback URL and osdf_config's credentials based on service name + :param request_json: + :param service: so or cm + :return: rc -- RestClient + """ + callback_url = request_json["requestInfo"]["callbackUrl"] + prefix = creds_prefixes[service] + config = osdf_config.deployment + c_userid, c_passwd = config[prefix + "Username"], config[prefix + "Password"] + return RestClient(url=callback_url, userid=c_userid, passwd=c_passwd) + + +class RestClient(object): + """Simple REST Client that supports get/post and basic auth""" + + def __init__(self, userid=None, passwd=None, log_func=None, url=None, timeout=None, headers=None, + method="POST", req_id=None): + self.auth = (userid, passwd) if userid and passwd else None + self.headers = headers if headers else {} + self.method = method + self.url = url + self.log_func = log_func + self.timeout = (30, 90) if timeout is None else timeout + self.req_id = req_id + + def add_headers(self, headers): + self.headers.update(headers) + + def request(self, url=None, method=None, asjson=True, ok_codes=(2, ), + raw_response=False, noresponse=False, timeout=None, **kwargs): + """ + :param url: REST end point to query + :param method: GET or POST (default is None => self.method) + :param asjson: whether the expected response is in json format + :param ok_codes: expected codes (prefix matching -- e.g. can be (20, 21, 32) or (2, 3)) + :param noresponse: If no response is expected (as long as response codes are OK) + :param raw_response: If we need just the raw response (e.g. conductor sends transaction IDs in headers) + :param timeout: Connection and read timeouts + :param kwargs: Other parameters + :return: + """ + if not self.req_id: + debug_log.debug("Requesting URL: {}".format(url or self.url)) + else: + debug_log.debug("Requesting URL: {} for request ID: {}".format(url or self.url, self.req_id)) + + res = requests.request(url=url or self.url, method=method or self.method, + auth=self.auth, headers=self.headers, + timeout=timeout or self.timeout, **kwargs) + + if self.log_func: + self.log_func(MH.received_http_response(res)) + + res_code = str(res.status_code) + if not any(res_code.startswith(x) for x in map(str, ok_codes)): + raise res.raise_for_status() + + if raw_response: + return res + elif noresponse: + return None + elif asjson: + return res.json() + else: + return res.content diff --git a/osdf/utils/local_processing.py b/osdf/utils/local_processing.py new file mode 100644 index 0000000..6768839 --- /dev/null +++ b/osdf/utils/local_processing.py @@ -0,0 +1,43 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import os + +from osdf.logging.osdf_logging import metrics_log, MH, warn_audit_error + + +def local_create_job_file(req_id, json_req, fname='osdf-req-data.json'): + """Creates a "work" folder for local processing and place relevant + job task file in there""" + + work_dir = 'osdf-optim/work/' + req_id + work_file = '{}/{}'.format(work_dir, fname) + try: + cur_task = "Making a local directory in the OSDF manager for req-id: {}".format(req_id) + metrics_log.info(MH.creating_local_env(cur_task)) + os.makedirs(work_dir, exist_ok=True) + except Exception as err: + warn_audit_error(MH.error_local_env(req_id, "Can't create directory {}".format(work_dir), err)) + return None + try: + with open(work_file, 'w') as fid: + fid.write(json_req['payload']) + return work_dir + except Exception as err: + warn_audit_error(MH.error_local_env(req_id, "can't create file {}".format(work_file), err)) + return None diff --git a/osdf/utils/programming_utils.py b/osdf/utils/programming_utils.py new file mode 100644 index 0000000..a0a8fde --- /dev/null +++ b/osdf/utils/programming_utils.py @@ -0,0 +1,105 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +import collections +import itertools + + +class DotDict(dict): + """A dot-dict mixin to be able to access a dictionary via dot notation + source: https://stackoverflow.com/questions/2352181/how-to-use-a-dot-to-access-members-of-dictionary + """ + __getattr__ = dict.get + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ + + +class MetaSingleton(type): + """Singleton class (2nd Chapter) from Learning Python Design Patterns - 2nd ed. + Chetan Giridhar, Packt Publ. 2016""" + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +def namedtuple_with_defaults(typename, field_names, default_values=()): + """A namedtuple with default values -- Stack overflow recipe from Mark Lodato + http://stackoverflow.com/questions/11351032/named-tuple-and-optional-keyword-arguments + :param typename: Name for the class (same as for namedtuple) + :param field_names: Field names (same as for namedtuple) + :param default_values: Can be specified as a dictionary or as a list + :return: New namedtuple object + """ + T = collections.namedtuple(typename, field_names) + T.__new__.__defaults__ = (None,) * len(T._fields) + if isinstance(default_values, collections.Mapping): + prototype = T(**default_values) + else: + prototype = T(*default_values) + T.__new__.__defaults__ = tuple(prototype) + return T + + +def dot_notation(dict_like, dot_spec): + """Return the value corresponding to the dot_spec from a dict_like object + :param dict_like: dictionary, JSON, etc. + :param dot_spec: a dot notation (e.g. a1.b1.c1.d1 => a1["b1"]["c1"]["d1"]) + :return: the value referenced by the dot_spec + """ + attrs = dot_spec.split(".") # we split the path + parent = dict_like.get(attrs[0]) + children = ".".join(attrs[1:]) + if not (parent and children): # if no children or no parent, bail out + return parent + if isinstance(parent, list): # here, we apply remaining path spec to all children + return [dot_notation(j, children) for j in parent] + elif isinstance(parent, dict): + return dot_notation(parent, children) + else: + return None + + +def list_flatten(l): + """ + Flatten a complex nested list of nested lists into a flat list (DFS). + For example, [ [1, 2], [[[2,3,4], [2,3,4]], [3,4,5, 'hello']]] + will produce [1, 2, 2, 3, 4, 2, 3, 4, 3, 4, 5, 'hello'] + """ + return list(itertools.chain(*[list_flatten(j) if isinstance(j, list) else [j] for j in l])) + + +def inverted_dict(keys: list, key_val_dict: dict) -> dict: + """ + Get val -> [keys] mapping for the given keys using key_val_dict + :param keys: the keys we are interested in (a list) + :param key_val_dict: the key -> val mapping + :return: inverted dictionary of val -> [keys] (for the subset dict of given keys) + """ + res = {} + all_tuples = ((k, key_val_dict[k] if k in key_val_dict else 'no-parent-' + k) for k in keys) + for k, v in all_tuples: + if v in res: + res[v].append(k) + else: + res[v] = [k] + # making sure to remove duplicate keys + res = dict((v, list(set(k_list))) for v, k_list in res.items()) + return res diff --git a/osdf/webapp/__init__.py b/osdf/webapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osdf/webapp/appcontroller.py b/osdf/webapp/appcontroller.py new file mode 100644 index 0000000..49f84ff --- /dev/null +++ b/osdf/webapp/appcontroller.py @@ -0,0 +1,47 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +from flask import request +from flask_httpauth import HTTPBasicAuth +from flask import Response +import json +import osdf +from osdf.config.base import http_basic_auth_credentials + +auth_basic = HTTPBasicAuth() + +error_body = { + "serviceException": { + "text": "Unauthorized, check username and password" + } +} + +unauthorized_message = json.dumps(error_body) + +@auth_basic.get_password +def get_pw(username): + end_point = request.url.split('/')[-1] + auth_group = osdf.end_point_auth_mapping.get(end_point) + return http_basic_auth_credentials[auth_group].get(username) if auth_group else None + +@auth_basic.error_handler +def auth_error(): + response = Response(unauthorized_message, content_type='application/json; charset=utf-8') + response.headers.add('content-length', len(unauthorized_message)) + response.status_code = 401 + return response diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..351f97e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +docutils>=0.12 +docopt>=0.6.2 +Flask>=0.11.1 +Flask-HTTPAuth>=3.2.2 +jsonschema>=2.5.1 +lxml>=3.6.4 +nose>=1.3.7 +python-dateutil>=2.5.3 +PyYAML>=3.12 +requests>=2.14.2 +schematics>=2.0.0 +docopt>=0.6.2 +pydevd>=1.0.0 diff --git a/test/bad_test_Utils.py b/test/bad_test_Utils.py new file mode 100644 index 0000000..3e5cecd --- /dev/null +++ b/test/bad_test_Utils.py @@ -0,0 +1,21 @@ +import unittest +import json + +from osdf.config.base import osdf_config +from osdf.utils.programming_utils import dot_notation + + +class TestUtils(unittest.TestCase): + + def test_metrics(self): + with open('test/placement-tests/request.json', 'r') as f: + data = json.load(f) + placementInfo = data["placementInfo"] + config_local = osdf_config.core + self.assertEqual("USOSTCDALTX0101UJZZ11", dot_notation(placementInfo, config_local['service_info']['vCPE']['vcpeHostName'])) + self.assertEqual("200", dot_notation(placementInfo, config_local['service_info']['vCPE']['e2eVpnKey'])) + self.assertEqual(['vGMuxInfra', 'vG'], dot_notation(placementInfo, 'demandInfo.placementDemand.resourceModuleName')) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/dummy_test_dummy.py b/test/dummy_test_dummy.py new file mode 100644 index 0000000..dc46d50 --- /dev/null +++ b/test/dummy_test_dummy.py @@ -0,0 +1,14 @@ +import osdf +# import osdfapp +from osdf.config.loader import load_config_file + + +def test_dummy(): + """Generate time constraints from cm-request.json and cm-policy-response.json""" + try: + load_config_file("DUMMY") + except: + pass + return 1 + + diff --git a/test/placement-tests/request.json b/test/placement-tests/request.json new file mode 100644 index 0000000..2fd425c --- /dev/null +++ b/test/placement-tests/request.json @@ -0,0 +1,87 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "callbackUrl": "https://test.url.com:5000/callback/", + "sourceId": "so", + "optimizers": ["placement"], + "numSolutions": 1, + "timeout": 600 + }, + "placementInfo": { + "serviceModelInfo": { + "modelType": "service", + "modelInvariantId": "fad5f4d5-1c94-4890-927d-9cec6e82997f", + "modelVersionId": "6e13c5e1-f172-436c-9cc4-0d64c94eb7f4", + "modelName": "vCPE", + "modelVersion": "1.0" + }, + "subscriberInfo": { + "globalSubscriberId": "SUB12_0325_UD_0833", + "subscriberName": "SUB_12_0325_UD_0833", + "subscriberCommonSiteId": "DALTX0101" + }, + "demandInfo": { + "placementDemand": [{ + "resourceInstanceType": "allotted", + "serviceResourceId": "61d563e8-e714-4393-8f99-cc480144a05e", + "resourceModuleName": "vGMuxInfra", + "exclusionCandidateInfo": [{ + "candidateType": "cloud", + "candidates": ["MDT54NJ", "BDM78NJ"] + }, { + "candidateType": "service", + "candidates": ["RT76U8F789", "PO098HJG"] + } + ], + "requiredCandidateInfo": [{ + "candidateType": "cloud", + "candidates": ["DHU87NY"] + }, { + "candidateType": "service", + "candidates": ["YHT675YH"] + } + ], + "resourceModelInfo": { + "modelCustomizationId": "", + "modelInvariantId": "h59988ce-3d81-4e07-81b5-53d3aa821134", + "modelName": "", + "modelVersion": "2.0", + "modelVersionId": "51d563e8-e714-4393-8f99-cc480144a05e", + "modelType": "allotted" + }, + "tenantId": "", + "tenantName": "" + }, { + "resourceInstanceType": "allotted", + "serviceResourceId": "71d563e8-e714-4393-8f99-cc480144a05e", + "resourceModuleName": "vG", + "resourceModelInfo": { + "modelCustomizationId": "", + "modelInvariantId": "a59988ce-3d81-4e07-81b5-53d3aa821134", + "modelName": "", + "modelVersion": "2.0", + "modelVersionId": "91d563e8-e714-4393-8f99-cc480144a05e", + "modelType": "allotted" + }, + "tenantId": "", + "tenantName": "" + } + ], + "licenseDemand": [] + }, + "policyId": [ + "" + ], + "serviceInstanceId": "1234-fsdf-23sdf-24kjnk", + "requestParameters": { + + "commonSiteId": "DALTX0101", + "vendorName": "xyz", + "e2eVpnKey": "200", + "vcpeHostName": "USOSTCDALTX0101UJZZ11" + + } + + } +} \ No newline at end of file diff --git a/test/placement-tests/request_mso.json b/test/placement-tests/request_mso.json new file mode 100644 index 0000000..2d854cb --- /dev/null +++ b/test/placement-tests/request_mso.json @@ -0,0 +1,63 @@ +{ + "requestInfo": { + "transactionId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "requestId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "callbackUrl": "http://127.0.0.1:7001", + "sourceId": "mso", + "optimizer": [ + "placement", + "license" + ], + "numSolutions": 1, + "timeout": 600 + }, + "placementInfo": { + "serviceModelInfo": { + "modelType": "", + "modelInvariantId": "598e3f9e-3244-4d8f-a8e0-0e5d7a29eda9", + "modelVersionId": "b8c45108-68df-48c5-8d58-c8dd4de833bb", + "modelName": "ADIOD vMX vPE_BV Service 488", + "modelVersion": "1.0" + }, + "subscriberInfo": { + "globalSubscriberId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "subscriberName": "Avteet_Chayal", + "subscriberCommonSiteId": "MDTWNJ2B17" + }, + "demandInfo": { + "placementDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + }, + "tenantId": "", + "tenantName": "" + } + ], + "licenseDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + } + } + ] + }, + "policyId": [], + "serviceInstanceId": "28014cbe-b334-4d5c-839d-980157929b0b", + "orderInfo": "{\"requestParameters\": {\"aLaCarte\":false,\"usePreload\":true,\"subscriptionServiceType\":\"VPE\",\"alaCarteSet\":true,\"rebuildVolumeGroups\":false,\"userParams\":[{\"name\":\"2017488_adiodvpe0_vnf_config_template_version\",\"value\":\"17.2\"},{\"name\":\"2017488_adiodvpe0_bandwidth_units\",\"value\":\"Gbps\"},{\"name\":\"2017488_adiodvpe0_bandwidth\",\"value\":\"10\"},{\"name\":\"2017488_adiodvpe0_AIC_CLLI\",\"value\":\"MDTWNJ2B17\"},{\"name\":\"2017488_adiodvpe0_vnf_instance_name\",\"value\":\"mtnj309me6\"}],\"autoBuildVfModules\":false,\"cascadeDelete\":false}}" + } +} diff --git a/test/placement-tests/request_mso_subs_name_blank.json b/test/placement-tests/request_mso_subs_name_blank.json new file mode 100644 index 0000000..3195786 --- /dev/null +++ b/test/placement-tests/request_mso_subs_name_blank.json @@ -0,0 +1,63 @@ +{ + "requestInfo": { + "transactionId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "requestId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "callbackUrl": "http://127.0.0.1:7001", + "sourceId": "mso", + "optimizer": [ + "placement", + "license" + ], + "numSolutions": 1, + "timeout": 600 + }, + "placementInfo": { + "serviceModelInfo": { + "modelType": "", + "modelInvariantId": "598e3f9e-3244-4d8f-a8e0-0e5d7a29eda9", + "modelVersionId": "b8c45108-68df-48c5-8d58-c8dd4de833bb", + "modelName": "ADIOD vMX vPE_BV Service 488", + "modelVersion": "1.0" + }, + "subscriberInfo": { + "globalSubscriberId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "subscriberName": "", + "subscriberCommonSiteId": "MDTWNJ2B17" + }, + "demandInfo": { + "placementDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + }, + "tenantId": "", + "tenantName": "" + } + ], + "licenseDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + } + } + ] + }, + "policyId": [], + "serviceInstanceId": "28014cbe-b334-4d5c-839d-980157929b0b", + "orderInfo": "{\"requestParameters\": {\"aLaCarte\":false,\"usePreload\":true,\"subscriptionServiceType\":\"VPE\",\"alaCarteSet\":true,\"rebuildVolumeGroups\":false,\"userParams\":[{\"name\":\"2017488_adiodvpe0_vnf_config_template_version\",\"value\":\"17.2\"},{\"name\":\"2017488_adiodvpe0_bandwidth_units\",\"value\":\"Gbps\"},{\"name\":\"2017488_adiodvpe0_bandwidth\",\"value\":\"10\"},{\"name\":\"2017488_adiodvpe0_AIC_CLLI\",\"value\":\"MDTWNJ2B17\"},{\"name\":\"2017488_adiodvpe0_vnf_instance_name\",\"value\":\"mtnj309me6\"}],\"autoBuildVfModules\":false,\"cascadeDelete\":false}}" + } +} diff --git a/test/placement-tests/request_mso_subs_name_default.json b/test/placement-tests/request_mso_subs_name_default.json new file mode 100644 index 0000000..86e2f82 --- /dev/null +++ b/test/placement-tests/request_mso_subs_name_default.json @@ -0,0 +1,63 @@ +{ + "requestInfo": { + "transactionId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "requestId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "callbackUrl": "http://127.0.0.1:7001", + "sourceId": "mso", + "optimizer": [ + "placement", + "license" + ], + "numSolutions": 1, + "timeout": 600 + }, + "placementInfo": { + "serviceModelInfo": { + "modelType": "", + "modelInvariantId": "598e3f9e-3244-4d8f-a8e0-0e5d7a29eda9", + "modelVersionId": "b8c45108-68df-48c5-8d58-c8dd4de833bb", + "modelName": "ADIOD vMX vPE_BV Service 488", + "modelVersion": "1.0" + }, + "subscriberInfo": { + "globalSubscriberId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "subscriberName": "default", + "subscriberCommonSiteId": "MDTWNJ2B17" + }, + "demandInfo": { + "placementDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + }, + "tenantId": "", + "tenantName": "" + } + ], + "licenseDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + } + } + ] + }, + "policyId": [], + "serviceInstanceId": "28014cbe-b334-4d5c-839d-980157929b0b", + "orderInfo": "{\"requestParameters\": {\"aLaCarte\":false,\"usePreload\":true,\"subscriptionServiceType\":\"VPE\",\"alaCarteSet\":true,\"rebuildVolumeGroups\":false,\"userParams\":[{\"name\":\"2017488_adiodvpe0_vnf_config_template_version\",\"value\":\"17.2\"},{\"name\":\"2017488_adiodvpe0_bandwidth_units\",\"value\":\"Gbps\"},{\"name\":\"2017488_adiodvpe0_bandwidth\",\"value\":\"10\"},{\"name\":\"2017488_adiodvpe0_AIC_CLLI\",\"value\":\"MDTWNJ2B17\"},{\"name\":\"2017488_adiodvpe0_vnf_instance_name\",\"value\":\"mtnj309me6\"}],\"autoBuildVfModules\":false,\"cascadeDelete\":false}}" + } +} diff --git a/test/placement-tests/request_mso_subs_name_none.json b/test/placement-tests/request_mso_subs_name_none.json new file mode 100644 index 0000000..214e011 --- /dev/null +++ b/test/placement-tests/request_mso_subs_name_none.json @@ -0,0 +1,62 @@ +{ + "requestInfo": { + "transactionId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "requestId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "callbackUrl": "http://127.0.0.1:7001", + "sourceId": "mso", + "optimizer": [ + "placement", + "license" + ], + "numSolutions": 1, + "timeout": 600 + }, + "placementInfo": { + "serviceModelInfo": { + "modelType": "", + "modelInvariantId": "598e3f9e-3244-4d8f-a8e0-0e5d7a29eda9", + "modelVersionId": "b8c45108-68df-48c5-8d58-c8dd4de833bb", + "modelName": "ADIOD vMX vPE_BV Service 488", + "modelVersion": "1.0" + }, + "subscriberInfo": { + "globalSubscriberId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "subscriberCommonSiteId": "MDTWNJ2B17" + }, + "demandInfo": { + "placementDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + }, + "tenantId": "", + "tenantName": "" + } + ], + "licenseDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + } + } + ] + }, + "policyId": [], + "serviceInstanceId": "28014cbe-b334-4d5c-839d-980157929b0b", + "orderInfo": "{\"requestParameters\": {\"aLaCarte\":false,\"usePreload\":true,\"subscriptionServiceType\":\"VPE\",\"alaCarteSet\":true,\"rebuildVolumeGroups\":false,\"userParams\":[{\"name\":\"2017488_adiodvpe0_vnf_config_template_version\",\"value\":\"17.2\"},{\"name\":\"2017488_adiodvpe0_bandwidth_units\",\"value\":\"Gbps\"},{\"name\":\"2017488_adiodvpe0_bandwidth\",\"value\":\"10\"},{\"name\":\"2017488_adiodvpe0_AIC_CLLI\",\"value\":\"MDTWNJ2B17\"},{\"name\":\"2017488_adiodvpe0_vnf_instance_name\",\"value\":\"mtnj309me6\"}],\"autoBuildVfModules\":false,\"cascadeDelete\":false}}" + } +} diff --git a/test/placement-tests/request_mso_subs_name_null.json b/test/placement-tests/request_mso_subs_name_null.json new file mode 100644 index 0000000..df7ae8f --- /dev/null +++ b/test/placement-tests/request_mso_subs_name_null.json @@ -0,0 +1,63 @@ +{ + "requestInfo": { + "transactionId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "requestId": "1b98ee53-3fbd-48ca-9b85-302792bf5918", + "callbackUrl": "http://127.0.0.1:7001", + "sourceId": "mso", + "optimizer": [ + "placement", + "license" + ], + "numSolutions": 1, + "timeout": 600 + }, + "placementInfo": { + "serviceModelInfo": { + "modelType": "", + "modelInvariantId": "598e3f9e-3244-4d8f-a8e0-0e5d7a29eda9", + "modelVersionId": "b8c45108-68df-48c5-8d58-c8dd4de833bb", + "modelName": "ADIOD vMX vPE_BV Service 488", + "modelVersion": "1.0" + }, + "subscriberInfo": { + "globalSubscriberId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "subscriberName": "null", + "subscriberCommonSiteId": "MDTWNJ2B17" + }, + "demandInfo": { + "placementDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + }, + "tenantId": "", + "tenantName": "" + } + ], + "licenseDemand": [{ + "resourceInstanceType": "VNF", + "serviceResourceId": "6ea4128a-ba41-4872-a1f8-2748d0e1c361", + "resourceModuleName": "2017-488_ADIOD-vPE 0", + "resourceModelInfo": { + "modelCustomizationId": "06ed941b-1d83-4132-8a1d-1874f4f4b89b", + "modelInvariantId": "5be7e99e-8eb2-4d97-be63-8081ff3cd10e", + "modelName": "2017-488_ADIOD-vPE", + "modelVersion": "1.0", + "modelVersionId": "7cd2d399-2bcb-4ecf-bd32-c36cdca7aa03", + "modelType": "" + } + } + ] + }, + "policyId": [], + "serviceInstanceId": "28014cbe-b334-4d5c-839d-980157929b0b", + "orderInfo": "{\"requestParameters\": {\"aLaCarte\":false,\"usePreload\":true,\"subscriptionServiceType\":\"VPE\",\"alaCarteSet\":true,\"rebuildVolumeGroups\":false,\"userParams\":[{\"name\":\"2017488_adiodvpe0_vnf_config_template_version\",\"value\":\"17.2\"},{\"name\":\"2017488_adiodvpe0_bandwidth_units\",\"value\":\"Gbps\"},{\"name\":\"2017488_adiodvpe0_bandwidth\",\"value\":\"10\"},{\"name\":\"2017488_adiodvpe0_AIC_CLLI\",\"value\":\"MDTWNJ2B17\"},{\"name\":\"2017488_adiodvpe0_vnf_instance_name\",\"value\":\"mtnj309me6\"}],\"autoBuildVfModules\":false,\"cascadeDelete\":false}}" + } +} diff --git a/test/placement-tests/request_vCPE.json b/test/placement-tests/request_vCPE.json new file mode 100644 index 0000000..03e32d9 --- /dev/null +++ b/test/placement-tests/request_vCPE.json @@ -0,0 +1,105 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "callbackUrl": "https://so:5000/callbackUrl", + "sourceId": "SO", + "requestType": "create", + "numSolutions": 1, + "optimizers": ["placement"], + "timeout": 600 + }, + "requestParameters": { + "customerLatitude": 32.897480, + "customerLongitude": -97.040443, + "customerName": "xyz" + }, + "placementDemands": [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "vGMuxInfra-xx", + "tenantId": "vGMuxInfra-tenant", + "resourceModelInfo": { + "modelInvariantId": "vGMuxInfra-modelInvariantId", + "modelVersionId": "vGMuxInfra-versionId", + "modelName": "vGMuxInfra-model", + "modelType": "resource", + "modelVersion": "1.0", + "modelCustomizationName": "vGMuxInfra-customeModelName" + }, + "existingCandidates": { + "identifierType": "service_instance_id", + "identifiers": ["87257b49-9602-4ca1-9817-094e52bc873b"] + }, + "excludedCandidates": { + "identifierType": "service_instance_id", + "identifiers": ["1ac71fb8-ad43-4e16-9459-c3f372b8236d"] + }, + "requiredCandidates": { + "identifierType": "service_instance_id", + "identifiers": ["7e6c3e57-62cd-44f6-aa88-d0896998f7ec"] + } + }, + { + "resourceModuleName": "vG", + "serviceResourceId": "71d563e8-e714-4393-8f99-cc480144a05e", + "tenantId": "vG-tenant", + "resourceModelInfo": { + "modelInvariantId": "vG-modelInvariantId", + "modelVersionId": "vG-versionId", + "modelName": "vG-model", + "modelType": "resource", + "modelVersion": "1.0", + "modelCustomizationName": "vG-customeModelName" + }, + "existingCandidates": { + "identifierType": "service_instance_id", + "identifiers": ["21d5f3e8-e714-4383-8f99-cc480144505a"] + }, + "excludedCandidates": { + "identifierType": "service_instance_id", + "identifiers": ["1ac71fb8-ad43-4e16-9459-c3f372b8236d"] + }, + "requiredCandidates": { + "identifierType": "cloud_region_id", + "identifiers": ["TXAUS219"] + } + } + ], + "serviceInfo": { + "serviceInstanceId": "d61b2543-5914-4b8f-8e81-81e38575b8ec", + "serviceModelInfo": { + "modelInvariantId": "vCPE-invariantId", + "modelVersionId": "vCPE-versionId", + "modelName": "vCPE-model", + "modelType": "service", + "modelVersion": "1.0", + "modelCustomizationName": "vCPE-customeModelName" + } + }, + "licenseDemands": [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "vGMuxInfra-xx", + "tenantId": "vGMuxInfra-tenant", + "resourceModelInfo": { + "modelInvariantId": "vGMuxInfra-modelInvariantId", + "modelVersionId": "vGMuxInfra-versionId", + "modelName": "vGMuxInfra-model", + "modelType": "resource", + "modelVersion": "1.0", + "modelCustomizationName": "vGMuxInfra-customeModelName" + }, + "existingLicenses": { + "entitlementPoolUUID": [ + "87257b49-9602-4ca1-9817-094e52bc873b", + "43257b49-9602-4fe5-9337-094e52bc9435" + ], + "licenseKeyGroupUUID": [ + "87257b49-9602-4ca1-9817-094e52bc873b", + "43257b49-9602-4fe5-9337-094e52bc9435" + ] + } + } + ] +} diff --git a/test/placement-tests/scopePolicies.json b/test/placement-tests/scopePolicies.json new file mode 100644 index 0000000..123c8e2 --- /dev/null +++ b/test/placement-tests/scopePolicies.json @@ -0,0 +1,21 @@ + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"ResourceRegionPolicy\",\"policyName\":\"bg4702.ResourceRegionPolicy_vhnportal_v1\",\"description\":\"ResourceRegionPolicy@CreatedBy:mh7921\",\"templateVersion\":\"1802V01\",\"version\":\"1802V01\",\"priority\":\"1\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vhnPortalResourceRegion\",\"policyScope\":{\"serviceType\":[\"DHV\"],\"geoRegion\":[\"US\",\"INTERNATIONAL\"],\"subscriberRole\":[\"FFA Homing\"],\"networkType\":[\"ip\"],\"resourceInstanceType\":[\"Primary Service_Admin\",\"Secondary Service_Admin\"]},\"resourceRegionProperty\":{\"request\":\"{\\\"dhv_service_instance\\\": {\\\"get_param\\\": \\\"SERVICE_INST\\\"}, \\\"service_type\\\": \\\"vHNPortal\\\", \\\"e2evpnkey\\\": {\\\"get_param\\\": \\\"E2EVPNKEY\\\"}}\",\"controller\":\"SDN-C\"},\"type\":\"region_fit\",\"resourceInstanceType\":[\"Primary Service_Admin\",\"Secondary Service_Admin\"]}}", + "policyName": "bg4702.Config_MS_ResourceRegionPolicy_vhnportal_v1.1.xml", + "policyVersion": "1", + "matchingConditions": { + "serviceType": "DHV", + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "geoRegion": "US,INTERNATIONAL", + "service": "ResourceRegionPolicy", + "subscriberRole": "FFA Homing", + "type": "region_fit", + "networkType": "ip", + "resourceInstanceType": "Primary Service_Admin,Secondary Service_Admin" + }, + "responseAttributes": {}, + "property": null + } \ No newline at end of file diff --git a/test/placement-tests/testScoperequest.json b/test/placement-tests/testScoperequest.json new file mode 100644 index 0000000..36f0c17 --- /dev/null +++ b/test/placement-tests/testScoperequest.json @@ -0,0 +1,144 @@ +{ + "placementInfo": { + "serviceModelInfo": { + "modelVersion": "1.0", + "modelName": "vCPE", + "modelInvariantId": "250c90b4-42f9-4cd9-9270-fd33a0676f92", + "modelVersionId": "c233e7f3-bd70-4a2c-a88f-4f5743109e8d", + "modelType": "service" + }, + "orderInfo":" { \"requestParameters\": { \"e2eVpnKey\": \"VPNL61657\", \"dhvVendorName\": \"VELOCLOUD\", \"dhvIPSec2TransportBandwidthUp\": \"10\", \"vpnList\": [ { \"vpnInfo\": { \"pvcId\": \"5952413\", \"vpnId\": \"61657\" } } ], \"dhvSiteEffectiveTransportBandwidth\": \"10\", \"ucpeHostName\": \"US292IORLFL0102UJZZ01\", \"commonSiteId\": \"90101124\", \"dhvIPSec2TransportBandwidthDown\": \"10\", \"vnfList\": [ { \"vnfInfo\": { \"vnfType\": \"HN\", \"veloCloudNominalThroughput\": \"100\", \"vnfHostName\": \"US292IORLFL0102UVHN01\", \"vnfPartNumber\": \"DHV-VNF-VC-10M\", \"vnfManagementOption\": \"ATT\", \"vnfSoftwareVersion\": \"2.4.1\" } } ] } }", + "serviceInstanceId": "4701bd3c-b722-4d07-abc0-183ea398fac5", + "demandInfo": { + "placementDemand": [ + { + "tenantName": "", + "tenantId": "", + "serviceResourceId": "a297f69d-4d68-4d1f-8b06-be61bddf9e7f", + "resourceInstanceType": "vVIGaaS", + "resourceModuleName": "Primary Tunnel_XConn for DHV Testing_1 0", + "resourceModelInfo": { + "modelVersion": "1.0", + "modelName": "Tunnel_XConn for DHV Testing_1", + "modelInvariantId": "b2ac0b6a-c157-4f27-a226-4fc6c1d5b08c", + "modelCustomizationId": "8ade4a5f-a446-4b14-9d12-3ccdd80ef55c", + "modelVersionId": "c3c3531a-a0c6-498f-8512-03793f7772fa", + "modelType": "allottedResource" + } + }, + { + "tenantName": "", + "tenantId": "", + "serviceResourceId": "73190cfb-e9de-4185-8f18-cb339df6b92a", + "resourceInstanceType": "vVIGaaS", + "resourceModuleName": "Secondary Tunnel_XConn for DHV Testing_1 1", + "resourceModelInfo": { + "modelVersion": "1.0", + "modelName": "Tunnel_XConn for DHV Testing_1", + "modelInvariantId": "b2ac0b6a-c157-4f27-a226-4fc6c1d5b08c", + "modelCustomizationId": "32b80123-84ea-4bda-82d9-4c70e812b450", + "modelVersionId": "c3c3531a-a0c6-498f-8512-03793f7772fa", + "modelType": "allottedResource" + } + }, + { + "tenantName": "", + "tenantId": "", + "serviceResourceId": "f8489f98-db3d-4e84-9ec7-7f7b17b9857f", + "resourceInstanceType": "vHNPortalaaS", + "resourceModuleName": "Primary Service_Admin for DHV Test_1 0", + "resourceModelInfo": { + "modelVersion": "1.0", + "modelName": "Service_Admin for DHV Test_1", + "modelInvariantId": "a8031455-34bc-4608-b731-973c258822d2", + "modelCustomizationId": "bace7e9f-c0e7-4479-93df-aa10d387038b", + "modelVersionId": "0e830d97-39fc-4310-a11d-ebab6c71b35e", + "modelType": "allottedResource" + } + }, + { + "tenantName": "", + "tenantId": "", + "serviceResourceId": "8a8973d4-3a91-4fe6-a846-6f4c282f9005", + "resourceInstanceType": "vHNPortalaaS", + "resourceModuleName": "Secondary Service_Admin for DHV Test_1 1", + "resourceModelInfo": { + "modelVersion": "1.0", + "modelName": "Service_Admin for DHV Test_1", + "modelInvariantId": "a8031455-34bc-4608-b731-973c258822d2", + "modelCustomizationId": "cb6d359d-8f83-41b6-b0cc-fb3cdf978e25", + "modelVersionId": "0e830d97-39fc-4310-a11d-ebab6c71b35e", + "modelType": "allottedResource" + } + }, + { + "tenantName": "", + "tenantId": "", + "serviceResourceId": "3f2b0c6d-6867-4369-b597-d929305da414", + "resourceInstanceType": "vHNGWaaS", + "resourceModuleName": "Primary IP_Mux_Demux updated_1 0", + "resourceModelInfo": { + "modelVersion": "1.0", + "modelName": "IP_Mux_Demux updated_1", + "modelInvariantId": "72ad23e8-575d-4bc1-a88d-bb63ca66b85f", + "modelCustomizationId": "925db703-945a-4b14-aafa-607c99c32f46", + "modelVersionId": "cb760674-1c09-4316-837f-1ee1e816c26f", + "modelType": "allottedResource" + } + }, + { + "tenantName": "", + "tenantId": "", + "serviceResourceId": "caea369e-90e6-4bf0-9aa4-c80ffb10c77e", + "resourceInstanceType": "vHNGWaaS", + "resourceModuleName": "Secondary IP_Mux_Demux updated_1 1", + "resourceModelInfo": { + "modelVersion": "1.0", + "modelName": "IP_Mux_Demux updated_1", + "modelInvariantId": "72ad23e8-575d-4bc1-a88d-bb63ca66b85f", + "modelCustomizationId": "5f5793d7-843c-4f8e-b01d-35ece0b17ead", + "modelVersionId": "cb760674-1c09-4316-837f-1ee1e816c26f", + "modelType": "allottedResource" + } + } + ] + }, + "subscriberInfo": { + "subscriberCommonSiteId": null, + "globalSubscriberId": "300NCQ", + "subscriberName": "Test Customer" + }, + "policyId": [ + "SNIRO.DistanceToLocationPolicy_vhngw", + "SNIRO.VNFPolicy_vhngatewayprimary1_v1", + "SNIRO.ResourceInstancePolicy_hngateway", + "SNIRO.ResourceRegionPolicy_hngateway_v1", + "SNIRO.VNFPolicy_vhngatewaysecondary1_v1", + "SNIRO.ZonePolicy_vhngw", + "SNIRO.PlacementOptimizationPolicy_dhv_v3", + "SNIRO.VNFPolicy_vhnportal_primary1_v1", + "SNIRO.ResourceInstancePolicy_vhnportal_v3", + "SNIRO.ResourceRegionPolicy_vhnportal_v1", + "SNIRO.VNFPolicy_vhnportalsecondary1_v1", + "SNIRO.ZonePolicy_vhnportal", + "SNIRO.DistanceToLocationPolicy_vvig", + "SNIRO.InventoryGroupPolicy_vvig", + "SNIRO.VNFPolicy_vvigprimary1_v1", + "SNIRO.ResourceInstancePolicy_vvig", + "SNIRO.VNFPolicy_vvigsecondary1_v1" + ] + }, + "requestInfo": { + "transactionId": "264e9db9-6d59-4888-9c90-51245d7c811f", + "sourceId": "mso", + "requestType": "initial", + "callbackUrl": "http://127.0.0.1:7001", + "requestId": "264e9db9-6d59-4888-9c90-51245d7c811f", + "optimizer": [ + "placement", + "license" + ], + "numSolutions": 1, + "timeout": 1800 + } +} diff --git a/test/placement-tests/test_by_scope.yaml b/test/placement-tests/test_by_scope.yaml new file mode 100644 index 0000000..0b53e5f --- /dev/null +++ b/test/placement-tests/test_by_scope.yaml @@ -0,0 +1,20 @@ +policy_info: + placement: + policy_fetch: by_scope + policy_scope: + default_scope: XXX_1802 + scope_vcpe: oof_beijing + service_name: placementInfo.serviceModelInfo.modelName + policy_subscriber: SubscriberPolicy + subscriber_name: placementInfo.subscriberInfo.subscriberName + policy_type_vcpe: + - CloudAttributePolicy + - DistanceToLocationPolicy + - instanceReservationPolicy + - PlacementOptimizationPolicy + - ResourceInstancePolicy + - VNFPolicy + - ZonePolicy + default: # if no explicit service related information is needed + policy_fetch: by_name + policy_scope: none diff --git a/test/placement-tests/vnfGroupPolicies.txt b/test/placement-tests/vnfGroupPolicies.txt new file mode 100644 index 0000000..16aca6c --- /dev/null +++ b/test/placement-tests/vnfGroupPolicies.txt @@ -0,0 +1,197 @@ +[ + { + "content": { + "identity": "vhngateway-pri-sec-1", + "policyScope": { + "geoRegion": [ + "US", + "INTERNATIONAL" + ], + "networkType": [ + "ip" + ], + "resourceInstanceType": [ + "Primary IP_Mux_Demux", + "Secondary IP_Mux_Demux" + ], + "serviceType": [ + "DHV" + ], + "subscriberRole": [ + "FFA Homing" + ] + }, + "property": [ + { + "attributes": { + "equipmentRole": "", + "globalCustomerId": "", + "modelInvariantId": "", + "modelVersionId": "", + "operationalStatus": "out-of-service-path", + "orchestrationStatus": [ + "Activate", + "Activated" + ] + }, + "inventoryProvider": "aai", + "inventoryType": "cloud" + }, + { + "attributes": { + "equipmentRole": "", + "globalCustomerId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "modelInvariantId": "", + "modelVersionId": "", + "operationalStatus": "out-of-service-path", + "orchestrationStatus": [ + "Activate", + "Activated" + ] + }, + "inventoryProvider": "aai", + "inventoryType": "service" + } + ], + "resourceInstanceType": [ + "Primary IP_Mux_Demux", + "Secondary IP_Mux_Demux" + ], + "type": "vnfPolicy" + }, + "description": "VNFPolicy@CreatedBy:mh7921", + "guard": "False", + "policyName": "bg4702.VNFPolicy_vhngatewayprimary_v1", + "priority": "1", + "riskLevel": "3", + "riskType": "test", + "service": "VNFPolicy", + "templateVersion": "1802V01", + "version": "1802V01" + }, + { + "content": { + "identity": "vhnportal-pri-sec-1", + "policyScope": { + "geoRegion": [ + "US", + "INTERNATIONAL" + ], + "networkType": [ + "ip" + ], + "resourceInstanceType": [ + "Primary Service_Admin", + "Secondary Service_Admin" + ], + "serviceType": [ + "DHV" + ], + "subscriberRole": [ + "FFA Homing" + ] + }, + "property": [ + { + "attributes": { + "equipmentRole": "", + "globalCustomerId": "", + "modelInvariantId": "", + "modelVersionId": "", + "operationalStatus": "out-of-service-path", + "orchestrationStatus": [ + "Activate", + "Activated" + ] + }, + "inventoryProvider": "aai", + "inventoryType": "cloud" + }, + { + "attributes": { + "equipmentRole": "", + "globalCustomerId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "modelInvariantId": "", + "modelVersionId": "", + "operationalStatus": "out-of-service-path", + "orchestrationStatus": [ + "Activate", + "Activated" + ] + }, + "inventoryProvider": "aai", + "inventoryType": "service" + } + ], + "resourceInstanceType": [ + "Primary Service_Admin", + "Secondary Service_Admin" + ], + "type": "vnfPolicy" + }, + "description": "VNFPolicy@CreatedBy:mh7921", + "guard": "False", + "policyName": "bg4702.VNFPolicy_vhnportal_primary_v1", + "priority": "1", + "riskLevel": "3", + "riskType": "test", + "service": "VNFPolicy", + "templateVersion": "1802V01", + "version": "1802V01" + }, + { + "content": { + "identity": "vvig-pri-sec-1", + "policyScope": { + "geoRegion": [ + "US", + "INTERNATIONAL" + ], + "networkType": [ + "ip" + ], + "resourceInstanceType": [ + "Primary Tunnel_XConn", + "Secondary Tunnel_XConn" + ], + "serviceType": [ + "DHV" + ], + "subscriberRole": [ + "FFA Homing" + ] + }, + "property": [ + { + "attributes": { + "equipmentRole": "", + "globalCustomerId": "21014aa2-526b-11e6-beb8-9e71128cae77", + "modelInvariantId": "", + "modelVersionId": "", + "operationalStatus": "out-of-service-path", + "orchestrationStatus": [ + "Activate", + "Activated" + ] + }, + "inventoryProvider": "aai", + "inventoryType": "service" + } + ], + "resourceInstanceType": [ + "Primary Tunnel_XConn", + "Secondary Tunnel_XConn" + ], + "type": "vnfPolicy" + }, + "description": "VNFPolicy@CreatedBy:mh7921", + "guard": "False", + "policyName": "bg4702.VNFPolicy_vvig1_v1", + "priority": "1", + "riskLevel": "3", + "riskType": "test", + "service": "VNFPolicy", + "templateVersion": "1802V01", + "version": "1802V01" + } +] \ No newline at end of file diff --git a/test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json b/test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json new file mode 100644 index 0000000..57c0039 --- /dev/null +++ b/test/policy-local-files/CloudAttributePolicy_vGMuxInfra_1.json @@ -0,0 +1,34 @@ +{ + "service": "CloudAttributePolicy", + "policyName": "CloudAttributePolicy_vGMuxInfra", + "description": "Attribute policy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "3", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vGMuxInfra_cloud_attributes", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vGMuxInfra"] + }, + "cloudAttributeProperty": { + "networkRoles": { + "all": [ + "vGMuxInfra.OAM", + "vGMuxInfra.SR_IOV_Provider2_1", + "vGMuxInfra.SR_IOV_Provider2_2" + ] + }, + "complex": { + "any": [ ] + } + }, + "type": "attribute", + "resourceInstanceType": ["vGMuxInfra"] + } +} diff --git a/test/policy-local-files/CloudAttributePolicy_vG_1.json b/test/policy-local-files/CloudAttributePolicy_vG_1.json new file mode 100644 index 0000000..cbe2a88 --- /dev/null +++ b/test/policy-local-files/CloudAttributePolicy_vG_1.json @@ -0,0 +1,34 @@ +{ + "service": "CloudAttributePolicy", + "policyName": "cloud AttributePolicy_vG", + "description": "Attribute policy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "10", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vG_cloud_attributes", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vG"] + }, + "cloudAttributeProperty": { + "networkRoles": { + "all": [ + "vG.OAM", + "vG.SR_IOV_Provider2_1", + "vG.SR_IOV_Provider2_2" + ] + }, + "complex": { + "any": [ ] + } + }, + "type": "attribute", + "resourceInstanceType": ["vG"] + } +} diff --git a/test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json b/test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json new file mode 100644 index 0000000..414c167 --- /dev/null +++ b/test/policy-local-files/DistanceToLocationPolicy_vGMuxInfra_1.json @@ -0,0 +1,30 @@ +{ + "service": "DistanceToLocationPolicy", + "policyName": "DistanceToLocationPolicy_vGMuxInfra", + "description": "DistanceToLocationPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "distanceToLocationProperty": { + "locationInfo": "customer_loc", + "distanceCondition": { + "parameter": "distance", + "value": "50000 km", + "operator": "less" + } + }, + "identity": "distance-vGMuxInfra", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vGMuxInfra"] + }, + "type": "distance_to_location", + "resourceInstanceType": ["vGMuxInfra"] + } +} diff --git a/test/policy-local-files/DistanceToLocationPolicy_vG_1.json b/test/policy-local-files/DistanceToLocationPolicy_vG_1.json new file mode 100644 index 0000000..737ee19 --- /dev/null +++ b/test/policy-local-files/DistanceToLocationPolicy_vG_1.json @@ -0,0 +1,30 @@ +{ + "service": "DistanceToLocationPolicy", + "policyName": "DistanceToLocationPolicy_vG", + "description": "DistanceToLocationPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "distanceToLocationProperty": { + "locationInfo": "customer_loc", + "distanceCondition": { + "parameter": "distance", + "value": "50000 km", + "operator": "less" + } + }, + "identity": "distance-vG", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vG"] + }, + "type": "distance_to_location", + "resourceInstanceType": ["vG"] + } +} diff --git a/test/policy-local-files/InventoryGroup_vGMuxInfra_1.json b/test/policy-local-files/InventoryGroup_vGMuxInfra_1.json new file mode 100644 index 0000000..20ff7f7 --- /dev/null +++ b/test/policy-local-files/InventoryGroup_vGMuxInfra_1.json @@ -0,0 +1,22 @@ +{ + "service": "InventoryGroupPolicy", + "policyName": "InventoryGroupPolicy_vGMuxInfra", + "description": "InventoryGroupPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vGMuxInfra-pri-sec-2", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vGMuxInfra"] + }, + "type": "inventory_group", + "resourceInstanceType": ["vGMuxInfra"] + } +} diff --git a/test/policy-local-files/InventoryGroup_vG_1.json b/test/policy-local-files/InventoryGroup_vG_1.json new file mode 100644 index 0000000..99ae309 --- /dev/null +++ b/test/policy-local-files/InventoryGroup_vG_1.json @@ -0,0 +1,22 @@ +{ + "service": "InventoryGroupPolicy", + "policyName": "InventoryGroupPolicy_vG", + "description": "InventoryGroupPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vG-pri-sec-1", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vG"] + }, + "type": "inventory_group", + "resourceInstanceType": ["vG"] + } +} diff --git a/test/policy-local-files/PlacementOptimizationPolicy.json b/test/policy-local-files/PlacementOptimizationPolicy.json new file mode 100644 index 0000000..7c43435 --- /dev/null +++ b/test/policy-local-files/PlacementOptimizationPolicy.json @@ -0,0 +1,41 @@ +{ + "service": "PlacementOptimizationPolicy", + "policyName": "PlacementOptimizationPolicy", + "description": "PlacementOptimizationPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "5", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "objectiveParameter": { + "parameterAttributes": [ + { + "resource": ["vGMuxInfra"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "2", + "operator": "product" + }, + { + "resource": ["vG"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + } + ], + "operator": "sum" + }, + "identity": "optimization", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vGMuxInfra","vG"] + }, + "type": "placementOptimization", + "objective": "minimize" + } +} diff --git a/test/policy-local-files/ResourceInstancePolicy_vG_1.json b/test/policy-local-files/ResourceInstancePolicy_vG_1.json new file mode 100644 index 0000000..21ae0e4 --- /dev/null +++ b/test/policy-local-files/ResourceInstancePolicy_vG_1.json @@ -0,0 +1,26 @@ +{ + "service": "ResourceInstancePolicy", + "policyName": "ResourceInstancePolicy_vG", + "description": "ResourceInstancePolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "5", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vG-resourceInstance", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vG", "vGMuxInfra"] + }, + "resourceInstanceProperty": { + "request": "{\"test\": \"123\"}", + "controller": "SDN-C" + }, + "type": "instance_fit", + "resourceInstanceType": ["vG", "vGMuxInfra"] + } +} diff --git a/test/policy-local-files/VNFPolicy_vGMuxInfra_1.json b/test/policy-local-files/VNFPolicy_vGMuxInfra_1.json new file mode 100644 index 0000000..b0963d6 --- /dev/null +++ b/test/policy-local-files/VNFPolicy_vGMuxInfra_1.json @@ -0,0 +1,36 @@ +{ + "service": "VNFPolicy", + "policyName": "VNFPolicy_vGMuxInfra", + "description": "VNFPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vGMuxInfra-pri-sec-1", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vGMuxInfra"] + }, + "property": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "" + }, + { + "inventoryProvider": "aai", + "serviceType": "vGMuxInfraaaS", + "inventoryType": "service", + "customerId": "21014aa2-526b-11e6-beb8-9e71128cae77" + } + ], + "type": "vnfPolicy", + "resourceInstanceType": ["vGMuxInfra"] + } +} diff --git a/test/policy-local-files/VNFPolicy_vG_1.json b/test/policy-local-files/VNFPolicy_vG_1.json new file mode 100644 index 0000000..de0a158 --- /dev/null +++ b/test/policy-local-files/VNFPolicy_vG_1.json @@ -0,0 +1,36 @@ +{ + "service": "VNFPolicy", + "policyName": "VNFPolicy_vG", + "description": "VNFPolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vG-pri-sec-1", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vG"] + }, + "property": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": " " + }, + { + "inventoryProvider": "aai", + "serviceType": "vGaaS", + "inventoryType": "service", + "customerId": "21014aa2-526b-11e6-beb8-9e71128cae77" + } + ], + "type": "vnfPolicy", + "resourceInstanceType": ["vG"] + } +} diff --git a/test/policy-local-files/ZonePolicy_vGMuxInfra_1.json b/test/policy-local-files/ZonePolicy_vGMuxInfra_1.json new file mode 100644 index 0000000..9f941e4 --- /dev/null +++ b/test/policy-local-files/ZonePolicy_vGMuxInfra_1.json @@ -0,0 +1,26 @@ +{ + "service": "ZonePolicy", + "policyName": "ZonePolicy_vGMuxInfra", + "description": "ZonePolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "zone-vGMuxInfra", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vGMuxInfra"] + }, + "zoneProperty": { + "qualifier": "different", + "category": "complex" + }, + "type": "zone", + "resourceInstanceType": ["vGMuxInfra"] + } +} diff --git a/test/policy-local-files/ZonePolicy_vG_1.json b/test/policy-local-files/ZonePolicy_vG_1.json new file mode 100644 index 0000000..8104f6b --- /dev/null +++ b/test/policy-local-files/ZonePolicy_vG_1.json @@ -0,0 +1,26 @@ +{ + "service": "ZonePolicy", + "policyName": "ZonePolicy_vG", + "description": "ZonePolicy", + "templateVersion": "1702.03", + "version": "1707", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "zone-vG", + "policyScope": { + "serviceType": ["vCPE"], + "geoRegion": ["US", "INTERNATIONAL"], + "networkType": ["ip"], + "resourceInstanceType": ["vG"] + }, + "zoneProperty": { + "qualifier": "different", + "category": "complex" + }, + "type": "zone", + "resourceInstanceType": ["vG"] + } +} diff --git a/test/test-requirements.txt b/test/test-requirements.txt new file mode 100644 index 0000000..b16a37e --- /dev/null +++ b/test/test-requirements.txt @@ -0,0 +1,4 @@ +coverage +moto +pytest +pytest-tap diff --git a/test/test_ConductorApiBuilder.py b/test/test_ConductorApiBuilder.py new file mode 100644 index 0000000..b988efa --- /dev/null +++ b/test/test_ConductorApiBuilder.py @@ -0,0 +1,39 @@ +import unittest +import json +import yaml +from osdf.optimizers.placementopt.conductor.api_builder import conductor_api_builder + + +class TestConductorApiBuilder(unittest.TestCase): + + def test_conductor_api_call_builder(self): + #main_dir = ".." + main_dir = "" + conductor_api_template = main_dir + "osdf/templates/conductor_interface.json" + parameter_data_file = main_dir + "test/placement-tests/request.json" + policy_data_path = main_dir + "test/policy-local-files/" + local_config_file = main_dir + "config/common_config.yaml" + + policy_data_files = ["CloudAttributePolicy_vGMuxInfra_1.json", + "CloudAttributePolicy_vG_1.json", + "DistanceToLocationPolicy_vGMuxInfra_1.json", + "DistanceToLocationPolicy_vG_1.json", + "InventoryGroup_vGMuxInfra_1.json", + "InventoryGroup_vG_1.json", + "PlacementOptimizationPolicy.json", + "ResourceInstancePolicy_vG_1.json", + "VNFPolicy_vGMuxInfra_1.json", + "VNFPolicy_vG_1.json", + "ZonePolicy_vGMuxInfra_1.json", + "ZonePolicy_vG_1.json"] + request_json = json.loads(open(parameter_data_file).read()) + policies = [json.loads(open(policy_data_path + file).read()) for file in policy_data_files] + local_config = yaml.load(open(local_config_file)) + templ_string = conductor_api_builder(request_json, policies, local_config, [], conductor_api_template) + templ_json = json.loads(templ_string) + self.assertEqual(templ_json["name"], "yyy-yyy-yyyy") + + +if __name__ == "__main__": + unittest.main() + diff --git a/test/test_PolicyCalls.py b/test/test_PolicyCalls.py new file mode 100644 index 0000000..b05b6dc --- /dev/null +++ b/test/test_PolicyCalls.py @@ -0,0 +1,115 @@ +import json +import unittest + +from osdf.config.base import osdf_config +from osdf.adapters.policy import interface +from osdf.utils.interfaces import RestClient +import yaml +from mock import patch +from osdf.optimizers.placementopt.conductor import translation + + +class TestPolicyCalls(unittest.TestCase): + + def test_get_subscriber_name(self): + req_json_obj = json.loads(open("./test/placement-tests/request_mso.json").read()) + config_core = osdf_config.core + pmain = config_core['policy_info']['placement'] + print(pmain) + subs_name = interface.get_subscriber_name(req_json_obj, pmain) + print("subscriber_name=", subs_name) + self.assertEquals(subs_name, "Avteet_Chayal") + + + def test_get_subscriber_name_null(self): + req_json_file = "./test/placement-tests/request_mso_subs_name_null.json" + req_json_obj = json.loads(open(req_json_file).read()) + config_core = osdf_config.core + + pmain = config_core['policy_info']['placement'] + print(pmain) + subs_name = interface.get_subscriber_name(req_json_obj, pmain) + print("subscriber_name=", subs_name) + self.assertEquals(subs_name, "DEFAULT") + + + def test_get_subscriber_name_blank(self): + req_json_file = "./test/placement-tests/request_mso_subs_name_blank.json" + req_json_obj = json.loads(open(req_json_file).read()) + config_core = osdf_config.core + + pmain = config_core['policy_info']['placement'] + print(pmain) + subs_name = interface.get_subscriber_name(req_json_obj, pmain) + print("subscriber_name=", subs_name) + self.assertEquals(subs_name, "DEFAULT") + + + def test_get_subscriber_name_default(self): + req_json_file = "./test/placement-tests/request_mso_subs_name_default.json" + req_json_obj = json.loads(open(req_json_file).read()) + config_core = osdf_config.core + + pmain = config_core['policy_info']['placement'] + print(pmain) + subs_name = interface.get_subscriber_name(req_json_obj, pmain) + print("subscriber_name=", subs_name) + self.assertEquals(subs_name, "DEFAULT") + + + def test_get_subscriber_name_none(self): + req_json_file = "./test/placement-tests/request_mso_subs_name_none.json" + req_json_obj = json.loads(open(req_json_file).read()) + config_core = osdf_config.core + + pmain = config_core['policy_info']['placement'] + print(pmain) + subs_name = interface.get_subscriber_name(req_json_obj, pmain) + print("subscriber_name=", subs_name) + self.assertEquals(subs_name, "DEFAULT") + + + def test_get_by_scope(self): + req_json_file = "./test/placement-tests/testScoperequest.json" + allPolicies = "./test/placement-tests/scopePolicies.json" + req_json_obj = json.loads(open(req_json_file).read()) + req_json_obj2 = json.loads(open(allPolicies).read()) + config_core = osdf_config.core + yamlFile = "./test/placement-tests/test_by_scope.yaml" + + with open(yamlFile) as yamlFile2: + policyConfigFile = yaml.load(yamlFile2) + with patch('osdf.adapters.policy.interface.get_subscriber_role', return_value=('FFA Homing', [])) as mock_open: + with patch('osdf.utils.interfaces.RestClient.request', return_value = req_json_obj2): + policiesList = interface.get_by_scope(RestClient, req_json_obj, policyConfigFile, 'placement') + print(policiesList) + #catches Exception if policiesList is null + self.assertTrue(policiesList, 'is null') + self.assertRaises(Exception) + + def test_gen_demands(self): + actionsList = [] + genDemandslist = [] + req_json = "./test/placement-tests/testScoperequest.json" + policiesList = "./test/placement-tests/vnfGroupPolicies.txt" + fh = json.loads(open(policiesList).read()) + #print(fh) + req_json = json.loads(open(req_json).read()) + config_core = osdf_config.core + service_type = req_json['placementInfo'].get('serviceType', None) + # service_type = data_mapping.get_request_service_type(req_json_file) + genDemands = translation.gen_demands(req_json['placementInfo']['demandInfo'], fh) + #print(genDemands) + #print(req_json_file['placementInfo']['demandInfo']['placementDemand'][0]) + for action in req_json['placementInfo']['demandInfo']['placementDemand']: + #print(action['resourceModuleName']) + actionsList.append(action['resourceModuleName']) + for key2,value in genDemands.items(): + #print(key2) + genDemandslist.append(key2) + #genDemandslist.remove('Primary IP_Mux_Demux updated_1 0') + #catches Exception if lists are not equal + self.assertListEqual(genDemandslist, actionsList, 'generated demands are not equal to the passed input [placementDemand][resourceModuleName] list') + +if __name__ == '__main__': + unittest.main() diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..309296d --- /dev/null +++ b/tox.ini @@ -0,0 +1,17 @@ + +[tox] +skipsdist=True +envlist = py3 + +[testenv] +distribute = False +commands = + coverage run --module pytest + coverage report --omit=".tox/py3/*","test/*" + # TODO: need to update the above "omit" when we package osdf as pip-installable + # coverage html --omit=.tox/py3/* -d htmlcov +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test/test-requirements.txt + +[testenv:py3] +basepython=python3.6 diff --git a/utils/__init__.py b/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/utils/data_conversion.py b/utils/data_conversion.py deleted file mode 100644 index 2f678fa..0000000 --- a/utils/data_conversion.py +++ /dev/null @@ -1,62 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import itertools -from collections import defaultdict - -from dateutil import tz -from dateutil.parser import parse - - -def tuples_to_multi_val_dict(kvw_tuples, colnums=(0, 1)): - """Given a list of k,v tuples, get a dictionary of the form k -> [v1,v2,...,vn] - :param kvw_tuples: list of k,v,w tuples (e.g. [(k1,v1,a1), (k2,v2,a2), (k1,v3,a3), (k1,v4,a4)] - :param colnums: column numbers - :return: a dict of str:set, something like {k1: {v1, v3, v4}, k2: {v2}} or {k1: {a1, a3, a4}, k2: {a2}} - """ - res = defaultdict(set) - for x in kvw_tuples: - key, val = x[colnums[0]], x[colnums[1]] - res[key].add(val) - return dict((k, set(v)) for k, v in res.items()) - - -def tuples_to_dict(kvw_tuples, colnums=(0, 1)): - """Given a list of k,v tuples, get a dictionary of the form k -> v - :param kvw_tuples: list of k,v,w tuples (e.g. [(k1,v1,a1), (k2,v2,a2), (k3,v3,a3), (k1,v4,a4)] - :param colnums: column numbers - :return: a dict; something like {k1: v4, k2: v2, k3: v3} (note, k1 is repeated, so last val is retained) - """ - return dict((x[colnums[0]], x[colnums[1]]) for x in kvw_tuples) - - -def utc_time_from_ts(timestamp): - """Return corresponding UTC timestamp for a given ISO timestamp (or anything that parse accepts)""" - return parse(timestamp).astimezone(tz.tzutc()).strftime('%Y-%m-%d %H:%M:%S') - - -def list_flatten(l): - """Flatten a complex nested list of nested lists into a flat list""" - return itertools.chain(*[list_flatten(j) if isinstance(j, list) else [j] for j in l]) - - -text_to_symbol = { - 'greater': ">", - 'less': "<", - 'equal': "=" -} diff --git a/utils/data_types.py b/utils/data_types.py deleted file mode 100644 index 877d4a1..0000000 --- a/utils/data_types.py +++ /dev/null @@ -1,30 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import collections - - -def list_like(obj): - """Check if an object is a list-like object, but not a string or dict""" - return isinstance(obj, collections.Sequence) and not isinstance(obj, (str, bytes)) - - -def dict_like(obj): - """Check if an object is a list-like object, but not a string or dict""" - return isinstance(obj, collections.Mapping) - diff --git a/utils/interfaces.py b/utils/interfaces.py deleted file mode 100644 index 7a0e3a9..0000000 --- a/utils/interfaces.py +++ /dev/null @@ -1,90 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import requests - -from osdf.config.base import osdf_config, creds_prefixes -from osdf.logging.osdf_logging import MH, debug_log - - -def get_rest_client(request_json, service): - """Get a RestClient based on request_json's callback URL and osdf_config's credentials based on service name - :param request_json: - :param service: so or cm - :return: rc -- RestClient - """ - callback_url = request_json["requestInfo"]["callbackUrl"] - prefix = creds_prefixes[service] - config = osdf_config.deployment - c_userid, c_passwd = config[prefix + "Username"], config[prefix + "Password"] - return RestClient(url=callback_url, userid=c_userid, passwd=c_passwd) - - -class RestClient(object): - """Simple REST Client that supports get/post and basic auth""" - - def __init__(self, userid=None, passwd=None, log_func=None, url=None, timeout=None, headers=None, - method="POST", req_id=None): - self.auth = (userid, passwd) if userid and passwd else None - self.headers = headers if headers else {} - self.method = method - self.url = url - self.log_func = log_func - self.timeout = (30, 90) if timeout is None else timeout - self.req_id = req_id - - def add_headers(self, headers): - self.headers.update(headers) - - def request(self, url=None, method=None, asjson=True, ok_codes=(2, ), - raw_response=False, noresponse=False, timeout=None, **kwargs): - """ - :param url: REST end point to query - :param method: GET or POST (default is None => self.method) - :param asjson: whether the expected response is in json format - :param ok_codes: expected codes (prefix matching -- e.g. can be (20, 21, 32) or (2, 3)) - :param noresponse: If no response is expected (as long as response codes are OK) - :param raw_response: If we need just the raw response (e.g. conductor sends transaction IDs in headers) - :param timeout: Connection and read timeouts - :param kwargs: Other parameters - :return: - """ - if not self.req_id: - debug_log.debug("Requesting URL: {}".format(url or self.url)) - else: - debug_log.debug("Requesting URL: {} for request ID: {}".format(url or self.url, self.req_id)) - - res = requests.request(url=url or self.url, method=method or self.method, - auth=self.auth, headers=self.headers, - timeout=timeout or self.timeout, **kwargs) - - if self.log_func: - self.log_func(MH.received_http_response(res)) - - res_code = str(res.status_code) - if not any(res_code.startswith(x) for x in map(str, ok_codes)): - raise res.raise_for_status() - - if raw_response: - return res - elif noresponse: - return None - elif asjson: - return res.json() - else: - return res.content diff --git a/utils/local_processing.py b/utils/local_processing.py deleted file mode 100644 index 6768839..0000000 --- a/utils/local_processing.py +++ /dev/null @@ -1,43 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import os - -from osdf.logging.osdf_logging import metrics_log, MH, warn_audit_error - - -def local_create_job_file(req_id, json_req, fname='osdf-req-data.json'): - """Creates a "work" folder for local processing and place relevant - job task file in there""" - - work_dir = 'osdf-optim/work/' + req_id - work_file = '{}/{}'.format(work_dir, fname) - try: - cur_task = "Making a local directory in the OSDF manager for req-id: {}".format(req_id) - metrics_log.info(MH.creating_local_env(cur_task)) - os.makedirs(work_dir, exist_ok=True) - except Exception as err: - warn_audit_error(MH.error_local_env(req_id, "Can't create directory {}".format(work_dir), err)) - return None - try: - with open(work_file, 'w') as fid: - fid.write(json_req['payload']) - return work_dir - except Exception as err: - warn_audit_error(MH.error_local_env(req_id, "can't create file {}".format(work_file), err)) - return None diff --git a/utils/programming_utils.py b/utils/programming_utils.py deleted file mode 100644 index a0a8fde..0000000 --- a/utils/programming_utils.py +++ /dev/null @@ -1,105 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -import collections -import itertools - - -class DotDict(dict): - """A dot-dict mixin to be able to access a dictionary via dot notation - source: https://stackoverflow.com/questions/2352181/how-to-use-a-dot-to-access-members-of-dictionary - """ - __getattr__ = dict.get - __setattr__ = dict.__setitem__ - __delattr__ = dict.__delitem__ - - -class MetaSingleton(type): - """Singleton class (2nd Chapter) from Learning Python Design Patterns - 2nd ed. - Chetan Giridhar, Packt Publ. 2016""" - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) - return cls._instances[cls] - - -def namedtuple_with_defaults(typename, field_names, default_values=()): - """A namedtuple with default values -- Stack overflow recipe from Mark Lodato - http://stackoverflow.com/questions/11351032/named-tuple-and-optional-keyword-arguments - :param typename: Name for the class (same as for namedtuple) - :param field_names: Field names (same as for namedtuple) - :param default_values: Can be specified as a dictionary or as a list - :return: New namedtuple object - """ - T = collections.namedtuple(typename, field_names) - T.__new__.__defaults__ = (None,) * len(T._fields) - if isinstance(default_values, collections.Mapping): - prototype = T(**default_values) - else: - prototype = T(*default_values) - T.__new__.__defaults__ = tuple(prototype) - return T - - -def dot_notation(dict_like, dot_spec): - """Return the value corresponding to the dot_spec from a dict_like object - :param dict_like: dictionary, JSON, etc. - :param dot_spec: a dot notation (e.g. a1.b1.c1.d1 => a1["b1"]["c1"]["d1"]) - :return: the value referenced by the dot_spec - """ - attrs = dot_spec.split(".") # we split the path - parent = dict_like.get(attrs[0]) - children = ".".join(attrs[1:]) - if not (parent and children): # if no children or no parent, bail out - return parent - if isinstance(parent, list): # here, we apply remaining path spec to all children - return [dot_notation(j, children) for j in parent] - elif isinstance(parent, dict): - return dot_notation(parent, children) - else: - return None - - -def list_flatten(l): - """ - Flatten a complex nested list of nested lists into a flat list (DFS). - For example, [ [1, 2], [[[2,3,4], [2,3,4]], [3,4,5, 'hello']]] - will produce [1, 2, 2, 3, 4, 2, 3, 4, 3, 4, 5, 'hello'] - """ - return list(itertools.chain(*[list_flatten(j) if isinstance(j, list) else [j] for j in l])) - - -def inverted_dict(keys: list, key_val_dict: dict) -> dict: - """ - Get val -> [keys] mapping for the given keys using key_val_dict - :param keys: the keys we are interested in (a list) - :param key_val_dict: the key -> val mapping - :return: inverted dictionary of val -> [keys] (for the subset dict of given keys) - """ - res = {} - all_tuples = ((k, key_val_dict[k] if k in key_val_dict else 'no-parent-' + k) for k in keys) - for k, v in all_tuples: - if v in res: - res[v].append(k) - else: - res[v] = [k] - # making sure to remove duplicate keys - res = dict((v, list(set(k_list))) for v, k_list in res.items()) - return res diff --git a/webapp/__init__.py b/webapp/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/webapp/appcontroller.py b/webapp/appcontroller.py deleted file mode 100644 index 49f84ff..0000000 --- a/webapp/appcontroller.py +++ /dev/null @@ -1,47 +0,0 @@ -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# - -from flask import request -from flask_httpauth import HTTPBasicAuth -from flask import Response -import json -import osdf -from osdf.config.base import http_basic_auth_credentials - -auth_basic = HTTPBasicAuth() - -error_body = { - "serviceException": { - "text": "Unauthorized, check username and password" - } -} - -unauthorized_message = json.dumps(error_body) - -@auth_basic.get_password -def get_pw(username): - end_point = request.url.split('/')[-1] - auth_group = osdf.end_point_auth_mapping.get(end_point) - return http_basic_auth_credentials[auth_group].get(username) if auth_group else None - -@auth_basic.error_handler -def auth_error(): - response = Response(unauthorized_message, content_type='application/json; charset=utf-8') - response.headers.add('content-length', len(unauthorized_message)) - response.status_code = 401 - return response -- cgit 1.2.3-korg