diff options
452 files changed, 21349 insertions, 5465 deletions
diff --git a/.coveragerc b/.coveragerc index a5afd52..1fa0d3b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,7 +2,7 @@ [run] branch = True cover_pylib = False -include = osdf/**/*.py +include = osdf/**/*.py, apps/**/*.py, runtime/*.py, runtime/**/*.py [report] # Regexes for lines to exclude from consideration @@ -29,6 +29,9 @@ wheels/ .installed.cfg *.egg MANIFEST +AUTHORS +ChangeLog +logs/ # PyInstaller # Usually these files are written by a python script from a template @@ -116,4 +119,6 @@ xunit*.xml # Autogenerated for simulations simulator-logs test/functest/simulators/config +test/functest/simulators/policy/response-payloads test/functest/simulators/osdf +/pylint.out diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..f56b3b7 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +--- +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +# Required + +version: 2 +build: + os: ubuntu-20.04 + tools: + python: "3.8" + +python: + install: + - requirements: docs/requirements-docs.txt + +sphinx: + configuration: docs/conf.py @@ -1,80 +1,104 @@ --- project: 'optf-osdf' project_creation_date: '2017-07-06' -lifecycle_state: 'Incubation' +lifecycle_state: 'Mature' project_lead: &onap_releng_ptl - name: 'Shankaranarayanan Puzhavakath Narayanan' - email: 'snarayanan@research.att.com' - id: 'snarayanan' - company: 'ATT' - timezone: 'America/Bedminster' + name: 'Krishna Moorthy' + email: 'krishna.moorthy6@wipro.com' + id: 'krishnaa96' + company: 'Wipro' + timezone: 'India/IST UTC+5:30' project_category: '' primary_contact: *onap_releng_ptl issue_tracking: - type: 'jira' - url: 'https://jira.onap.org/projects/OPTFRA' - key: 'OPTFRA' + type: 'jira' + url: 'https://jira.onap.org/projects/OPTFRA' + key: 'OPTFRA' mailing_list: - type: 'groups.io' - url: 'lists.onap.org' - tag: '<[sub-project_name]>' + type: 'groups.io' + url: 'lists.onap.org' + tag: '<[sub-project_name]>' realtime_discussion: '' meetings: - - type: 'zoom' - agenda: 'https://wiki.onap.org/display/DW/Project+Resources+for+OOF' - url: 'https://wiki.onap.org/display/DW/Optimization+Framework+Project' - server: 'n/a' - channel: 'n/a' - repeats: 'weekly' - time: '15:00 UTC' + - type: 'zoom' + agenda: 'https://wiki.onap.org/display/DW/Project+Resources+for+OOF' + url: 'https://wiki.onap.org/display/DW/Optimization+Framework+Project' + server: 'n/a' + channel: 'n/a' + repeats: 'weekly' + time: '15:00 UTC' repositories: - - 'optf-cmso' - - 'optf-fgps' - - 'optf-has' - - 'optf-osdf' + - 'optf/osdf' committers: - - <<: *onap_releng_ptl - - name: 'Sarat Puthenpura' - email: 'sarat@research.att.com' - company: 'ATT' - id: 'sarat' - timezone: 'America/Bedminster' - - name: 'ramki krishnan' - email: 'ramkri123@gmail.com' - company: 'VMWare' - id: 'ramkri123' - timezone: 'America/Los_Angeles' - - name: 'Dileep Ranganathan' - email: 'dileep.ranganathan@intel.com' - company: 'Intel' - id: 'dileep.ranganathan' - timezone: 'America/Los_Angeles' - - name: 'Vikas Varma' - email: 'vikas.varma@att.com' - company: 'ATT' - id: 'vrvarma' - timezone: 'America/New_York' + - <<: *onap_releng_ptl + - name: 'Sarat Puthenpura' + email: 'sarat@research.att.com' + company: 'ATT' + id: 'sarat' + timezone: 'America/Bedminster' + - name: 'Vikas Varma' + email: 'vikas.varma@att.com' + company: 'ATT' + id: 'vrvarma' + timezone: 'America/New_York' + - name: 'Arthur Martella' + email: 'arthur.martella.1@att.com' + company: 'ATT' + id: 'amartell' + timezone: 'America/New_York' + - name: 'Shankaranarayanan Puzhavakath Narayanan' + email: 'snarayanan@research.att.com' + company: 'ATT' + id: 'snarayanan' + timezone: 'America/Bedminster' + - name: 'Dhebeha MJ' + email: 'dhebeha.mj71@wipro.com' + company: 'Wipro' + id: 'dhebeha' + timezone: 'India/IST UTC+5:30' tsc: - approval: 'https://lists.onap.org/pipermail/onap-tsc' - changes: - - type: 'Addition' - name: 'Ankitkumar Patel' - link: 'https://lists.onap.org/pipermail/onap-tsc/2018-April/004657.html' - - type: 'Removal' - name: 'maopeng zhang' - name: 'Sastry Isukapalli' - name: 'Yoram Zini' - link: 'https://lists.onap.org/pipermail/onap-tsc/2018-June/004975.html' - - type: 'Addition' - name: 'ramki krishnan' - name: 'Dileep Ranganathan' - link: 'https://lists.onap.org/g/ONAP-TSC/message/3205' - - type: 'Removal' - name: 'Ankitkumar Patel' - link: 'https://lists.onap.org/g/ONAP-TSC/message/3550' - - type: 'Addition' - name: 'Vikas Varma' - link: 'http://ircbot.wl.linuxfoundation.org/meetings/onap-meeting/2018/onap-meeting.2018-08-30-13.57.log.txt' - - type: 'Addition' - name: 'Shankaranarayanan Puzhavakath Narayanan' - link: 'https://civs.cs.cornell.edu/cgi-bin/results.pl?id=E_2696d1c15c2fdd16'
\ No newline at end of file + approval: 'https://lists.onap.org/pipermail/onap-tsc' + changes: + - type: 'Addition' + name: 'Ankitkumar Patel' + link: 'https://lists.onap.org/pipermail/onap-tsc/2018-April/004657.html' + - type: 'Removal' + name: 'maopeng zhang' + link: 'https://lists.onap.org/pipermail/onap-tsc/2018-June/004975.html' + - type: 'Removal' + name: 'Sastry Isukapalli' + link: 'https://lists.onap.org/pipermail/onap-tsc/2018-June/004975.html' + - type: 'Removal' + name: 'Yoram Zini' + link: 'https://lists.onap.org/pipermail/onap-tsc/2018-June/004975.html' + - type: 'Addition' + name: 'ramki krishnan' + link: 'https://lists.onap.org/g/ONAP-TSC/message/3205' + - type: 'Addition' + name: 'Dileep Ranganathan' + link: 'https://lists.onap.org/g/ONAP-TSC/message/3205' + - type: 'Removal' + name: 'Ankitkumar Patel' + link: 'https://lists.onap.org/g/ONAP-TSC/message/3550' + - type: 'Addition' + name: 'Vikas Varma' + link: 'https://wiki.onap.org/x/IplFAg' + - type: 'Addition' + name: 'Shankaranarayanan Puzhavakath Narayanan' + # yamllint disable-line rule:line-length + link: 'https://civs.cs.cornell.edu/cgi-bin/results.pl?id=E_2696d1c15c2fdd16' + - type: 'Addition' + name: 'Arthur Martella' + link: 'https://wiki.onap.org/x/qiVIB' + - type: 'Addition' + name: 'Krishna Moorthy' + link: 'https://wiki.onap.org/x/-5ELBQ' + - type: 'Removal' + name: 'Dileep Ranganathan' + link: 'https://lists.onap.org/g/onap-tsc/message/6416' + - type: 'Removal' + name: 'Ramki Krishnan' + link: 'https://lists.onap.org/g/onap-oof/message/568' + - type: 'Addition' + name: 'Dhebeha MJ' + link: 'https://wiki.onap.org/x/PrULBQ' diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e2641a --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + + +#osdf + + +#cipher-utility + + python3 setup.py install + export PYTHONPATH=$PYTHONPATH:`pwd` + diff --git a/osdf/logging/onap_common_v1/__init__.py b/apps/__init__.py index e69de29..e69de29 100755..100644 --- a/osdf/logging/onap_common_v1/__init__.py +++ b/apps/__init__.py diff --git a/osdf/optimizers/pciopt/__init__.py b/apps/license/__init__.py index e69de29..e69de29 100644 --- a/osdf/optimizers/pciopt/__init__.py +++ b/apps/license/__init__.py diff --git a/osdf/optimizers/pciopt/solver/__init__.py b/apps/license/optimizers/__init__.py index e69de29..e69de29 100644 --- a/osdf/optimizers/pciopt/solver/__init__.py +++ b/apps/license/optimizers/__init__.py diff --git a/osdf/optimizers/licenseopt/simple_license_allocation.py b/apps/license/optimizers/simple_license_allocation.py index 74d220f..b2aaba4 100644 --- a/osdf/optimizers/licenseopt/simple_license_allocation.py +++ b/apps/license/optimizers/simple_license_allocation.py @@ -15,6 +15,7 @@ # # ------------------------------------------------------------------------- # +from osdf.utils.mdc_utils import mdc_from_json def license_optim(request_json): @@ -24,6 +25,7 @@ def license_optim(request_json): :param request_json: Request in a JSON format :return: A tuple of licensekey-group-uuid-list and entitlement-group-uuid-list """ + mdc_from_json(request_json) req_id = request_json["requestInfo"]["requestId"] model_name = request_json.get('placementInfo', {}).get('serviceInfo', {}).get('modelInfo', {}).get('modelName') diff --git a/apps/nsst/__init__.py b/apps/nsst/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nsst/__init__.py diff --git a/apps/nsst/models/api/nsstSelectionRequest.py b/apps/nsst/models/api/nsstSelectionRequest.py new file mode 100644 index 0000000..3355ea2 --- /dev/null +++ b/apps/nsst/models/api/nsstSelectionRequest.py @@ -0,0 +1,43 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Huawei 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.models.api.common import OSDFModel +from schematics.types import BaseType +from schematics.types.compound import DictType +from schematics.types.compound import ModelType +from schematics.types import IntType +from schematics.types import StringType +from schematics.types import URLType + + +class RequestInfo(OSDFModel): + """Info for northbound request from client such as SO""" + + transactionId = StringType(required=True) + requestId = StringType(required=True) + callbackUrl = URLType(required=True) + callbackHeader = DictType(BaseType) + sourceId = StringType(required=True) + timeout = IntType() + + +class NSSTSelectionAPI(OSDFModel): + """Request for NST selection """ + + requestInfo = ModelType(RequestInfo, required=True) + sliceProfile = DictType(BaseType) diff --git a/apps/nsst/optimizers/__init__.py b/apps/nsst/optimizers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nsst/optimizers/__init__.py diff --git a/apps/nsst/optimizers/conf/configIinputs.json b/apps/nsst/optimizers/conf/configIinputs.json new file mode 100644 index 0000000..11247bb --- /dev/null +++ b/apps/nsst/optimizers/conf/configIinputs.json @@ -0,0 +1,53 @@ +{ + "NST": [{ + "NST1 ": { + "name": "EmbbNst", + "id": "EmbbNst_1", + "latency": 20, + "uplink": 5, + "downlink": 8, + "reliability": 95, + "areaTrafficCapDL": 10, + "areaTrafficCapUL": 100, + "maxNumberofUEs": 10000, + "areas": " area1|area2", + "expDataRateDL": 10, + "expDataRateUL": 1000, + "uEMobilityLevel": "stationary", + "resourceSharingLevel": "shared", + "skip_post_instantiation_configuration": "true", + "controller_actor": "SO-REF-DATA", + "sNSSAI": "01-3226E7D1", + "pLMNIdList": "39-00", + "sST": "embb", + "uEMobilityLevel": "stationary", + "activityFactor": "0", + "coverageAreaTAList": "Beijing;Beijing;HaidanDistrict;WanshouluStreet", + "modeluuid": "fe6c82b9-4e53-4322-a671-e2d8637bfbb7", + "modelinvariantuuid": "7d7df980-cb81-45f8-bad9-4e5ad2876393" + + } + }, + { + "NST2 ": { + "name": "NST_2", + "id": "NST_2_id", + "latency": 3, + "uplink": 7, + "downlink": 1, + "areaTrafficCapDL": 100, + "areaTrafficCapUL": 100, + "maxNumberofUEs": 300, + "areas": " area1|area2", + "expDataRateDL": 10, + "expDataRateUL": 30, + "uEMobilityLevel": "stationary", + "resourceSharingLevel": "shared", + "skip_post_instantiation_configuration": "true", + "controller_actor": "SO-REF-DATA", + "modeluuid": "7981375e-5e0a-4bf5-93fa-f3e3c02f2b15", + "modelinvariantuuid": "087f11b4-aca0-4341-8104-e5bb2b73285g" + } + } + ] +} diff --git a/apps/nsst/optimizers/nsst_select_processor.py b/apps/nsst/optimizers/nsst_select_processor.py new file mode 100644 index 0000000..90f40f2 --- /dev/null +++ b/apps/nsst/optimizers/nsst_select_processor.py @@ -0,0 +1,155 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Huawei 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 NST SELECTION API calls using the information received from SO +""" +import os +from osdf.adapters.conductor import conductor +from osdf.adapters.policy.interface import get_policies +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log +from osdf.utils.interfaces import get_rest_client +from requests import RequestException +from threading import Thread +import traceback +BASE_DIR = os.path.dirname(__file__) + + +# This is the class for NST Selection + + +class NsstSelection(Thread): + + def __init__(self, osdf_config, request_json): + super().__init__() + self.osdf_config = osdf_config + self.request_json = request_json + self.request_info = self.request_json['requestInfo'] + self.request_info['numSolutions'] = 1 + + def run(self): + self.process_nsst_selection() + + def process_nsst_selection(self): + """Process a PCI request from a Client (build config-db, policy and API call, make the call, return result) + + :param req_object: Request parameters from the client + :param osdf_config: Configuration specific to OSDF application (core + deployment) + :return: response from NST Opt + """ + try: + rest_client = get_rest_client(self.request_json, service='so') + solution = self.get_nsst_solution() + except Exception as err: + error_log.error("Error for {} {}".format(self.request_info.get('requestId'), + traceback.format_exc())) + error_message = str(err) + solution = self.error_response(error_message) + + try: + rest_client.request(json=solution, noresponse=True) + except RequestException: + error_log.error("Error sending asynchronous notification for {} {}". + format(self.request_info['requestId'], traceback.format_exc())) + + def get_nsst_solution(self): + """the file is in the same folder for now will move it to the conf folder of the has once its + + integrated there... + """ + req_info = self.request_json['requestInfo'] + requirements = self.request_json['sliceProfile'] + model_name = "nsst" + policies = self.get_app_policies(model_name, "nsst_selection") + conductor_response = self.get_conductor(req_info, requirements, policies, model_name) + return conductor_response + + def get_nsst_selection_response(self, solutions): + """Get NST selection response from final solution + + :param solutions: final solutions + :return: NST selection response to send back as dictionary + """ + return {'requestId': self.request_info['requestId'], + 'transactionId': self.request_info['transactionId'], + 'requestStatus': 'completed', + 'statusMessage': '', + 'solutions': solutions} + + def error_response(self, error_message): + """Form response message from the error message + + :param error_message: error message while processing the request + :return: response json as dictionary + """ + return {'requestId': self.request_info['requestId'], + 'transactionId': self.request_info['transactionId'], + 'requestStatus': 'error', + 'statusMessage': error_message} + + def get_app_policies(self, model_name, app_name): + policy_request_json = self.request_json.copy() + policy_request_json['serviceInfo'] = {'serviceName': model_name} + debug_log.debug("policy_request_json {}".format(str(policy_request_json))) + return get_policies(policy_request_json, app_name) # app_name: nsst_selection + + def get_conductor(self, req_info, request_parameters, policies, model_name): + demands = [ + { + "resourceModuleName": model_name, + "resourceModelInfo": {} + } + ] + + try: + template_fields = { + 'location_enabled': False, + 'version': '2020-08-13' + } + resp = conductor.request(req_info, demands, request_parameters, {}, template_fields, + self.osdf_config, policies) + except RequestException as e: + resp = e.response.json() + error = resp['plans'][0]['message'] + if "Unable to find any" in error: + return self.get_nsst_selection_response([]) + error_log.error('Error from conductor {}'.format(error)) + return self.error_response(error) + debug_log.debug("Response from conductor in get_conductor method {}".format(str(resp))) + recommendations = resp["plans"][0].get("recommendations") + return self.process_response(recommendations, model_name) + + def process_response(self, recommendations, model_name): + """Process conductor response to form the response for the API request + + :param recommendations: recommendations from conductor + :return: response json as a dictionary + """ + if not recommendations: + return self.get_nsst_selection_response([]) + solutions = [self.get_solution_from_candidate(rec[model_name]['candidate']) + for rec in recommendations] + return self.get_nsst_selection_response(solutions) + + def get_solution_from_candidate(self, candidate): + if candidate['inventory_type'] == 'nsst': + return { + 'UUID': candidate['model_version_id'], + 'invariantUUID': candidate['model_invariant_id'], + 'NSSTName': candidate['model_name'], + } diff --git a/apps/nst/__init__.py b/apps/nst/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nst/__init__.py diff --git a/apps/nst/models/api/nstSelectionRequest.py b/apps/nst/models/api/nstSelectionRequest.py new file mode 100644 index 0000000..99c5df6 --- /dev/null +++ b/apps/nst/models/api/nstSelectionRequest.py @@ -0,0 +1,43 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Huawei 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.models.api.common import OSDFModel +from schematics.types import BaseType +from schematics.types.compound import DictType +from schematics.types.compound import ModelType +from schematics.types import IntType +from schematics.types import StringType +from schematics.types import URLType + + +class RequestInfo(OSDFModel): + """Info for northbound request from client such as SO""" + + transactionId = StringType(required=True) + requestId = StringType(required=True) + callbackUrl = URLType(required=True) + callbackHeader = DictType(BaseType) + sourceId = StringType(required=True) + timeout = IntType() + + +class NSTSelectionAPI(OSDFModel): + """Request for NST selection """ + + requestInfo = ModelType(RequestInfo, required=True) + serviceProfile = DictType(BaseType) diff --git a/apps/nst/optimizers/__init__.py b/apps/nst/optimizers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nst/optimizers/__init__.py diff --git a/apps/nst/optimizers/conf/configIinputs.json b/apps/nst/optimizers/conf/configIinputs.json new file mode 100644 index 0000000..11247bb --- /dev/null +++ b/apps/nst/optimizers/conf/configIinputs.json @@ -0,0 +1,53 @@ +{ + "NST": [{ + "NST1 ": { + "name": "EmbbNst", + "id": "EmbbNst_1", + "latency": 20, + "uplink": 5, + "downlink": 8, + "reliability": 95, + "areaTrafficCapDL": 10, + "areaTrafficCapUL": 100, + "maxNumberofUEs": 10000, + "areas": " area1|area2", + "expDataRateDL": 10, + "expDataRateUL": 1000, + "uEMobilityLevel": "stationary", + "resourceSharingLevel": "shared", + "skip_post_instantiation_configuration": "true", + "controller_actor": "SO-REF-DATA", + "sNSSAI": "01-3226E7D1", + "pLMNIdList": "39-00", + "sST": "embb", + "uEMobilityLevel": "stationary", + "activityFactor": "0", + "coverageAreaTAList": "Beijing;Beijing;HaidanDistrict;WanshouluStreet", + "modeluuid": "fe6c82b9-4e53-4322-a671-e2d8637bfbb7", + "modelinvariantuuid": "7d7df980-cb81-45f8-bad9-4e5ad2876393" + + } + }, + { + "NST2 ": { + "name": "NST_2", + "id": "NST_2_id", + "latency": 3, + "uplink": 7, + "downlink": 1, + "areaTrafficCapDL": 100, + "areaTrafficCapUL": 100, + "maxNumberofUEs": 300, + "areas": " area1|area2", + "expDataRateDL": 10, + "expDataRateUL": 30, + "uEMobilityLevel": "stationary", + "resourceSharingLevel": "shared", + "skip_post_instantiation_configuration": "true", + "controller_actor": "SO-REF-DATA", + "modeluuid": "7981375e-5e0a-4bf5-93fa-f3e3c02f2b15", + "modelinvariantuuid": "087f11b4-aca0-4341-8104-e5bb2b73285g" + } + } + ] +} diff --git a/apps/nst/optimizers/nst_select_processor.py b/apps/nst/optimizers/nst_select_processor.py new file mode 100644 index 0000000..9e44522 --- /dev/null +++ b/apps/nst/optimizers/nst_select_processor.py @@ -0,0 +1,155 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Huawei 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 NST SELECTION API calls using the information received from SO +""" +import os +from osdf.adapters.conductor import conductor +from osdf.adapters.policy.interface import get_policies +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log +from osdf.utils.interfaces import get_rest_client +from requests import RequestException +from threading import Thread +import traceback +BASE_DIR = os.path.dirname(__file__) + + +# This is the class for NST Selection + + +class NstSelection(Thread): + + def __init__(self, osdf_config, request_json): + super().__init__() + self.osdf_config = osdf_config + self.request_json = request_json + self.request_info = self.request_json['requestInfo'] + self.request_info['numSolutions'] = 1 + + def run(self): + self.process_nst_selection() + + def process_nst_selection(self): + """Process a PCI request from a Client (build config-db, policy and API call, make the call, return result) + + :param req_object: Request parameters from the client + :param osdf_config: Configuration specific to OSDF application (core + deployment) + :return: response from NST Opt + """ + try: + rest_client = get_rest_client(self.request_json, service='so') + solution = self.get_nst_solution() + except Exception as err: + error_log.error("Error for {} {}".format(self.request_info.get('requestId'), + traceback.format_exc())) + error_message = str(err) + solution = self.error_response(error_message) + + try: + rest_client.request(json=solution, noresponse=True) + except RequestException: + error_log.error("Error sending asynchronous notification for {} {}". + format(self.request_info['requestId'], traceback.format_exc())) + + def get_nst_solution(self): + """the file is in the same folder for now will move it to the conf folder of the has once its + + integrated there... + """ + req_info = self.request_json['requestInfo'] + requirements = self.request_json['serviceProfile'] + model_name = "nst" + policies = self.get_app_policies(model_name, "nst_selection") + conductor_response = self.get_conductor(req_info, requirements, policies, model_name) + return conductor_response + + def get_nst_selection_response(self, solutions): + """Get NST selection response from final solution + + :param solutions: final solutions + :return: NST selection response to send back as dictionary + """ + return {'requestId': self.request_info['requestId'], + 'transactionId': self.request_info['transactionId'], + 'requestStatus': 'completed', + 'statusMessage': '', + 'solutions': solutions} + + def error_response(self, error_message): + """Form response message from the error message + + :param error_message: error message while processing the request + :return: response json as dictionary + """ + return {'requestId': self.request_info['requestId'], + 'transactionId': self.request_info['transactionId'], + 'requestStatus': 'error', + 'statusMessage': error_message} + + def get_app_policies(self, model_name, app_name): + policy_request_json = self.request_json.copy() + policy_request_json['serviceInfo'] = {'serviceName': model_name} + debug_log.debug("policy_request_json {}".format(str(policy_request_json))) + return get_policies(policy_request_json, app_name) # app_name: nst_selection + + def get_conductor(self, req_info, request_parameters, policies, model_name): + demands = [ + { + "resourceModuleName": model_name, + "resourceModelInfo": {} + } + ] + + try: + template_fields = { + 'location_enabled': False, + 'version': '2020-08-13' + } + resp = conductor.request(req_info, demands, request_parameters, {}, template_fields, + self.osdf_config, policies) + except RequestException as e: + resp = e.response.json() + error = resp['plans'][0]['message'] + if "Unable to find any" in error: + return self.get_nst_selection_response([]) + error_log.error('Error from conductor {}'.format(error)) + return self.error_response(error) + debug_log.debug("Response from conductor in get_conductor method {}".format(str(resp))) + recommendations = resp["plans"][0].get("recommendations") + return self.process_response(recommendations, model_name) + + def process_response(self, recommendations, model_name): + """Process conductor response to form the response for the API request + + :param recommendations: recommendations from conductor + :return: response json as a dictionary + """ + if not recommendations: + return self.get_nst_selection_response([]) + solutions = [self.get_solution_from_candidate(rec[model_name]['candidate']) + for rec in recommendations] + return self.get_nst_selection_response(solutions) + + def get_solution_from_candidate(self, candidate): + if candidate['inventory_type'] == 'nst': + return { + 'UUID': candidate['model_version_id'], + 'invariantUUID': candidate['model_invariant_id'], + 'NSTName': candidate['model_name'], + } diff --git a/apps/nxi_termination/__init__.py b/apps/nxi_termination/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nxi_termination/__init__.py diff --git a/apps/nxi_termination/models/api/_init_.py b/apps/nxi_termination/models/api/_init_.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nxi_termination/models/api/_init_.py diff --git a/apps/nxi_termination/models/api/nxi_termination_request.py b/apps/nxi_termination/models/api/nxi_termination_request.py new file mode 100644 index 0000000..45456cf --- /dev/null +++ b/apps/nxi_termination/models/api/nxi_termination_request.py @@ -0,0 +1,45 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.models.api.common import OSDFModel +from schematics.types import BaseType +from schematics.types.compound import DictType +from schematics.types.compound import ModelType +from schematics.types import IntType +from schematics.types import StringType +from schematics.types import URLType + + +class RequestInfo(OSDFModel): + """Info for northbound request from client such as SO""" + transactionId = StringType(required=True) + requestId = StringType(required=True) + callbackUrl = URLType(required=True) + callbackHeader = DictType(BaseType) + sourceId = StringType(required=True) + timeout = IntType() + addtnlArgs = DictType(BaseType) + + +class NxiTerminationApi(OSDFModel): + """Request for nxi termination (specific to optimization and additional metadata""" + requestInfo = ModelType(RequestInfo, required=True) + type = StringType(required=True, choices=['NSI', 'NSSI']) + NxIId = StringType(required=True) + UUID = StringType() + invariantUUID = StringType() diff --git a/apps/nxi_termination/optimizers/__init__.py b/apps/nxi_termination/optimizers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/nxi_termination/optimizers/__init__.py diff --git a/apps/nxi_termination/optimizers/remote_opt_processor.py b/apps/nxi_termination/optimizers/remote_opt_processor.py new file mode 100644 index 0000000..fc3bc17 --- /dev/null +++ b/apps/nxi_termination/optimizers/remote_opt_processor.py @@ -0,0 +1,107 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 jinja2 import Template + +from apps.nxi_termination.optimizers.response_processor import get_nxi_termination_response +from osdf.adapters.aai.fetch_aai_data import AAIException +from osdf.adapters.aai.fetch_aai_data import execute_dsl_query +from osdf.adapters.aai.fetch_aai_data import get_aai_data +from osdf.logging.osdf_logging import debug_log + + +def process_nxi_termination_opt(request_json, osdf_config): + """Process the nxi Termination request from API layer + + :param request_json: api request + :param osdf_config: configuration specific to OSDF app + :return: response as a success,failure + """ + + request_type = request_json["type"] + request_info = request_json.get("requestInfo", {}) + addtnl_args = request_info.get("addtnlArgs", {}) + query_templates = osdf_config.core["nxi_termination"]["query_templates"] + + inputs = { + "instance_id": request_json["NxIId"] + } + + try: + if request_type == "NSSI": + templates = query_templates["nssi"] + for template in templates: + resource_count = get_resource_count(template, inputs, osdf_config) + if resource_count == -1: + continue + elif resource_count > 1 or (resource_count == 1 and not addtnl_args.get("serviceInstanceId")): + terminate_response = False + elif resource_count == 0: + terminate_response = True + elif resource_count == 1 and addtnl_args.get("serviceInstanceId"): + new_template = template + "('service-instance-id','{}')".format(addtnl_args["serviceInstanceId"]) + terminate_response = get_resource_count(new_template, inputs, osdf_config) == 1 + return set_response("success", "", request_info, terminate_response) + + if request_type == "NSI": + allotted_resources = get_allotted_resources(request_json, osdf_config) + resource_count = len(allotted_resources) + if resource_count == 1 and addtnl_args.get("serviceInstanceId"): + debug_log.debug("resource count {}".format(resource_count)) + terminate_response = False + properties = allotted_resources[0]["relationship-data"] + for property in properties: + if property["relationship-key"] == "service-instance.service-instance-id" \ + and property["relationship-value"] == addtnl_args.get("serviceInstanceId"): + terminate_response = True + elif resource_count > 1 or (resource_count == 1 and not addtnl_args.get("serviceInstanceId")): + terminate_response = False + elif resource_count == 0: + terminate_response = True + + return set_response("success", "", request_info, terminate_response) + except AAIException as e: + reason = str(e) + return set_response("failure", reason, request_info) + + except Exception as e: + reason = "{} Exception Occurred while processing".format(str(e)) + return set_response("failure", reason, request_info) + + +def set_response(status, reason, request_info, terminate_response=None): + res = dict() + res["requestStatus"] = status + res["terminateResponse"] = terminate_response + res["reason"] = reason + return get_nxi_termination_response(request_info, res) + + +def get_resource_count(query_template, inputs, osdf_config): + query = Template(query_template).render(inputs) + dsl_response = execute_dsl_query(query, "count", osdf_config) + debug_log.debug("dsl_response {}".format(dsl_response)) + # the dsl query with format "count" includes the original service-instance, hence reducing one from the result + count = dsl_response["results"][0] + return count.get("service-instance", 0) - 1 + + +def get_allotted_resources(request_json, osdf_config): + response = get_aai_data(request_json, osdf_config) + rel_list = response["relationship-list"]["relationship"] + return [rel for rel in rel_list if rel["related-to"] == "allotted-resource"] diff --git a/osdf/logging/onap_common_v1/makefile b/apps/nxi_termination/optimizers/response_processor.py index 498127e..3ea35c0 100755..100644 --- a/osdf/logging/onap_common_v1/makefile +++ b/apps/nxi_termination/optimizers/response_processor.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,25 +16,17 @@ # ------------------------------------------------------------------------- # -test: - rm -f /tmp/cl.*.log - python CommonLogger.py - rm -f /tmp/cl.*.log - python3 CommonLogger.py -k -v - # python CommonLogger_test.py - # python3 CommonLogger_test.py -# STAGEDIR is overridden in ../makefile -STAGEDIR=/tmp +def get_nxi_termination_response(request_info, response): -build: CommonLogger.html - mkdir -p $(STAGEDIR)/python - cp -p *.py *.config *.md CommonLogger.html $(STAGEDIR)/python - chmod a+x $(STAGEDIR)/python/*.py + """Get NXI termination response from final solution -CommonLogger.html: CommonLogger.py - pydoc -w ./CommonLogger.py - -clean: - rm -rf __pycache__ *.pyc CommonLogger.html - rm -rf *~ + :param request_info: request info + :param response: response to be send + :return: NxI Termination response to send back as dictionary + """ + return {'requestId': request_info['requestId'], + 'transactionId': request_info['transactionId'], + 'requestStatus': response["requestStatus"], + 'terminateResponse': response["terminateResponse"], + 'reason': response['reason']} diff --git a/apps/pci/__init__.py b/apps/pci/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/pci/__init__.py diff --git a/apps/pci/models/__init__.py b/apps/pci/models/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/pci/models/__init__.py diff --git a/apps/pci/models/api/__init__.py b/apps/pci/models/api/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/pci/models/api/__init__.py diff --git a/osdf/models/api/pciOptimizationRequest.py b/apps/pci/models/api/pciOptimizationRequest.py index f5ec6b7..2aa22f1 100644 --- a/osdf/models/api/pciOptimizationRequest.py +++ b/apps/pci/models/api/pciOptimizationRequest.py @@ -19,7 +19,7 @@ from schematics.types import BaseType, StringType, URLType, IntType from schematics.types.compound import ModelType, ListType, DictType -from .common import OSDFModel +from osdf.models.api.common import OSDFModel class RequestInfo(OSDFModel): @@ -45,6 +45,8 @@ class CellInfo(OSDFModel): networkId = StringType(required=True) cellIdList = ListType(StringType(required=True)) anrInputList = ListType(ModelType(ANRInfo)) + fixedPCICells = ListType(StringType()) + priorityTreatmentCells = ListType(StringType()) trigger = StringType() diff --git a/osdf/models/api/pciOptimizationResponse.py b/apps/pci/models/api/pciOptimizationResponse.py index 71d0986..019a43a 100644 --- a/osdf/models/api/pciOptimizationResponse.py +++ b/apps/pci/models/api/pciOptimizationResponse.py @@ -19,7 +19,7 @@ from schematics.types import StringType, IntType from schematics.types.compound import ModelType, ListType -from .common import OSDFModel +from osdf.models.api.common import OSDFModel class PCISolution(OSDFModel): diff --git a/apps/pci/optimizers/__init__.py b/apps/pci/optimizers/__init__.py new file mode 100644 index 0000000..35bc5a0 --- /dev/null +++ b/apps/pci/optimizers/__init__.py @@ -0,0 +1,2 @@ +from apps.pci.optimizers.config import configdb +from apps.pci.optimizers.config import cps diff --git a/apps/pci/optimizers/config/__init__.py b/apps/pci/optimizers/config/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/pci/optimizers/config/__init__.py diff --git a/apps/pci/optimizers/config/config_client.py b/apps/pci/optimizers/config/config_client.py new file mode 100644 index 0000000..7e5a737 --- /dev/null +++ b/apps/pci/optimizers/config/config_client.py @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2021 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 ConfigClient(object): + + subclasses = {} + + @classmethod + def register_subclass(cls, type): + def decorator(subclass): + cls.subclasses[type] = subclass + return subclass + + return decorator + + @classmethod + def create(cls, type): + if type not in cls.subclasses: + raise ValueError('Bad config client type {}'.format(type)) + + return cls.subclasses[type]() diff --git a/apps/pci/optimizers/config/configdb.py b/apps/pci/optimizers/config/configdb.py new file mode 100644 index 0000000..cfc7ce1 --- /dev/null +++ b/apps/pci/optimizers/config/configdb.py @@ -0,0 +1,51 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2021 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 datetime import datetime as dt +import uuid + +from apps.pci.optimizers.config.config_client import ConfigClient +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import debug_log +from osdf.utils.interfaces import RestClient + + +@ConfigClient.register_subclass('configdb') +class ConfigDb(ConfigClient): + + def __init__(self): + self.config = osdf_config.deployment + uid, passwd = self.config['configDbUserName'], self.config['configDbPassword'] + headers = dict(transaction_id=str(uuid.uuid4())) + self.rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug, headers=headers) + + def get_cell_list(self, network_id): + ts = dt.strftime(dt.now(), '%Y-%m-%d %H:%M:%S%z') + cell_list_url = '{}/{}/{}/{}'.format(self.config['configDbUrl'], + self.config['configDbGetCellListUrl'], network_id, ts) + return self.rc.request(raw_response=True, url=cell_list_url).json() + + def get_nbr_list(self, network_id, cell_id): + ts = dt.strftime(dt.now(), '%Y-%m-%d %H:%M:%S%z') + nbr_list_url = '{}/{}/{}/{}'.format(self.config['configDbUrl'], + self.config['configDbGetNbrListUrl'], cell_id, ts) + response = self.rc.request(url=nbr_list_url, raw_response=True).json() + + debug_log.debug("cell_id {} nbr_list {}".format(cell_id, response.get('nbrList'))) + + return response.get('nbrList', []) diff --git a/apps/pci/optimizers/config/cps.py b/apps/pci/optimizers/config/cps.py new file mode 100644 index 0000000..9cf1b1f --- /dev/null +++ b/apps/pci/optimizers/config/cps.py @@ -0,0 +1,72 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2021 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 apps.pci.optimizers.config.config_client import ConfigClient +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import debug_log +from osdf.utils.interfaces import RestClient + + +@ConfigClient.register_subclass('cps') +class Cps(ConfigClient): + + def __init__(self): + self.config = osdf_config.deployment + username, password = self.config['cpsUsername'], self.config['cpsPassword'] + headers = { + "Content-Type": "application/json", + "Accept": "application/json" + } + self.rc = RestClient(userid=username, passwd=password, method="POST", + log_func=debug_log.debug, headers=headers) + + def get_cell_list(self, network_id): + cell_list_url = '{}/{}'.format(self.config['cpsUrl'], self.config['cpsCellListUrl']) + data = { + 'inputParameters': { + 'regionId': network_id + } + } + response = self.rc.request(url=cell_list_url, data=json.dumps(data)) + debug_log.debug("cell list response {}".format(response)) + return sorted([x['idNRCellCU'] for x in response.get('NRCellCU')]) + + def get_nbr_list(self, network_id, cell_id): + nbr_list_url = '{}/{}'.format(self.config['cpsUrl'], self.config['cpsNbrListUrl']) + data = { + 'inputParameters': { + 'regionId': network_id, + 'idNRCellCU': cell_id + } + } + response = self.rc.request(url=nbr_list_url, data=json.dumps(data)) + debug_log.debug("nbr list response {}".format(response)) + nbr_list = [] + for cell_relation in response.get('NRCellRelation'): + nbr = { + 'targetCellId': cell_relation['attributes']['nRTCI'], + 'pciValue': int(cell_relation['attributes']['nRPCI']), + 'ho': cell_relation['attributes']['isHOAllowed'] + } + nbr_list.append(nbr) + + debug_log.debug("cell_id {} nbr_list {}".format(cell_id, nbr_list)) + + return nbr_list diff --git a/osdf/optimizers/pciopt/configdb.py b/apps/pci/optimizers/config_request.py index 8f003c2..f62641d 100644 --- a/osdf/optimizers/pciopt/configdb.py +++ b/apps/pci/optimizers/config_request.py @@ -16,54 +16,40 @@ # ------------------------------------------------------------------------- # -from datetime import datetime as dt - -from osdf.logging.osdf_logging import debug_log -from osdf.utils.interfaces import RestClient +from apps.pci.optimizers.config.config_client import ConfigClient def request(req_object, osdf_config, flat_policies): - """ - Process a configdb request from a Client (build Conductor API call, make the call, return result) + """Process a configdb 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 OSDF application (core + deployment) :param flat_policies: policies related to PCI Opt (fetched based on request) :return: response from ConfigDB (accounting for redirects from Conductor service """ cell_list_response = {} - config = osdf_config.deployment - local_config = osdf_config.core - uid, passwd = config['configDbUserName'], config['configDbPassword'] - req_id = req_object['requestInfo']['requestId'] - transaction_id = req_object['requestInfo']['transactionId'] - headers = dict(transaction_id=transaction_id) network_id = req_object['cellInfo']['networkId'] cell_list_response['network_id'] = network_id - ts = dt.strftime(dt.now(), '%Y-%m-%d %H:%M:%S%z') - - rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug, headers=headers) + config = osdf_config.deployment - cell_list_url = '{}/{}/{}/{}'.format(config['configDbUrl'], config['configDbGetCellListUrl'], network_id, ts) + config_client = ConfigClient.create(config['configClientType']) - cell_list_resp = rc.request(raw_response=True, url=cell_list_url) - cell_resp = cell_list_resp.json() + cell_resp = config_client.get_cell_list(network_id) cell_list = [] count = 0 for cell_id in cell_resp: - cell_info = {'cell_id': cell_id, 'id': count} - nbr_list_url = '{}/{}/{}/{}'.format(config['configDbUrl'], config['configDbGetNbrListUrl'], cell_id, ts) - nbr_list_raw = rc.request(url=nbr_list_url, raw_response=True) - cell_info['nbr_list'] = get_neighbor_list(nbr_list_raw.json()) + cell_info = { + 'cell_id': cell_id, + 'id': count, + 'nbr_list': config_client.get_nbr_list(network_id, cell_id) + } cell_list.append(cell_info) count += 1 cell_list_response['cell_list'] = cell_list - return cell_resp, cell_list_response - -def get_neighbor_list(nbr_list_response): - return nbr_list_response.get('nbrList', []) + return cell_resp, cell_list_response diff --git a/osdf/optimizers/pciopt/pci_opt_processor.py b/apps/pci/optimizers/pci_opt_processor.py index 03c742b..a58d2f4 100644 --- a/osdf/optimizers/pciopt/pci_opt_processor.py +++ b/apps/pci/optimizers/pci_opt_processor.py @@ -18,14 +18,19 @@ import traceback +from onaplogging.mdcContext import MDC from requests import RequestException -from osdf.logging.osdf_logging import metrics_log, MH, error_log +from apps.pci.optimizers.config_request import request as config_request +from apps.pci.optimizers.solver.optimizer import pci_optimize as optimize +from apps.pci.optimizers.solver.pci_utils import get_cell_id +from apps.pci.optimizers.solver.pci_utils import get_pci_value +from osdf.logging.osdf_logging import error_log +from osdf.logging.osdf_logging import metrics_log +from osdf.logging.osdf_logging import MH from osdf.operation.error_handling import build_json_error_body from osdf.utils.interfaces import get_rest_client -from .configdb import request as config_request -from .solver.optimizer import pci_optimize as optimize -from .solver.pci_utils import get_cell_id, get_pci_value +from osdf.utils.mdc_utils import mdc_from_json """ This application generates PCI Optimization API calls using the information received from PCI-Handler-MS, SDN-C @@ -34,14 +39,15 @@ and Policy. def process_pci_optimation(request_json, osdf_config, flat_policies): - """ - Process a PCI request from a Client (build config-db, policy and API call, make the call, return result) + """Process a PCI request from a Client (build config-db, policy and API call, make the call, return result) + :param req_object: Request parameters from the client :param osdf_config: Configuration specific to OSDF application (core + deployment) :param flat_policies: policies related to pci (fetched based on request) :return: response from PCI Opt """ try: + mdc_from_json(request_json) rc = get_rest_client(request_json, service="pcih") req_id = request_json["requestInfo"]["requestId"] cell_info_list, network_cell_info = config_request(request_json, osdf_config, flat_policies) @@ -56,11 +62,13 @@ def process_pci_optimation(request_json, osdf_config, flat_policies): metrics_log.info(MH.sending_response(req_id, "ERROR")) rc.request(json=body, noresponse=True) except RequestException: + MDC.put('requestID', req_id) error_log.error("Error sending asynchronous notification for {} {}".format(req_id, traceback.format_exc())) - return + raise err try: metrics_log.info(MH.calling_back_with_body(req_id, rc.url, pci_response)) + error_log.error("pci response: {}".format(str(pci_response))) rc.request(json=pci_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())) @@ -84,10 +92,16 @@ def get_solutions(cell_info_list, network_cell_info, request_json): def build_solution_list(cell_info_list, network_cell_info, request_json): status = "success" req_id = request_json["requestInfo"]["requestId"] + pci_solutions = [] + anr_solutions = [] try: opt_solution = optimize(network_cell_info, cell_info_list, request_json) - pci_solutions = build_pci_solution(network_cell_info, opt_solution['pci']) - anr_solutions = build_anr_solution(network_cell_info, opt_solution.get('removables', {})) + if opt_solution == 'UNSATISFIABLE': + status = 'inconsistent input' + return status, pci_solutions, anr_solutions + else: + pci_solutions = build_pci_solution(network_cell_info, opt_solution['pci']) + anr_solutions = build_anr_solution(network_cell_info, opt_solution.get('removables', {})) except RuntimeError: error_log.error("Failed finding solution for {} {}".format(req_id, traceback.format_exc())) status = "failed" diff --git a/apps/pci/optimizers/solver/__init__.py b/apps/pci/optimizers/solver/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/pci/optimizers/solver/__init__.py diff --git a/osdf/optimizers/pciopt/solver/min_confusion.mzn b/apps/pci/optimizers/solver/min_confusion.mzn index ff56c18..ff56c18 100644 --- a/osdf/optimizers/pciopt/solver/min_confusion.mzn +++ b/apps/pci/optimizers/solver/min_confusion.mzn diff --git a/osdf/optimizers/pciopt/solver/min_confusion_inl.mzn b/apps/pci/optimizers/solver/min_confusion_inl.mzn index 0f0fc91..e677e27 100644 --- a/osdf/optimizers/pciopt/solver/min_confusion_inl.mzn +++ b/apps/pci/optimizers/solver/min_confusion_inl.mzn @@ -52,6 +52,14 @@ int: NUM_IGNORABLE_NEIGHBOR_LINKS; % of the links, like the previous structures. array[1..NUM_IGNORABLE_NEIGHBOR_LINKS, 1..2] of int: IGNORABLE_NEIGHBOR_LINKS; +% ids of cells for which the pci should remain unchanged +set of int: PCI_UNCHANGEABLE_CELLS; + +% This array has the original pcis of all the cells. array is indexed by the ids +% of the cell. eg. ORIGINAL_PCIS[3] returns the pci of cell whose id is 3. +% ids start from 0 +array[1..NUM_NODES] of 0..NUM_PCIS-1: ORIGINAL_PCIS; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Decision variables %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -65,6 +73,14 @@ array[1..NUM_IGNORABLE_NEIGHBOR_LINKS] of var 0..1: used_ignorables; % Constraints %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% fixed pci cells +constraint +if(length(PCI_UNCHANGEABLE_CELLS) !=0) then +forall(i in PCI_UNCHANGEABLE_CELLS)( + pci[i] == ORIGINAL_PCIS[i+1] +) +endif; + % Direct neighbors must have different PCIs for avoid **COLLISION**. % Forced links. constraint diff --git a/apps/pci/optimizers/solver/ml_model.py b/apps/pci/optimizers/solver/ml_model.py new file mode 100644 index 0000000..c239be8 --- /dev/null +++ b/apps/pci/optimizers/solver/ml_model.py @@ -0,0 +1,72 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 apps.pci.optimizers.solver.pci_utils import get_id +from osdf.adapters.dcae import des +from osdf.adapters.dcae.des import DESException +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import error_log + + +class MlModel(object): + def __init__(self): + self.config = osdf_config.core['PCI'] + + def get_additional_inputs(self, dzn_data, network_cell_info): + """Add/update additional info to the existing models. + + The method returns nothing. Instead, it modifies the dzn_data + :params: dzn_data: map with data for the optimization + """ + self.compute_ml_model(dzn_data, network_cell_info) + + def compute_ml_model(self, dzn_data, network_cell_info): + average_ho_threshold = self.config['ML']['average_ho_threshold'] + latest_ho_threshold = self.config['ML']['latest_ho_threshold'] + + fixed_cells = set() + for cell in network_cell_info['cell_list']: + cell_id = cell['cell_id'] + average_ho, latest_ho = self.get_ho_details(cell['cell_id']) + if average_ho > average_ho_threshold or latest_ho > latest_ho_threshold: + fixed_cells.add(get_id(network_cell_info, cell_id)) + + fixed_cells.update(dzn_data.get('PCI_UNCHANGEABLE_CELLS', [])) + dzn_data['PCI_UNCHANGEABLE_CELLS'] = fixed_cells + + def get_ho_details(self, cell_id): + service_id = self.config['DES']['service_id'] + request_data = self.config['DES']['filter'] + request_data['cell_id'] = cell_id + try: + result = des.extract_data(service_id, json.dumps(request_data)) + except DESException as e: + error_log.error("Error while calling DES {}".format(e)) + return 0, 0 + + if not result: + return 0, 0 + + ho_list = [] + for pm_data in result: + ho = pm_data['overallHoAtt'] + ho_list.append(ho) + + return sum(ho_list) / len(ho_list), ho_list[0] diff --git a/osdf/optimizers/pciopt/solver/no_conflicts_no_confusion.mzn b/apps/pci/optimizers/solver/no_conflicts_no_confusion.mzn index 0a9b3e3..f059d4a 100644 --- a/osdf/optimizers/pciopt/solver/no_conflicts_no_confusion.mzn +++ b/apps/pci/optimizers/solver/no_conflicts_no_confusion.mzn @@ -44,6 +44,15 @@ int: NUM_SECOND_LEVEL_NEIGHBORS; % Each line represents an edge between undirect neighbors as defined before. array[1..NUM_SECOND_LEVEL_NEIGHBORS, 1..2] of int: SECOND_LEVEL_NEIGHBORS; +% ids of cells for which the pci should remain unchanged +set of int: PCI_UNCHANGEABLE_CELLS; + +% This array has the original pcis of all the cells. array is indexed by the ids +% of the cell. eg. ORIGINAL_PCIS[3] returns the pci of cell whose id is 3. +% ids start from 0 +% array[0..NUM_NODES-1] of 0..NUM_PCIS-1: ORIGINAL_PCIS; +array[1..NUM_NODES] of 0..NUM_PCIS-1: ORIGINAL_PCIS; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Decision variables %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -55,6 +64,14 @@ array[0..NUM_NODES-1] of var 0..NUM_PCIS-1: pci; % Constraints %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +constraint +if(length(PCI_UNCHANGEABLE_CELLS) !=0) then +forall(i in PCI_UNCHANGEABLE_CELLS)( + pci[i] == ORIGINAL_PCIS[i+1] +) +endif; + + % Direct neighbors must have different PCIs for avoid **COLLISION**. constraint forall(i in 1..NUM_NEIGHBORS)( diff --git a/osdf/optimizers/pciopt/solver/optimizer.py b/apps/pci/optimizers/solver/optimizer.py index 0a6d5a4..13298ed 100644 --- a/osdf/optimizers/pciopt/solver/optimizer.py +++ b/apps/pci/optimizers/solver/optimizer.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,31 +17,69 @@ # ------------------------------------------------------------------------- # +from collections import defaultdict import itertools import os -from collections import defaultdict - import pymzn -from .pci_utils import get_id +from apps.pci.optimizers.solver.ml_model import MlModel +from apps.pci.optimizers.solver.pci_utils import get_id +from apps.pci.optimizers.solver.pci_utils import mapping +from osdf.config.base import osdf_config BASE_DIR = os.path.dirname(__file__) +cell_id_mapping = dict() +id_cell_mapping = dict() def pci_optimize(network_cell_info, cell_info_list, request_json): + global cell_id_mapping, id_cell_mapping + cell_id_mapping, id_cell_mapping = mapping(network_cell_info) + original_pcis = get_original_pci_list(network_cell_info) + unchangeable_pcis = get_ids_of_fixed_pci_cells(request_json['cellInfo'].get('fixedPCICells', [])) neighbor_edges = get_neighbor_list(network_cell_info) second_level_edges = get_second_level_neighbor(network_cell_info) ignorable_links = get_ignorable_links(network_cell_info, request_json) anr_flag = is_anr(request_json) - dzn_data = build_dzn_data(cell_info_list, ignorable_links, neighbor_edges, second_level_edges, anr_flag) + dzn_data = build_dzn_data(cell_info_list, ignorable_links, neighbor_edges, second_level_edges, anr_flag, + original_pcis, unchangeable_pcis) + + ml_enabled = osdf_config.core['PCI']['ml_enabled'] + if ml_enabled: + MlModel().get_additional_inputs(dzn_data, network_cell_info) return build_pci_solution(dzn_data, ignorable_links, anr_flag) +def get_ids_of_fixed_pci_cells(fixed_pci_list): + fixed_pci_ids = set() + for cell in fixed_pci_list: + fixed_pci_ids.add(cell_id_mapping[cell]) + return fixed_pci_ids + + +def get_cell_id_pci_mapping(network_cell_info): + original_pcis = dict() + for cell in network_cell_info['cell_list']: + for nbr in cell['nbr_list']: + if cell_id_mapping[nbr['targetCellId']] not in original_pcis: + original_pcis[cell_id_mapping[nbr['targetCellId']]] = nbr['pciValue'] + return original_pcis + + +def get_original_pci_list(network_cell_info): + cell_id_pci_mapping = get_cell_id_pci_mapping(network_cell_info) + original_pcis_list = [] + for i in range(len(cell_id_pci_mapping)): + original_pcis_list.append(cell_id_pci_mapping.get(i)) + return original_pcis_list + + def build_pci_solution(dzn_data, ignorable_links, anr_flag): mzn_solution = solve(get_mzn_model(anr_flag), dzn_data) - + if mzn_solution == 'UNSATISFIABLE': + return mzn_solution solution = {'pci': mzn_solution[0]['pci']} if anr_flag: @@ -55,14 +94,17 @@ def build_pci_solution(dzn_data, ignorable_links, anr_flag): return solution -def build_dzn_data(cell_info_list, ignorable_links, neighbor_edges, second_level_edges, anr_flag): +def build_dzn_data(cell_info_list, ignorable_links, neighbor_edges, second_level_edges, anr_flag, original_pcis, + unchangeable_pcis): dzn_data = { 'NUM_NODES': len(cell_info_list), 'NUM_PCIS': len(cell_info_list), 'NUM_NEIGHBORS': len(neighbor_edges), 'NEIGHBORS': get_list(neighbor_edges), 'NUM_SECOND_LEVEL_NEIGHBORS': len(second_level_edges), - 'SECOND_LEVEL_NEIGHBORS': get_list(second_level_edges) + 'SECOND_LEVEL_NEIGHBORS': get_list(second_level_edges), + 'PCI_UNCHANGEABLE_CELLS': unchangeable_pcis, + 'ORIGINAL_PCIS': original_pcis } if anr_flag: dzn_data['NUM_IGNORABLE_NEIGHBOR_LINKS'] = len(ignorable_links) diff --git a/osdf/optimizers/pciopt/solver/pci_utils.py b/apps/pci/optimizers/solver/pci_utils.py index 04829cf..7db3a6f 100644 --- a/osdf/optimizers/pciopt/solver/pci_utils.py +++ b/apps/pci/optimizers/solver/pci_utils.py @@ -16,6 +16,13 @@ # ------------------------------------------------------------------------- # +def mapping(network_cell_info): + cell_id_mapping= dict() + id_cell_mapping = dict() + for i in network_cell_info['cell_list']: + cell_id_mapping[i['cell_id']] = i['id'] + id_cell_mapping[i['id']] = i['cell_id'] + return cell_id_mapping, id_cell_mapping def get_id(network_cell_info, cell_id): for i in network_cell_info['cell_list']: diff --git a/apps/placement/__init__.py b/apps/placement/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/placement/__init__.py diff --git a/apps/placement/models/__init__.py b/apps/placement/models/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/placement/models/__init__.py diff --git a/apps/placement/models/api/__init__.py b/apps/placement/models/api/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/placement/models/api/__init__.py diff --git a/osdf/models/api/placementRequest.py b/apps/placement/models/api/placementRequest.py index 7d6bde4..e04c2af 100644 --- a/osdf/models/api/placementRequest.py +++ b/apps/placement/models/api/placementRequest.py @@ -16,7 +16,7 @@ # ------------------------------------------------------------------------- # -from .common import OSDFModel +from osdf.models.api.common import OSDFModel from schematics.types import BaseType, StringType, URLType, IntType, BooleanType from schematics.types.compound import ModelType, ListType, DictType @@ -71,7 +71,6 @@ class PlacementDemand(OSDFModel): resourceModuleName = StringType(required=True) serviceResourceId = StringType(required=True) tenantId = StringType() - unique = BooleanType() # to be implemented on the policy level resourceModelInfo = ModelType(ModelMetaData, required=True) existingCandidates = ListType(ModelType(Candidates)) excludedCandidates = ListType(ModelType(Candidates)) @@ -103,4 +102,4 @@ class PlacementAPI(OSDFModel): requestInfo = ModelType(RequestInfo, required=True) placementInfo = ModelType(PlacementInfo, required=True) licenseInfo = ModelType(LicenseInfo) - serviceInfo = ModelType(ServiceInfo, required=True) + serviceInfo = ModelType(ServiceInfo, required=True)
\ No newline at end of file diff --git a/osdf/models/api/placementResponse.py b/apps/placement/models/api/placementResponse.py index 063a9a8..13b8d7a 100644 --- a/osdf/models/api/placementResponse.py +++ b/apps/placement/models/api/placementResponse.py @@ -16,7 +16,7 @@ # ------------------------------------------------------------------------- # -from .common import OSDFModel +from osdf.models.api.common import OSDFModel from schematics.types import BaseType, StringType from schematics.types.compound import ModelType, ListType, DictType diff --git a/apps/placement/optimizers/__init__.py b/apps/placement/optimizers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/placement/optimizers/__init__.py diff --git a/osdf/optimizers/__init__.py b/apps/placement/optimizers/conductor/__init__.py index 4b25e5b..4b25e5b 100644 --- a/osdf/optimizers/__init__.py +++ b/apps/placement/optimizers/conductor/__init__.py diff --git a/apps/placement/optimizers/conductor/remote_opt_processor.py b/apps/placement/optimizers/conductor/remote_opt_processor.py new file mode 100644 index 0000000..2e681be --- /dev/null +++ b/apps/placement/optimizers/conductor/remote_opt_processor.py @@ -0,0 +1,178 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 jinja2 import Template +import json +from requests import RequestException +import traceback + +from apps.license.optimizers.simple_license_allocation import license_optim +from osdf.adapters.conductor import conductor +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log +from osdf.logging.osdf_logging import metrics_log +from osdf.logging.osdf_logging import MH +from osdf.operation.error_handling import build_json_error_body +from osdf.utils.interfaces import get_rest_client +from osdf.utils.mdc_utils import mdc_from_json + + +def conductor_response_processor(conductor_response, req_id, transaction_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", + "cloud": "cloudRegionId", "service": "serviceInstanceId", "is_rehome": "isRehome", + "location_id": "locationId", "location_type": "locationType", "directives": "oof_directives"} + for reco in conductor_response['plans'][0]['recommendations']: + for resource in reco.keys(): + c = reco[resource]['candidate'] + solution = { + 'resourceModuleName': resource, + 'serviceResourceId': reco[resource].get('service_resource_id', ""), + 'solution': {"identifierType": name_map.get(c['inventory_type'], c['inventory_type']), + 'identifiers': [c['candidate_id']], + 'cloudOwner': c.get('cloud_owner', "")}, + 'assignmentInfo': [] + } + for key, value in c.items(): + if key in ["location_id", "location_type", "is_rehome", "host_id"]: + try: + solution['assignmentInfo'].append({"key": name_map.get(key, key), "value": value}) + except KeyError: + debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info" + .format(key)) + + for key, value in reco[resource]['attributes'].items(): + try: + solution['assignmentInfo'].append({"key": name_map.get(key, key), "value": value}) + except KeyError: + debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info" + .format(key)) + composite_solutions.append(solution) + + request_status = "completed" if conductor_response['plans'][0]['status'] == "done" \ + else conductor_response['plans'][0]['status'] + status_message = conductor_response.get('plans')[0].get('message', "") + + solution_info = {} + if composite_solutions: + solution_info.setdefault('placementSolutions', []) + solution_info['placementSolutions'].append(composite_solutions) + + resp = { + "transactionId": transaction_id, + "requestId": req_id, + "requestStatus": request_status, + "statusMessage": status_message, + "solutions": solution_info + } + return resp + + +def conductor_no_solution_processor(conductor_response, request_id, transaction_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, license_solutions=[], + transactionId=transaction_id, + requestStatus="completed", statusMessage=status_message, json=json)) + + +def process_placement_opt(request_json, policies, osdf_config): + """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: + mdc_from_json(request_json) + 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 request_json.get('licenseInfo', {}).get('licenseDemands'): + license_info = license_optim(request_json) + + # Conductor only handles placement, only call Conductor if placementDemands exist + if request_json.get('placementInfo', {}).get('placementDemands'): + metrics_log.info(MH.requesting("placement/conductor", req_id)) + req_info = request_json['requestInfo'] + demands = request_json['placementInfo']['placementDemands'] + request_parameters = request_json['placementInfo']['requestParameters'] + service_info = request_json['serviceInfo'] + template_fields = { + 'location_enabled': True, + 'version': '2017-10-10' + } + resp = conductor.request(req_info, demands, request_parameters, service_info, template_fields, + osdf_config, policies) + if resp["plans"][0].get("recommendations"): + placement_response = conductor_response_processor(resp, req_id, transaction_id) + else: # "solved" but no solutions found + placement_response = conductor_no_solution_processor(resp, req_id, transaction_id) + 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, + "requestStatus": "completed", + "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/templates/plc_opt_request.jsont b/apps/placement/templates/plc_opt_request.jsont index a218b8a..a218b8a 100755 --- a/osdf/templates/plc_opt_request.jsont +++ b/apps/placement/templates/plc_opt_request.jsont diff --git a/osdf/templates/plc_opt_response.jsont b/apps/placement/templates/plc_opt_response.jsont index e5709e7..e5709e7 100755 --- a/osdf/templates/plc_opt_response.jsont +++ b/apps/placement/templates/plc_opt_response.jsont diff --git a/osdf/templates/policy_request.jsont b/apps/placement/templates/policy_request.jsont index 3a9e201..3a9e201 100755 --- a/osdf/templates/policy_request.jsont +++ b/apps/placement/templates/policy_request.jsont diff --git a/apps/route/__init__.py b/apps/route/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/route/__init__.py diff --git a/apps/route/optimizers/__init__.py b/apps/route/optimizers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/route/optimizers/__init__.py diff --git a/apps/route/optimizers/inter_domain_route_opt.py b/apps/route/optimizers/inter_domain_route_opt.py new file mode 100644 index 0000000..253c7b2 --- /dev/null +++ b/apps/route/optimizers/inter_domain_route_opt.py @@ -0,0 +1,370 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Fujitsu Limited 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 itertools +import json +import requests +from requests.auth import HTTPBasicAuth +import urllib3 + +from osdf.logging.osdf_logging import audit_log +import pymzn +from sklearn import preprocessing + +BASE_DIR = os.path.dirname(__file__) +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + +class InterDomainRouteOpt: + + """ + This values will need to deleted.. + only added for the debug purpose + """ + aai_headers = { + "X-TransactionId": "9999", + "X-FromAppId": "OOF", + "Accept": "application/json", + "Content-Type": "application/json", + } + + + def get_route(self, request, osdf_config): + """ + This method processes the mdons route request + and returns an optimised path for the given + two ports + """ + + try: + route_info = request["routeInfo"]["routeRequest"] + src_controller_id = route_info["srcDetails"]["controllerId"] + src_port_id = route_info["srcDetails"]["interfaceId"] + dst_controller_id = route_info["dstDetails"]["controllerId"] + dst_port_id = route_info["dstDetails"]["interfaceId"] + service_rate = route_info["serviceRate"] + dzn_data, mapping_table = self.build_dzn_data(osdf_config, src_controller_id, + dst_controller_id, service_rate) + audit_log.info("Dzn data") + audit_log.info(dzn_data) + mzn_model = os.path.join(BASE_DIR, 'route_opt.mzn') + links_list = self.find_suitable_path(mzn_model, dzn_data, mapping_table) + ordered_list = self.get_ordered_route_list(links_list, + src_controller_id, dst_controller_id) + solution = self.get_solution_object(ordered_list, src_port_id, dst_port_id) + return { + "requestId": request["requestInfo"]["requestId"], + "transactionId": request["requestInfo"]["transactionId"], + "statusMessage": "SUCCESS", + "requestStatus": "accepted", + "solutions": solution + } + except Exception as err: + audit_log.info(err) + raise err + + def get_solution_object(self, ordered_list, src_port_id, dst_port_id): + """ + :param ordered_list: service_route list + :param src_port_id: source port id of route + :param dst_port_id: destination port id of route + :return: solution object of the route respone + """ + service_route_list = [] + link_list = [] + for value in ordered_list: + service_route_object = {} + service_route_object["srcInterfaceId"] = src_port_id + service_route_object["dstInterfaceId"] = value["srcPortId"] + service_route_object["controllerId"] = value["srcControllerId"] + service_route_list.append(service_route_object) + link_list.append(value["linkName"]) + src_port_id = value["dstPortId"] + dst_controller_id = value["dstControllerId"] + service_route_object = {} + service_route_object["srcInterfaceId"] = src_port_id + service_route_object["dstInterfaceId"] = dst_port_id + service_route_object["controllerId"] = dst_controller_id + service_route_list.append(service_route_object) + route_info_object = { + "serviceRoute" : service_route_list, + "linkList" : link_list + } + solution = { + "routeInfo" : route_info_object + } + return solution + + + def get_ordered_route_list(self, link_list, src_controller_id, dst_controller_id): + """ + :param link_list: link list from the minizinc response + :param src_controller_id: source port id of route + :param dst_controller_id: destination port id of route + :return: route list in order + """ + ordered_link_list = [] + flag = True + while flag: + for item in link_list: + if item["srcControllerId"] == src_controller_id: + ordered_link_list.append(item) + src_controller_id = item["dstControllerId"] + if src_controller_id == dst_controller_id: + flag = False + return ordered_link_list + + + def find_suitable_path(self, mzn_model, dzn_data, mapping_table): + """ + :param mzn_model: minizinc model details + :param dzn_data: minizinc data + :param mapping_table: list that maintains AAI link details + :return: list of link from after running minizinc + """ + minizinc_solution = self.solve(mzn_model, dzn_data) + audit_log.info("Minizinc Solution ==========>") + routes = list(minizinc_solution) + audit_log.info(routes) + try: + arr = routes[0]['x'] + except Exception as err: + audit_log.info("No minizinc solutions found") + raise err + links_list = [] + for i in range(0, len(routes[0]['x'])): + if arr[i] == 1: + links_list.append(mapping_table[i]) + return links_list + + + def process_inter_domain_link(self, logical_link, osdf_config): + """ + :param logical_link: logical links from AAI + :param osdf_config: OSDF config details + :return: list of link object with src and dst controller details + """ + link_details = {} + link_details["linkName"] = logical_link["link-name"] + relationship = logical_link["relationship-list"]["relationship"] + flag = 1 + + for value in relationship: + if value["related-to"] == "p-interface" and flag == 1: + src_port_id = value["relationship-data"][1]["relationship-value"] + src_controller_id = self.get_controller_for_interface(osdf_config, src_port_id) + link_details["srcPortId"] = src_port_id + link_details["srcControllerId"] = src_controller_id + flag += 1 + elif value["related-to"] == "p-interface" and flag == 2: + dest_port_id = value["relationship-data"][1]["relationship-value"] + dest_controller_id = self.get_controller_for_interface(osdf_config, dest_port_id) + link_details["dstPortId"] = dest_port_id + link_details["dstControllerId"] = dest_controller_id + return link_details + + + def prepare_map_table(self, osdf_config, logical_links): + """ + :param logical_links: logical links from AAI + :param osdf_config: OSDF config details + :return: list of link object with src and dst controller details + """ + results = map(self.process_inter_domain_link, logical_links, + itertools.repeat(osdf_config, len(logical_links))) + new_results = list(results) + + new_list = [] + new_list += new_results + for i in new_results: + link_details = {} + link_details["linkName"] = i["linkName"] + link_details["srcPortId"] = i["dstPortId"] + link_details["srcControllerId"] = i["dstControllerId"] + link_details["dstPortId"] = i["srcPortId"] + link_details["dstControllerId"] = i["srcControllerId"] + new_list.append(link_details) + return new_list + + + def solve(self, mzn_model, dzn_data): + """ + :param mzn_model: minizinc template + :param dzn_data: minizinc data model + :return: minizinc response + """ + return pymzn.minizinc(mzn=mzn_model, data=dzn_data) + + + def get_links_based_on_bandwidth_attributes(self, logical_links_list, + osdf_config, service_rate): + """ + This method filters the logical links based on the + bandwidth attribute availability of the interfaces + from AAI + :return: filtered_list[] + """ + filtered_list = [] + for logical_link in logical_links_list: + relationship = logical_link["relationship-list"]["relationship"] + count = 0 + for value in relationship: + if value["related-to"] == "p-interface": + interface_url = value["related-link"] + if self.get_available_bandwidth_aai(interface_url, osdf_config, service_rate): + count += 1 + if count == 2: + filtered_list.append(logical_link) + + return filtered_list + + + def build_dzn_data(self, osdf_config, src_controller_id, dst_controller_id, service_rate): + """ + :param osdf_config: OSDF config details + :param src_controller_id: controller Id of the source port + :param dst_controller_id: controller id of the destination port + :param service_rate: service rate + :return: mapping atble which maintains link details from AAI + and minizinc data model to be used by template + """ + logical_links = self.get_inter_domain_links(osdf_config) + logical_links_list = logical_links["logical-link"] + mapping_table = self.prepare_map_table(osdf_config, + self.get_links_based_on_bandwidth_attributes(logical_links_list, osdf_config, service_rate)) + + edge_start = [] + edge_end = [] + for item in mapping_table: + edge_start.append(item["srcControllerId"]) + edge_end.append(item["dstControllerId"]) + link_cost = [] + for k in range(0, len(edge_start)): + link_cost.append(1) + list_controllers = self.get_controllers_from_aai(osdf_config) + le = preprocessing.LabelEncoder() + le.fit(list_controllers) + + start_edge = le.transform(edge_start) + end_edge = le.transform(edge_end) + source = le.transform([src_controller_id]) + destination = le.transform([dst_controller_id]) + + final_dzn_start_arr = [] + for i in start_edge: + final_dzn_start_arr.append(i) + + final_dzn_end_arr = [] + for j in end_edge: + final_dzn_end_arr.append(j) + + contollers_length = len(list_controllers) + no_of_edges = len(final_dzn_start_arr) + dzn_data = { + 'N': contollers_length, + 'M': no_of_edges, + 'Edge_Start': final_dzn_start_arr, + 'Edge_End': final_dzn_end_arr, + 'L': link_cost, + 'Start': source[0], + 'End' : destination[0] + } + return dzn_data, mapping_table + + + def get_inter_domain_links(self, osdf_config): + """ + This method returns list of all cross ONAP links + from /aai/v19/network/logical-links?link-type=inter-domain&operational-status="Up" + :return: logical-links[] + """ + + config = osdf_config.deployment + aai_url = config["aaiUrl"] + aai_req_url = aai_url + config["aaiGetInterDomainLinksUrl"] + response = requests.get(aai_req_url, headers=self.aai_headers, + auth=HTTPBasicAuth("AAI", "AAI"), verify=False) + if response.status_code == 200: + return response.json() + + + def get_controller_for_interface(self, osdf_config, port_id): + """ + This method returns returns the controller id + given a p-interface from the below query + :return: controller_id + """ + data = { + "start": ["external-system"], + "query": "query/getDomainController?portid=" + } + query = data.get("query") + port_id + data.update(query=query) + config = osdf_config.deployment + aai_url = config["aaiUrl"] + aai_req_url = aai_url + config["controllerQueryUrl"] + response = requests.put(aai_req_url, data=json.dumps(data), + headers=self.aai_headers, + auth=HTTPBasicAuth("AAI", "AAI"), + verify=False) + if response.status_code == 200: + response_body = response.json() + return response_body["results"][0]["esr-thirdparty-sdnc"]["thirdparty-sdnc-id"] + + + def get_controllers_from_aai(self, osdf_config): + """ + This method returns returns the list of + controller names in AAI + :return: controllers_list[] + """ + controllers_list = [] + config = osdf_config.deployment + aai_url = config["aaiUrl"] + aai_req_url = aai_url + config["aaiGetControllersUrl"] + response = requests.get(aai_req_url, + headers=self.aai_headers, + auth=HTTPBasicAuth("AAI", "AAI"), + verify=False) + if response.status_code == 200: + response_body = response.json() + esr_thirdparty_list = response_body["esr-thirdparty-sdnc"] + + for item in esr_thirdparty_list: + controllers_list.append(item["thirdparty-sdnc-id"]) + return controllers_list + + + def get_available_bandwidth_aai(self, interface_url, osdf_config, service_rate): + """ + Checks if the given interface has the required bandwidth + :return: boolean flag + """ + config = osdf_config.deployment + aai_url = config["aaiUrl"] + aai_req_url = aai_url + interface_url + "?depth=all" + response = requests.get(aai_req_url, + headers=self.aai_headers, + auth=HTTPBasicAuth("AAI", "AAI"), verify=False) + if response.status_code == 200: + response_body = response.json() + available_bandwidth = response_body["bandwidth-attributes"]["bandwidth-attribute"][0]["available-bandwidth-map"]["available-bandwidth"] + for i in available_bandwidth: + if i["odu-type"] == service_rate and i["number"] > 0: + return True diff --git a/apps/route/optimizers/route_opt.mzn b/apps/route/optimizers/route_opt.mzn new file mode 100644 index 0000000..7aa73cb --- /dev/null +++ b/apps/route/optimizers/route_opt.mzn @@ -0,0 +1,53 @@ + +% Number of nodes +int: N; + % Start node +0..N-1: Start; + % End node +0..N-1: End; + % Number of edges (directed arcs) +int: M; + % The actual edges +set of int: Edges = 1..M; + % Edge lengths +array[Edges] of int: L; + % Edge start node +array[Edges] of 0..N-1: Edge_Start; +array[Edges] of 0..N-1: Edge_End; + + % Variable indicating if edge is used +array[Edges] of var 0..1: x; + +constraint + forall( i in 0..N-1 ) ( + if i = Start then + % outgoing flow + sum(e in Edges where Edge_Start[e] = i)(x[e]) - + % incoming flow + sum(e in Edges where Edge_End[e] = i)(x[e]) + = 1 + elseif i = End then + sum(e in Edges where Edge_Start[e] = i)(x[e]) - + sum(e in Edges where Edge_End[e] = i)(x[e]) + = -1 + else + sum(e in Edges where Edge_Start[e] = i)(x[e]) - + sum(e in Edges where Edge_End[e] = i)(x[e]) + = 0 + endif + ); + + +solve minimize sum(e in Edges)( L[e] * x[e] ); +%solve satisfy; + +output ["Length: ", show(sum(e in Edges)(L[e] * x[e])), "\n"] ++ + ["Start : ", show(Start), "\n"] ++ + ["End : ", show(End), "\n\n"] ++ + ["Edges in shortest path:\n"] ++ + [ if fix(x[e]) = 1 + then show(Edge_Start[e]) ++ " -> " ++ show(Edge_End[e]) ++ "\n" + else "" + endif | e in Edges + ]; + diff --git a/apps/route/optimizers/simple_route_opt.py b/apps/route/optimizers/simple_route_opt.py new file mode 100644 index 0000000..9113516 --- /dev/null +++ b/apps/route/optimizers/simple_route_opt.py @@ -0,0 +1,266 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Huawei 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 +import json +from requests.auth import HTTPBasicAuth + +from osdf.utils.mdc_utils import mdc_from_json +from osdf.logging.osdf_logging import MH, audit_log, error_log, debug_log +import pymzn +from sklearn import preprocessing + +import os +BASE_DIR = os.path.dirname(__file__) + +class RouteOpt: + + """ + This values will need to deleted.. + only added for the debug purpose + """ + # DNS server and standard port of AAI.. + # TODO: read the port from the configuration and add to DNS + aai_headers = { + "X-TransactionId": "9999", + "X-FromAppId": "OOF", + "Accept": "application/json", + "Content-Type": "application/json", + } + + def is_cross_onap_link(self, logical_link): + """ + This method checks if cross link is cross onap + :param logical_link: + :return: + """ + for relationship in logical_link["relationship-list"]["relationship"]: + if relationship["related-to"] == "ext-aai-network": + return True + return False + + def get_links_name(self, routes,initial_start_edge,initial_end_edge, mappingTable): + routes=list(routes) + try: + arr=routes[0]['x'] + except Exception as err: + audit_log.info("No satisfiable solutions found") + raise err + listOfLinks=[] + for i in range(0, len(routes[0]['x'])): + individual_link = {} + if arr[i] == 1 : + # listOfLinks.append(self.fetchLogicalLinks(initial_start_edge[i], initial_end_edge[i], mappingTable)) + individual_link["link"] = mappingTable[initial_start_edge[i] + ":" + initial_end_edge[i]] + individual_link["start_node"] = initial_start_edge[i] + individual_link["end_node"] = initial_end_edge[i] + listOfLinks.append(individual_link) + + return listOfLinks + + def solve(self, mzn_model, dzn_data): + return pymzn.minizinc(mzn=mzn_model, data=dzn_data) + + def get_links(self, mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable): + routes = self.solve(mzn_model, dzn_data) + audit_log.info("mocked minizinc solution====>") + audit_log.info(routes) + + converted_links=self.get_links_name(routes, initial_start_edge,initial_end_edge, mappingTable) + audit_log.info("converted links===>") + audit_log.info(converted_links) + return converted_links + + def addition(self, data): + res = "" + if 'relationship-list' in data.keys(): + relationship = data["relationship-list"]["relationship"] + for index, eachItem in enumerate(relationship): + temp = eachItem["relationship-data"][0] + if index == len(relationship) - 1: + res += temp['relationship-value'] + else: + res += temp['relationship-value'] + ":" + + return data["link-name"], res + else: + return data["link-name"], res + + def create_map_table(self, logical_links): + result = map(self.addition, logical_links) + + parseTemplate = {} + + for eachItem in result: + parseTemplate[eachItem[1]] = eachItem[0] + audit_log.info("mapping table") + audit_log.info(parseTemplate) + return parseTemplate + + def build_dzn_data(self, src_access_node_id, dst_access_node_id, osdf_config): + Edge_Start = [] + Edge_End = [] + logical_links = self.get_logical_links(osdf_config) + + + logical_links = logical_links['logical-link'] + audit_log.info("mocked response of AAI received (logical links) successful===>") + audit_log.info(logical_links) + # prepare map table + mappingTable = self.create_map_table(logical_links) + audit_log.info("mapping table created successfully====>") + audit_log.info(mappingTable) + # take the logical link where both the p-interface in same onap + if logical_links is not None: + audit_log.info('logical links not empty=====>') + for logical_link in logical_links: + audit_log.info('logical_link') + audit_log.info(logical_link) + + if 'relationship-list' in logical_link.keys(): + if not self.is_cross_onap_link(logical_link): + # link is in local ONAP + audit_log.info('link is inside onap===>') + relationship = logical_link["relationship-list"]["relationship"] + + relationshipStartNode = relationship[0] + audit_log.info('relationshipStartNode') + audit_log.info(relationshipStartNode) + relationshipStartNodeID = relationshipStartNode["related-link"].split("/")[-4] + audit_log.info('relationshipStartNodeID') + audit_log.info(relationshipStartNodeID) + Edge_Start.append(relationshipStartNodeID) + + relationshipEndtNode = relationship[1] + relationshipEndNodeID = relationshipEndtNode["related-link"].split("/")[-4] + audit_log.info('relationshipEndNodeID') + audit_log.info(relationshipEndNodeID) + Edge_End.append(relationshipEndNodeID) + else: + continue + + audit_log.info("edge start and end array of i/p address are===>") + audit_log.info(Edge_Start) + audit_log.info(Edge_End) + # labeling ip to number for mapping + le = preprocessing.LabelEncoder() + le.fit(Edge_Start + Edge_End) + dzn_start_edge = le.transform(Edge_Start) + + final_dzn_start_arr = [] + for i in range(0, len(dzn_start_edge)): + final_dzn_start_arr.append(dzn_start_edge[i]) + + final_dzn_end_arr = [] + dzn_end_edge = le.transform(Edge_End) + for j in range(0, len(dzn_end_edge)): + final_dzn_end_arr.append(dzn_end_edge[j]) + + audit_log.info("start and end array that passed in dzn_data===>") + audit_log.info(final_dzn_start_arr) + audit_log.info(final_dzn_end_arr) + + link_cost = [] + for k in range(0, len(final_dzn_start_arr)): + link_cost.append(1) + + audit_log.info("src_access_node_id") + audit_log.info(src_access_node_id) + source= le.transform([src_access_node_id]) + audit_log.info("vallue of source===>") + audit_log.info(source) + if source in final_dzn_start_arr : + start = source[0] + audit_log.info("source node") + audit_log.info(start) + + audit_log.info("dst_access_node_id") + audit_log.info(dst_access_node_id) + destination= le.transform([dst_access_node_id]) + if destination in final_dzn_end_arr : + end = destination[0] + audit_log.info("destination node") + audit_log.info(end) + # data to be prepared in the below format: + dzn_data = { + 'N': self.total_node(final_dzn_start_arr + final_dzn_end_arr), + 'M': len(final_dzn_start_arr), + 'Edge_Start': final_dzn_start_arr, + 'Edge_End': final_dzn_end_arr, + 'L': link_cost, + 'Start': start, + 'End': end, + } + # can not do reverse mapping outside of this scope, so doing here + audit_log.info("reverse mapping after prepared dzn_data") + initial_start_edge=le.inverse_transform(final_dzn_start_arr) + initial_end_edge=le.inverse_transform(final_dzn_end_arr) + audit_log.info(initial_start_edge) + audit_log.info(initial_end_edge) + return dzn_data, initial_start_edge,initial_end_edge, mappingTable + + def total_node(self, node): + nodeSet = set() + for i in range(0, len(node)): + nodeSet.add(node[i]) + total_node = len(nodeSet) + return total_node + + def get_route(self, request, osdf_config): + """ + This method checks + :param logical_link: + :return: + """ + try: + routeInfo = request["routeInfo"]["routeRequests"] + routeRequest = routeInfo[0] + src_access_node_id = routeRequest["srcPort"]["accessNodeId"] + dst_access_node_id = routeRequest["dstPort"]["accessNodeId"] + + dzn_data, initial_start_edge, initial_end_edge, mappingTable = self.build_dzn_data(src_access_node_id, dst_access_node_id, osdf_config) + #mzn_model = "/home/root1/Videos/projects/osdf/test/functest/simulators/osdf/optimizers/routeopt/route_opt.mzn" + mzn_model = os.path.join(BASE_DIR, 'route_opt.mzn') + + routeSolutions = self.get_links(mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable) + + return { + "requestId": request["requestInfo"]["requestId"], + "transactionId": request["requestInfo"]["transactionId"], + "statusMessage": " ", + "requestStatus": "accepted", + "solutions": routeSolutions + } + except Exception as err: + audit_log.info(err) + raise err + + def get_logical_links(self, osdf_config): + """ + This method returns list of all cross ONAP links + from /aai/v14/network/logical-links?operation-status="Up" + :return: logical-links[] + """ + + config = osdf_config.deployment + aai_url = config["aaiUrl"] + aai_req_url = aai_url + config["aaiGetLinksUrl"] + + response = requests.get(aai_req_url,headers=self.aai_headers,auth=HTTPBasicAuth("AAI", "AAI"),verify=False) + if response.status_code == 200: + return response.json()
\ No newline at end of file diff --git a/apps/slice_selection/__init__.py b/apps/slice_selection/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/slice_selection/__init__.py diff --git a/osdf/optimizers/routeopt/__init__.py b/apps/slice_selection/models/api/__init__.py index c235f2a..b45f74d 100644 --- a/osdf/optimizers/routeopt/__init__.py +++ b/apps/slice_selection/models/api/__init__.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (c) 2018 Huawei Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/apps/slice_selection/models/api/nsi_selection_request.py b/apps/slice_selection/models/api/nsi_selection_request.py new file mode 100644 index 0000000..b395012 --- /dev/null +++ b/apps/slice_selection/models/api/nsi_selection_request.py @@ -0,0 +1,62 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.models.api.common import OSDFModel +from schematics.types import BaseType +from schematics.types import BooleanType +from schematics.types.compound import DictType +from schematics.types.compound import ListType +from schematics.types.compound import ModelType +from schematics.types import IntType +from schematics.types import StringType +from schematics.types import URLType + + +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) + callbackHeader = DictType(BaseType) + timeout = IntType() + numSolutions = IntType() + addtnlArgs = DictType(BaseType) + + +class NxTInfo(OSDFModel): + """Information about NST/NSST model""" + invariantUUID = StringType(required=True) + UUID = StringType(required=True) + name = StringType(required=True) + + +class SubnetCapability(OSDFModel): + """Subnet capability of every subnet""" + domainType = StringType(required=True) + capabilityDetails = DictType(BaseType, required=True) + + +class NSISelectionAPI(OSDFModel): + """Request for nsi selection (specific to optimization and additional metadata""" + requestInfo = ModelType(RequestInfo, required=True) + NSTInfo = ModelType(NxTInfo, required=True) + NSSTInfo = ListType(ModelType(NxTInfo), required=False) + serviceProfile = DictType(BaseType, required=True) + subnetCapabilities = ListType(ModelType(SubnetCapability), required=True) + preferReuse = BooleanType() diff --git a/apps/slice_selection/models/api/nsi_selection_response.py b/apps/slice_selection/models/api/nsi_selection_response.py new file mode 100644 index 0000000..3c6d35b --- /dev/null +++ b/apps/slice_selection/models/api/nsi_selection_response.py @@ -0,0 +1,54 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.models.api.common import OSDFModel +from schematics.types import BaseType, StringType, BooleanType +from schematics.types.compound import ModelType, ListType, DictType + + +# TODO: update osdf.models +class SharedNSISolution(OSDFModel): + """Represents the shared NSI Solution object""" + invariantUUID = StringType(required=True) + UUID = StringType(required=True) + NSIName = StringType(required=True) + NSIId = StringType(required=True) + matchLevel = StringType(required=True) + + +class NewNSISolution(OSDFModel): + """Represents the New NSI Solution object containing tuple of slice profiles""" + sliceProfiles = ListType(DictType(BaseType), required=True) + matchLevel = StringType(required=True) + + +class NSISolution(OSDFModel): + """Represents the NSI Solution object""" + """This solution object contains either sharedNSISolution or newNSISolution""" + existingNSI = BooleanType(required=True) + sharedNSISolution = ModelType(SharedNSISolution) + newNSISolution = ModelType(NewNSISolution) + + +class NSISelectionResponse(OSDFModel): + """Response sent to NSMF(SO)""" + transactionId = StringType(required=True) + requestId = StringType(required=True) + requestStatus = StringType(required=True) + solutions = ListType(ModelType(NSISolution), required=True) + statusMessage = StringType() diff --git a/apps/slice_selection/models/api/nssi_selection_request.py b/apps/slice_selection/models/api/nssi_selection_request.py new file mode 100644 index 0000000..c670abe --- /dev/null +++ b/apps/slice_selection/models/api/nssi_selection_request.py @@ -0,0 +1,41 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.models.api.common import OSDFModel +from schematics.types import BaseType, StringType, URLType, IntType +from schematics.types.compound import ModelType, DictType + +from apps.slice_selection.models.api.nsi_selection_request import NxTInfo + + +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) + callbackHeader = DictType(BaseType) + timeout = IntType() + numSolutions = IntType() + addtnlArgs = DictType(BaseType) + + +class NSSISelectionAPI(OSDFModel): + """Request for NSSI selection (specific to optimization and additional metadata""" + requestInfo = ModelType(RequestInfo, required=True) + NSSTInfo = ModelType(NxTInfo, required=True) + sliceProfile = DictType(BaseType, required=True) diff --git a/apps/slice_selection/models/api/nssi_selection_response.py b/apps/slice_selection/models/api/nssi_selection_response.py new file mode 100644 index 0000000..af67f65 --- /dev/null +++ b/apps/slice_selection/models/api/nssi_selection_response.py @@ -0,0 +1,40 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.models.api.common import OSDFModel +from schematics.types import StringType +from schematics.types.compound import ModelType, ListType + + +# TODO: update osdf.models +class SharedNSSISolution(OSDFModel): + """Represents the shared NSSI Solution object""" + invariantUUID = StringType(required=True) + UUID = StringType(required=True) + NSSIName = StringType(required=True) + NSSIId = StringType(required=True) + matchLevel = StringType(required=True) + + +class NSSISelectionResponse(OSDFModel): + """Response sent to NSSMF(SO)""" + transactionId = StringType(required=True) + requestId = StringType(required=True) + requestStatus = StringType(required=True) + solutions = ListType(ModelType(SharedNSSISolution), required=True) + statusMessage = StringType() diff --git a/osdf/optimizers/placementopt/__init__.py b/apps/slice_selection/optimizers/__init__.py index 4b25e5b..b45f74d 100644 --- a/osdf/optimizers/placementopt/__init__.py +++ b/apps/slice_selection/optimizers/__init__.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/apps/slice_selection/optimizers/conductor/__init__.py b/apps/slice_selection/optimizers/conductor/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/slice_selection/optimizers/conductor/__init__.py diff --git a/apps/slice_selection/optimizers/conductor/remote_opt_processor.py b/apps/slice_selection/optimizers/conductor/remote_opt_processor.py new file mode 100644 index 0000000..68c9409 --- /dev/null +++ b/apps/slice_selection/optimizers/conductor/remote_opt_processor.py @@ -0,0 +1,134 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +""" +Module for processing slice selection request +""" + +from requests import RequestException +from threading import Thread +import traceback + +from apps.slice_selection.optimizers.conductor.response_processor import ResponseProcessor +from osdf.adapters.conductor import conductor +from osdf.adapters.policy.interface import get_policies +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log +from osdf.utils.interfaces import get_rest_client +from osdf.utils.mdc_utils import mdc_from_json + + +class SliceSelectionOptimizer(Thread): + def __init__(self, osdf_config, slice_config, request_json, model_type): + super().__init__() + self.osdf_config = osdf_config + self.slice_config = slice_config + self.request_json = request_json + self.model_type = model_type + self.response_processor = ResponseProcessor(request_json['requestInfo'], slice_config) + + def run(self): + self.process_slice_selection_opt() + + def process_slice_selection_opt(self): + """Process the slice selection request from the API layer""" + req_info = self.request_json['requestInfo'] + rc = get_rest_client(self.request_json, service='so') + + try: + if self.model_type == 'NSSI' \ + and self.request_json['sliceProfile'].get('resourceSharingLevel', "") \ + in ['not-shared', 'non-shared']: + final_response = self.response_processor.get_slice_selection_response([]) + + else: + final_response = self.do_slice_selection() + + except Exception as ex: + error_log.error("Error for {} {}".format(req_info.get('requestId'), + traceback.format_exc())) + error_message = str(ex) + final_response = self.response_processor.process_error_response(error_message) + + try: + rc.request(json=final_response, noresponse=True) + except RequestException: + error_log.error("Error sending asynchronous notification for {} {}".format(req_info['request_id'], + traceback.format_exc())) + + def do_slice_selection(self): + req_info = self.request_json['requestInfo'] + app_info = self.slice_config['app_info'][self.model_type] + mdc_from_json(self.request_json) + requirements = self.request_json.get(app_info['requirements_field'], {}) + model_info = self.request_json.get(app_info['model_info']) + model_name = model_info['name'] + policies = self.get_app_policies(model_name, app_info['app_name']) + request_parameters = self.get_request_parameters(requirements, model_info) + + demands = [ + { + "resourceModuleName": model_name, + "resourceModelInfo": {} + } + ] + + try: + template_fields = { + 'location_enabled': False, + 'version': '2020-08-13' + } + resp = conductor.request(req_info, demands, request_parameters, {}, template_fields, + self.osdf_config, policies) + except RequestException as e: + resp = e.response.json() + error = resp['plans'][0]['message'] + if isinstance(error, list) and "Unable to find any" in error[0]: + return self.response_processor.get_slice_selection_response([]) + error_log.error('Error from conductor {}'.format(error)) + return self.response_processor.process_error_response(error) + + debug_log.debug("Response from conductor {}".format(str(resp))) + recommendations = resp["plans"][0].get("recommendations") + subnets = [subnet['domainType'] for subnet in self.request_json['subnetCapabilities']] \ + if self.request_json.get('subnetCapabilities') else [] + return self.response_processor.process_response(recommendations, model_info, subnets, self.model_type) + + def get_request_parameters(self, requirements, model_info): + camel_to_snake = self.slice_config['attribute_mapping']['camel_to_snake'] + request_params = {camel_to_snake[key]: value for key, value in requirements.items()} + subnet_capabilities = self.request_json.get('subnetCapabilities') + if subnet_capabilities: + for subnet_capability in subnet_capabilities: + domain_type = f"{subnet_capability['domainType']}_" + capability_details = subnet_capability['capabilityDetails'] + for key, value in capability_details.items(): + request_params[f"{domain_type}{camel_to_snake[key]}"] = value + request_params.update(model_info) + return request_params + + def get_app_policies(self, model_name, app_name): + policy_request_json = self.request_json.copy() + policy_request_json['serviceInfo'] = {'serviceName': model_name} + if 'serviceProfile' in self.request_json: + slice_scope = self.request_json['serviceProfile']['resourceSharingLevel'] + if 'preferReuse' in self.request_json and slice_scope == "shared": + slice_scope = slice_scope + "," + ("reuse" if self.request_json['preferReuse'] else "create_new") + policy_request_json['slice_scope'] = slice_scope + debug_log.debug("policy_request_json {}".format(str(policy_request_json))) + return get_policies(policy_request_json, app_name) diff --git a/apps/slice_selection/optimizers/conductor/response_processor.py b/apps/slice_selection/optimizers/conductor/response_processor.py new file mode 100644 index 0000000..2357ab9 --- /dev/null +++ b/apps/slice_selection/optimizers/conductor/response_processor.py @@ -0,0 +1,108 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +""" +Module for processing response from conductor for slice selection +""" + +import re + + +class ResponseProcessor(object): + def __init__(self, request_info, slice_config): + self.request_info = request_info + self.slice_config = slice_config + + def process_response(self, recommendations, model_info, subnets, model_type): + """Process conductor response to form the response for the API request + + :param recommendations: recommendations from conductor + :param model_info: model info from the request + :param subnets: list of subnets + :param model_type: NSI or NSSI + :return: response json as a dictionary + """ + if not recommendations: + return self.get_slice_selection_response([]) + model_name = model_info['name'] + solutions = [self.get_solution_from_candidate(rec[model_name]['candidate'], model_info, subnets, model_type) + for rec in recommendations] + return self.get_slice_selection_response(solutions) + + def get_solution_from_candidate(self, candidate, model_info, subnets, model_type): + if candidate['inventory_type'] == 'slice_profiles': + return { + 'existingNSI': False, + 'newNSISolution': { + 'sliceProfiles': self.get_slice_profiles_from_candidate(candidate, subnets) + } + } + elif model_type == 'NSSI': + return { + 'UUID': model_info['UUID'], + 'invariantUUID': model_info['invariantUUID'], + 'NSSIName': candidate['instance_name'], + 'NSSIId': candidate['instance_id'] + } + + elif model_type == 'NSI': + return { + 'existingNSI': True, + 'sharedNSISolution': { + 'UUID': model_info['UUID'], + 'invariantUUID': model_info['invariantUUID'], + 'NSIName': candidate['instance_name'], + 'NSIId': candidate['instance_id'] + } + } + + def get_slice_profiles_from_candidate(self, candidate, subnets): + slice_profiles = [] + for subnet in subnets: + slice_profile = {self.get_profile_attribute(k, subnet): v for k, v in candidate.items() + if k.startswith(subnet)} + slice_profile['domainType'] = subnet + slice_profiles.append(slice_profile) + return slice_profiles + + def get_profile_attribute(self, attribute, subnet): + snake_to_camel = self.slice_config['attribute_mapping']['snake_to_camel'] + return snake_to_camel[re.sub(f'^{subnet}_', '', attribute)] + + def process_error_response(self, error_message): + """Form response message from the error message + + :param error_message: error message while processing the request + :return: response json as dictionary + """ + return {'requestId': self.request_info['requestId'], + 'transactionId': self.request_info['transactionId'], + 'requestStatus': 'error', + 'statusMessage': error_message} + + def get_slice_selection_response(self, solutions): + """Get NSI selection response from final solution + + :param solutions: final solutions + :return: NSI selection response to send back as dictionary + """ + return {'requestId': self.request_info['requestId'], + 'transactionId': self.request_info['transactionId'], + 'requestStatus': 'completed', + 'statusMessage': '', + 'solutions': solutions} diff --git a/osdf/templates/cms_opt_request.jsont b/apps/templates/cms_opt_request.jsont index 006562b..006562b 100755 --- a/osdf/templates/cms_opt_request.jsont +++ b/apps/templates/cms_opt_request.jsont diff --git a/osdf/templates/cms_opt_request.jsont_1707_v1 b/apps/templates/cms_opt_request.jsont_1707_v1 index 75ecbe5..75ecbe5 100755 --- a/osdf/templates/cms_opt_request.jsont_1707_v1 +++ b/apps/templates/cms_opt_request.jsont_1707_v1 diff --git a/osdf/templates/cms_opt_request_1702.jsont b/apps/templates/cms_opt_request_1702.jsont index bcafa45..bcafa45 100755 --- a/osdf/templates/cms_opt_request_1702.jsont +++ b/apps/templates/cms_opt_request_1702.jsont diff --git a/osdf/templates/cms_opt_response.jsont b/apps/templates/cms_opt_response.jsont index a8817df..a8817df 100644 --- a/osdf/templates/cms_opt_response.jsont +++ b/apps/templates/cms_opt_response.jsont diff --git a/osdf/templates/license_opt_request.jsont b/apps/templates/license_opt_request.jsont index 7baa759..7baa759 100644 --- a/osdf/templates/license_opt_request.jsont +++ b/apps/templates/license_opt_request.jsont diff --git a/osdf/templates/test_cms_nb_req_from_client.jsont b/apps/templates/test_cms_nb_req_from_client.jsont index a60c8ff..a60c8ff 100755 --- a/osdf/templates/test_cms_nb_req_from_client.jsont +++ b/apps/templates/test_cms_nb_req_from_client.jsont diff --git a/osdf/templates/test_plc_nb_req_from_client.jsont b/apps/templates/test_plc_nb_req_from_client.jsont index 998ffb3..998ffb3 100755 --- a/osdf/templates/test_plc_nb_req_from_client.jsont +++ b/apps/templates/test_plc_nb_req_from_client.jsont diff --git a/assembly.xml b/assembly.xml index 24379d4..b966691 100644 --- a/assembly.xml +++ b/assembly.xml @@ -28,8 +28,10 @@ <include>*.txt</include> <include>*.ini</include> <include>*.md</include> + <include>ssl_certs/**</include> </includes> <excludes> + <exclude>**/__pycache__/**</exclude> <exclude>**/*.pyc</exclude> <exclude>config/preload_secrets.yaml</exclude> </excludes> diff --git a/config/common_config.yaml b/config/common_config.yaml index 7eced05..713e15f 100644 --- a/config/common_config.yaml +++ b/config/common_config.yaml @@ -11,9 +11,12 @@ osdf_temp: # special configuration required for "workarounds" or testing local_policies: global_disabled: True local_placement_policies_enabled: True + local_slice_selection_policies_enabled: True + local_nst_selection_policies_enabled: True placement_policy_dir_vcpe: "./test/policy-local-files/" placement_policy_files_vcpe: # workaroud for policy platform glitches (or "work-arounds" for other components) - Affinity_vCPE_1.json + - Attribute_vNS_1.json #- Capacity_vGMuxInfra.json #- Capacity_vG_1.json - Distance_vG_1.json @@ -36,8 +39,25 @@ osdf_temp: # special configuration required for "workarounds" or testing placement_policy_files_vfw_td: - vnfPolicy_vFW_TD.json - vnfPolicy_vPGN_TD.json - - affinity_vFW_TD.json + - Affinity_vFW_TD.json - QueryPolicy_vFW_TD.json + slice_selection_policy_dir_embb-nst: "./test/policy-local-files/slice-selection-files/" + slice_selection_policy_files_embb-nst: + - query_policy_nsi.json + - threshold_policy_nsi.json + - vnf_policy_nsi_shared_case.json + nst_selection_policy_dir_embb-nst: "./test/policy-local-files/nst-selection-files/" + nst_selection_policy_files_embb-nst: + - query_policy_nst.json + - attribute_policy_nst.json + - vnf_policy_nst.json + nst_selection_policy_dir_nst: "./test/policy-local-files/nst-selection-files/" + nst_selection_policy_files_nst: + - query_policy_nst.json + - attribute_policy_nst.json + - vnf_policy_nst.json + - optimization_policy_nst.json + service_info: vCPE: vcpeHostName: requestParameters.vcpeHostName @@ -50,34 +70,108 @@ references: service_name: source: request value: serviceInfo.serviceName + resource: + source: request + value: placementInfo.placementDemands.resourceModuleName subscriber_role: - source: SubscriberPolicy - value: content.properties.subscriberRole + source: onap.policies.optimization.SubscriberPolicy + value: properties.properties.subscriberRole + slice_scope: + source: request + value: slice_scope policy_info: prioritization_attributes: policy_type: - - content.policyType + - type resources: - - content.resources - - content.objectiveParameter.parameterAttributes.resources + - properties.resources + - properties.objectiveParameter.parameterAttributes.resources service_name: - - content.serviceName + - properties.services - placement: + slice_selection: policy_fetch: by_scope policy_scope: - default_scope: OSDF_DUBLIN - vcpe_scope: OSDF_DUBLIN - vfw_scope: OSDF_DUBLIN - td_scope: OSDF_DUBLIN - secondary_scopes: - - + - + scope: + - get_param: slice_scope + services: + - get_param: service_name + resources: - get_param: service_name + + nst_selection: + policy_fetch: by_scope + policy_scope: + - + scope: + - OSDF_GUILIN + services: + - nst + resources: + - nst + + nsst_selection: + policy_fetch: by_scope + policy_scope: + - + scope: + - OSDF_GUILIN + services: + - nsst + resources: + - nsst + + subnet_selection: + policy_fetch: by_scope + policy_scope: + - scope: + - OSDF_GUILIN + services: + - get_param: service_name + resources: + - get_param: service_name + + placement: + policy_fetch: by_scope + policy_scope: + - + scope: + - OSDF_FRANKFURT + geography: - US + services: + - get_param: service_name + resources: + - get_param: resource # - # - get_param: service_name # - get_param: subscriber_role default: # if no explicit service related information is needed policy_fetch: by_name policy_scope: none + +PCI: + ML: + average_ho_threshold: 10000 + latest_ho_threshold: 500 + DES: + service_id: ho_metric + filter: + interval: 10 + ml_enabled: false + +nxi_termination: + query_templates: + nsi: "service-instance*('service-instance-id','{{instance_id}}') > service-instance*('service-role','e2eserviceprofile-service')" + nsi_with_profile: "service-instance*('service-instance-id','{{instance_id}}') > service-instance*('service-role','e2eserviceprofile-service')('service-instance-id','{{profile_id}}')" + nssi: + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'CN') > service-instance*('service-role','nsi')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'CN') > service-instance*('service-role','nsi')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'TN-BH') > service-instance*('service-role','nsi')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'AN') > service-instance*('service-role','nsi')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'AN-NF') > service-instance*('workload-context','AN')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'TN-MH') > service-instance*('workload-context','AN')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'TN-FH') > service-instance*('workload-context','AN')" + - "service-instance*('service-instance-id','{{instance_id}}')('workload-context', 'AN-NF') > service-instance*('workload-context','AN')" diff --git a/config/has_config.yaml b/config/has_config.yaml index 38a4781..2f4a1cd 100644 --- a/config/has_config.yaml +++ b/config/has_config.yaml @@ -1,5 +1,5 @@ policy_config_mapping: - attributes: + filtering_attributes: hypervisor: hypervisor cloudVersion: cloud_version cloudType: cloud_type @@ -19,6 +19,12 @@ policy_config_mapping: cloudRegionId: cloud-region-id orchestrationStatus: orchestration-status provStatus: prov-status + cloudRegion: cloud-region + cloud_region_attributes: + serviceRequests: service-requests + cloudRequests: cloud-requests + passthrough_attributes: {} + default_attributes: {} candidates: # for (k1, v1), if k1 is in demand, set prop[k2] = _get_candidates(demand[k1]) excludedCandidates: excluded_candidates diff --git a/config/log.yml b/config/log.yml new file mode 100644 index 0000000..ad0de21 --- /dev/null +++ b/config/log.yml @@ -0,0 +1,100 @@ +version: 1 +disable_existing_loggers: True + +loggers: + error: + handlers: [error_handler, console_handler] + level: "WARN" + propagate: True + debug: + handlers: [debug_handler, console_handler] + level: "DEBUG" + propagate: True + metrics: + handlers: [metrics_handler, console_handler] + level: "INFO" + propagate: True + audit: + handlers: [audit_handler, console_handler] + level: "INFO" + propagate: True +handlers: + debug_handler: + level: "DEBUG" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/debug.log" + formatter: "debugFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + error_handler: + level: "WARN" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/error.log" + formatter: "errorFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + metrics_handler: + level: "INFO" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/metrics.log" + formatter: "metricsFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + audit_handler: + level: "INFO" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/audit.log" + formatter: "auditFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + console_handler: + level: "DEBUG" + class: "logging.StreamHandler" + formatter: "metricsFormat" + +formatters: + standard: + format: "%(asctime)s|||||%(name)s||%(thread)||%(funcName)s||%(levelname)s||%(message)s" + debugFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "%(asctime)s.%(msecs)03d+00:00|{requestID}|%(threadName)s|{server}|%(levelname)s|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + errorFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "%(asctime)s.%(msecs)03d+00:00|{requestID}|%(threadName)s|{serviceName}|{partnerName}\ + |{targetEntity}|{targetServiceName}|%(levelname)s|{errorCode}|{errorDescription}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + auditFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "{entryTimestamp}+00:00|%(asctime)s.%(msecs)03d+00:00|{requestID}|{serviceInstanceID}\ + |%(threadName)s|{server}|{serviceName}|{partnerName}|{statusCode}|{responseCode}|{responseDescription}\ + |{instanceUUID}|%(levelname)s|{severity}|{serverIPAddress}|{timer}|{server}|{IPAddress}||{unused}\ + |{processKey}|{customField1}|{customField2}|{customField3}|{customField4}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + metricsFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "{entryTimestamp}+00:00|%(asctime)s.%(msecs)03d+00:00|{requestID}|{serviceInstanceID}\ + |%(threadName)s|{server}|{serviceName}|{partnerName}|{targetEntity}|{targetServiceName}|{statusCode}|{responseCode}|{responseDescription}\ + |{instanceUUID}|%(levelname)s|{severity}|{serverIPAddress}|{timer}|{server}|{IPAddress}||{unused}\ + |{processKey}|{TargetVirtualEntity}|{customField1}|{customField2}|{customField3}|{customField4}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + mdcFormat: + format: "%(asctime)s.%(msecs)03d+00:00|||||%(name)s||%(thread)s||%(funcName)s||%(levelname)s||%(message)s||||%(mdc)s" + mdcfmt: "{requestID} {invocationID} {serviceName} {serverIPAddress}" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter diff --git a/config/opteng_config.yaml b/config/opteng_config.yaml new file mode 100755 index 0000000..6b61eb0 --- /dev/null +++ b/config/opteng_config.yaml @@ -0,0 +1,25 @@ +# Policy Platform -- requires Authorization +policyPlatformUrl: https://policy-xacml-pdp:6969/policy/pdpx/decision/v1 # Policy Dev platform URL + +# AAF Authentication config +is_aaf_enabled: False +aaf_cache_expiry_mins: 5 +aaf_url: https://aaftest.simpledemo.onap.org:8095 +aaf_user_roles: + - '/optmodel:org.onap.oof.access|*|read ALL' + - '/optengine:org.onap.oof.access|*|read ALL' + +# Secret Management Service from AAF +aaf_sms_url: http://localhost:10443 +aaf_sms_timeout: 30 +secret_domain: osdf +aaf_ca_certs: ssl_certs/aaf_root_ca.cer + +osdfDatabaseHost: localhost +osdfDatabaseSchema: osdf +osdfDatabaseUsername: osdf +osdfDatabasePassword: osdf +osdfDatabasePort: 3306 + +#key +appkey: os35@rrtky400fdntc#001t5 diff --git a/config/osdf_config.yaml b/config/osdf_config.yaml index cf5426b..6780d13 100755 --- a/config/osdf_config.yaml +++ b/config/osdf_config.yaml @@ -17,9 +17,8 @@ conductorMaxRetries: 30 # if we don't get something in 30 minutes, give up # versions to be set in HTTP header conductorMinorVersion: 0 -# Policy Platform -- requires ClientAuth, Authorization, and Environment -policyPlatformUrl: http://policy.api.simpledemo.onap.org:8081/pdp/api/getConfig # Policy Dev platform URL -policyPlatformEnv: TEST # Environment for policy platform +# Policy Platform -- requires Authorization +policyPlatformUrl: https://policy-xacml-pdp:6969/policy/pdpx/v1/decision # Policy Dev platform URL # URL for policy model uploading policyPlatformUrlModelUpload: https://policy.api.simpledemo.onap.org:8081/policy/api/v1/policytypes pathPolicyModelUpload: ../../models/policy/placement/tosca_upload/ @@ -46,7 +45,50 @@ aaf_sms_timeout: 30 secret_domain: osdf aaf_ca_certs: ssl_certs/aaf_root_ca.cer +configClientType: configdb + # config db api configDbUrl: http://config.db.url:8080 configDbGetCellListUrl: 'SDNCConfigDBAPI/getCellList' configDbGetNbrListUrl: 'SDNCConfigDBAPI/getNbrList' +configDbUserName: '' +configDbPassword: '' + +# cps api +cpsUrl: http://cps-tbdmt:8000/execute +cpsCellListUrl: 'ran-network/getCellList' +cpsNbrListUrl: 'ran-network/getNbrList' +cpsUsername: '' +cpsPassword: '' + +#aai api +aaiUrl: "https://aai.url:30233" +aaiGetLinksUrl: "/aai/v16/network/logical-links" +aaiServiceInstanceUrl : "/aai/v20/nodes/service-instances/service-instance/" +aaiGetControllersUrl: /aai/v19/external-system/esr-thirdparty-sdnc-list +controllerQueryUrl: /aai/v19/query?format=resource +aaiGetInterDomainLinksUrl: /aai/v19/network/logical-links?link-type=inter-domain&operational-status=up +dslQueryPath: /aai/v23/dsl?format= + +#DES api +desUrl: http://des.url:9000 +desApiPath: /datalake/v1/exposure/ +desHeaders: + Accept: application/json + Content-Type: application/json +desUsername: +desPassword: + +pciHMSUsername: test +pciHMSPassword: passwd + +#key +#appkey: os35@rrtky400fdntc#001t5 + +#consulconfig +consulHost: '127.0.0.1' +consulPort: 8500 +consulScheme: 'http' +consulVerify: True +consulCert: None +activateConsulConfig: False diff --git a/config/preload_secrets.yaml b/config/preload_secrets.yaml index 1d2ea01..b95f1c1 100755 --- a/config/preload_secrets.yaml +++ b/config/preload_secrets.yaml @@ -1,55 +1,55 @@ --- domain: osdf secrets: -- name: so - values: - UserName: '' - Password: '' -- name: conductor - values: - UserName: admin1 - Password: plan.15 -- name: policyPlatform - values: - UserName: testpdp - Password: alpha123 -- name: policyClient - values: - UserName: python - Password: test -- name: dmaap - values: - UserName: NA - Password: NA -- name: sdc - values: - UserName: NA - Password: NA -- name: osdfPlacement - values: - UserName: test - Password: testpwd -- name: osdfPlacementSO - values: - UserName: so_test - Password: so_testpwd -- name: osdfPlacementVFC - values: - UserName: vfc_test - Password: vfc_testpwd -- name: osdfCMScheduler - values: - UserName: test1 - Password: testpwd1 -- name: configDb - values: - UserName: osdf - Password: passwd -- name: pciHMS - values: - UserName: '' - Password: '' -- name: osdfPCIOpt - values: - UserName: pci_test - Password: pci_testpwd + - name: so + values: + UserName: '' + Password: '' + - name: conductor + values: + UserName: admin1 + Password: 22234d3472ef5da8ecba5a096110a024f1db5cf195c665f910d558c9e83db19d + - name: policyPlatform + values: + UserName: healthcheck + Password: 49a03554e86ecdb8e9e224127791c579b44993b264549a333172af77c2ae95fc + - name: dmaap + values: + UserName: NA + Password: NA + - name: sdc + values: + UserName: NA + Password: NA + - name: osdfPlacement + values: + UserName: test + Password: c66b1570ae257375e500f9fe0e62b2a325466137ac5f29581e2e05cce1170212 + - name: osdfPlacementSO + values: + UserName: so_test + Password: 3d62d49b3e4ada38fd4146d2d82f4ba2f09345a46f15970cd439924c991b8202 + - name: osdfPlacementVFC + values: + UserName: vfc_test + Password: 1fb1cd581f96060d29ecad06be97151656bf29bce66bad587cd2fbaf5ea1e66d + - name: osdfCMScheduler + values: + UserName: test1 + Password: c5279fb02d7bac5269b1a644ac8e36f41f6ba7a2eae03dc469cb80d71811322b + - name: configDb + values: + UserName: osdf + Password: 40697f254409c2b97763892ecdeb50c847d605f5beb6f988f1c142a7e0344d0c + - name: pciHMS + values: + UserName: '' + Password: '' + - name: osdfPCIOpt + values: + UserName: pci_test + Password: fbf4dcb7f7cda8fdfb742838b0c90ae5bea249801f3f725fdc98941a6e4c347c + - name: osdfOptEngine + values: + UserName: opt_test + Password: 02946408ce6353d45540cd01d912686f19f48c3d8a955d5effdc14c6a43477e5 diff --git a/config/slicing_config.yaml b/config/slicing_config.yaml new file mode 100644 index 0000000..179f54a --- /dev/null +++ b/config/slicing_config.yaml @@ -0,0 +1,96 @@ +app_info: + NSI: + app_name: slice_selection + requirements_field: serviceProfile + model_info: NSTInfo + NSSI: + app_name: subnet_selection + requirements_field: sliceProfile + model_info: NSSTInfo + +attribute_mapping: + camel_to_snake: + maxBandwidth: max_bandwidth + jitter: jitter + sST: sst + latency: latency + resourceSharingLevel: resource_sharing_level + uEMobilityLevel: ue_mobility_level + maxNumberofUEs: max_number_of_ues + dLThptPerUE: dl_thpt_per_ue + uLThptPerUE: ul_thpt_per_ue + sNSSAI: s_nssai + pLMNIdList: plmn_id_list + activityFactor: activity_factor + coverageAreaTAList: coverage_area_ta_list + availability: availability + cSAvailabilityTarget: cs_availability_target + reliability: reliability + cSReliabilityMeanTime: cs_reliability_mean_time + dLThptPerSlice: dl_thpt_per_slice + expDataRateDL: exp_data_rate_dl + uLThptPerSlice: ul_thpt_per_slice + expDataRateUL: exp_data_rate_ul + maxPktSize: max_pkt_size + msgSizeByte: msg_size_byte + maxNumberofConns: max_number_of_conns + maxNumberofPDUSession: max_number_of_pdu_session + termDensity: terminal_density + survivalTime: survival_time + areaTrafficCapDL: area_traffic_cap_dl + areaTrafficCapUL: area_traffic_cap_ul + overallUserDensity: overall_user_density + transferIntervalTarget: transfer_interval_target + expDataRate: exp_data_rate + security: security + maxThroughput: max_throughput + sliceProfileId: slice_profile_id + snssaiList: s_nssai_list + domainType: domain_type + logicInterfaceId: logical_interface_id + ipAddress: ip_address + nextHopInfo: next_hop_info + perfReq: perf_req + + snake_to_camel: + max_bandwidth: maxBandwidth + jitter: jitter + sst: sST + latency: latency + resource_sharing_level: resourceSharingLevel + ue_mobility_level: uEMobilityLevel + max_number_of_ues: maxNumberofUEs + dl_thpt_per_ue: dLThptPerUE + ul_thpt_per_ue: uLThptPerUE + s_nssai: sNSSAI + plmn_id_list: pLMNIdList + activity_factor: activityFactor + coverage_area_ta_list: coverageAreaTAList + availability: availability + cs_availability_target: cSAvailabilityTarget + reliability: reliability + cs_reliability_mean_time: cSReliabilityMeanTime + dl_thpt_per_slice: dLThptPerSlice + exp_data_rate_dl: expDataRateDL + ul_thpt_per_slice: uLThptPerSlice + exp_data_rate_ul: expDataRateUL + max_pkt_size: maxPktSize + msg_size_byte: msgSizeByte + max_number_of_conns: maxNumberofConns + max_number_of_pdu_session: maxNumberofPDUSession + terminal_density: termDensity + survival_time: survivalTime + area_traffic_cap_dl: areaTrafficCapDL + area_traffic_cap_ul: areaTrafficCapUL + overall_user_density: overallUserDensity + transfer_interval_target: transferIntervalTarget + exp_data_rate: expDataRate + security: security + max_throughput: maxThroughput + slice_profile_id: sliceProfileId + s_nssai_list: snssaiList + domain_type: domainType + logical_interface_id: logicInterfaceId + ip_address: ipAddress + next_hop_info: nextHopInfo + perf_req: perfReq diff --git a/csit/.gitignore b/csit/.gitignore new file mode 100644 index 0000000..c8865c2 --- /dev/null +++ b/csit/.gitignore @@ -0,0 +1,2 @@ +env.properties +archives/ diff --git a/csit/plans/default/setup.sh b/csit/plans/default/setup.sh new file mode 100755 index 0000000..76f071f --- /dev/null +++ b/csit/plans/default/setup.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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. +# +# ------------------------------------------------------------------------- +# + +# +echo "# aaf-sms setup.sh script"; +source ${WORKSPACE}/scripts/setup-sms.sh + +echo "# simulator scripts calling"; +source ${WORKSPACE}/scripts/simulator_script.sh + +# add here eventual scripts needed for optf/osdf +# +echo "# optf/osdf scripts calling"; +source ${WORKSPACE}/scripts/osdf_script.sh + +# +# add here below the start of all docker containers needed for optf/osdf CSIT testing +# +echo "# optf/osdf scripts docker containers spinoff"; + +# +# add here all the configuration steps eventually needed to be carried out for optf/osdf CSIT testing +# +echo "# optf/osdf configuration step"; + + +# +# add here all ROBOT_VARIABLES settings +# +echo "# optf/osdf robot variables settings"; +echo "osdf ip = ${OSDF_IP}" + +ROBOT_VARIABLES="-v OSDF_HOSTNAME:http://${OSDF_IP} -v OSDF_PORT:8699" + +echo ${ROBOT_VARIABLES} + + + diff --git a/csit/plans/default/teardown.sh b/csit/plans/default/teardown.sh new file mode 100755 index 0000000..02aa669 --- /dev/null +++ b/csit/plans/default/teardown.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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. +# +# ------------------------------------------------------------------------- +# + + +# +# add here below the killing of all docker containers used for optf/osdf CSIT testing +# + +# +# optf/osdf scripts docker containers killing"; +# + +${WORKSPACE}/scripts/kill-instance.sh optf-osdf +${WORKSPACE}/scripts/kill-instance.sh osdf_sim + +echo "# aaf-sms teardown.sh script"; +${WORKSPACE}/scripts/kill-instance.sh sms +${WORKSPACE}/scripts/kill-instance.sh vault + diff --git a/csit/plans/default/testplan.txt b/csit/plans/default/testplan.txt new file mode 100644 index 0000000..239cc37 --- /dev/null +++ b/csit/plans/default/testplan.txt @@ -0,0 +1,22 @@ +# +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +# Test suites are relative paths under [integration.git]/test/csit/tests/. +# Place the suites in run order. +osdf diff --git a/csit/prepare-csit.sh b/csit/prepare-csit.sh new file mode 100755 index 0000000..d46b17a --- /dev/null +++ b/csit/prepare-csit.sh @@ -0,0 +1,47 @@ +#!/bin/bash -x +# +# Copyright 2019-2021 © Samsung Electronics Co., Ltd. +# Modifications Copyright (C) 2021 Pantheon.tech +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 script installs common libraries required by CSIT tests +# +# Branched from ccsdk/distribution to this repository Feb 23, 2021 +# + +if [ -z "$WORKSPACE" ]; then + export WORKSPACE=`git rev-parse --show-toplevel` +fi + +TESTPLANDIR=${WORKSPACE}/${TESTPLAN} + +# Assume that if ROBOT_VENV is set and virtualenv with system site packages can be activated, +# ci-management/jjb/integration/include-raw-integration-install-robotframework.sh has already +# been executed + +if [ -f ${WORKSPACE}/env.properties ]; then + source ${WORKSPACE}/env.properties +fi +if [ -f ${ROBOT_VENV}/bin/activate ]; then + source ${ROBOT_VENV}/bin/activate +else + rm -rf /tmp/ci-management + rm -f ${WORKSPACE}/env.properties + cd /tmp + git clone "https://gerrit.onap.org/r/ci-management" + source /tmp/ci-management/jjb/integration/include-raw-integration-install-robotframework.sh +fi + +pip freeze + diff --git a/csit/run-csit.sh b/csit/run-csit.sh new file mode 100755 index 0000000..9220ef9 --- /dev/null +++ b/csit/run-csit.sh @@ -0,0 +1,197 @@ +#!/bin/bash -x +# +# Copyright 2016-2017 Huawei Technologies Co., Ltd. +# Modification Copyright 2019-2021 © Samsung Electronics Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# $1 project/functionality +# $2 robot options + +# Branched from ccsdk/distribution to this repository Feb 23, 2021 + +# +# functions +# + +function on_exit(){ + rc=$? + if [[ ${WORKSPACE} ]]; then + if [[ ${WORKDIR} ]]; then + rsync -av "$WORKDIR/" "$WORKSPACE/archives/$TESTPLAN" + fi + # Record list of active docker containers + docker ps --format "{{.Image}}" > "$WORKSPACE/archives/$TESTPLAN/_docker-images.log" + + # show memory consumption after all docker instances initialized + docker_stats | tee "$WORKSPACE/archives/$TESTPLAN/_sysinfo-2-after-robot.txt" + fi + # Run teardown script plan if it exists + cd "${TESTPLANDIR}" + TEARDOWN="${TESTPLANDIR}/teardown.sh" + if [ -f "${TEARDOWN}" ]; then + echo "Running teardown script ${TEARDOWN}" + source_safely "${TEARDOWN}" + fi + # TODO: do something with the output + exit $rc +} +# ensure that teardown and other finalizing steps are always executed +trap on_exit EXIT + +function docker_stats(){ + #General memory details + echo "> top -bn1 | head -3" + top -bn1 | head -3 + echo + + echo "> free -h" + free -h + echo + + #Memory details per Docker + echo "> docker ps" + docker ps + echo + + echo "> docker stats --no-stream" + docker stats --no-stream + echo +} + +# save current set options +function save_set() { + RUN_CSIT_SAVE_SET="$-" + RUN_CSIT_SHELLOPTS="$SHELLOPTS" +} + +# load the saved set options +function load_set() { + _setopts="$-" + + # bash shellopts + for i in $(echo "$SHELLOPTS" | tr ':' ' ') ; do + set +o ${i} + done + for i in $(echo "$RUN_CSIT_SHELLOPTS" | tr ':' ' ') ; do + set -o ${i} + done + + # other options + for i in $(echo "$_setopts" | sed 's/./& /g') ; do + set +${i} + done + set -${RUN_CSIT_SAVE_SET} +} + +# set options for quick bailout when error +function harden_set() { + set -xeo pipefail + set +u # enabled it would probably fail too many often +} + +# relax set options so the sourced file will not fail +# the responsibility is shifted to the sourced file... +function relax_set() { + set +e + set +o pipefail +} + +# wrapper for sourcing a file +function source_safely() { + [ -z "$1" ] && return 1 + relax_set + . "$1" + load_set +} + +# +# main +# + +# set and save options for quick failure +harden_set && save_set + +if [ $# -eq 0 ] +then + echo + echo "Usage: $0 plans/<project>/<functionality> [<robot-options>]" + echo + echo " <project>, <functionality>, <robot-options>: " + echo " The same values as for the '{project}-csit-{functionality}' JJB job template." + echo + exit 1 +fi + +if [ -z "$WORKSPACE" ]; then + export WORKSPACE=$(git rev-parse --show-toplevel) +fi + +if [ -f "${WORKSPACE}/${1}/testplan.txt" ]; then + export TESTPLAN="${1}" +else + echo "testplan not found: ${WORKSPACE}/${TESTPLAN}/testplan.txt" + exit 2 +fi + +export TESTOPTIONS="${2}" + +rm -rf "$WORKSPACE/archives/$TESTPLAN" +mkdir -p "$WORKSPACE/archives/$TESTPLAN" + +TESTPLANDIR="${WORKSPACE}/${TESTPLAN}" + +# Run installation of prerequired libraries +source_safely "${WORKSPACE}/prepare-csit.sh" + +# Activate the virtualenv containing all the required libraries installed by prepare-csit.sh +source_safely "${ROBOT_VENV}/bin/activate" + +WORKDIR=$(mktemp -d --suffix=-robot-workdir) +cd "${WORKDIR}" + +# Add csit scripts to PATH +export PATH="${PATH}:${WORKSPACE}/docker/scripts:${WORKSPACE}/scripts:${ROBOT_VENV}/bin" +export SCRIPTS="${WORKSPACE}/scripts" +export ROBOT_VARIABLES= + +# Sign in to nexus3 docker repo +docker login -u docker -p docker nexus3.onap.org:10001 + +# Run setup script plan if it exists +cd "${TESTPLANDIR}" +SETUP="${TESTPLANDIR}/setup.sh" +if [ -f "${SETUP}" ]; then + echo "Running setup script ${SETUP}" + source_safely "${SETUP}" +fi + +# show memory consumption after all docker instances initialized +docker_stats | tee "$WORKSPACE/archives/$TESTPLAN/_sysinfo-1-after-setup.txt" + +# Run test plan +cd "$WORKDIR" +echo "Reading the testplan:" +cat "${TESTPLANDIR}/testplan.txt" | egrep -v '(^[[:space:]]*#|^[[:space:]]*$)' | sed "s|^|${WORKSPACE}/tests/|" > testplan.txt +cat testplan.txt +SUITES=$( xargs -a testplan.txt ) + +echo ROBOT_VARIABLES="${ROBOT_VARIABLES}" +echo "Starting Robot test suites ${SUITES} ..." +relax_set +python -m robot.run -N ${TESTPLAN} -v WORKSPACE:/tmp ${ROBOT_VARIABLES} ${TESTOPTIONS} ${SUITES} +RESULT=$? +load_set +echo "RESULT: $RESULT" +# Note that the final steps are done in on_exit function after this exit! +exit $RESULT diff --git a/csit/run-project-csit.sh b/csit/run-project-csit.sh new file mode 100755 index 0000000..a231c5e --- /dev/null +++ b/csit/run-project-csit.sh @@ -0,0 +1,35 @@ +#!/bin/bash -x +# +# Copyright 2020-2021 © Samsung Electronics Co., Ltd. +# Modifications Copyright (C) 2021 Pantheon.tech +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Branched from ccsdk/distribution to this repository Feb 23, 2021 +# + +# $1 test options (passed on to run-csit.sh as such) + +export TESTOPTIONS=${1} +export WORKSPACE=$(git rev-parse --show-toplevel)/csit + +rm -rf ${WORKSPACE}/archives +mkdir -p ${WORKSPACE}/archives +cd ${WORKSPACE} + +# Execute all test-suites defined under plans subdirectory +for dir in plans/*/ +do + dir=${dir%*/} # remove the trailing / + ./run-csit.sh ${dir} ${TESTOPTIONS} +done diff --git a/csit/scripts/common_functions.sh b/csit/scripts/common_functions.sh new file mode 100755 index 0000000..684c418 --- /dev/null +++ b/csit/scripts/common_functions.sh @@ -0,0 +1,263 @@ +#!/bin/bash + +# Copyright 2016-2017 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function memory_details(){ + #General memory details + echo "> top -bn1 | head -3" + top -bn1 | head -3 + echo + + echo "> free -h" + free -h + echo + + #Memory details per Docker + echo "> docker ps" + docker ps + echo + + echo "> docker stats --no-stream" + docker stats --no-stream + echo +} +function fibonacci_number(){ + set +x + if [ $1 -le 1 ] + then + echo "1" + elif [ $1 -le 10 ] + then + Num=$1 + f1=0 + f2=1 + fn=-1 + for i in `eval echo {1..$Num}`;do + fn=$((f1+f2)) + f1=$f2 + f2=$fn + done + echo $fn + else + echo "30" + fi +} +function wait_curl_driver(){ + #Parameters: + #CURL_COMMAND - the URL on which the curl command will be executed + #GREP_STRING - Desired string to be found inside the body response of the + # previous curl command + #EXCLUDE_STRING - If the filtered string (GREP_STRING) must not exist in + # the body response of the curl + #WAIT_MESSAGE - the message to be displayed for logging purpose. (optional) + #REPEAT_NUMBER - the maximum number of tries before abandoning the curl + # command (optional, by default = 15) + #MAX_TIME - Maximum time allowed for the transfer (in seconds) + #STATUS_CODE - A HTTP status code desired to be found by getting the link + # /!\ IMPORTANT NOTICE: the usage of STATUS_CODE option turn GREP_STRING/ + # /!\ EXCLUDE_STRING/and the MAX_TIME options becomes obsolete with no + # /!\ execution impact + #MEMORY_USAGE - If Parameters exists shows the memory usage after curl + # execution(s) + + repeat_max=15 + parameters="$@" + + #WAIT_MESSAGE + if [[ $parameters == *"WAIT_MESSAGE"* ]] + then + wait_message=`echo $parameters | sed -e "s/.*WAIT_MESSAGE=//g"` + wait_message=`echo $wait_message | sed -e "s/ .*//g"` + else + wait_message="wait ..." + fi + + #REPEAT_NUMBER + if [[ $parameters == *"REPEAT_NUMBER"* ]] + then + repeat_max=`echo $parameters | sed -e "s/.*REPEAT_NUMBER=//g"` + repeat_max=`echo $repeat_max | sed -e "s/ .*//g"` + fi + + #CURL_COMMAND + if [[ $parameters == *"CURL_COMMAND"* ]] + then + curl_command=`echo $parameters | sed -e 's/.*CURL_COMMAND=//g'` + curl_command=`echo $curl_command | sed -e 's/ .*//g'` + else + echo "-Curl is empty-" # Or no parameterseter passed. + return 0 + fi + + #MAX_TIME + if [[ $parameters == *"MAX_TIME"* ]] + then + max_time=`echo $parameters | sed -e 's/.*MAX_TIME=//g'` + max_time=`echo $max_time | sed -e 's/ .*//g'` + else + max_time="5" + fi + + exclude_string="" + #EXCLUDE_STRING + if [[ $parameters == *"EXCLUDE_STRING"* ]] + then + exclude_string="-v" + fi + + status_code="" + #STATUS_CODE + if [[ $parameters == *"STATUS_CODE"* ]] + then + status_code=`echo $parameters | sed -e 's/.*STATUS_CODE=//g'` + status_code=`echo $status_code | sed -e 's/ .*//g'` + fi + + for i in `eval echo {1..$repeat_max}`; do + response_code=`curl -o /dev/null --silent --head --write-out '%{http_code}' $curl_command` + echo "..." + if [[ ! -z $status_code ]] ; then + if [ "$status_code" -eq "$response_code" ] + then + echo "SUCCESS:Actual Status code <$response_code> match the expected code <$status_code>" + return 0 + else + echo "WARNING:Expected <$status_code> but Actual <$response_code>" + fi + else + #GREP_STRING + if [[ $parameters == *"GREP_STRING"* ]] + then + grep_command=`echo $parameters | sed -e 's/.*GREP_STRING=//g'` + grep_command=`echo $grep_command | sed -e 's/ REPEAT_NUMBER=.*//g' | sed -e 's/ CURL_COMMAND=.*//g' | sed -e 's/ WAIT_MESSAGE=.*//g' | sed -e 's/ MAX_TIME=.*//g' | sed -e 's/ EXCLUDE_STRING.*//g'` + else + echo "-Grep_command is empty-" # Or no parameters passed. + return 0 + fi + + str=`curl -sS -m$max_time $curl_command | grep "$grep_command"` + echo "BODY::$str" + if [[ ! -z $exclude_string ]] + then + if [[ -z $str ]] + then + echo "SUCCESS: body response does not contains '$grep_command'"; + break; + else + echo "Fall_Short: Body response still contains '$grep_command'" + fi + else + if [[ ! -z $str ]] + then + echo "SUCCESS: body response contains '$grep_command'"; + break; + else + echo "Fall_Short: Element '$grep_command' not found yet # "$i"" + fi + fi + + if [ "$?" = "7" ]; then + echo 'Connection refused or can not connect to server/proxy'; + str='' + fi + fi + seconds2sleep=`fibonacci_number $i` + echo $wait_message + echo "Iteration::$i out of $repeat_max " + echo "Quiet time for $seconds2sleep seconds ..." + sleep $seconds2sleep + + # if waiting for a long time, log system load + if [ $i -gt 45 ] + then + memory_details + fi + done + #MEMORY_USAGE + if [[ $parameters == *"MEMORY_USAGE"* ]] + then + echo "==========================MEMORY USAGE==================================" + memory_details + echo "========================================================================" + fi + return 0 +} + +function run_simulator () +{ + run_robottestlib + run_simulator_docker $1 +} + +function run_robottestlib () +{ + #Start the robottest REST library if not started + if ! pgrep -f robottest > /dev/null + then + #Download the latest robottest jar + wget -q -O ${SCRIPTS}/integration/mockserver/org.openo.robottest.jar "https://nexus.open-o.org/service/local/artifact/maven/redirect?r=snapshots&g=org.openo.integration&a=org.openo.robottest&e=jar&v=LATEST" + chmod +x ${SCRIPTS}/integration/mockserver/org.openo.robottest.jar + eval `java -cp ${SCRIPTS}/integration/mockserver/org.openo.robottest.jar org.openo.robot.test.robottest.MyRemoteLibrary` & + fi +} + +function run_simulator_docker () +{ + #Start the simulator docker if not started + SIMULATOR_IP=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' simulator` + if [[ -z $SIMULATOR_IP ]] + then + echo "Starting simulator docker..." + SIMULATOR_JSON=$1 + if [[ -z $SIMULATOR_JSON ]] + then + SIMULATOR_JSON=main.json + fi + docker run -d -i -t --name simulator -e SIMULATOR_JSON=$SIMULATOR_JSON -p 18009:18009 -p 18008:18008 openoint/simulate-test-docker + SIMULATOR_IP=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' simulator` + fi + + #Set the simulator IP in robot variables + ROBOT_VARIABLES=${ROBOT_VARIABLES}" -v SIMULATOR_IP:${SIMULATOR_IP} -v SCRIPTS:${SCRIPTS}" + echo ${ROBOT_VARIABLES} +} + +function get_docker_compose_service () +{ + local service=$1 + local compose_file=${2:-docker-compose.yml} + + echo $(docker-compose --file ./${compose_file} ps | grep $service | cut -d " " -f1 ) +} + +function bypass_ip_adress () +{ + local ip_address=$1 + + if [[ $no_proxy && $no_proxy != *$ip_address* ]]; then + export no_proxy=$no_proxy,$ip_address + fi +} + +function wait_for_service_init () +{ + local service_url=$1 + + for delay in {1..50}; do + curl -sS ${service_url} && break + echo "$delay - Waiting for $service_url..." + sleep $delay + done +} diff --git a/csit/scripts/get-instance-ip.sh b/csit/scripts/get-instance-ip.sh new file mode 100755 index 0000000..a236c02 --- /dev/null +++ b/csit/scripts/get-instance-ip.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016-2017 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $1 diff --git a/csit/scripts/kill-instance.sh b/csit/scripts/kill-instance.sh new file mode 100755 index 0000000..5997098 --- /dev/null +++ b/csit/scripts/kill-instance.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# Copyright 2016-2017 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# $1 nickname for the instance + +mkdir -p $WORKSPACE/archives + +running_containers=$(docker ps --filter name=$1 -q) +if [ -z "$running_containers" ] +then + echo "$1 already terminated" +else + echo "Stopping and removing containers" + docker logs $running_containers >> $WORKSPACE/archives/$1.log + docker stop $running_containers + docker rm $running_containers +fi + diff --git a/ssl_certs/aaf_root_ca.cer b/csit/scripts/osdf-properties/aaf_root_ca.cer index e9a50d7..e9a50d7 100644 --- a/ssl_certs/aaf_root_ca.cer +++ b/csit/scripts/osdf-properties/aaf_root_ca.cer diff --git a/csit/scripts/osdf-properties/osdf.json b/csit/scripts/osdf-properties/osdf.json new file mode 100644 index 0000000..ae059f3 --- /dev/null +++ b/csit/scripts/osdf-properties/osdf.json @@ -0,0 +1,105 @@ +{ + "domain": { + "name": "osdf", + "secrets": [ + { + "name": "so", + "values": { + "UserName": "", + "Password": "" + } + }, + { + "name": "conductor", + "values": { + "UserName": "admin1", + "Password": "plan.15" + } + }, + { + "name": "policyPlatform", + "values": { + "UserName": "testpdp", + "Password": "alpha123" + } + }, + { + "name": "policyClient", + "values": { + "UserName": "python", + "Password": "test" + } + }, + { + "name": "dmaap", + "values": { + "UserName": "NA", + "Password": "NA" + } + }, + { + "name": "sdc", + "values": { + "UserName": "NA", + "Password": "NA" + } + }, + { + "name": "osdfPlacement", + "values": { + "UserName": "test", + "Password": "testpwd" + } + }, + { + "name": "osdfPlacementSO", + "values": { + "UserName": "so_test", + "Password": "so_testpwd" + } + }, + { + "name": "osdfPlacementVFC", + "values": { + "UserName": "vfc_test", + "Password": "vfc_testpwd" + } + }, + { + "name": "osdfCMScheduler", + "values": { + "UserName": "test1", + "Password": "testpwd1" + } + }, + { + "name": "configDb", + "values": { + "UserName": "osdf", + "Password": "passwd" + } + }, + { + "name": "pciHMS", + "values": { + "UserName": "", + "Password": "" + } + }, + { + "name": "osdfPCIOpt", + "values": { + "UserName": "pci_test", + "Password": "pci_testpwd" + } + }, + { + "name": "osdfOptEngine", + "values": { + "UserName": "opt_test", + "Password": "opt_testpwd" + } + } + ] + } +}
\ No newline at end of file diff --git a/csit/scripts/osdf-properties/osdf_config.yaml b/csit/scripts/osdf-properties/osdf_config.yaml new file mode 100755 index 0000000..f97a743 --- /dev/null +++ b/csit/scripts/osdf-properties/osdf_config.yaml @@ -0,0 +1,70 @@ +placementVersioningEnabled: False + +# Placement API latest version numbers to be set in HTTP header +placementMajorVersion: "1" +placementMinorVersion: "0" +placementPatchVersion: "0" + +# Placement API default version numbers to be set in HTTP header +placementDefaultMajorVersion: "1" +placementDefaultMinorVersion: "0" +placementDefaultPatchVersion: "0" + +# Config for Conductor +conductorUrl: "http://127.0.0.1:5000/simulated/oof/has-api/flow1-success-simple/main.json" +conductorPingWaitTime: 2 # seconds to wait before calling the conductor retry URL +conductorMaxRetries: 5 # if we don't get something in 30 minutes, give up +# versions to be set in HTTP header +conductorMinorVersion: 0 + +# Policy Platform -- requires ClientAuth, Authorization, and Environment +policyPlatformUrl: http://127.0.0.1:5000/simulated/policy/pdpx/decision/v1 # Policy Dev platform URL +policyPlatformEnv: TEST # Environment for policy platform + +# Config for DMaaP +messageReaderHosts: https://DMAAP-HOST1:3905,https://DMAAP-HOST2:3905,https://DMAAP-HOST3:3905 +messageReaderTopic: org.onap.oof.osdf.multicloud + +# Config for SDC +sdcUrl: https://SDC-HOST:8443/sdc/v1/catalog +sdcONAPInstanceID: ONAP-OSDF + +osdfPlacementUrl: "http://127.0.0.1:24699/osdf/api/v2/placement" + +is_aaf_enabled: False +aaf_cache_expiry_hrs: 3 +aaf_url: https://aaftest.simpledemo.onap.org:8095 +aaf_user_roles: + - /api/oof/v1/placement:org.onap.osdf.access|*|read ALL + +# Secret Management Service from AAF +aaf_sms_url: http://aaf-sms.onap:10443 +aaf_sms_timeout: 30 +secret_domain: osdf +aaf_ca_certs: /opt/aaf_root_ca.cer + +configClientType: configdb + +# config db api +configDbUrl: http://127.0.0.1:5000/simulated/configdb +configDbGetCellListUrl: 'getCellList' +configDbGetNbrListUrl: 'getNbrList' + +# cps api +cpsUrl: http://localhost:8080/execute +cpsCellListUrl: 'e2e-cavsta-schemaset/get-cell-list' +cpsNbrListUrl: 'e2e-cavsta-schemaset/get-nbr-list' +cpsUsername: '' +cpsPassword: '' + +#aai api +aaiUrl: "http://127.0.0.1:5000" +aaiGetLinksUrl: "/aai/v16/network/logical-links" +aaiServiceInstanceUrl : "/simulated/aai/v23/nodes/service-instances/service-instance/" +aaiGetControllersUrl: /aai/v19/external-system/esr-thirdparty-sdnc-list +controllerQueryUrl: /aai/v19/query?format=resource +aaiGetInterDomainLinksUrl: /aai/v19/network/logical-links?link-type=inter-domain&operational-status=up +dslQueryPath: /simulated/aai/v23/dsl?format= + +#key +appkey: diff --git a/csit/scripts/osdf_proxy_settings.sh b/csit/scripts/osdf_proxy_settings.sh new file mode 100755 index 0000000..c17d9d5 --- /dev/null +++ b/csit/scripts/osdf_proxy_settings.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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. +# +# ------------------------------------------------------------------------- +# + +# put into this file local proxy settings in case they are needed on your local environment +echo "### This is ${WORKSPACE}/scripts/osdf_proxy_settings.sh" + +echo "optf/osdf proxy settings" +if [ "$#" -eq "1" ]; then + echo "$1" + cd $1 + pwd +else + exit 1 +fi + +# don't remove following lines: commands can be attached here + + diff --git a/csit/scripts/osdf_script.sh b/csit/scripts/osdf_script.sh new file mode 100755 index 0000000..0d86a63 --- /dev/null +++ b/csit/scripts/osdf_script.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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. +# +# ------------------------------------------------------------------------- +# + +echo "### This is ${WORKSPACE}/scripts/osdf_script.sh" +# +# add here whatever commands is needed to prepare the optf/osdf CSIT testing +# + +# assume the base is /tmp dir +DIR=/tmp + +# the directory of the script +echo ${DIR} +cd ${DIR} + +# create directory for volume and copy configuration file +# run docker containers +OSDF_CONF=/tmp/osdf/properties/osdf_config.yaml +AAF_CER=/tmp/osdf/properties/aaf_root_ca.cer +IMAGE_NAME=nexus3.onap.org:10003/onap/optf-osdf +IMAGE_VER=latest + +mkdir -p /tmp/osdf/properties +mkdir -p /tmp/sms/properties + +cp ${WORKSPACE}/scripts/osdf-properties/*.yaml /tmp/osdf/properties/. +cp ${WORKSPACE}/scripts/osdf-properties/aaf_root_ca.cer /tmp/osdf/properties/. +cp ${WORKSPACE}/scripts/osdf-properties/osdf.json /tmp/sms/properties/. + +#change conductor/configdb simulator urls +OSDF_SIM_IP=`${WORKSPACE}/scripts/get-instance-ip.sh osdf_sim` +echo "OSDF_SIM_IP=${OSDF_SIM_IP}" +SMS_IP=`${WORKSPACE}/scripts/get-instance-ip.sh sms` +echo "SMS_IP=${SMS_IP}" + +sed -i -e "s%127.0.0.1:5000%${OSDF_SIM_IP}:5000%g" $OSDF_CONF +sed -i -e "s%aaf-sms.onap:10443%${SMS_IP}:10443%g" $OSDF_CONF + +#Preload secrets +docker exec --user root -i sms /bin/sh -c "mkdir -p /preload/config" +docker cp /tmp/sms/properties/osdf.json sms:/preload/config/osdf.json +docker exec --user root -i sms /bin/sh -c "/sms/bin/preload -cacert /sms/certs/aaf_root_ca.cer -jsondir /preload/config -serviceport 10443 -serviceurl http://localhost" + +docker logs vault +docker run -d --name optf-osdf -v ${AAF_CER}:/opt/aaf_root_ca.cer -v ${OSDF_CONF}:/opt/osdf/config/osdf_config.yaml -p "8698:8699" ${IMAGE_NAME}:${IMAGE_VER} + +sleep 20 + +OSDF_IP=`${WORKSPACE}/scripts/get-instance-ip.sh optf-osdf` +${WORKSPACE}/scripts/wait_for_port.sh ${OSDF_IP} 8699 + +echo "inspect docker things for tracing purpose" +docker inspect optf-osdf diff --git a/csit/scripts/setup-sms.sh b/csit/scripts/setup-sms.sh new file mode 100755 index 0000000..180bd0a --- /dev/null +++ b/csit/scripts/setup-sms.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# +# Copyright 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Not sure why this is needed. +source ${WORKSPACE}/scripts/common_functions.sh + +CONFIG_FILE=$(pwd)/config/smsconfig.json + +mkdir -p $(pwd)/config + +docker login -u docker -p docker nexus3.onap.org:10001 +docker pull nexus3.onap.org:10001/onap/aaf/sms:4.0.0 +docker pull docker.io/vault:1.3.3 + +# +# Running vault in dev server mode here for CSIT +# In HELM it runs in production mode +# +docker run -e "VAULT_DEV_ROOT_TOKEN_ID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" \ + -e SKIP_SETCAP=true \ + --name vault -d -p 8200:8200 vault:1.3.3 + +SMSDB_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' vault) +cat << EOF > $CONFIG_FILE +{ + "cafile": "auth/selfsignedca.pem", + "servercert": "auth/server.cert", + "serverkey": "auth/server.key", + + "smsdbaddress": "http://$SMSDB_IP:8200", + "vaulttoken": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "disable_tls": true +} +EOF + +cat $CONFIG_FILE + +docker run --workdir /sms -v $CONFIG_FILE:/sms/smsconfig.json \ + --name sms -d -p 10443:10443 --user root nexus3.onap.org:10001/onap/aaf/sms:4.0.0 + +SMS_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' sms) + +echo "###### WAITING FOR ALL CONTAINERS TO COME UP" +sleep 20 +for i in {1..20}; do + curl -sS -m 1 http://${SMSDB_IP}:8200/v1/sys/seal-status && break + echo sleep $i + sleep $i +done + +# +# add here all ROBOT_VARIABLES settings +# +echo "# sms robot variables settings"; +ROBOT_VARIABLES="-v SMS_HOSTNAME:http://${SMS_IP} -v SMS_PORT:10443" + +echo ${ROBOT_VARIABLES} diff --git a/csit/scripts/simulator_script.sh b/csit/scripts/simulator_script.sh new file mode 100755 index 0000000..f6daab6 --- /dev/null +++ b/csit/scripts/simulator_script.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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. +# +# ------------------------------------------------------------------------- +# + +echo "### This is ${WORKSPACE}/scripts/simulator_script.sh" +# +# add here whatever commands is needed to prepare the optf/osdf CSIT testing +# + +#echo "i am ${USER} : only non jenkins users may need proxy settings" +if [ ${USER} != 'jenkins' ]; then + + # add proxy settings into this script when you work behind a proxy + ${WORKSPACE}/scripts/osdf_proxy_settings.sh ${WORK_DIR} + +fi + +# prepare osdf_sim +cd ${WORKSPACE}/../test/functest/simulators + +# check Dockerfile content +cat ./Dockerfile + +# build osdf_sim +chmod +x ./build_sim_image.sh +./build_sim_image.sh + +# run osdf_sim +docker run -d --name osdf_sim -p "5000:5000" osdf_sim:latest; + +#wait for docker setup a while +sleep 10 + +docker ps -a + +echo "logs of the osdf_sim" +sudo cat `docker inspect -f '{{.LogPath}}' osdf_sim` + + +OSDF_SIM_IP=`${WORKSPACE}/scripts/get-instance-ip.sh osdf_sim` +echo "OSDF_SIM_IP=${OSDF_SIM_IP}" + +docker inspect osdf_sim + +${WORKSPACE}/scripts/wait_for_port.sh ${OSDF_SIM_IP} 5000 + + +# wait a while before continuing +sleep 2 + +echo "inspect docker things for tracing purpose" + diff --git a/csit/scripts/wait_for_port.sh b/csit/scripts/wait_for_port.sh new file mode 100755 index 0000000..360fc21 --- /dev/null +++ b/csit/scripts/wait_for_port.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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. +# +# ------------------------------------------------------------------------- +# + +if [[ $# -ne 2 ]]; then + echo "Usage: wait-for-port hostname port" >&2 + exit 1 +fi + +host=$1 +port=$2 + +echo "Waiting for $host port $port open" +until telnet $host $port </dev/null 2>/dev/null | grep -q '^Connected'; do + sleep 1 +done + +echo "$host port $port is open" + +exit 0 diff --git a/csit/tests/osdf/__init__.robot b/csit/tests/osdf/__init__.robot new file mode 100644 index 0000000..fb654d7 --- /dev/null +++ b/csit/tests/osdf/__init__.robot @@ -0,0 +1,4 @@ +*** Settings *** +Documentation Integration - Suite 1 + + diff --git a/csit/tests/osdf/data/pci-opt-request.json b/csit/tests/osdf/data/pci-opt-request.json new file mode 100644 index 0000000..09a2f97 --- /dev/null +++ b/csit/tests/osdf/data/pci-opt-request.json @@ -0,0 +1,20 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "callbackUrl": "https://wiki.onap.org:5000/callbackUrl/", + "sourceId": "SO", + "requestType": "create", + "numSolutions": 1, + "optimizers": [ + "placement" + ], + "timeout": 600 + }, + "cellInfo": { + "networkId": "netw1000", + "cellIdList": [ + "cell0" + ] + } +}
\ No newline at end of file diff --git a/csit/tests/osdf/data/placement_request.json b/csit/tests/osdf/data/placement_request.json new file mode 100644 index 0000000..d9b16ca --- /dev/null +++ b/csit/tests/osdf/data/placement_request.json @@ -0,0 +1,102 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "callbackUrl": "https://wiki.onap.org:5000/callbackUrl/", + "sourceId": "SO", + "requestType": "create", + "numSolutions": 1, + "optimizers": ["placement"], + "timeout": 600 + }, + "placementInfo": { + "requestParameters": { "customerLatitude": 32.89748, "customerLongitude": -97.040443, "customerName": "xyz" }, + "subscriberInfo": {"globalSubscriberId": "xxx-xx-xxx", "subscriberName": "subscriber_x"}, + "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" + } + }, + { + "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": "serviceInstanceId", + "cloudOwner": "", + "identifiers": ["gjhd-098-fhd-987"] + } + ], + "excludedCandidates": [ + { + "identifierType": "serviceInstanceId", + "cloudOwner": "", + "identifiers": ["gjhd-098-fhd-987"] + }, + { + "identifierType": "vimId", + "cloudOwner": "vmware", + "identifiers": ["NYMDT67"] + } + ], + "requiredCandidates": [ + { + "identifierType": "vimId", + "cloudOwner": "amazon", + "identifiers": ["TXAUS219"] + } + ] + } + ] + }, + "serviceInfo": { + "serviceInstanceId": "d61b2543-5914-4b8f-8e81-81e38575b8ec", + "serviceName": "vCPE", + "modelInfo": { + "modelInvariantId": "vCPE-invariantId", + "modelVersionId": "vCPE-versionId", + "modelName": "vCPE-model", + "modelType": "service", + "modelVersion": "1.0", + "modelCustomizationName": "vCPE-customeModelName" + } + }, + "licenseInfo": { + "licenseDemands": [ + { + "resourceModuleName": "vGMuxInfra", + "serviceResourceId": "vGMuxInfra-xx", + "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"] + } + } + ] + } +}
\ No newline at end of file diff --git a/csit/tests/osdf/data/termination_request.json b/csit/tests/osdf/data/termination_request.json new file mode 100644 index 0000000..b219645 --- /dev/null +++ b/csit/tests/osdf/data/termination_request.json @@ -0,0 +1,14 @@ +{ + "requestInfo": { + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "sourceId": "SO", + "timeout": 5, + "addtnlArgs": {} + }, + "type":"NSI", + "NxIId":"9629e36c-a3d9-4aed-8368-f72b8be1cd34", + "UUID":"9629e36c-a3d9-4aed-8368-f72b8be1cd34", + "invariantUUID":"9629e36c-a3d9-4aed-8368-f72b8be1cd34" +}
\ No newline at end of file diff --git a/csit/tests/osdf/optf_osdf_nxi_termination.robot b/csit/tests/osdf/optf_osdf_nxi_termination.robot new file mode 100644 index 0000000..881d4cd --- /dev/null +++ b/csit/tests/osdf/optf_osdf_nxi_termination.robot @@ -0,0 +1,69 @@ +*** Settings *** +Library copy +Library json +Library Collections +Library OperatingSystem +Resource ./resources/common-keywords.robot + +Suite Teardown Delete All Sessions + +*** Variables *** +&{placement_auth} = username=test password=testpwd + +*** Keywords *** + +NxiTerminationRequest + [Documentation] Sends request to NxiTermination API + [Arguments] ${data} + ${data_str}= json.dumps ${data} + ${resp}= Http Post host=${osdf_host} restUrl=/api/oof/terminate/nxi/v1 data=${data_str} auth=${placement_auth} + ${response_json} json.loads ${resp.content} + Should Be Equal As Integers ${resp.status_code} 200 + [Return] ${response_json} + +*** Test Cases *** + +TerminationRequestGeneration + [Documentation] This test case will generate request json for different scenarios + ${data}= Get Binary File ${CURDIR}${/}data${/}termination_request.json + ${nsi_termination_request}= json.loads ${data} + Set Global Variable ${nsi_termination_request} + ${nssi_termination_request}= copy.deepcopy ${nsi_termination_request} + Set To Dictionary ${nssi_termination_request} type=NSSI + Set Global Variable ${nssi_termination_request} + ${nsi_termination_request_args}= copy.deepcopy ${nsi_termination_request} + ${request_info}= Set Variable ${nsi_termination_request_args["requestInfo"]} + ${addtnl_args}= Create Dictionary serviceInstanceId=660ca85c-1a0f-4521-a559-65f23e794699 + Set To Dictionary ${request_info} addtnlArgs=${addtnl_args} + Set To Dictionary ${nsi_termination_request_args} requestInfo=${request_info} + Set Global Variable ${nsi_termination_request_args} + ${nssi_termination_request_args}= copy.deepcopy ${nssi_termination_request} + ${request_info}= Set Variable ${nssi_termination_request_args["requestInfo"]} + ${addtnl_args}= Create Dictionary serviceInstanceId=660ca85c-1a0f-4521-a559-65f23e794699 + Set To Dictionary ${request_info} addtnlArgs=${addtnl_args} + Set To Dictionary ${nssi_termination_request_args} requestInfo=${request_info} + Set Global Variable ${nssi_termination_request_args} + +NSITermination + [Documentation] It sends a NSI termination request with no additional arguments + ${response_json}= NxiTerminationRequest ${nsi_termination_request} + Should Be Equal success ${response_json['requestStatus']} + Should Be True ${response_json['terminateResponse']} + +NSSITermination + [Documentation] It sends a NSSI termination request with no additional arguments + ${response_json}= NxiTerminationRequest ${nssi_termination_request} + Should Be Equal success ${response_json['requestStatus']} + Should Be True ${response_json['terminateResponse']} + +NSITerminationWithAddtnlArgs + [Documentation] It sends a NSSI termination request with additional arguments + ${response_json}= NxiTerminationRequest ${nsi_termination_request_args} + Should Be Equal success ${response_json['requestStatus']} + Should Be True ${response_json['terminateResponse']} + +NSSITerminationWithAddtnlArgs + [Documentation] It sends a NSSI termination request with additional arguments + ${response_json}= NxiTerminationRequest ${nssi_termination_request_args} + Should Be Equal success ${response_json['requestStatus']} + Should Be True ${response_json['terminateResponse']} diff --git a/csit/tests/osdf/optf_osdf_setup.robot b/csit/tests/osdf/optf_osdf_setup.robot new file mode 100644 index 0000000..ae3c271 --- /dev/null +++ b/csit/tests/osdf/optf_osdf_setup.robot @@ -0,0 +1,12 @@ +*** Settings *** +Documentation Suite description +Resource ./resources/common-keywords.robot + +*** Test Cases *** +Check OSDF_SIM Docker Container + [Documentation] It checks osdf_simulator docker container is running + Verify Docker RC Status osdf_sim + +Check OSDF Docker Container + [Documentation] It checks optf-osdf docker container is running + Verify Docker RC Status optf-osdf diff --git a/csit/tests/osdf/optf_osdf_test.robot b/csit/tests/osdf/optf_osdf_test.robot new file mode 100644 index 0000000..292df64 --- /dev/null +++ b/csit/tests/osdf/optf_osdf_test.robot @@ -0,0 +1,42 @@ +*** Settings *** +Library json +Library OperatingSystem +Resource ./resources/common-keywords.robot + +Suite Teardown Delete All Sessions + +*** Variables *** +&{placement_auth} = username=test password=testpwd +&{wrong_authorization} = username=test password=test +&{pci_auth}= username=pci_test password=pci_testpwd + +*** Test Cases *** +Healthcheck + [Documentation] It sends a REST GET request to healthcheck url + ${resp}= Http Get ${osdf_host} /api/oof/v1/healthcheck + Should Be Equal As Integers ${resp.status_code} 200 + +SendPlacementWithInvalidAuth + [Documentation] It sends a POST request to osdf fail authentication + ${data}= Get Binary File ${CURDIR}${/}data${/}placement_request.json + ${resp}= Http Post host=${osdf_host} restUrl=/api/oof/v1/placement data=${data} auth=${wrong_authorization} + ${response_json} json.loads ${resp.content} + Should Be Equal Unauthorized, check username and password ${response_json['serviceException']['text']} + Should Be Equal As Integers ${resp.status_code} 401 + +SendPlacementWithValidAuth + [Documentation] It sends a POST request to osdf with correct authentication + ${data}= Get Binary File ${CURDIR}${/}data${/}placement_request.json + ${resp}= Http Post host=${osdf_host} restUrl=/api/oof/v1/placement data=${data} auth=${placement_auth} + ${response_json} json.loads ${resp.content} + Should Be Equal As Integers ${resp.status_code} 202 + Should Be Equal accepted ${response_json['requestStatus']} + +SendPCIOptimizationWithAuth + [Documentation] It sends a POST request PCI Optimization service + + ${data}= Get Binary File ${CURDIR}${/}data${/}pci-opt-request.json + ${resp}= Http Post host=${osdf_host} restUrl=/api/oof/v1/pci data=${data} auth=${pci_auth} + ${response_json} json.loads ${resp.content} + Should Be Equal As Integers ${resp.status_code} 202 + Should Be Equal accepted ${response_json['requestStatus']} diff --git a/csit/tests/osdf/resources/common-keywords.robot b/csit/tests/osdf/resources/common-keywords.robot new file mode 100644 index 0000000..114c8a0 --- /dev/null +++ b/csit/tests/osdf/resources/common-keywords.robot @@ -0,0 +1,51 @@ +*** Settings *** +Documentation Suite description +Library OperatingSystem +Library RequestsLibrary +Library json +Library RequestsLibrary +*** Variables *** +&{headers}= Content-Type=application/json Accept=application/json +${osdf_host} ${OSDF_HOSTNAME}:${OSDF_PORT} +*** Keywords *** +Verify Docker RC Status + [Documentation] Method to verify whether docker instance is up and running + [Arguments] ${process_name} + ${rc} ${output}= Run and Return RC and Output docker ps + Log To Console ********************* + Log To Console retrurn_code = ${rc} + Log To Console output = ${output} + Should Be Equal As Integers ${rc} 0 + Should Contain ${output} ${process_name} + +Http Get + [Documentation] Wrapper for Http GET + [Arguments] ${host} ${restUrl} + Create Session optf-osdf ${host} + ${resp}= Get Request optf-osdf ${restUrl} + Log To Console ********************* + Log To Console response = ${resp} + Log To Console body = ${resp.text} + [Return] ${resp} + +Http Post + [Documentation] Wrapper for Http POST + [Arguments] ${host} ${restUrl} ${auth} ${data} + ${pci_auth}= Create List ${auth['username']} ${auth['password']} + Create Session optf-osdf ${host} headers=${headers} auth=${pci_auth} + ${resp}= Post Request optf-osdf ${restUrl} data=${data} headers=${headers} + Log To Console ********************* + Log To Console response = ${resp} + Log To Console body = ${resp.text} + [Return] ${resp} + +Http Put + [Documentation] Wrapper for Http PUT + [Arguments] ${host} ${restUrl} ${auth} ${data} + ${pci_auth}= Create List ${auth['username']} ${auth['password']} + Create Session optf-osdf ${host} headers=${headers} auth=${pci_auth} + ${resp}= Put Request optf-osdf ${restUrl} data=${data} headers=${headers} + Log To Console ********************* + Log To Console response = ${resp} + Log To Console body = ${resp.text} + [Return] ${resp} diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 5a2c460..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,65 +0,0 @@ -# -# ------------------------------------------------------------------------- -# Copyright (c) 2018 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 ubuntu:16.04 - -ARG MVN_ARTIFACT_VERSION -ARG REPO -ARG HTTP_PROXY=${HTTP_PROXY} -ARG HTTPS_PROXY=${HTTPS_PROXY} - -ENV http_proxy $HTTP_PROXY -ENV https_proxy $HTTPS_PROXY - -ENV OSDF_PORT "8699" -EXPOSE ${OSDF_PORT} - -ENV MZN 2.1.6 -ENV MZN_BASENAME MiniZincIDE-${MZN}-bundle-linux-x86_64 -ENV MZN_GH_BASE https://github.com/MiniZinc/MiniZincIDE -ENV MZN_DL_URL ${MZN_GH_BASE}/releases/download/${MZN}/${MZN_BASENAME}.tgz - -RUN apt-get update -y \ - && apt-get install -y vim unzip wget libmpfr-dev \ - && apt-get install -y git libqt5printsupport5 build-essential \ - && apt-get install -y python3 python3-setuptools python3-dev \ - && easy_install3 pip \ - && pip install --upgrade virtualenv pip wheel - -RUN ln -s /usr/bin/python3.5 /usr/bin/python - -# Minizinc -RUN wget -q $MZN_DL_URL -O mz.tgz \ - && tar xzf mz.tgz \ - && mv $MZN_BASENAME /mz-dist \ - && rm mz.tgz \ - && echo PATH=/mz-dist:$PATH >> ~/.bashrc - -ENV SHELL /bin/bash -ENV PATH /mz-dist:$PATH - -# OSDF -WORKDIR /opt/osdf -RUN wget -O /opt/osdf.zip "https://nexus.onap.org/service/local/artifact/maven/redirect?r=${REPO}&g=org.onap.optf.osdf&a=optf-osdf&e=zip&v=${MVN_ARTIFACT_VERSION}" && \ - unzip -q -o -B /opt/osdf.zip -d /opt/ && \ - rm -f /opt/osdf.zip -RUN mkdir -p /var/log/onap/optf/osdf/ -RUN pip install --no-cache-dir -r requirements.txt - -CMD [ "/opt/osdf/osdfapp.sh" ] diff --git a/docker/opteng/Dockerfile b/docker/opteng/Dockerfile new file mode 100644 index 0000000..037771a --- /dev/null +++ b/docker/opteng/Dockerfile @@ -0,0 +1,42 @@ +# +# ------------------------------------------------------------------------- +# Copyright (c) 2020 AT&T Intellectual Property +# Copyright (C) 2021 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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-base:latest + +EXPOSE 8699 + +# OSDF +WORKDIR /opt/osdf + +COPY onap-osdf-tm/requirements-opteng.txt /tmp/requirements-opteng.txt + +RUN pip install --no-cache-dir -r /tmp/requirements-opteng.txt + +COPY onap-osdf-tm/ /opt/osdf + +RUN mkdir -p /var/log/onap/optf/osdf/ \ + && chown onap:onap /var/log/onap -R \ + && chown onap:onap /opt/osdf -R + +USER onap + +ENV OSDF_CONFIG_FILE "/opt/osdf/config/opteng_config.yaml" + +CMD [ "python", "solverapp.py" ] diff --git a/docker/opteng/assembly/osdf-files.xml b/docker/opteng/assembly/osdf-files.xml new file mode 100644 index 0000000..03a6c45 --- /dev/null +++ b/docker/opteng/assembly/osdf-files.xml @@ -0,0 +1,58 @@ +<!-- + Copyright (C) 2020 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. + +--> +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd"> + <id>osdf-files</id> + + <formats> + <format>tar.gz</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + + + <fileSets> + <fileSet> + <includes> + <include>${project.build.finalName}.zip</include> + </includes> + <directory>${project.build.directory}</directory> + <outputDirectory>/</outputDirectory> + </fileSet> + <fileSet> + <includes> + <include>runtime/**</include> + <include>osdf/**</include> + <include>config/**</include> + <include>setup.py</include> + <include>solverapp.py</include> + </includes> + <excludes> + <exclude>**/*.pyc</exclude> + <exclude>**/__pycache__/**</exclude> + </excludes> + <outputDirectory>/</outputDirectory> + </fileSet> + <fileSet> + <includes> + <include>requirements-opteng.txt</include> + </includes> + <outputDirectory>/</outputDirectory> + </fileSet> + </fileSets> +</assembly> diff --git a/docker/osdf-lib-base/Dockerfile b/docker/osdf-lib-base/Dockerfile new file mode 100644 index 0000000..e8de5ab --- /dev/null +++ b/docker/osdf-lib-base/Dockerfile @@ -0,0 +1,49 @@ +# +# ------------------------------------------------------------------------- +# Copyright (C) 2021 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +FROM python:3.9-slim-buster + +ARG HTTP_PROXY=${HTTP_PROXY} +ARG HTTPS_PROXY=${HTTPS_PROXY} +ARG DEBIAN_FRONTEND=noninteractive + +ENV http_proxy $HTTP_PROXY +ENV https_proxy $HTTPS_PROXY + +RUN groupadd onap \ + && useradd -m -g onap onap + +# Minizinc +RUN apt-get update \ + && apt-get install -y wget \ + && wget -q https://github.com/MiniZinc/MiniZincIDE/releases/download/2.4.3/MiniZincIDE-2.4.3-bundle-linux-x86_64.tgz -O /tmp/mz.tgz \ + && mkdir -p /mz-dist && tar xzf /tmp/mz.tgz -C /mz-dist --strip-components 1\ + && rm /tmp/mz.tgz \ + && wget -q https://github.com/google/or-tools/releases/download/v7.8/or-tools_ubuntu-20.04_v7.8.7959.tar.gz -O /tmp/flatzinc.tar.gz \ + && mkdir -p /ortools \ + && tar zxf /tmp/flatzinc.tar.gz -C /ortools --strip-components 1 \ + && rm /tmp/flatzinc.tar.gz \ + && apt-get remove -y wget + +ENV SHELL /bin/bash +ENV PATH /mz-dist/bin:$PATH + +COPY onap-osdf-tm/requirements.txt /tmp/requirements.txt + +RUN pip install --no-cache-dir -r /tmp/requirements.txt diff --git a/docker/osdf-lib-base/assembly/osdf-lib-files.xml b/docker/osdf-lib-base/assembly/osdf-lib-files.xml new file mode 100644 index 0000000..d3cb5dd --- /dev/null +++ b/docker/osdf-lib-base/assembly/osdf-lib-files.xml @@ -0,0 +1,43 @@ +<!-- + Copyright (C) 2021 Wipro Limited. + + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + +--> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd"> + <id>osdf-lib-files</id> + + <formats> + <format>tar.gz</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + + + <fileSets> + <fileSet> + <includes> + <include>requirements.txt</include> + </includes> + <excludes> + <exclude>**/*.pyc</exclude> + <exclude>**/__pycache__/**</exclude> + </excludes> + <outputDirectory>/</outputDirectory> + </fileSet> + + </fileSets> +</assembly> diff --git a/docker/osdf/Dockerfile b/docker/osdf/Dockerfile new file mode 100644 index 0000000..d1fd2c4 --- /dev/null +++ b/docker/osdf/Dockerfile @@ -0,0 +1,40 @@ +# +# ------------------------------------------------------------------------- +# Copyright (c) 2018 AT&T Intellectual Property +# Copyright (C) 2021 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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-base:latest + +EXPOSE 8699 + +# OSDF +WORKDIR /opt/osdf + +COPY onap-osdf-tm/requirements-osdf.txt /tmp/requirements-osdf.txt + +RUN pip install --no-cache-dir -r /tmp/requirements-osdf.txt + +COPY onap-osdf-tm /opt/osdf + +RUN mkdir -p /var/log/onap/optf/osdf/ \ + && chown -R onap:onap /var/log/onap \ + && chown -R onap:onap /opt/osdf + +USER onap + +CMD [ "python", "osdfapp.py" ] diff --git a/docker/osdf/assembly/osdf-files.xml b/docker/osdf/assembly/osdf-files.xml new file mode 100644 index 0000000..1b7c349 --- /dev/null +++ b/docker/osdf/assembly/osdf-files.xml @@ -0,0 +1,65 @@ +<!-- + Copyright (C) 2019 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. + +--> + +<!-- + Copyright (c) 2018 Intel Corporation. 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. +--> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd"> + <id>osdf-files</id> + + <formats> + <format>tar.gz</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + + + <fileSets> + <fileSet> + <includes> + <include>apps/**</include> + <include>osdf/**</include> + <include>config/**</include> + <include>setup.py</include> + <include>osdfapp.py</include> + <include>requirements.txt</include> + <include>requirements-osdf.txt</include> + </includes> + <excludes> + <exclude>**/*.pyc</exclude> + <exclude>**/__pycache__/**</exclude> + </excludes> + <outputDirectory>/</outputDirectory> + </fileSet> + + </fileSets> +</assembly> diff --git a/docker/build_image.sh b/docker/osdf/build_image.sh index 3d9d823..3d9d823 100755 --- a/docker/build_image.sh +++ b/docker/osdf/build_image.sh diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..43ca5b6 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +/.tox +/_build/* +/__pycache__/* diff --git a/docs/_static/css/ribbon.css b/docs/_static/css/ribbon.css new file mode 100644 index 0000000..db927a4 --- /dev/null +++ b/docs/_static/css/ribbon.css @@ -0,0 +1,64 @@ +.ribbon { + z-index: 1000; + background-color: #a00; + overflow: hidden; + white-space: nowrap; + position: fixed; + top: 25px; + right: -50px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + -webkit-box-shadow: 0 0 10px #888; + -moz-box-shadow: 0 0 10px #888; + box-shadow: 0 0 10px #888; + + } + + .ribbon a { + border: 1px solid #faa; + color: #fff; + display: block; + font: bold 81.25% 'Helvetica Neue', Helvetica, Arial, sans-serif; + margin: 1px 0; + padding: 10px 50px; + text-align: center; + text-decoration: none; + text-shadow: 0 0 5px #444; + transition: 0.5s; + } + + .ribbon a:hover { + background: #c11; + color: #fff; + } + + + /* override table width restrictions */ + @media screen and (min-width: 767px) { + + .wy-table-responsive table td, .wy-table-responsive table th { + /* !important prevents the common CSS stylesheets from overriding + this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; + } + + .wy-table-responsive { + overflow: visible !important; + } + } + + @media screen and (max-width: 767px) { + .wy-table-responsive table td { + white-space: nowrap; + } + } + + /* fix width of the screen */ + + .wy-nav-content { + max-width: 800px; + } +
\ No newline at end of file diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico Binary files differnew file mode 100755 index 0000000..cb712eb --- /dev/null +++ b/docs/_static/favicon.ico diff --git a/docs/_static/logo_onap_2017.png b/docs/_static/logo_onap_2017.png Binary files differnew file mode 100644 index 0000000..5d064f4 --- /dev/null +++ b/docs/_static/logo_onap_2017.png diff --git a/docs/api/swagger/oof-optf-opteng-api.json b/docs/api/swagger/oof-optf-opteng-api.json new file mode 100644 index 0000000..4e77f76 --- /dev/null +++ b/docs/api/swagger/oof-optf-opteng-api.json @@ -0,0 +1,584 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is the ONAP Optimization Engine (Generic Solver) API", + "version": "1.0.0", + "title": "ONAP Optimization ENGINE API", + "contact": { + "email": "vikas.varma@att.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "securityDefinitions": { + "basicAuth": { + "type": "basic", + "description": "HTTP Basic Auth" + } + }, + "security": [ + { + "basicAuth": [] + } + ], + "paths": { + "/optengine/v1": { + "post": { + "tags": [ + "Generic Solver Optimization" + ], + "summary": "Call the Generic Optimization engine", + "operationId": "optimizationRequest", + "description": "call optimization engine", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "optimizationRequest", + "description": "optimization request", + "schema": { + "$ref": "#/definitions/OptimizationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/OptimizationResponse" + } + }, + "202": { + "description": "An optimization request is accepted" + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/optmodel/v1": { + "post": { + "tags": [ + "Request to add the Optimizer model, metadata" + ], + "summary": "Add/Insert the optimization models in the database", + "operationId": "optimModelRequestAPI", + "description": "Request to add update the Optimizer model, metadata", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "optimModelRequest", + "description": "optimization model request", + "schema": { + "$ref": "#/definitions/OptimModelRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/OptimModelResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + }, + "put": { + "tags": [ + "Request to update the Optimizer model, metadata" + ], + "summary": "Add/update the optimization models in the database", + "operationId": "updateModelRequestAPI", + "description": "Request to add update the Optimizer model, metadata", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "optimModelRequest", + "description": "optimization model request", + "schema": { + "$ref": "#/definitions/OptimModelRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/OptimModelResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + }, + "get": { + "tags": [ + "Retrieve all models" + ], + "summary": "Gets all Optim Model data", + "description": "Retrieves all Optim Models", + "operationId": "getAllOptModelData", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ArrayOfOptimModelResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/optmodel/v1/{model_id}": { + "get": { + "tags": [ + "Retrieve Model Data" + ], + "summary": "Gets the Optim Model data", + "description": "Retrieves the Optim Model data given modelId", + "operationId": "getOptModelById", + "parameters": [ + { + "in": "path", + "name": "model_id", + "description": "Model ID", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/OptimModelResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + }, + "delete": { + "tags": [ + "Delete Model Data" + ], + "summary": "Delete the Optim Model data", + "description": "Deletes the Optim Model data given modelId", + "operationId": "deleteOptModelById", + "parameters": [ + { + "in": "path", + "name": "model_id", + "description": "Model ID", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/DeleteModelResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + } + }, + "definitions": { + "OptimizationResponse": { + "type": "object", + "required": [ + "transactionId", + "requestID", + "requestStatus" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestID": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestStatus": { + "type": "string", + "description": "request status (accepted, done, completed,failed)", + "example": "done" + }, + "statusMessage": { + "type": "string", + "description": "Status message (incomplete, complete, unsatisfiable, unknown, unbounded, unsat_or_unbounded, error)", + "example": "complete" + }, + "solutions": { + "additionalProperties": { + "type": "object" + }, + "example": { + "SCHEDULED": [ + [ + 0, + 1 + ], + [ + 0, + 1 + ] + ], + "OPTIMIZED": 2 + } + } + } + }, + "OptimizationRequest": { + "type": "object", + "required": [ + "requestInfo", + "optimInfo" + ], + "properties": { + "requestInfo": { + "$ref": "#/definitions/RequestInfo" + }, + "optimInfo": { + "$ref": "#/definitions/OptimInfo" + } + } + }, + "RequestInfo": { + "type": "object", + "required": [ + "transactionId", + "requestID", + "sourceId" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestID": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "callbackUrl": { + "type": "string", + "format": "url", + "description": "The end point of a callback service where recommendations are posted.", + "example": "myDomain.com/myCallback" + }, + "sourceId": { + "type": "string", + "description": "The unique ID of a client making an optimization call.", + "example": "son-handler" + }, + "timeout": { + "type": "integer", + "description": "A tolerance window (in second) for expecting solutions", + "example": 5 + } + } + }, + "OptimInfo": { + "type": "object", + "properties": { + "modelId": { + "type": "string", + "description": "ModelId from the database, if its not populated, assume that solverModel will be populated", + "example": "pci_model1" + }, + "solver": { + "type": "string", + "description": "type of solver (mzn, py, etc.)", + "example": "mzn" + }, + "solverArgs": { + "type": "object", + "description": "Arguments for solver", + "additionalProperties": { + "type": "object" + }, + "example": { + "solver": "cbc", + "timeout": 5 + } + }, + "modelContent": { + "type": "string", + "description": "a large blob string containing the model (which is not that problematic since models are fairly small)." + }, + "optData": { + "$ref": "#/definitions/DataInfo" + } + } + }, + "DataInfo": { + "type": "object", + "description": "Data Payload, input data for the solver, either text or json", + "properties": { + "text": { + "type": "string", + "description": "Solver data as a string", + "example": "flour = 8000; \r\nbanana = 11;\r\n " + }, + "json": { + "type": "object", + "description": "Solver data as a json", + "additionalProperties": { + "type": "object" + }, + "example": { + "flour": 8000, + "banana": 11 + } + } + } + }, + "OptimModelRequest": { + "type": "object", + "required": [ + "requestInfo", + "modelInfo" + ], + "properties": { + "requestInfo": { + "$ref": "#/definitions/ModelRequestInfo" + }, + "modelInfo": { + "$ref": "#/definitions/OptimModelInfo" + } + } + }, + "ModelRequestInfo": { + "type": "object", + "required": [ + "transactionId", + "requestID", + "sourceId" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestID": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "sourceId": { + "type": "string", + "description": "The unique ID of a client making an optimization call.", + "example": "optf-osdf" + } + } + }, + "OptimModelInfo": { + "type": "object", + "required": [ + "modelId", + "solver", + "description", + "modelContent" + ], + "properties": { + "modelId": { + "type": "string", + "description": "ModelId from the database", + "example": "pci_anr_model1" + }, + "solver": { + "type": "string", + "description": "type of solver (mzn, py, etc.)", + "example": "mzn" + }, + "description": { + "type": "string", + "description": "Description of the model", + "example": "mzn model to optimize pci/anr models" + }, + "modelContent": { + "type": "string", + "description": "a large blob string containing the model (which is not that problematic since models are fairly small).", + "example": "mzn content" + } + } + }, + "ArrayOfOptimModelResponse": { + "type": "array", + "items": { + "$ref": "#/definitions/OptimModelResponse" + } + }, + "OptimModelResponse": { + "type": "object", + "required": [ + "modelId", + "solver", + "modelContent" + ], + "properties": { + "modelId": { + "type": "string", + "description": "ModelId from the database", + "example": "pci_anr_model1" + }, + "solver": { + "type": "string", + "description": "type of solver (mzn, py, etc.)", + "example": "mzn" + }, + "description": { + "type": "string", + "description": "Description of the model", + "example": "mzn model to optimize pci/anr models" + }, + "modelContent": { + "type": "string", + "description": "a large blob string containing the model (which is not that problematic since models are fairly small).", + "example": "mzn content" + }, + "statusMessage": { + "type": "string", + "description": "status message.", + "example": "mzn content" + } + } + }, + "DeleteModelResponse": { + "type": "object", + "required": [ + "statusMessage" + ], + "properties": { + "statusMessage": { + "type": "string", + "description": "status message.", + "example": "model data for modelId pci_anr_model1 deleted" + } + } + } + }, + "schemes": [ + "https" + ], + "host": "virtserver.swaggerhub.com", + "basePath": "/api/oof/" +} diff --git a/docs/api/swagger/oof-osdf-has-api.json b/docs/api/swagger/oof-osdf-has-api.json new file mode 100644 index 0000000..a2fa43f --- /dev/null +++ b/docs/api/swagger/oof-osdf-has-api.json @@ -0,0 +1,2155 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is the ONAP OOF OSDF (Optimization Service Design Framework) API", + "version": "1.0.0", + "title": "OSDF API", + "contact": { + "email": "frank.sandoval@oamtechnologies.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "securityDefinitions": { + "basicAuth": { + "type": "basic", + "description": "HTTP Basic Auth" + } + }, + "security": [ + { + "basicAuth": [] + } + ], + "paths": { + "/v2/placement": { + "post": { + "tags": [ + "Placement Optimization" + ], + "summary": "create/update a placement", + "operationId": "createPlacement", + "description": "create/update a placement", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "placementRequest", + "description": "placement request", + "schema": { + "$ref": "#/definitions/PlacementRequest" + } + } + ], + "responses": { + "201": { + "description": "An optimization solution is found." + }, + "202": { + "description": "An optimization request is accepted" + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/placement/v1": { + "post": { + "tags": [ + "Placement Optimization" + ], + "summary": "create/update a placement", + "operationId": "createPlacementv1", + "description": "create/update a placement", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "placementRequest", + "description": "placement request", + "schema": { + "$ref": "#/definitions/PlacementRequest" + } + } + ], + "responses": { + "201": { + "description": "An optimization solution is found." + }, + "202": { + "description": "An optimization request is accepted" + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/v1/pci": { + "post": { + "tags": [ + "PCI/ANR Optimization" + ], + "summary": "Initiate PCI/ANR Optimization", + "operationId": "initiatePCIOptRequest", + "description": "Initiate PCI/ANR Optimization", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "PCIOptimizationRequest", + "description": "PCI request", + "schema": { + "$ref": "#/definitions/PCIOptRequest" + } + } + ], + "responses": { + "201": { + "description": "An optimization solution is found." + }, + "202": { + "description": "An optimization request is accepted" + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/pci/v1": { + "post": { + "tags": [ + "PCI/ANR Optimization" + ], + "summary": "Initiate PCI/ANR Optimization", + "operationId": "initiatePCIOptRequestv1", + "description": "Initiate PCI/ANR Optimization", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "PCIOptimizationRequest", + "description": "PCI request", + "schema": { + "$ref": "#/definitions/PCIOptRequest" + } + } + ], + "responses": { + "201": { + "description": "An optimization solution is found." + }, + "202": { + "description": "An optimization request is accepted" + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/selection/nst/v1": { + "post": { + "tags": [ + "NST Selection" + ], + "summary": "NST selection", + "operationId": "selectNstRequest", + "description": "Request for NST selection", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "NSTSelectionRequest", + "description": "nst selection request", + "schema": { + "$ref": "#/definitions/NSTSelectionRequest" + } + } + ], + "responses": { + "202": { + "description": "An optimization request is accepted", + "schema": { + "$ref": "#/definitions/SynchronousResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/selection/nsi/v1": { + "post": { + "tags": [ + "NSI Selection" + ], + "summary": "NSI selection", + "operationId": "selectNsiRequest", + "description": "Request for NSI selection", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "NSISelectionRequest", + "description": "NSI selection request", + "schema": { + "$ref": "#/definitions/NSISelectionRequest" + } + } + ], + "responses": { + "202": { + "description": "An optimization request is accepted", + "schema": { + "$ref": "#/definitions/SynchronousResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/selection/nssi/v1": { + "post": { + "tags": [ + "NSSI Selection" + ], + "summary": "NSSI selection", + "operationId": "selectNssiRequest", + "description": "Request for NSSI selection", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "NSSISelectionRequest", + "description": "NSSI selection request", + "schema": { + "$ref": "#/definitions/NSSISelectionRequest" + } + } + ], + "responses": { + "202": { + "description": "An optimization request is accepted", + "schema": { + "$ref": "#/definitions/SynchronousResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/terminate/nxi/v1": { + "post": { + "tags": [ + "NSSI/NSI Termination" + ], + "summary": "NSSI/NSI Termination", + "operationId": "terminateNxiRequest", + "description": "Request for NSSI/NSI Termination", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "NxITerminationRequest", + "description": "NSSI/NSI termination request", + "schema": { + "$ref": "#/definitions/NxITerminationRequest" + } + } + ], + "responses": { + "200": { + "description": "request has succeeded", + "schema": { + "$ref": "#/definitions/SynchronousTerminationResponse" + } + }, + "400": { + "description": "bad request" + }, + "401": { + "description": "Request body is not compliant with the API definition" + }, + "404": { + "description": "The server cannot find the requested URI" + }, + "405": { + "description": "The requested method is not supported by a server." + }, + "500": { + "description": "The server encountered an internal server error or timed out" + } + } + } + }, + "/api/oof/route/v1": { + "post": { + "tags": [ + "Route Select" + ], + "summary": "Find the optimistic route between OTN domains", + "description": "", + "operationId": "getRoute", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Source and Destination nodes across which optmistic route have to be obtained.", + "required": true, + "schema": { + "$ref": "#/definitions/RouteRequest" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/routeResponseBody" + } + } + }, + "405": { + "description": "Invalid input" + } + } + } + }, + "/api/oof/mdons/route/v1": { + "post": { + "tags": [ + "Inter Domain Route Select" + ], + "summary":"Find the optimistic inter domain route for multi domain optical system", + "description":"", + "operationId":"getInterDomainRoute", + "consumes": [ + "application/json" + ], + "produces":[ + "application/json" + ], + "parameters":[ + { + "in":"body", + "name":"body", + "description":"Source and Destination interfaces across which optmistic route have to be obtained.", + "required":true, + "schema":{ + "$ref":"#/definitions/InterDomainRouteRequest" + } + } + ], + "responses":{ + "200":{ + "description":"successful operation", + "schema":{ + "$ref":"#/definitions/InterDomainRouteResponseBody" + } + }, + "400":{ + "description":"bad request" + }, + "401":{ + "description":"Request body is not compliant with the API definition" + }, + "404":{ + "description":"The server cannot find the requested URI" + }, + "405":{ + "description":"The requested method is not supported by a server." + }, + "500":{ + "description":"The server encountered an internal server error or timed out" + } + } + } + } + }, + "definitions": { + "InterDomainRouteRequest":{ + "type":"object", + "properties":{ + "requestInfo":{ + "$ref":"#/definitions/requestInfo" + }, + "routeInfo":{ + "$ref":"#/definitions/interDomainRouteInfo" + } + } + }, + "interDomainRouteInfo":{ + "type":"object", + "properties":{ + "routeRequest":{ + "$ref":"#/definitions/interDomainRouteRequest" + } + } + }, + "interDomainRouteRequest":{ + "type":"object", + "properties":{ + "srcDetails":{ + "$ref":"#/definitions/interDomainPortDetails" + }, + "dstDetails":{ + "$ref":"#/definitions/interDomainPortDetails" + }, + "serviceRate":{ + "type":"string", + "description":"The rate of the service." + } + } + }, + "interDomainPortDetails":{ + "type":"object", + "properties":{ + "interfaceId":{ + "type":"string", + "description":"The port id." + }, + "nodeId":{ + "type":"string", + "description":"The node id." + }, + "controllerId":{ + "type":"string", + "description":"The controller id to which the node belongs to." + } + } + }, + "InterDomainRouteResponseBody":{ + "type":"object", + "properties":{ + "requestId":{ + "type":"string", + "description":"A unique Id for an ONAP transaction." + }, + "transactionId":{ + "type":"string", + "description":"A unique ID to track multiple requests associated with a transaction." + }, + "statusMessage":{ + "type":"string", + "description":"Reasoning if a requestStatus is failure." + }, + "requestStatus":{ + "type":"string", + "description":"The status of a request." + }, + "solutions":{ + "$ref":"#/definitions/InterDomainRouteSolutionInfo" + } + } + }, + "InterDomainRouteSolutionInfo":{ + "type":"object", + "properties":{ + "routeInfo":{ + "$ref":"#/definitions/interDomainResponseRouteinfo" + } + } + }, + "interDomainResponseRouteinfo":{ + "type":"object", + "properties":{ + "serviceRoute":{ + "type":"array", + "items":{ + "$ref":"#/definitions/serviceRouteDetails" + } + }, + "linkList":{ + "type":"array", + "items":{ + "type":"string" + }, + "description":"A list of link names of the route.", + "example":[ + "link1", + "link2" + ] + } + } + }, + "serviceRouteDetails":{ + "type":"object", + "properties":{ + "srcInterfaceId":{ + "type":"string", + "description":"Source port Id of the domain Service." + }, + "dstInterfaceId":{ + "type":"string", + "description":"Destination Port Id of the domain Service." + }, + "controllerId":{ + "type":"string", + "description":"Controller Id of the domain." + } + } + }, + "RouteRequest": { + "type": "object", + "properties": { + "requestInfo": { + "$ref": "#/definitions/requestInfo" + }, + "routeInfo": { + "$ref": "#/definitions/routeInfo" + } + } + }, + "requestInfo": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "A unique ID to track an ONAP transaction." + }, + "requestId": { + "type": "string", + "description": "A unique ID to track multiple requests associated with a transaction." + }, + "callbackUrl": { + "type": "string", + "description": "The end point of a callback service where recommendations are posted." + }, + "callbackHeader": { + "type": "string", + "description": "The header information a client expecting in a async callback." + }, + "sourceId": { + "type": "string", + "description": "The unique ID of a client making an optimization call." + }, + "requestType": { + "type": "string", + "format": "string", + "description": "The type of request being placed.", + "enum": [ + "create", + "update", + "delete" + ] + }, + "numSolutions": { + "type": "integer", + "format": "int32", + "description": "Expected number of solutions. numSolution can also be specified using an optimization query policies, where the default configured value is 1. The value from a request gets higher precedence over the value defined in a policy." + }, + "optimizers": { + "description": "A list of optimization services that can be used to resolve the route", + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer", + "format": "int32", + "description": "A tolerance window (in secs) for expecting solutions. Default is 600 secs." + } + } + }, + "routeInfo": { + "type": "object", + "properties": { + "routeRequest": { + "$ref": "#/definitions/routeRequest" + } + } + }, + "routeRequest": { + "type": "object", + "properties": { + "srcPort": { + "$ref": "#/definitions/routePortInfo" + }, + "destPort": { + "$ref": "#/definitions/routePortInfo" + } + } + }, + "routePortInfo": { + "type": "object", + "properties": { + "accessTopologyId": { + "type": "string", + "description": "A unique ID of the Access Topology." + }, + "accessClientId": { + "type": "string", + "format": "string", + "description": "A unique ID of the client which provides the access." + }, + "accessProviderId": { + "type": "string", + "format": "string", + "description": "A unique ID of the access provider" + }, + "accessNodeId": { + "type": "string", + "format": "string", + "description": "A unique ID of the node to/from which the route has to be established." + }, + "accessLtpId": { + "type": "integer", + "format": "int32", + "description": "A unique ID of the Termination Point to/from which the route has to be established." + } + } + }, + "routeResponseBody": { + "type": "object", + "properties": { + "requestId": { + "type": "string", + "description": "A unique Id for an ONAP transaction." + }, + "transactionId": { + "type": "string", + "description": "A unique ID to track multiple requests associated with a transaction." + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failure." + }, + "requestStatus": { + "type": "string", + "description": "The status of a request." + }, + "solutions": { + "$ref": "#/definitions/RouteSolutionInfo" + } + } + }, + "RouteSolutionInfo": { + "type": "object", + "properties": { + "startTime": { + "type": "string", + "format": "date-time", + "description": "start time of the operation in RFC 3339 notation for example, 2017-07-21T17:32:28Z." + }, + "finishTime": { + "type": "string", + "format": "date-time", + "description": "end time of the operation in RFC 3339 notation for example, 2017-07-21T17:32:28Z." + }, + "links": { + "description": "A list of vpn info that can be used to establish the route between source and destination port/node.", + "type": "array", + "items": { + "$ref": "#/definitions/link" + } + } + } + }, + "link": { + "type": "object", + "properties": { + "linkId": { + "type": "string", + "description": "Id or name identifies a link uniquely." + } + } + }, + "PlacementRequest": { + "type": "object", + "required": [ + "requestInfo", + "placementInfo", + "licenseInfo", + "serviceInfo" + ], + "properties": { + "requestInfo": { + "$ref": "#/definitions/RequestInfo" + }, + "placementInfo": { + "$ref": "#/definitions/PlacementInfo" + }, + "licenseInfo": { + "$ref": "#/definitions/LicenseInfo" + }, + "serviceInfo": { + "$ref": "#/definitions/ServiceInfo" + } + } + }, + "RequestInfo": { + "type": "object", + "required": [ + "transactionId", + "requestId", + "callbackUrl", + "sourceId", + "requestType", + "optimizers", + "timeout" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "callbackUrl": { + "type": "string", + "format": "url", + "description": "The end point of a callback service where recommendations are posted.", + "example": "myDomain.com/myCallback" + }, + "callbackHeader": { + "type": "string", + "description": "JSON blob. The header information a client expecting in a async callback.", + "example": { + "blob": "content" + } + }, + "sourceId": { + "type": "string", + "description": "The unique ID of a client making an optimization call.", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestType": { + "type": "string", + "enum": [ + "create", + "update", + "delete" + ], + "description": "The type of a request", + "example": "create" + }, + "numSolutions": { + "type": "integer", + "description": "Expected number of solutions.", + "example": 1 + }, + "optimizers": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "placement", + "pci", + "pci-anr" + ] + }, + "description": "A list of optimization services.", + "example": [ + "placement" + ] + }, + "timeout": { + "type": "integer", + "description": "A tolerance window (in second) for expecting solutions.", + "example": 5 + } + } + }, + "PlacementInfo": { + "type": "object", + "required": [ + "requestParameters", + "placementDemands" + ], + "properties": { + "requestParameters": { + "type": "string", + "description": "JSON blob. A service ordering information", + "example": { + "blob": "content" + } + }, + "placementDemands": { + "type": "array", + "items": { + "$ref": "#/definitions/PlacementDemand" + }, + "description": "The resource information for a placement service" + }, + "subscriberInfo": { + "type": "object", + "items": { + "$ref": "#/definitions/SubscriberInfo" + }, + "description": "The information of a service subscriber." + } + } + }, + "PlacementDemand": { + "type": "object", + "required": [ + "resourceModuleName", + "serviceResourceId", + "resourceModelInfo" + ], + "properties": { + "resourceModuleName": { + "type": "string", + "description": "A resource name as defined in a service mode", + "example": "myResourceName" + }, + "serviceResourceId": { + "type": "string", + "description": "A unique resource Id with a local scope between client and OOF.", + "example": "myResourceId" + }, + "givenPlacement": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "description": "placement parameters defined in the ordering system, keyname include tenantId", + "example": { + "tenantId": "1" + } + }, + "resourceModelInfo": { + "$ref": "#/definitions/ModelMetaData" + }, + "existingCandidates": { + "$ref": "#/definitions/Candidates" + }, + "excludedCandidates": { + "$ref": "#/definitions/Candidates" + }, + "requiredCandidates": { + "$ref": "#/definitions/Candidates" + } + } + }, + "ModelMetaData": { + "type": "object", + "required": [ + "modelInvariantId", + "modelVersionId" + ], + "properties": { + "modelInvariantId": { + "type": "string", + "description": "A model invariant Id as defined in a service model.", + "example": "my model invariant Id" + }, + "modelVersionId": { + "type": "string", + "description": "A unique model Id as defined in a service model.", + "example": "my unique model Id" + }, + "modelName": { + "type": "string", + "description": "A model name as defined in a service model", + "example": "my model name" + }, + "modelType": { + "type": "string", + "description": "A model type as defined in a service model.", + "example": "my model type" + }, + "modelVersion": { + "type": "string", + "description": "A model version as defined in a service model.", + "example": "my model version" + }, + "modelCustomizationName": { + "type": "string", + "description": "A model customization name as defined in a service model.", + "example": "my model customization" + } + } + }, + "Candidates": { + "type": "object", + "required": [ + "identifierType", + "identifiers" + ], + "properties": { + "identifierType": { + "type": "string", + "enum": [ + "service_instance_id", + "vnf_name", + "cloud_region_id" + ], + "description": "The type of a candidate.", + "example": "service_instance_id" + }, + "identifiers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of identifiers.", + "example": "candidateId" + }, + "cloudOwner": { + "type": "string", + "description": "The name of a cloud owner. Only required if identifierType is cloud_region_id", + "example": "cloud_owner" + } + } + }, + "SubscriberInfo": { + "type": "object", + "required": [ + "globalSubscriberId", + "subscriberName", + "subscriberCommonSiteId" + ], + "properties": { + "globalSubscriberId": { + "type": "string", + "description": "An ID of a subscriber.", + "example": "subscriber_id" + }, + "subscriberName": { + "type": "string", + "description": "The name of a subscriber. If the name is not known, the value must be 'unknown'", + "example": "subscriber_name" + }, + "subscriberCommonSiteId": { + "type": "string", + "description": "Id representing a subscriber location", + "example": "subscriber_location_id" + } + } + }, + "LicenseInfo": { + "type": "object", + "required": [ + "licenseDemands" + ], + "properties": { + "licenseDemands": { + "type": "array", + "items": { + "$ref": "#/definitions/LicenseDemands" + }, + "description": "A list of resources for license selection" + } + } + }, + "LicenseDemands": { + "type": "object", + "required": [ + "resourceModuleName", + "serviceResourceId", + "resourceModelInfo" + ], + "properties": { + "resourceModuleName": { + "type": "string", + "description": "A resource name as defined in a service model.", + "example": "service_instance_id" + }, + "serviceResourceId": { + "type": "string", + "description": "A unique resource Id with a local scope between client and OOF.", + "example": "service_instance_id" + }, + "resourceModelInfo": { + "$ref": "#/definitions/ModelMetaData" + }, + "existingLicenses": { + "$ref": "#/definitions/LicenseModel" + } + } + }, + "LicenseModel": { + "type": "object", + "required": [ + "entitlementPoolUUID", + "licenseKeyGroupUUID" + ], + "properties": { + "entitlementPoolUUID": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "Entitlement pool UUIDs associated with a resource.", + "example": "candidateId" + }, + "licenseKeyGroupUUID": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "License key groups associated with a resource", + "example": "candidateId" + } + } + }, + "SynchronousResponse": { + "type": "object", + "required": [ + "requestId", + "transactionId", + "requestStatus" + ], + "properties": { + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique Id for an ONAP transaction", + "example": "ONAP transaction id" + }, + "transactionId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction.", + "example": "requests id" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failure.", + "example": "requestStatus" + }, + "requestStatus": { + "type": "string", + "enum": [ + "success", + "failure" + ], + "description": "The status of a request.", + "example": "success" + } + } + }, + "SynchronousTerminationResponse": { + "type": "object", + "required": [ + "requestId", + "transactionId", + "requestStatus", + "terminateResponse" + ], + "properties": { + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique Id for an ONAP transaction", + "example": "ONAP transaction id" + }, + "transactionId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction.", + "example": "requests id" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failure.", + "example": "requestStatus" + }, + "requestStatus": { + "type": "string", + "enum": [ + "success", + "failure" + ], + "description": "The status of a request.", + "example": "success" + }, + "terminateResponse": { + "type": "boolean" + }, + "reason": { + "type": "string", + "description": "Reason if terminateResponse is false", + "example": "Restricted by Policy" + } + } + }, + "PlacementAsynchronousResponse": { + "type": "object", + "required": [ + "requestId", + "transactionId", + "requestStatus", + "solutions" + ], + "properties": { + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique Id for an ONAP transaction", + "example": "ONAP transaction id" + }, + "transactionId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction.", + "example": "requests id" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failure.", + "example": "requestStatus" + }, + "requestStatus": { + "type": "string", + "enum": [ + "success", + "failure" + ], + "description": "The status of a request.", + "example": "success" + }, + "solutions": { + "$ref": "#/definitions/Solutions" + } + } + }, + "Solutions": { + "type": "object", + "required": [ + "placementSolutions", + "licenseSolutions" + ], + "properties": { + "placementSolutions": { + "type": "array", + "items": { + "$ref": "#/definitions/ComprehensiveSolution" + }, + "description": "A list of placement solutions." + }, + "licenseSolutions": { + "type": "array", + "items": { + "$ref": "#/definitions/LicenseSolution" + }, + "description": "A list of license solutions." + } + } + }, + "ComprehensiveSolution": { + "type": "object", + "required": [ + "placementSolutions" + ], + "properties": { + "placementSolutions": { + "type": "array", + "items": { + "$ref": "#/definitions/PlacementSolution" + }, + "description": "A list of placement solutions." + } + } + }, + "PlacementSolution": { + "type": "object", + "required": [ + "resourceModuleName", + "serviceResourceId", + "identifierType", + "identifier" + ], + "properties": { + "resourceModuleName": { + "type": "string", + "description": "The name of a resource as defined in the service model", + "example": "resource name" + }, + "serviceResourceId": { + "type": "string", + "description": "A resource Id as defined in a service model.", + "example": "resource id" + }, + "identifierType": { + "type": "string", + "enum": [ + "service_instance_id" + ], + "description": "The type of a candidate.", + "example": "candidate type" + }, + "identifier": { + "type": "string", + "description": "The id of a candidate.", + "example": "candidate id" + }, + "assignmentInfo": { + "type": "array", + "items": { + "$ref": "#/definitions/AssignmentInfo" + }, + "description": "Additional information related to a candidate." + } + } + }, + "AssignmentInfo": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string", + "description": "An attribute name", + "example": "attribute name" + }, + "value": { + "type": "string", + "description": "An attribute value.", + "example": "attribute value" + } + } + }, + "LicenseSolution": { + "type": "object", + "required": [ + "resourceModuleName", + "serviceResourceId", + "entitlementPoolUUID", + "licenseKeyGroupUUID", + "entitlementPoolInvariantUUID", + "licenseKeyGroupInvariantUUID" + ], + "properties": { + "resourceModuleName": { + "type": "string", + "description": "A resource name as defined in a service", + "example": "resource name" + }, + "serviceResourceId": { + "type": "string", + "description": "A resource Id as defined in a service.", + "example": "resource Id" + }, + "entitlementPoolUUID": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "A list of entitlementPoolUUIDs", + "example": "entitlementPoolUUID" + }, + "licenseKeyGroupUUID": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "A list of licenseKeyGroupUUID.", + "example": "licenseKeyGroupUUID" + }, + "entitlementPoolInvariantUUID": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "A list of entitlementPoolInvariantUUID", + "example": "entitlementPoolInvariantUUID" + }, + "licenseKeyGroupInvariantUUID": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "A list of licenseKeyGroupInvariantUUID", + "example": "licenseKeyGroupInvariantUUID" + } + } + }, + "ServiceInfo": { + "type": "object", + "required": [ + "serviceInstanceId", + "modelInfo", + "serviceName" + ], + "properties": { + "serviceInstanceId": { + "type": "string", + "description": "A service instance id associated with a request.", + "example": "service_instance_id" + }, + "modelInfo": { + "$ref": "#/definitions/ModelMetaData" + }, + "serviceName": { + "type": "string", + "description": "The name of a service", + "example": "service_name" + } + } + }, + "PCIOptRequest": { + "type": "object", + "required": [ + "requestInfo", + "cellInfo" + ], + "properties": { + "requestInfo": { + "$ref": "#/definitions/RequestInfo" + }, + "cellInfo": { + "$ref": "#/definitions/CellInfo" + } + } + }, + "CellInfo": { + "type": "object", + "required": [ + "networkId", + "cellIdList", + "anrInputList", + "trigger" + ], + "properties": { + "networkId": { + "type": "string", + "description": "Id of network requiring PCI optimization", + "example": 100 + }, + "cellIdList": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of cellIds triggering need for PCI optimization (eg.potential confusion)", + "example": [ + "cell0001", + "cell0002" + ] + }, + "anrInputList": { + "type": "array", + "items": { + "$ref": "#/definitions/ANRInfo" + }, + "description": "A list of ANR Input." + }, + "fixedPCICells": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of blacklisted cells whose PCI values should not be changed", + "example": [ + "cell0007", + "cell0009" + ] + }, + "priorityTreatmentCells": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of cells which should be given special treatment during optimization", + "example": [ + "cell0010", + "cell0003" + ] + }, + "trigger": { + "type": "string", + "description": "Type of trigger causing need for PCI optimization", + "example": "NbrListChange" + } + } + }, + "PCIAsynchronousResponse": { + "type": "object", + "required": [ + "requestId", + "transactionId", + "requestStatus", + "solutions" + ], + "properties": { + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique Id for an ONAP transaction", + "example": "ONAP transaction id" + }, + "transactionId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction.", + "example": "requests id" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failure.", + "example": "requestStatus" + }, + "requestStatus": { + "type": "string", + "enum": [ + "success", + "failure" + ], + "description": "The status of a request.", + "example": "success" + }, + "solutions": { + "$ref": "#/definitions/PCIANRSolutions" + } + } + }, + "PCIANRSolutions": { + "type": "object", + "required": [ + "networkId", + "pciSolutions", + "anrSolutions" + ], + "properties": { + "networkId": { + "type": "string", + "description": "Id of network requiring PCI optimization", + "example": 100 + }, + "pciSolutions": { + "type": "array", + "items": { + "$ref": "#/definitions/PCISolution" + }, + "description": "A list of PCI solutions." + }, + "anrSolutions": { + "type": "array", + "items": { + "$ref": "#/definitions/ANRInfo" + }, + "description": "A list of ANR solutions." + } + } + }, + "PCISolution": { + "type": "object", + "required": [ + "cellId", + "pci" + ], + "properties": { + "cellId": { + "type": "string", + "description": "cellId with modified PCI value", + "example": "cell0001" + }, + "pci": { + "type": "integer", + "description": "New PCI value for cellId", + "example": 1 + } + } + }, + "ANRInfo": { + "type": "object", + "required": [ + "cellId", + "removeableNeighbors" + ], + "properties": { + "cellId": { + "type": "string", + "description": "cellId with modified PCI value", + "example": "cell0001" + }, + "removeableNeighbors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of neighbors to be removed", + "example": [ + "cell0002", + "cell0003" + ] + } + } + }, + "NSTSelectionRequest": { + "type": "object", + "required": [ + "requestInfo", + "serviceProfile" + ], + "properties": { + "requestInfo": { + "$ref": "#/definitions/RequestInfo2" + }, + "serviceProfile": { + "$ref": "#/definitions/ServiceProfile" + } + } + }, + "NSISelectionRequest": { + "type": "object", + "required": [ + "requestInfo", + "serviceProfile", + "NSTInfo", + "NSSTInfo", + "subnetCapabilities" + ], + "properties": { + "serviceProfile": { + "$ref": "#/definitions/ServiceProfile" + }, + "requestInfo": { + "$ref": "#/definitions/RequestInfo2" + }, + "NSTInfo": { + "$ref": "#/definitions/NSTInfo" + }, + "NSSTInfo": { + "type": "array", + "description": "List of constituent NSST(s) of the NST", + "items": { + "$ref": "#/definitions/NSSTInfo" + } + }, + "preferReuse": { + "type": "boolean", + "description": "true if reusing an existing NSI is preferred/false if creating a new NSI is preferred " + }, + "subnetCapabilities": { + "type": "array", + "description": "List of subnet capabilities", + "items": { + "$ref": "#/definitions/SubnetCapability" + } + } + } + }, + "NSSISelectionRequest": { + "type": "object", + "required": [ + "requestInfo", + "NSSTInfo", + "sliceProfile" + ], + "properties": { + "sliceProfile": { + "$ref": "#/definitions/SliceProfile" + }, + "requestInfo": { + "$ref": "#/definitions/RequestInfo2" + }, + "NSSTInfo": { + "$ref": "#/definitions/NSSTInfo" + } + } + }, + "NxITerminationRequest": { + "type": "object", + "required": [ + "requestInfo", + "type", + "NxIId" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "NSI", + "NSSI" + ], + "description": "indicates if the request is for NSI/NSSI termination" + }, + "NxIId": { + "type": "string", + "format": "UUID", + "description": "Id of NSI / NSSI" + }, + "UUID": { + "type": "string", + "format": "UUID", + "description": "UUID of NST/NSST model" + }, + "invariantUUID": { + "type": "string", + "format": "UUID", + "description": "invariant UUID of NST/NSST model" + }, + "requestInfo": { + "$ref": "#/definitions/RequestInfo2" + } + } + }, + "SubnetCapability": { + "type": "object", + "required": [ + "domainType", + "capabilityDetails" + ], + "properties": { + "domainType": { + "type": "string", + "description": "type of the subnet" + }, + "capabilityDetails": { + "type": "string", + "description": "A JSON object containing capability parameters", + "example": { + "blob": "content" + } + } + } + }, + "NSTInfo": { + "type": "object", + "required": [ + "UUID", + "invariantUUID", + "name" + ], + "properties": { + "UUID": { + "type": "string", + "format": "uuid", + "description": "UUID of NST" + }, + "invariantUUID": { + "type": "string", + "format": "uuid", + "description": "Invariant UUID" + }, + "name": { + "type": "string", + "description": "name of the NST model" + } + } + }, + "NSSTInfo": { + "type": "object", + "required": [ + "UUID", + "invariantUUID", + "name" + ], + "properties": { + "UUID": { + "type": "string", + "format": "uuid", + "description": "UUID of NSST" + }, + "invariantUUID": { + "type": "string", + "format": "uuid", + "description": "Invariant UUID" + }, + "name": { + "type": "string", + "description": "name of the NSST model" + } + } + }, + "ServiceProfile": { + "type": "string", + "description": "JSON blob. Containing service profile parameters. The contents are based on 3GPP TS 23.541 Release 16 contents, and will be in the form of attribute value pairs.", + "example": { + "blob": "content" + } + }, + "SliceProfile": { + "type": "string", + "description": "JSON blob. Containing slice profile parameters. The contents are based on 3GPP TS 23.541 Release 16 contents, and will be in the form of attribute value pairs.", + "example": { + "blob": "content" + } + }, + "RequestInfo2": { + "type": "object", + "required": [ + "transactionId", + "requestId", + "callbackUrl", + "sourceId" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "callbackUrl": { + "type": "string", + "format": "url", + "description": "The end point of a callback service where recommendations are posted.", + "example": "myDomain.com/myCallback" + }, + "callbackHeader": { + "type": "string", + "description": "JSON blob. The header information a client expecting in a async callback.", + "example": { + "blob": "content" + } + }, + "sourceId": { + "type": "string", + "description": "The unique ID of a client making an optimization call.", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "timeout": { + "type": "integer", + "description": "A tolerance window (in second) for expecting solutions.", + "example": 5 + }, + "numSolutions": { + "type": "integer", + "description": "Expected number of solutions.", + "example": 1 + }, + "addtnlArgs": { + "type": "string", + "description": "Any additional parameters that have to be considered during selection", + "example": { + "blob": "content" + } + } + } + }, + "NSTAsynchronousResponse": { + "type": "object", + "required": [ + "transactionId", + "requestId", + "requestStatus", + "solutions" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failed." + }, + "requestStatus": { + "type": "string", + "enum": [ + "completed", + "failed", + "pending" + ], + "description": "The status of a request." + }, + "solutions": { + "type": "array", + "items": { + "$ref": "#/definitions/NSTSolution" + }, + "description": "A list of NST solutions" + } + } + }, + "NSTSolution": { + "type": "object", + "required": [ + "UUID", + "NSTName", + "invariantUUID", + "matchLevel" + ], + "properties": { + "invariantUUID": { + "type": "string", + "format": "uuid", + "description": "Invariant UUID of NST" + }, + "UUID": { + "type": "string", + "format": "UUID of NST" + }, + "NSTName": { + "type": "string", + "description": "NST name" + }, + "matchLevel": { + "type": "string", + "description": "JSON blob. Containing details of match of requirements in slice profile and percentage of fit" + } + } + }, + "NSIAsynchronousResponse": { + "type": "object", + "required": [ + "transactionId", + "requestId", + "requestStatus", + "solutions" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failed." + }, + "requestStatus": { + "type": "string", + "enum": [ + "completed", + "failed", + "pending" + ], + "description": "The status of a request." + }, + "solutions": { + "type": "array", + "items": { + "$ref": "#/definitions/NSISolution" + }, + "description": "A list of NSI solutions" + } + } + }, + "NSISolution": { + "type": "object", + "required": [ + "existingNSI" + ], + "properties": { + "existingNSI": { + "type": "boolean", + "description": "true if NSISolution object has shareNSISolution/false if NSISolution object has newNSISolution" + }, + "sharedNSISolution": { + "$ref": "#/definitions/SharedNSISolution" + }, + "newNSISolution": { + "$ref": "#/definitions/NewNSISolution" + } + } + }, + "SharedNSISolution": { + "type": "object", + "required": [ + "invariantUUID", + "UUID", + "NSIName", + "NSIId", + "matchLevel" + ], + "properties": { + "invariantUUID": { + "type": "string", + "format": "uuid", + "description": "Invariant UUID of NST" + }, + "UUID": { + "type": "string", + "format": "uuid", + "description": "UUID of NST" + }, + "NSIName": { + "type": "string", + "description": "Name of NSI" + }, + "NSIId": { + "type": "string", + "format": "uuid", + "description": "Instance Id of NSI" + }, + "matchLevel": { + "type": "string", + "description": "JSON blob. Containing details of match of requirements in service profile, and recommendation rank" + } + } + }, + "NewNSISolution": { + "type": "object", + "required": [ + "sliceProfiles", + "matchLevel" + ], + "properties": { + "sliceProfiles": { + "type": "array", + "description": "List of slice profiles", + "items": { + "$ref": "#/definitions/SliceProfile" + } + }, + "matchLevel": { + "type": "string", + "description": "JSON blob. Containing details of match of requirements in service profile, and recommendation rank" + } + } + }, + "NSSIAsynchronousResponse": { + "type": "object", + "required": [ + "transactionId", + "requestId", + "requestStatus", + "solutions" + ], + "properties": { + "transactionId": { + "type": "string", + "format": "uuid", + "description": "unique ID to track an ONAP transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "requestId": { + "type": "string", + "format": "uuid", + "description": "A unique ID to track multiple requests associated with a transaction", + "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + "statusMessage": { + "type": "string", + "description": "Reasoning if a requestStatus is failed." + }, + "requestStatus": { + "type": "string", + "enum": [ + "completed", + "failed", + "pending" + ], + "description": "The status of a request." + }, + "solutions": { + "type": "array", + "items": { + "$ref": "#/definitions/SharedNSSISolution" + }, + "description": "A list of NSSI solutions, empty list will be returned if no solution is found" + } + } + }, + "SharedNSSISolution": { + "type": "object", + "required": [ + "invariantUUID", + "UUID", + "NSSIName", + "NSSIId", + "matchLevel" + ], + "properties": { + "invariantUUID": { + "type": "string", + "format": "uuid", + "description": "Invariant UUID of NSST" + }, + "UUID": { + "type": "string", + "format": "uuid", + "description": "UUID of NSST" + }, + "NSSIName": { + "type": "string", + "description": "Name of NSSI" + }, + "NSSIId": { + "type": "string", + "description": "Instance Id of NSSI" + }, + "matchLevel": { + "type": "string", + "description": "JSON blob. Containing details of match of requirements in slice profile and percentage of fit" + } + } + } + }, + "schemes": [ + "https" + ], + "host": "virtserver.swaggerhub.com", + "basePath": "/oof-osdf/v1" +} diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..172975d --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,74 @@ +project = "onap" +release = "master" +version = "master" + +author = "Open Network Automation Platform" +# yamllint disable-line rule:line-length +copyright = "ONAP. Licensed under Creative Commons Attribution 4.0 International License" + +pygments_style = "sphinx" +html_theme = "sphinx_rtd_theme" +html_theme_options = { + "style_nav_header_background": "white", + "sticky_navigation": "False" } +html_logo = "_static/logo_onap_2017.png" +html_favicon = "_static/favicon.ico" +html_static_path = ["_static"] +html_show_sphinx = False + +extensions = [ + 'sphinx.ext.intersphinx', + 'sphinx.ext.graphviz', + 'sphinxcontrib.blockdiag', + 'sphinxcontrib.seqdiag', + 'sphinxcontrib.swaggerdoc', + 'sphinxcontrib.plantuml', + 'sphinxcontrib.redoc' +] + +# +# Map to 'latest' if this file is used in 'latest' (master) 'doc' branch. +# Change to {releasename} after you have created the new 'doc' branch. +# + +branch = 'latest' + +intersphinx_mapping = {} +doc_url = 'https://docs.onap.org/projects' +master_doc = 'index' + +exclude_patterns = ['.tox'] + +spelling_word_list_filename='spelling_wordlist.txt' +spelling_lang = "en_GB" + +# +# Example: +# intersphinx_mapping['onap-aai-aai-common'] = ('{}/onap-aai-aai-common/en/%s'.format(doc_url) % branch, None) +# + +html_last_updated_fmt = '%d-%b-%y %H:%M' + +def setup(app): + app.add_css_file("css/ribbon.css") + +linkcheck_ignore = [ + r'http://localhost:\d+/' +] + +redoc = [ + { + 'name': 'OSDF API', + 'page': 'sections/osdf-api', + 'spec': './api/swagger/oof-osdf-has-api.json', + 'embed': True, + }, + { + 'name': 'OPTENG API', + 'page': 'sections/opteng-api', + 'spec': './api/swagger/oof-optf-opteng-api.json', + 'embed': True, + } + ] + +redoc_uri = 'https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js' diff --git a/docs/index.rst b/docs/index.rst index c3ea9da..9a8302b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,5 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. _master_index: Optimization Framework: Optimization Service Design Framework (OSDF) @@ -17,4 +18,4 @@ Optimization Framework: Optimization Service Design Framework (OSDF) ./sections/administration.rst ./sections/humaninterfaces.rst ./sections/release-notes.rst - + ./sections/upgradestrategy.rst diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt new file mode 100644 index 0000000..38643a1 --- /dev/null +++ b/docs/requirements-docs.txt @@ -0,0 +1,9 @@ +sphinx>=4.2.0 # BSD +sphinx-rtd-theme>=1.0.0 # MIT +sphinxcontrib-blockdiag # BSD +sphinxcontrib-seqdiag # BSD +sphinxcontrib-swaggerdoc +sphinxcontrib-spelling +sphinxcontrib-plantuml +sphinxcontrib-redoc +six diff --git a/docs/sections/architecture.rst b/docs/sections/architecture.rst index 64bc43c..b11eec2 100644 --- a/docs/sections/architecture.rst +++ b/docs/sections/architecture.rst @@ -1,4 +1,5 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. _architecture: Architecture ============================================= diff --git a/docs/sections/offeredapis.rst b/docs/sections/offeredapis.rst index 5151431..c9af97d 100644 --- a/docs/sections/offeredapis.rst +++ b/docs/sections/offeredapis.rst @@ -1,8 +1,9 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. http://creativecommons.org/licenses/by/4.0 +.. _offeredapis: Offered APIs -============================================= +============ This document describes the OSDF HAS (Homing and Allocation Service) API @@ -10,8 +11,20 @@ This document describes the OSDF HAS (Homing and Allocation Service) API To view API documentation in the interactive swagger UI download the following and paste into the swagger tool here: https://editor.swagger.io -:download:`oof-osdf-has-api.json <./swaggerdoc/oof-osdf-has-api.json>` - -.. swaggerv2doc:: ./swaggerdoc/oof-osdf-has-api.json - - +.. csv-table:: + :header: "API name", "Swagger JSON" + :widths: 10,5 + + "OOF OSDF HAS API", ":download:`link <../api/swagger/oof-osdf-has-api.json>`" + "OOF OPTENG API", ":download:`link <../api/swagger/oof-optf-opteng-api.json>`" + +.. OOF OSDF HAS API +.. ................ +.. +.. `OSDF API <osdf-api.html>`_ +.. +.. +.. OOF OPTENG API +.. .............. +.. +.. `OPTENG API <opteng-api.html>`_ diff --git a/docs/sections/release-notes.rst b/docs/sections/release-notes.rst index 22c6b4b..63ab538 100644 --- a/docs/sections/release-notes.rst +++ b/docs/sections/release-notes.rst @@ -1,11 +1,606 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. _release_notes: ============= Release Notes ============= + +.. =========================== +.. * * * KOHN * * * +.. =========================== + +Abstract +======== + +This document provides the release notes for the KOHN release. + +Summary +======= + + +Release Data +============ + + ++--------------------------------------+--------------------------------------+ +| **OOF Project** | | +| | | ++--------------------------------------+--------------------------------------+ +| **Docker images** | optf-osdf 3.0.7 | +| | | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | 11.0.0 kohn | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 10/11/2022 (TBD) | +| | | ++--------------------------------------+--------------------------------------+ + + +New features +------------ + +Bug Fixes +--------- + +- OPTFRA-1080 - Fix timeout issue in OSDF on sending plan request to HAS-API + + +Known Limitations, Issues and Workarounds +========================================= + +System Limitations +------------------ + +Known Vulnerabilities +--------------------- + + +Workarounds +----------- + + +Security Notes +-------------- + +References +========== + +For more information on the ONAP Jakarta release, please see: + +#. `ONAP Home Page`_ +#. `ONAP Documentation`_ +#. `ONAP Release Downloads`_ +#. `ONAP Wiki Page`_ + +.. _`ONAP Home Page`: https://www.onap.org +.. _`ONAP Wiki Page`: https://wiki.onap.org +.. _`ONAP Documentation`: https://docs.onap.org +.. _`ONAP Release Downloads`: https://git.onap.org + +Quick Links: + +- `OOF project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ +- `Passing Badge information for OOF <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ + + +.. =========================== +.. * * * JAKARTA * * * +.. =========================== + +Abstract +======== + +This document provides the release notes for the JAKARTA release. + +Summary +======= + + +Release Data +============ + + ++--------------------------------------+--------------------------------------+ +| **OOF Project** | | +| | | ++--------------------------------------+--------------------------------------+ +| **Docker images** | optf-osdf 3.0.6 | +| | | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | 10.0.0 jakarta | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 02/06/2022 (TBD) | +| | | ++--------------------------------------+--------------------------------------+ + + +New features +------------ + + + +Bug Fixes +--------- + +- OPTFRA-1059 - Update configuration for TN-FH in NxI termination app + + +Known Limitations, Issues and Workarounds +========================================= + +System Limitations +------------------ + +Known Vulnerabilities +--------------------- + + +Workarounds +----------- + + +Security Notes +-------------- + + +References +========== + +For more information on the ONAP Jakarta release, please see: + +#. `ONAP Home Page`_ +#. `ONAP Documentation`_ +#. `ONAP Release Downloads`_ +#. `ONAP Wiki Page`_ + +.. _`ONAP Home Page`: https://www.onap.org +.. _`ONAP Wiki Page`: https://wiki.onap.org +.. _`ONAP Documentation`: https://docs.onap.org +.. _`ONAP Release Downloads`: https://git.onap.org + +Quick Links: + +- `OOF project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ +- `Passing Badge information for OOF <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ + + + +.. =========================== +.. * * * ISTANBUL * * * +.. =========================== + +Abstract +======== + +This document provides the release notes for the Istanbul release. + +Summary +======= + + +Release Data +============ + + ++--------------------------------------+--------------------------------------+ +| **OOF Project** | | +| | | ++--------------------------------------+--------------------------------------+ +| **Docker images** | optf-osdf 3.0.6 | +| | | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | 9.0.0 istanbul | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 28/10/2021 (TBD) | +| | | ++--------------------------------------+--------------------------------------+ + + +New features +------------ + +- Enhancements in NxI termination to support second level NSSIs +- Update NxI termination app to use AAI DSL query +- Add CPS client for PCI app +- OSDF image optimization + +Bug Fixes +--------- + +- OPTFRA-511 Return error when policies are not present +- OPTFRA-943 Fix issues in NSI selection response +- OPTFRA-853 Remove unwanted gplv3 components from docker image + + +Known Limitations, Issues and Workarounds +========================================= + +System Limitations +------------------ + + +Known Vulnerabilities +--------------------- + + +Workarounds +----------- + + +Security Notes +-------------- + + +References +========== + +For more information on the ONAP Honolulu release, please see: + +#. `ONAP Home Page`_ +#. `ONAP Documentation`_ +#. `ONAP Release Downloads`_ +#. `ONAP Wiki Page`_ + + +.. _`ONAP Home Page`: https://www.onap.org +.. _`ONAP Wiki Page`: https://wiki.onap.org +.. _`ONAP Documentation`: https://docs.onap.org +.. _`ONAP Release Downloads`: https://git.onap.org + +Quick Links: + - `OOF project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ + - `Passing Badge information for OOF <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ + + +.. =========================== +.. * * * HONOLULU * * * +.. =========================== + +Abstract +======== + +This document provides the release notes for the Honolulu release. + +Summary +======= + + +Release Data +============ + + ++--------------------------------------+--------------------------------------+ +| **OOF Project** | | +| | | ++--------------------------------------+--------------------------------------+ +| **Docker images** | optf-osdf 3.0.4 | +| | | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | 8.0.0 honolulu | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 04/08/2021 (TBD) | +| | | ++--------------------------------------+--------------------------------------+ + + +New features +------------ + +- Add NST selection feature with AAI & SDC +- Configuration management with consul KV store + +Bug Fixes +--------- + +- OPTFRA-906 Update slice/service profile attributes +- OPTFRA-871 Bug Fix in ML SON - convert cell_id before sending it to Minizinc + + +Known Limitations, Issues and Workarounds +========================================= + +System Limitations +------------------ + + +Known Vulnerabilities +--------------------- + + +Workarounds +----------- + + +Security Notes +-------------- + + +References +========== + +For more information on the ONAP Honolulu release, please see: + +#. `ONAP Home Page`_ +#. `ONAP Documentation`_ +#. `ONAP Release Downloads`_ +#. `ONAP Wiki Page`_ + + +.. _`ONAP Home Page`: https://www.onap.org +.. _`ONAP Wiki Page`: https://wiki.onap.org +.. _`ONAP Documentation`: https://docs.onap.org +.. _`ONAP Release Downloads`: https://git.onap.org + +Quick Links: + - `OOF project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ + - `Passing Badge information for OOF <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ + +.. =========================== +.. * * * GUILIN * * * +.. =========================== + +Abstract +======== + +This document provides the release notes for the Guilin release. + +Summary +======= + + +Release Data +============ + + ++--------------------------------------+--------------------------------------+ +| **OOF Project** | | +| | | ++--------------------------------------+--------------------------------------+ +| **Docker images** | optf-osdf 3.0.2 | +| | | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | 7.0.0 guilin | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 2020-11-19 (TBD) | +| | | ++--------------------------------------+--------------------------------------+ + + +New features +------------ + +- NSI selection enhancements +- Support for NSSI selection +- Support for NSI/NSSI Termination +- Support for Inter domain route optimization +- Support for ML based SON optimization +- Upgrade python version to 3.8 + +Bug Fixes +--------- + +- OPTFRA-839 Remove python 2.7 from OSDF docker image +- OPTFRA-855 Fix slice selection API +- OPTFRA-852 Callback implementation for NST selection + +Known Limitations, Issues and Workarounds +========================================= + +System Limitations +------------------ + + +Known Vulnerabilities +--------------------- + + +Workarounds +----------- + + +Security Notes +-------------- + + +References +========== + +For more information on the ONAP Guilin release, please see: + +#. `ONAP Home Page`_ +#. `ONAP Documentation`_ +#. `ONAP Release Downloads`_ +#. `ONAP Wiki Page`_ + + +.. _`ONAP Home Page`: https://www.onap.org +.. _`ONAP Wiki Page`: https://wiki.onap.org +.. _`ONAP Documentation`: https://docs.onap.org +.. _`ONAP Release Downloads`: https://git.onap.org + +Quick Links: + - `OOF project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ + - `Passing Badge information for OOF <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ + +.. =========================== +.. * * * FRANKFURT * * * +.. =========================== + +Abstract +======== + +This document provides the release notes for the Frankfurt release. + +Summary +======= + + +Release Data +============ + + ++--------------------------------------+--------------------------------------+ +| **OOF Project** | | +| | | ++--------------------------------------+--------------------------------------+ +| **Docker images** | optf-osdf 2.0.4 | +| | | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | 6.0.0 frankfurt | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 2020-05-07 (TBD) | +| | | ++--------------------------------------+--------------------------------------+ + + +New features +------------ + +- Project is restructured into apps and libs. +- A Generic optimizing engine is implemented. +- New optimizer model for route optimization is added. +- Policy apis are migrated from legacy apis to new decision api. +- The Conductor adapter is moved from placement to adapters. +- NST and NSI selection function has been implemented for Network + Slicing use case. +- PCI optimization is enhanced to support fixed cells field. + +Bug Fixes +--------- + +- OPTFRA-482 The field "unique" should be defined in vnf policy. +- OPTFRA-729 OSDF fails to start in the k8s environment. +- OPTFRA-731 OSDF Policy interface is not working +- OPTFRA-754 Return slice profile when service profile indicates the + resource sharing level as not shared +- OPTFRA-755 OOF Policy filtering does not work. + +Known Limitations, Issues and Workarounds +========================================= + +System Limitations +------------------ + + +Known Vulnerabilities +--------------------- + + +Workarounds +----------- + + +Security Notes +-------------- + + +References +========== + +For more information on the ONAP Frankfurt release, please see: + +#. `ONAP Home Page`_ +#. `ONAP Documentation`_ +#. `ONAP Release Downloads`_ +#. `ONAP Wiki Page`_ + + +.. _`ONAP Home Page`: https://www.onap.org +.. _`ONAP Wiki Page`: https://wiki.onap.org +.. _`ONAP Documentation`: https://docs.onap.org +.. _`ONAP Release Downloads`: https://git.onap.org + +Quick Links: + - `OOF project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ + - `Passing Badge information for OOF <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ + + +Version: 5.0.1 +-------------- + +:Release Date: 2019-09-30 (El Alto Release) + +The El Alto release is the fourth release for ONAP Optimization Framework (OOF). + +Artifacts released: + +optf-has:1.3.3 +optf-osdf:1.3.4 +optf-cmso:2.1.1 + +**New Features** + +While no new features were added in the release, the following Stories were delivered as enhancements. + + * [OPTFRA-415] Automation on policy model uploading + * [OPTFRA-427] CMSO - Schedule a workflow in SO and track status to completion + +* Platform Maturity Level 1 + * ~65.1+ unit test coverage + + +**Bug Fixes** + +The El Alto release for OOF fixed the following Bugs. + + * [OPTFRA-579] Json error in homing solution + * [OPTFRA-521] oof-has-api exposes plain text HTTP endpoint using port 30275 + * [OPTFRA-522] oof-osdf exposes plain text HTTP endpoint using port 30248 + * [OPTFRA-577] Need for "ReadWriteMany" access on storage when deploying on Kubernetes? + * [OPTFRA-517] Clean up optf/cmso in integration/csit for Dublin + * [OPTFRA-486] Support "identifiers" field as a list of values + * [OPTFRA-403] OOF CMSO Service kubernetes resources allocation is not done + * [OPTFRA-526] OOF pods not running + * [OPTFRA-409] Template example : purpose to be explained + * [OPTFRA-593] OOF-CSMO healthcheck is failing in Master + + +**Known Issues** + + * [OPTFRA-576] optf-has-master-csit-has is testing Dublin image + * [OPTFRA-596] CMSO - Sonar and CSIT jobs failing + * [OPTFRA-608] Error in Homing with multiple policies + +**Security Notes** + +*Fixed Security Issues* + + * [OJSI-122] In default deployment OPTFRA (oof-osdf) exposes HTTP port 30248 outside of cluster. + * [OPTFRA-521] oof-has-api exposes plain text HTTP endpoint using port 30275 + * [OPTFRA-522] oof-osdf exposes plain text HTTP endpoint using port 30248 + * [OPTFRA-455] CMSO - Mitigate License Threat tomcat-embed-core + +*Known Security Issues* + + * [OPTFRA-481] Fix Vulnerability with spring-data-jpa package + * [OPTFRA-431] Fix Vulnerability with spring-security-web package + +*Known Vulnerabilities in Used Modules* + +**Upgrade Notes** + + +**Deprecation Notes** + + +**Other** + + Version: 4.0.0 -------------- @@ -46,15 +641,17 @@ The Dublin release for OOF delivered the following Epics. *Known Security Issues* + * [`OJSI-122 <https://jira.onap.org/browse/OJSI-122>`_] In default deployment OPTFRA (oof-osdf) exposes HTTP port 30248 outside of cluster. + *Known Vulnerabilities in Used Modules* OPTFRA osdf code has been formally scanned during build time using NexusIQ and no Critical vulnerability was found. -The OPTF open Critical security vulnerabilities and their risk assessment have been documented as part of the `project <https://wiki.onap.org/pages/viewpage.action?pageId=64005463>`_. +The OPTF open Critical security vulnerabilities and their risk assessment have been documented as part of the `project <https://wiki.onap.org/pages/viewpage.action?pageId=64005463>`__. Quick Links: - - `OPTFRA project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ - - `Passing Badge information for OPTFRA <https://bestpractices.coreinfrastructure.org/en/projects/1720>`_ - - `Project Vulnerability Review Table for OPTF <https://wiki.onap.org/pages/viewpage.action?pageId=64005463>`_ + - `OPTFRA project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`__ + - `Passing Badge information for OPTFRA <https://bestpractices.coreinfrastructure.org/en/projects/1720>`__ + - `Project Vulnerability Review Table for OPTF <https://wiki.onap.org/pages/viewpage.action?pageId=64005463>`__ **Upgrade Notes** @@ -142,7 +739,7 @@ The Casablanca release for OOF delivered the following Epics. **Security Notes** OPTFRA osdf code has been formally scanned during build time using NexusIQ and no Critical vulnerability was found. -The OPTF open Critical security vulnerabilities and their risk assessment have been documented as part of the `project <https://wiki.onap.org/pages/viewpage.action?pageId=43385924>`_. +The OPTF open Critical security vulnerabilities and their risk assessment have been documented as part of the `project <https://wiki.onap.org/pages/viewpage.action?pageId=43385924>`__. Quick Links: - `OPTFRA project page <https://wiki.onap.org/display/DW/Optimization+Framework+Project>`_ diff --git a/docs/sections/swaggerdoc/oof-osdf-has-api.json b/docs/sections/swaggerdoc/oof-osdf-has-api.json deleted file mode 100644 index 16e9ab1..0000000 --- a/docs/sections/swaggerdoc/oof-osdf-has-api.json +++ /dev/null @@ -1,902 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "This is the ONAP OOF OSDF (Optimization Service Design Framework) API", - "version": "1.0.0", - "title": "OSDF API", - "contact": { - "email": "frank.sandoval@oamtechnologies.com" - }, - "license": { - "name": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - } - }, - "securityDefinitions": { - "basicAuth": { - "type": "basic", - "description": "HTTP Basic Auth" - } - }, - "security": [ - { - "basicAuth": [] - } - ], - "paths": { - "/v2/placement": { - "post": { - "summary": "create/update a placement", - "operationId": "createPlacement", - "description": "create/update a placement", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "placementRequest", - "description": "placement request", - "schema": { - "$ref": "#/definitions/PlacementRequest" - } - } - ], - "responses": { - "201": { - "description": "An optimization solution is found." - }, - "202": { - "description": "An optimization request is accepted" - }, - "400": { - "description": "bad request" - }, - "401": { - "description": "Request body is not compliant with the API definition" - }, - "404": { - "description": "The server cannot find the requested URI" - }, - "405": { - "description": "The requested method is not supported by a server." - }, - "500": { - "description": "The server encountered an internal server error or timed out" - } - } - } - }, - "/api/oof/placement/v1": { - "$ref": "#/paths/~1v2~1placement" - }, - "/api/oof/v1/pci": { - "post": { - "summary": "Initiate PCI/ANR Optimization", - "operationId": "initiatePCIOptRequest", - "description": "Initiate PCI/ANR Optimization", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "PCIOptimizationRequest", - "description": "PCI request", - "schema": { - "$ref": "#/definitions/PCIOptRequest" - } - } - ], - "responses": { - "201": { - "description": "An optimization solution is found." - }, - "202": { - "description": "An optimization request is accepted" - }, - "400": { - "description": "bad request" - }, - "401": { - "description": "Request body is not compliant with the API definition" - }, - "404": { - "description": "The server cannot find the requested URI" - }, - "405": { - "description": "The requested method is not supported by a server." - }, - "500": { - "description": "The server encountered an internal server error or timed out" - } - } - } - }, - "/api/oof/pci/v1": { - "$ref": "#/paths/~1api~1oof~1v1~1pci" - } - }, - "definitions": { - "PlacementRequest": { - "type": "object", - "required": [ - "requestInfo", - "placementInfo", - "licenseInfo", - "serviceInfo" - ], - "properties": { - "requestInfo": { - "$ref": "#/definitions/RequestInfo" - }, - "placementInfo": { - "$ref": "#/definitions/PlacementInfo" - }, - "licenseInfo": { - "$ref": "#/definitions/LicenseInfo" - }, - "serviceInfo": { - "$ref": "#/definitions/ServiceInfo" - } - } - }, - "RequestInfo": { - "type": "object", - "required": [ - "transactionId", - "requestId", - "callbackUrl", - "sourceId", - "requestType", - "optimizers", - "timeout" - ], - "properties": { - "transactionId": { - "type": "string", - "format": "uuid", - "description": "unique ID to track an ONAP transaction", - "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" - }, - "requestId": { - "type": "string", - "format": "uuid", - "description": "A unique ID to track multiple requests associated with a transaction", - "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" - }, - "callbackUrl": { - "type": "string", - "format": "url", - "description": "The end point of a callback service where recommendations are posted.", - "example": "myDomain.com/myCallback" - }, - "callbackHeader": { - "type": "string", - "description": "JSON blob. The header information a client expecting in a async callback.", - "example": { - "blob": "content" - } - }, - "sourceId": { - "type": "string", - "description": "The unique ID of a client making an optimization call.", - "example": "d290f1ee-6c54-4b01-90e6-d701748f0851" - }, - "requestType": { - "type": "string", - "enum": [ - "create", - "update", - "delete" - ], - "description": "The type of a request", - "example": "create" - }, - "numSolutions": { - "type": "integer", - "description": "Expected number of solutions.", - "example": 1 - }, - "optimizers": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "placement", - "pci", - "pci-anr" - ] - }, - "description": "A list of optimization services.", - "example": [ - "placement" - ] - }, - "timeout": { - "type": "integer", - "description": "A tolerance window (in second) for expecting solutions.", - "example": 5 - } - } - }, - "PlacementInfo": { - "type": "object", - "required": [ - "requestParameters", - "placementDemands" - ], - "properties": { - "requestParameters": { - "type": "string", - "description": "JSON blob. A service ordering information", - "example": { - "blob": "content" - } - }, - "placementDemands": { - "type": "array", - "items": { - "$ref": "#/definitions/PlacementDemand" - }, - "description": "The resource information for a placement service" - }, - "subscriberInfo": { - "type": "object", - "items": { - "$ref": "#/definitions/SubscriberInfo" - }, - "description": "The information of a service subscriber." - } - } - }, - "PlacementDemand": { - "type": "object", - "required": [ - "resourceModuleName", - "serviceResourceId", - "resourceModelInfo" - ], - "properties": { - "resourceModuleName": { - "type": "string", - "description": "A resource name as defined in a service mode", - "example": "myResourceName" - }, - "serviceResourceId": { - "type": "string", - "description": "A unique resource Id with a local scope between client and OOF.", - "example": "myResourceId" - }, - "givenPlacement": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "description": "placement parameters defined in the ordering system, keyname include tenantId", - "example": { - "tenantId": "1" - } - }, - "resourceModelInfo": { - "$ref": "#/definitions/ModelMetaData" - }, - "existingCandidates": { - "$ref": "#/definitions/Candidates" - }, - "excludedCandidates": { - "$ref": "#/definitions/Candidates" - }, - "requiredCandidates": { - "$ref": "#/definitions/Candidates" - } - } - }, - "ModelMetaData": { - "type": "object", - "required": [ - "modelInvariantId", - "modelVersionId" - ], - "properties": { - "modelInvariantId": { - "type": "string", - "description": "A model invariant Id as defined in a service model.", - "example": "my model invariant Id" - }, - "modelVersionId": { - "type": "string", - "description": "A unique model Id as defined in a service model.", - "example": "my unique model Id" - }, - "modelName": { - "type": "string", - "description": "A model name as defined in a service model", - "example": "my model name" - }, - "modelType": { - "type": "string", - "description": "A model type as defined in a service model.", - "example": "my model type" - }, - "modelVersion": { - "type": "string", - "description": "A model version as defined in a service model.", - "example": "my model version" - }, - "modelCustomizationName": { - "type": "string", - "description": "A model customization name as defined in a service model.", - "example": "my model customization" - } - } - }, - "Candidates": { - "type": "object", - "required": [ - "identifierType", - "identifiers" - ], - "properties": { - "identifierType": { - "type": "string", - "enum": [ - "service_instance_id", - "vnf_name", - "cloud_region_id" - ], - "description": "The type of a candidate.", - "example": "service_instance_id" - }, - "identifiers": { - "type": "array", - "items": { - "type": "string" - }, - "description": "A list of identifiers.", - "example": "candidateId" - }, - "cloudOwner": { - "type": "string", - "description": "The name of a cloud owner. Only required if identifierType is cloud_region_id", - "example": "cloud_owner" - } - } - }, - "SubscriberInfo": { - "type": "object", - "required": [ - "globalSubscriberId", - "subscriberName", - "subscriberCommonSiteId" - ], - "properties": { - "globalSubscriberId": { - "type": "string", - "description": "An ID of a subscriber.", - "example": "subscriber_id" - }, - "subscriberName": { - "type": "string", - "description": "The name of a subscriber. If the name is not known, the value must be 'unknown'", - "example": "subscriber_name" - }, - "subscriberCommonSiteId": { - "type": "string", - "description": "Id representing a subscriber location", - "example": "subscriber_location_id" - } - } - }, - "LicenseInfo": { - "type": "object", - "required": [ - "licenseDemands" - ], - "properties": { - "licenseDemands": { - "type": "array", - "items": { - "$ref": "#/definitions/LicenseDemands" - }, - "description": "A list of resources for license selection" - } - } - }, - "LicenseDemands": { - "type": "object", - "required": [ - "resourceModuleName", - "serviceResourceId", - "resourceModelInfo" - ], - "properties": { - "resourceModuleName": { - "type": "string", - "description": "A resource name as defined in a service model.", - "example": "service_instance_id" - }, - "serviceResourceId": { - "type": "string", - "description": "A unique resource Id with a local scope between client and OOF.", - "example": "service_instance_id" - }, - "resourceModelInfo": { - "$ref": "#/definitions/ModelMetaData" - }, - "existingLicenses": { - "$ref": "#/definitions/LicenseModel" - } - } - }, - "LicenseModel": { - "type": "object", - "required": [ - "entitlementPoolUUID", - "licenseKeyGroupUUID" - ], - "properties": { - "entitlementPoolUUID": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Entitlement pool UUIDs associated with a resource.", - "example": "candidateId" - }, - "licenseKeyGroupUUID": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "License key groups associated with a resource", - "example": "candidateId" - } - } - }, - "SynchronousResponse": { - "type": "object", - "required": [ - "requestId", - "transactionId", - "requestStatus" - ], - "properties": { - "requestId": { - "type": "string", - "format": "uuid", - "description": "A unique Id for an ONAP transaction", - "example": "ONAP transaction id" - }, - "transactionId": { - "type": "string", - "format": "uuid", - "description": "A unique ID to track multiple requests associated with a transaction.", - "example": "requests id" - }, - "statusMessage": { - "type": "string", - "description": "Reasoning if a requestStatus is failure.", - "example": "requestStatus" - }, - "requestStatus": { - "type": "string", - "enum": [ - "success", - "failure" - ], - "description": "The status of a request.", - "example": "success" - } - } - }, - "PlacementAsynchronousResponse": { - "type": "object", - "required": [ - "requestId", - "transactionId", - "requestStatus", - "solutions" - ], - "properties": { - "requestId": { - "type": "string", - "format": "uuid", - "description": "A unique Id for an ONAP transaction", - "example": "ONAP transaction id" - }, - "transactionId": { - "type": "string", - "format": "uuid", - "description": "A unique ID to track multiple requests associated with a transaction.", - "example": "requests id" - }, - "statusMessage": { - "type": "string", - "description": "Reasoning if a requestStatus is failure.", - "example": "requestStatus" - }, - "requestStatus": { - "type": "string", - "enum": [ - "success", - "failure" - ], - "description": "The status of a request.", - "example": "success" - }, - "solutions": { - "$ref": "#/definitions/Solutions" - } - } - }, - "Solutions": { - "type": "object", - "required": [ - "placementSolutions", - "licenseSolutions" - ], - "properties": { - "placementSolutions": { - "type": "array", - "items": { - "$ref": "#/definitions/ComprehensiveSolution" - }, - "description": "A list of placement solutions." - }, - "licenseSolutions": { - "type": "array", - "items": { - "$ref": "#/definitions/LicenseSolution" - }, - "description": "A list of license solutions." - } - } - }, - "ComprehensiveSolution": { - "type": "object", - "required": [ - "placementSolutions" - ], - "properties": { - "placementSolutions": { - "type": "array", - "items": { - "$ref": "#/definitions/PlacementSolution" - }, - "description": "A list of placement solutions." - } - } - }, - "PlacementSolution": { - "type": "object", - "required": [ - "resourceModuleName", - "serviceResourceId", - "identifierType", - "identifier" - ], - "properties": { - "resourceModuleName": { - "type": "string", - "description": "The name of a resource as defined in the service model", - "example": "resource name" - }, - "serviceResourceId": { - "type": "string", - "description": "A resource Id as defined in a service model.", - "example": "resource id" - }, - "identifierType": { - "type": "string", - "enum": [ - "service_instance_id" - ], - "description": "The type of a candidate.", - "example": "candidate type" - }, - "identifier": { - "type": "string", - "description": "The id of a candidate.", - "example": "candidate id" - }, - "assignmentInfo": { - "type": "array", - "items": { - "$ref": "#/definitions/AssignmentInfo" - }, - "description": "Additional information related to a candidate." - } - } - }, - "AssignmentInfo": { - "type": "object", - "required": [ - "key", - "value" - ], - "properties": { - "key": { - "type": "string", - "description": "An attribute name", - "example": "attribute name" - }, - "value": { - "type": "string", - "description": "An attribute value.", - "example": "attribute value" - } - } - }, - "LicenseSolution": { - "type": "object", - "required": [ - "resourceModuleName", - "serviceResourceId", - "entitlementPoolUUID", - "licenseKeyGroupUUID", - "entitlementPoolInvariantUUID", - "licenseKeyGroupInvariantUUID" - ], - "properties": { - "resourceModuleName": { - "type": "string", - "description": "A resource name as defined in a service", - "example": "resource name" - }, - "serviceResourceId": { - "type": "string", - "description": "A resource Id as defined in a service.", - "example": "resource Id" - }, - "entitlementPoolUUID": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "A list of entitlementPoolUUIDs", - "example": "entitlementPoolUUID" - }, - "licenseKeyGroupUUID": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "A list of licenseKeyGroupUUID.", - "example": "licenseKeyGroupUUID" - }, - "entitlementPoolInvariantUUID": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "A list of entitlementPoolInvariantUUID", - "example": "entitlementPoolInvariantUUID" - }, - "licenseKeyGroupInvariantUUID": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "A list of licenseKeyGroupInvariantUUID", - "example": "licenseKeyGroupInvariantUUID" - } - } - }, - "ServiceInfo": { - "type": "object", - "required": [ - "serviceInstanceId", - "modelInfo", - "serviceName" - ], - "properties": { - "serviceInstanceId": { - "type": "string", - "description": "A service instance id associated with a request.", - "example": "service_instance_id" - }, - "modelInfo": { - "$ref": "#/definitions/ModelMetaData" - }, - "serviceName": { - "type": "string", - "description": "The name of a service", - "example": "service_name" - } - } - }, - "PCIOptRequest": { - "type": "object", - "required": [ - "requestInfo", - "cellInfo" - ], - "properties": { - "requestInfo": { - "$ref": "#/definitions/RequestInfo" - }, - "cellInfo": { - "$ref": "#/definitions/CellInfo" - } - } - }, - "CellInfo": { - "type": "object", - "required": [ - "networkId", - "cellIdList", - "anrInputList", - "trigger" - ], - "properties": { - "networkId": { - "type": "string", - "description": "Id of network requiring PCI optimization", - "example": 100 - }, - "cellIdList": { - "type": "array", - "items": { - "type": "string" - }, - "description": "List of cellIds triggering need for PCI optimization (eg.potential confusion)", - "example": [ - "cell0001", - "cell0002" - ] - }, - "anrInputList": { - "type": "array", - "items": { - "$ref": "#/definitions/ANRInfo" - }, - "description": "A list of ANR Input." - }, - "trigger": { - "type": "string", - "description": "Type of trigger causing need for PCI optimization", - "example": "NbrListChange" - } - } - }, - "PCIAsynchronousResponse": { - "type": "object", - "required": [ - "requestId", - "transactionId", - "requestStatus", - "solutions" - ], - "properties": { - "requestId": { - "type": "string", - "format": "uuid", - "description": "A unique Id for an ONAP transaction", - "example": "ONAP transaction id" - }, - "transactionId": { - "type": "string", - "format": "uuid", - "description": "A unique ID to track multiple requests associated with a transaction.", - "example": "requests id" - }, - "statusMessage": { - "type": "string", - "description": "Reasoning if a requestStatus is failure.", - "example": "requestStatus" - }, - "requestStatus": { - "type": "string", - "enum": [ - "success", - "failure" - ], - "description": "The status of a request.", - "example": "success" - }, - "solutions": { - "$ref": "#/definitions/PCIANRSolutions" - } - } - }, - "PCIANRSolutions": { - "type": "object", - "required": [ - "networkId", - "pciSolutions", - "anrSolutions" - ], - "properties": { - "networkId": { - "type": "string", - "description": "Id of network requiring PCI optimization", - "example": 100 - }, - "pciSolutions": { - "type": "array", - "items": { - "$ref": "#/definitions/PCISolution" - }, - "description": "A list of PCI solutions." - }, - "anrSolutions": { - "type": "array", - "items": { - "$ref": "#/definitions/ANRInfo" - }, - "description": "A list of ANR solutions." - } - } - }, - "PCISolution": { - "type": "object", - "required": [ - "cellId", - "pci" - ], - "properties": { - "cellId": { - "type": "string", - "description": "cellId with modified PCI value", - "example": "cell0001" - }, - "pci": { - "type": "integer", - "description": "New PCI value for cellId", - "example": 1 - } - } - }, - "ANRInfo": { - "type": "object", - "required": [ - "cellId", - "removeableNeighbors" - ], - "properties": { - "cellId": { - "type": "string", - "description": "cellId with modified PCI value", - "example": "cell0001" - }, - "removeableNeighbors": { - "type": "array", - "items": { - "type": "string" - }, - "description": "List of neighbors to be removed", - "example": [ - "cell0002", - "cell0003" - ] - } - } - } - }, - "schemes": [ - "https" - ], - "host": "virtserver.swaggerhub.com", - "basePath": "/oof-osdf/v1" -}
\ No newline at end of file diff --git a/docs/sections/upgradestrategy.rst b/docs/sections/upgradestrategy.rst new file mode 100644 index 0000000..6357fac --- /dev/null +++ b/docs/sections/upgradestrategy.rst @@ -0,0 +1,22 @@ +.. + This work is licensed under a Creative Commons Attribution 4.0 + International License. + +================ +Upgrade Strategy +================ + +OSDF can be upgraded in place(remove and replace) or in a blue-green +strategy. + +There is no need for database migration. Since, there is no database +being used by OSDF. + +Supporting Facts +================ + +OSDF is a stateless component. It doesn't store any information in the +database. It holds on to the optimization request in memory only until +the optimization process is complete. The optimization is done either by +OSDF itself or other external components(such as HAS) are leveraged for +optimization. diff --git a/docs/tox.ini b/docs/tox.ini new file mode 100644 index 0000000..9d0f3ee --- /dev/null +++ b/docs/tox.ini @@ -0,0 +1,31 @@ +[tox] +minversion = 1.6 +envlist = docs,docs-linkcheck,docs-spellcheck +skipsdist = true + +[testenv:docs] +basepython = python3.8 +deps = + -r{toxinidir}/requirements-docs.txt + -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt + -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master +commands = + sphinx-build -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html + +[testenv:docs-linkcheck] +basepython = python3.8 +deps = + -r{toxinidir}/requirements-docs.txt + -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt + -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master +commands = + sphinx-build -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck + +[testenv:docs-spellcheck] +basepython = python3.8 +deps = + -r{toxinidir}/requirements-docs.txt + -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt + -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master +commands = + sphinx-build -q -b spelling -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/spellcheck diff --git a/examples/policies/nsi_policies/optimization_nsi_create_new.json b/examples/policies/nsi_policies/optimization_nsi_create_new.json new file mode 100644 index 0000000..af79be2 --- /dev/null +++ b/examples/policies/nsi_policies/optimization_nsi_create_new.json @@ -0,0 +1,37 @@ +{ + "maximizeCost_{{ service_name }}": { + "metadata": { + "policy-id": "maximizeCost_{{ service_name }}", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "maximize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "creation_cost", + "demand": "{{ service_name }}" + } + } + ] + }, + "resources": [ + "{{ service_name }}" + ], + "scope": [ + "shared,create_new" + ], + "services": [ + "{{ service_name }}" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} diff --git a/examples/policies/nsi_policies/optimization_nsi_non_shared.json b/examples/policies/nsi_policies/optimization_nsi_non_shared.json new file mode 100644 index 0000000..12d7d24 --- /dev/null +++ b/examples/policies/nsi_policies/optimization_nsi_non_shared.json @@ -0,0 +1,37 @@ +{ + "minimizeLatency_{{ service_name }}": { + "metadata": { + "policy-id": "minimizeLatency_{{ service_name }}", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "latency", + "demand": "{{ service_name }}" + } + } + ] + }, + "resources": [ + "{{ service_name }}" + ], + "scope": [ + "non-shared" + ], + "services": [ + "{{ service_name }}" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} diff --git a/examples/policies/nsi_policies/optimization_nsi_reuse.json b/examples/policies/nsi_policies/optimization_nsi_reuse.json new file mode 100644 index 0000000..459cf33 --- /dev/null +++ b/examples/policies/nsi_policies/optimization_nsi_reuse.json @@ -0,0 +1,37 @@ +{ + "minimizeCost_{{ service_name }}": { + "metadata": { + "policy-id": "minimizeCost_{{ service_name }}", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "creation_cost", + "demand": "{{ service_name }}" + } + } + ] + }, + "resources": [ + "{{ service_name }}" + ], + "scope": [ + "shared,reuse" + ], + "services": [ + "{{ service_name }}" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} diff --git a/examples/policies/nsi_policies/query_nsi.json b/examples/policies/nsi_policies/query_nsi.json new file mode 100644 index 0000000..e888a99 --- /dev/null +++ b/examples/policies/nsi_policies/query_nsi.json @@ -0,0 +1,69 @@ +{ + "queryPolicy_{{ service_name }}":{ + "type":"onap.policies.optimization.service.QueryPolicy", + "version":"1.0.0", + "type_version":"1.0.0", + "metadata":{ + "policy-id":"queryPolicy_{{ service_name }}", + "policy-version":1 + }, + "properties":{ + "scope":[ + "shared,reuse", + "shared,create_new", + "non-shared" + ], + "services":[ + "{{ service_name }}" + ], + "geography":[], + "identity":"queryPolicy_{{ service_name }}", + "queryProperties":[ + { + "attribute":"latency", + "attribute_location":"latency" + }, + { + "attribute":"max_number_of_ues", + "attribute_location":"max_number_of_ues" + }, + { + "attribute":"s_nssai", + "attribute_location":"s_nssai" + }, + { + "attribute":"resource_sharing_level", + "attribute_location":"resource_sharing_level" + }, + { + "attribute":"ue_mobility_level", + "attribute_location":"ue_mobility_level" + }, + { + "attribute":"sst", + "attribute_location":"sst" + }, + { + "attribute":"AN_latency", + "attribute_location":"AN_latency" + }, + { + "attribute":"CN_latency", + "attribute_location":"CN_latency" + }, + { + "attribute":"TN_BH_latency", + "attribute_location":"TN_BH_latency" + }, + { + "attribute":"model_invariant_id", + "attribute_location":"invariantUUID" + }, + { + "attribute":"model_version_id", + "attribute_location":"UUID" + } + ] + } + } +} diff --git a/examples/policies/nsi_policies/threshold_nsi.json b/examples/policies/nsi_policies/threshold_nsi.json new file mode 100644 index 0000000..ef4c362 --- /dev/null +++ b/examples/policies/nsi_policies/threshold_nsi.json @@ -0,0 +1,38 @@ +{ + "Threshold_{{ service_name }}":{ + "metadata":{ + "policy-id":"Threshold_{{ service_name }}", + "policy-version":1 + }, + "properties":{ + "geography":[ + + ], + "identity":"Threshold_{{ service_name }}", + "resources":[ + "{{ service_name }}" + ], + "scope":[ + "shared,reuse", + "shared,create_new", + "non-shared" + ], + "services":[ + "{{ service_name }}" + ], + "thresholdProperties":[ + { + "attribute":"latency", + "operator":"lte", + "threshold":{ + "get_param":"latency" + }, + "unit":"ms" + } + ] + }, + "type":"onap.policies.optimization.resource.ThresholdPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } +} diff --git a/examples/policies/nsi_policies/vnf_nsi_nonshared.json b/examples/policies/nsi_policies/vnf_nsi_nonshared.json new file mode 100644 index 0000000..49b1c95 --- /dev/null +++ b/examples/policies/nsi_policies/vnf_nsi_nonshared.json @@ -0,0 +1,92 @@ +{ + "vnfPolicy_{{ service_name }}_non_shared":{ + "metadata":{ + "policy-id":"vnfPolicy_{{ service_name }}_non_shared", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_{{ service_name }}", + "resources":["{{ service_name }}"], + "scope":[ + "non-shared" + ], + "services":[ + "{{ service_name }}" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "service_profile": { + "latency": {"value": {"get_param": "latency"}, "operator": "lte"}, + "s_nssai": {"value": {"get_param": "s_nssai"}, "operator": "eq"}, + "resource_sharing_level": {"value": {"get_param": "resource_sharing_level"}, "operator": "eq"}, + "ue_mobility_level": {"value": {"get_param": "ue_mobility_level"}, "operator": "eq"}, + "sst": {"value": {"get_param": "sst"}, "operator": "eq"}, + "max_number_of_ues": {"value": {"get_param": "max_number_of_ues"}, "operator": "eq"} + }, + "subnets": { + "CN":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"CN_latency"}, + "steps":5 + }, + "s_nssai":{ + "values":[ + {"get_param": "s_nssai"} + ] + }, + "max_number_of_ues": {"values":[{"get_param": "max_number_of_ues"}]}, + "sst": {"values":[{"get_param": "sst"}]}, + "resource_sharing_level": {"values":[{"get_param": "resource_sharing_level"}]}, + "plmn_id_list": {"values":["39-00"]}, + "survival_time": {"values":["10"]}, + "ue_mobility_level": {"values":[{"get_param": "ue_mobility_level"}]} + }, + "AN":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"AN_latency"}, + "steps":5 + }, + "s_nssai":{ + "values":[ + {"get_param": "s_nssai"} + ] + }, + "max_number_of_ues": {"values":[{"get_param": "max_number_of_ues"}]}, + "sst": {"values":[{"get_param": "sst"}]}, + "plmn_id_list": {"values":["39-00"]}, + "survival_time": {"values":["10"]}, + "resource_sharing_level": {"values":[{"get_param": "resource_sharing_level"}]}, + "ue_mobility_level": {"values":[{"get_param": "ue_mobility_level"}]} + }, + "TN_BH":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"TN_BH_latency"}, + "steps":5 + }, + "s_nssai": {"values": [{"get_param": "s_nssai"}]}, + "plmn_id_list": {"values":["39-00"]}, + "max_bandwidth": {"values":[1000]}, + "sst": {"values":[{"get_param": "sst"}]} + } + } + }, + "inventoryProvider":"generator", + "inventoryType":"slice_profiles", + "unique":"true", + "defaultAttributes":{ + "creation_cost" : 0.9 + } + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } +} + diff --git a/examples/policies/nsi_policies/vnf_nsi_notshared.json b/examples/policies/nsi_policies/vnf_nsi_notshared.json new file mode 100644 index 0000000..afdbd50 --- /dev/null +++ b/examples/policies/nsi_policies/vnf_nsi_notshared.json @@ -0,0 +1,121 @@ +{ + "vnfPolicy_{{ service_name }}_not_shared":{ + "metadata":{ + "policy-id":"vnfPolicy_{{ service_name }}_not_shared", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_{{ service_name }}", + "resources":["{{ service_name }}"], + "scope":[ + "non-shared" + ], + "services":[ + "{{ service_name }}" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "service_profile": { + "latency": {"value": {"get_param": "latency"}, "operator": "lte"}, + "max_throughput": {"value": {"get_param": "max_throughput"}, "operator": "gte"}, + "max_number_of_ues": {"value": {"get_param": "max_number_of_ues"}, "operator": "gte"}, + "terminal_density": {"value": {"get_param": "terminal_density"}, "operator": "gte"}, + "s_nssai_list": {"value": {"get_param": "s_nssai_list"}, "operator": "eq"}, + "plmn_id_list": {"value": {"get_param": "plmn_id_list"}, "operator": "eq"} + }, + "subnets": { + "AN_NF":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"AN_NF_latency"}, + "steps":1 + }, + "max_throughput":{ + "values":[ + {"get_param": "AN_NF_max_throughput"} + ] + }, + "max_number_of_ues":{ + "values":[ + {"get_param": "AN_NF_max_number_of_ues"} + ] + }, + "terminal_density":{ + "values":[ + {"get_param": "AN_NF_terminal_density"} + ] + }, + "s_nssai_list":{ + "values":[ + {"get_param": "s_nssai_list"} + ] + }, + "plmn_id_list":{ + "values":[ + {"get_param": "plmn_id_list"} + ] + } + }, + "TN_FH":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"TN_FH_latency"}, + "steps":1 + }, + "max_throughput":{ + "values":[ + {"get_param": "TN_FH_max_throughput"} + ] + }, + "s_nssai_list":{ + "values":[ + {"get_param": "s_nssai_list"} + ] + }, + "plmn_id_list":{ + "values":[ + {"get_param": "plmn_id_list"} + ] + } + }, + "TN_MH":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"TN_MH_latency"}, + "steps":1 + }, + "max_throughput":{ + "values":[ + {"get_param": "TN_MH_max_throughput"} + ] + }, + "s_nssai_list":{ + "values":[ + {"get_param": "s_nssai_list"} + ] + }, + "plmn_id_list":{ + "values":[ + {"get_param": "plmn_id_list"} + ] + } + } + } + }, + "inventoryProvider":"generator", + "inventoryType":"slice_profiles", + "unique":"true", + "defaultAttributes":{ + "creation_cost" : 0.9 + } + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } +} + diff --git a/examples/policies/nsi_policies/vnf_nsi_shared.json b/examples/policies/nsi_policies/vnf_nsi_shared.json new file mode 100644 index 0000000..cce2f73 --- /dev/null +++ b/examples/policies/nsi_policies/vnf_nsi_shared.json @@ -0,0 +1,105 @@ +{ + "vnfPolicy_{{ service_name }}":{ + "metadata":{ + "policy-id":"vnfPolicy_{{ service_name }}", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_{{ service_name }}", + "resources":["{{ service_name }}"], + "scope":[ + "shared,reuse", + "shared,create_new" + ], + "services":[ + "{{ service_name }}" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "modelInvariantId":{"get_param": "model_invariant_id"}, + "modelVersionId":{"get_param": "model_version_id"}, + "service-role":"nsi" + }, + "inventoryProvider":"aai", + "inventoryType":"nsi", + "unique":"true", + "defaultAttributes":{ + "creation_cost" : 0.1 + } + }, + { + "attributes":{ + "service_profile": { + "latency": {"value": {"get_param": "latency"}, "operator": "lte"}, + "s_nssai": {"value": {"get_param": "s_nssai"}, "operator": "eq"}, + "resource_sharing_level": {"value": {"get_param": "resource_sharing_level"}, "operator": "eq"}, + "ue_mobility_level": {"value": {"get_param": "ue_mobility_level"}, "operator": "eq"}, + "sst": {"value": {"get_param": "sst"}, "operator": "eq"}, + "max_number_of_ues": {"value": {"get_param": "max_number_of_ues"}, "operator": "eq"} + }, + "subnets": { + "CN":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"CN_latency"}, + "steps":5 + }, + "s_nssai":{ + "values":[ + {"get_param": "s_nssai"} + ] + }, + "sst": {"values":[{"get_param": "sst"}]}, + "resource_sharing_level": {"values":[{"get_param": "resource_sharing_level"}]}, + "plmn_id_list": {"values":["39-00"]}, + "survival_time": {"values":["10"]}, + "max_number_of_ues": {"values":[{"get_param": "max_number_of_ues"}]}, + "ue_mobility_level": {"values":[{"get_param": "ue_mobility_level"}]} + }, + "AN":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"AN_latency"}, + "steps":5 + }, + "s_nssai":{ + "values":[ + {"get_param": "s_nssai"} + ] + }, + "sst": {"values":[{"get_param": "sst"}]}, + "plmn_id_list": {"values":["39-00"]}, + "survival_time": {"values":["10"]}, + "max_number_of_ues": {"values":[{"get_param": "max_number_of_ues"}]}, + "resource_sharing_level": {"values":[{"get_param": "resource_sharing_level"}]}, + "ue_mobility_level": {"values":[{"get_param": "ue_mobility_level"}]} + }, + "TN_BH":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"TN_BH_latency"}, + "steps":5 + }, + "s_nssai": {"values":[{"get_param": "s_nssai"}]}, + "max_bandwidth": {"values":[1000]}, + "plmn_id_list": {"values":["39-00"]}, + "sst": {"values":[{"get_param": "sst"}]} + } + } + }, + "inventoryProvider":"generator", + "inventoryType":"slice_profiles", + "unique":"true", + "defaultAttributes":{ + "creation_cost" : 0.9 + } + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } +} diff --git a/examples/policies/nssi_policies/optimization_nssi.json b/examples/policies/nssi_policies/optimization_nssi.json new file mode 100644 index 0000000..9d744e9 --- /dev/null +++ b/examples/policies/nssi_policies/optimization_nssi.json @@ -0,0 +1,37 @@ +{ + "{{ goal }}_{{ attribute }}_{{ service_name }}": { + "metadata": { + "policy-id": "{{ goal }}_{{ attribute }}_{{ service_name }}", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "{{ goal }}", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "{{ attribute }}", + "demand": "{{ service_name }}" + } + } + ] + }, + "resources": [ + "{{ service_name }}" + ], + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "{{ service_name }}" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} diff --git a/examples/policies/nssi_policies/query_nssi.json b/examples/policies/nssi_policies/query_nssi.json new file mode 100644 index 0000000..c5eea26 --- /dev/null +++ b/examples/policies/nssi_policies/query_nssi.json @@ -0,0 +1,39 @@ +{ + "queryPolicy_{{ service_name }}":{ + "type":"onap.policies.optimization.service.QueryPolicy", + "version":"1.0.0", + "type_version":"1.0.0", + "metadata":{ + "policy-id":"queryPolicy_{{ service_name }}", + "policy-version":1 + }, + "properties":{ + "scope":[ + "OSDF_GUILIN" + ], + "services":[ + "{{ service_name }}" + ], + "geography":[], + "identity":"queryPolicy_{{ service_name }}", + "queryProperties":[ + { + "attribute":"latency", + "attribute_location":"latency" + }, + { + "attribute":"reliability", + "attribute_location":"reliability" + }, + { + "attribute":"model_invariant_id", + "attribute_location":"invariantUUID" + }, + { + "attribute":"model_version_id", + "attribute_location":"UUID" + } + ] + } + } +}
\ No newline at end of file diff --git a/examples/policies/nssi_policies/threshold_nssi.json b/examples/policies/nssi_policies/threshold_nssi.json new file mode 100644 index 0000000..f7b7ff6 --- /dev/null +++ b/examples/policies/nssi_policies/threshold_nssi.json @@ -0,0 +1,44 @@ +{ + "Threshold_{{ service_name }}":{ + "metadata":{ + "policy-id":"Threshold_{{ service_name }}", + "policy-version":1 + }, + "properties":{ + "geography":[ + + ], + "identity":"Threshold_{{ service_name }}", + "resources":[ + "{{ service_name }}" + ], + "scope":[ + "OSDF_GUILIN" + ], + "services":[ + "{{ service_name }}" + ], + "thresholdProperties":[ + { + "attribute":"latency", + "operator":"lte", + "threshold":{ + "get_param":"latency" + }, + "unit":"ms" + }, + { + "attribute":"reliability", + "operator":"gte", + "threshold":{ + "get_param":"reliability" + }, + "unit":"" + } + ] + }, + "type":"onap.policies.optimization.resource.ThresholdPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } +}
\ No newline at end of file diff --git a/examples/policies/nssi_policies/vnf_nssi.json b/examples/policies/nssi_policies/vnf_nssi.json new file mode 100644 index 0000000..fd54b7b --- /dev/null +++ b/examples/policies/nssi_policies/vnf_nssi.json @@ -0,0 +1,37 @@ +{ + "vnfPolicy_{{ service_name }}":{ + "metadata":{ + "policy-id":"vnfPolicy_{{ service_name }}", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_{{ service_name }}", + "resources":[ + "{{ service_name }}" + ], + "scope":[ + "OSDF_GUILIN" + ], + "services":[ + "{{ service_name }}" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "modelInvariantId":{"get_param": "model_invariant_id"}, + "modelVersionId":{"get_param": "model_version_id"}, + "service-function":"shared", + "service-role":"nssi" + }, + "inventoryProvider":"aai", + "inventoryType":"nssi", + "unique":"true" + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } +}
\ No newline at end of file diff --git a/examples/policies/nst_policies/attribute_policy_nst.json b/examples/policies/nst_policies/attribute_policy_nst.json new file mode 100644 index 0000000..da2e5fe --- /dev/null +++ b/examples/policies/nst_policies/attribute_policy_nst.json @@ -0,0 +1,41 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_1_0", + "topology_template": { + "policies": [ +{ + "Threshold_nst": { + "metadata": { + "policy-id": "Threshold_nst", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "nst_Threshold", + "resources": [ + "nst" + ], + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "nst" + ], + "thresholdProperties": [ + { + "attribute": "latency", + "operator": "lte", + "threshold": { + "get_param": "latency" + }, + "unit": "ms" + } + ] + }, + "type": "onap.policies.optimization.resource.ThresholdPolicy", + "type_version": "1.0.0", + "version": "1.0.0" + } +} + ] + } +} diff --git a/examples/policies/nst_policies/optimization_policy_nst.json b/examples/policies/nst_policies/optimization_policy_nst.json new file mode 100644 index 0000000..09737ad --- /dev/null +++ b/examples/policies/nst_policies/optimization_policy_nst.json @@ -0,0 +1,44 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_1_0", + "topology_template": { + "policies": [ +{ + "nst_minimize_latency": { + "metadata": { + "policy-id": "nst_minimize_latency", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "latency", + "demand": "nst" + } + } + ] + }, + "resources": [ + "nst" + ], + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "nst" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} + ] + } +} diff --git a/examples/policies/nst_policies/query_policy_nst.json b/examples/policies/nst_policies/query_policy_nst.json new file mode 100644 index 0000000..2ce441b --- /dev/null +++ b/examples/policies/nst_policies/query_policy_nst.json @@ -0,0 +1,38 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_1_0", + "topology_template": { + "policies": [ +{ + "queryPolicy_nst": { + "type": "onap.policies.optimization.service.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "queryPolicy_nst", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "nst" + ], + "geography": [], + "identity": "queryPolicy_nst", + "queryProperties": [ + { + "attribute": "latency", + "attribute_location": "latency" + }, + { + "attribute": "areaTrafficCapDL", + "attribute_location": "areaTrafficCapDL" + } + ] + } + } +} + ] + } +} diff --git a/examples/policies/nst_policies/vnf_policy_nst.json b/examples/policies/nst_policies/vnf_policy_nst.json new file mode 100644 index 0000000..422030c --- /dev/null +++ b/examples/policies/nst_policies/vnf_policy_nst.json @@ -0,0 +1,41 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_1_0", + "topology_template": { + "policies": [ +{ + "vnfPolicy_nst": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "vnfPolicy_nst", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_GUILIN" + ], + "resources": [ + "nst" + ], + "services": [ + "nst" + ], + "identity": "vnf_nst", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "inventoryType": "nst", + "unique": "true", + "attributes": { + "model-role": "NST" + } + } + ] + } + } +} + ] + } +} diff --git a/examples/policies/policy_types/optimization_v2.json b/examples/policies/policy_types/optimization_v2.json new file mode 100644 index 0000000..ea5ca6f --- /dev/null +++ b/examples/policies/policy_types/optimization_v2.json @@ -0,0 +1,61 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_1_0", + "policy_types": { + "onap.policies.optimization.resource.OptimizationPolicy": { + "derived_from": "onap.policies.optimization.Resource", + "version": "2.0.0", + "name": "onap.policies.optimization.resource.OptimizationPolicy", + "properties": { + "goal": { + "type": "list", + "required": true, + "entry_schema": { + "type": "string", + "constraints": [ + { + "valid_values": [ + "minimize", + "maximize" + ] + } + ] + } + }, + "operation_function": { + "type": "policy.data.operation_function_properties", + "required": true + } + } + } + }, + "data_types": { + "policy.data.operation_function_properties": { + "derived_from": "tosca.nodes.Root", + "properties": { + "operands": { + "type": "list", + "required": true, + "entry_schema": { + "type": "map" + } + }, + "operator": { + "type": "string", + "required": true, + "entry_schema": { + "type": "string", + "constraints": [ + { + "valid_values": [ + "sum", + "min", + "max" + ] + } + ] + } + } + } + } + } +}
\ No newline at end of file diff --git a/examples/policies/policy_types/threshold.json b/examples/policies/policy_types/threshold.json new file mode 100644 index 0000000..60c73fa --- /dev/null +++ b/examples/policies/policy_types/threshold.json @@ -0,0 +1,56 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_1_0", + "policy_types": { + "onap.policies.optimization.resource.ThresholdPolicy": { + "derived_from": "onap.policies.optimization.Resource", + "version": "1.0.0", + "properties": { + "applicableResources": { + "type": "list", + "required": true, + "entry_schema": { + "type": "string", + "constraints": [ + { + "valid_values": [ + "any", + "all" + ] + } + ] + } + }, + "thresholdProperties": { + "type": "list", + "required": true, + "entry_schema": { + "type": "policy.data.thresholdProperties_properties" + } + } + } + } + }, + "data_types": { + "policy.data.thresholdProperties_properties": { + "derived_from": "tosca.nodes.Root", + "properties": { + "attribute": { + "type": "string", + "required": true + }, + "operator": { + "type": "string", + "required": true + }, + "threshold": { + "type": "float", + "required": true + }, + "unit": { + "type": "string", + "required": false + } + } + } + } +}
\ No newline at end of file diff --git a/examples/policies/policy_utils.py b/examples/policies/policy_utils.py new file mode 100644 index 0000000..0c1b595 --- /dev/null +++ b/examples/policies/policy_utils.py @@ -0,0 +1,192 @@ +from jinja2 import Template +import json +import os +import requests +import sys + +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + +HEADERS = {'Content-Type': 'application/json'} +AUTH = requests.auth.HTTPBasicAuth('healthcheck', 'zb!XztG34') + + +def get_tosca_policy(policy): + pol = json.loads(policy) + tosca_policy = { + 'tosca_definitions_version': 'tosca_simple_yaml_1_1_0', + 'topology_template': { + 'policies': [pol] + } + } + return json.dumps(tosca_policy) + +def gen_policy(template_dir, gen_dir, filename, jinja_args): + with open(os.path.join(template_dir, filename), 'r') as file: + contents = file.read() + tm = Template(contents) + gen = tm.render(jinja_args) + tosca_policy = get_tosca_policy(gen) + with open(os.path.join(gen_dir, filename), 'w') as file: + file.write(tosca_policy) + +def create_and_push_policies(policy_dir): + for filename in os.listdir(policy_dir): + if filename.endswith('.json'): + with open(os.path.join(policy_dir, filename), 'r') as file: + data = json.loads(file.read()) + metadata = create_policy(data) + if metadata: + push_policy(metadata) + +def delete_policies(policy_dir): + for filename in os.listdir(policy_dir): + if filename.endswith('.json'): + with open(os.path.join(policy_dir, filename), 'r') as file: + data = json.loads(file.read()) + policy_id = list(data['topology_template']['policies'][0].keys())[0] + undeploy_policy(policy_id) + metadata = delete_policy(data) + +def create_policy(data): + policy = data['topology_template']['policies'][0] + content = policy[list(policy.keys())[0]] + policy_type = content['type'] + type_version = content['type_version'] + policy_url = "https://policy-api:6969" + path = '/policy/api/v1/policytypes/{}/versions/{}/policies'.format(policy_type, type_version) + url = policy_url + path + try: + response = requests.post(url, headers=HEADERS, auth=AUTH, data=json.dumps(data), verify=False) + except Exception as e: + print(str(e)) + return None + if response.status_code == 200: + print('Policy {} created'.format(content['metadata']['policy-id'])) + return content['metadata'] + else: + print(response.content) + return None + +def push_policy(metadata): + data = {'policies': [metadata]} + policy_url = "https://policy-pap:6969" + path = '/policy/pap/v1/pdps/policies' + url = policy_url + path + try: + response = requests.post(url, headers=HEADERS, auth=AUTH, data=json.dumps(data), verify=False) + except Exception as e: + print(str(e)) + print("Cannot push policy {}".format(metadata['policy-id'])) + if response.status_code == 200: + print("Policy {} pushed".format(metadata['policy-id'])) + else: + print(response.content) + +def undeploy_policy(policy_id): + policy_url = "https://policy-pap:6969" + path = '/policy/pap/v1/pdps/policies/{}'.format(policy_id) + url = policy_url + path + try: + response = requests.delete(url, headers=HEADERS, auth=AUTH, verify=False) + except Exception as e: + print(str(e)) + print("Cannot undeploy policy {}".format(policy_id)) + if response.status_code == 200: + print("Policy {} undeployed".format(policy_id)) + else: + print(response.content) + +def delete_policy(data): + policy = data['topology_template']['policies'][0] + content = policy[list(policy.keys())[0]] + policy_type = content['type'] + type_version = content['type_version'] + policy_id = content['metadata']['policy-id'] + version = content['version'] + policy_url = "https://policy-api:6969" + path = '/policy/api/v1/policytypes/{}/versions/{}/policies/{}/versions/{}'.format(policy_type, type_version, policy_id, version) + url = policy_url + path + try: + response = requests.delete(url, headers=HEADERS, auth=AUTH, data=json.dumps(data), verify=False) + except Exception as e: + print(str(e)) + return None + if response.status_code == 200: + print('Policy {} deleted'.format(content['metadata']['policy-id'])) + return content['metadata'] + else: + print(response.content) + return None + +def generate_nssi_policies(jinja_args): + template_dir = BASE_DIR + 'nssi_policies' + gen_dir = BASE_DIR + 'gen_nssi_policies' + + if not os.path.exists(gen_dir): + os.mkdir(gen_dir) + + for filename in os.listdir(template_dir): + if filename.endswith('.json'): + gen_policy(template_dir, gen_dir, filename, jinja_args) + +def generate_nsi_policies(jinja_args): + template_dir = BASE_DIR + 'nsi_policies' + gen_dir = BASE_DIR + 'gen_nsi_policies' + + if not os.path.exists(gen_dir): + os.mkdir(gen_dir) + + for filename in os.listdir(template_dir): + if filename.endswith('.json'): + gen_policy(template_dir, gen_dir, filename, jinja_args) + +def create_policy_types(policy_dir): + for filename in os.listdir(policy_dir): + if filename.endswith('.json'): + with open(os.path.join(policy_dir, filename), 'r') as file: + data = json.loads(file.read()) + create_policy_type(data) + +def create_policy_type(data): + policy_url = "https://policy-api:6969" + path = '/policy/api/v1/policytypes' + url = policy_url + path + try: + response = requests.post(url, headers=HEADERS, auth=AUTH, data=json.dumps(data), verify=False) + except Exception as e: + print(str(e)) + return None + if response.status_code == 200: + print('Policy type created') + else: + print(response.content) + return None + + +action = sys.argv[1] + +if action == "generate_nssi_policies": + jinja_args = { + 'service_name': sys.argv[2], + 'goal': sys.argv[3], + 'attribute': sys.argv[4] + } + generate_nssi_policies(jinja_args) + +elif action == "create_and_push_policies": + policy_dir = sys.argv[2] + create_and_push_policies(policy_dir) + +elif action == "delete_policies": + policy_dir = sys.argv[2] + delete_policies(policy_dir) + +elif action == "generate_nsi_policies": + jinja_args = { + 'service_name': sys.argv[2] + } + generate_nsi_policies(jinja_args) + +elif action == "create_policy_types": + policy_dir = sys.argv[2] + create_policy_types(policy_dir)
\ No newline at end of file diff --git a/examples/policies/requirements.txt b/examples/policies/requirements.txt new file mode 100644 index 0000000..1c579e7 --- /dev/null +++ b/examples/policies/requirements.txt @@ -0,0 +1 @@ +jinja2
\ No newline at end of file diff --git a/osdf/__init__.py b/osdf/__init__.py index c33639e..8036d89 100755 --- a/osdf/__init__.py +++ b/osdf/__init__.py @@ -20,11 +20,12 @@ from jinja2 import Template - end_point_auth_mapping = { # map a URL endpoint to auth group "cmscheduler": "CMScheduler", "placement": "Placement", - "pci": "PCIOpt" + "pci": "PCIOpt", + "optmodel": "OptEngine", + "optengine": "OptEngine" } userid_suffix, passwd_suffix = "Username", "Password" diff --git a/osdf/adapters/aaf/aaf_authentication.py b/osdf/adapters/aaf/aaf_authentication.py index 2a72c30..b9aa510 100644 --- a/osdf/adapters/aaf/aaf_authentication.py +++ b/osdf/adapters/aaf/aaf_authentication.py @@ -17,12 +17,14 @@ # import base64 -import re -from datetime import datetime, timedelta +from datetime import datetime +from datetime import timedelta from flask import request +import re from osdf.config.base import osdf_config -from osdf.logging.osdf_logging import error_log, debug_log +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log from osdf.utils.interfaces import RestClient AUTHZ_PERMS_USER = '{}/authz/perms/user/{}' @@ -101,8 +103,8 @@ def get_aaf_permissions(uid, passwd): def remote_api(passwd, uid): - headers = {"Accept": "application/Users+xml;q=1.0;charset=utf-8;version=2.0,text/xml;q=1.0;version=2.0", - "Accept": "application/Users+json;q=1.0;charset=utf-8;version=2.0,application/json;q=1.0;version=2.0,*/*;q=1.0"} + headers = {"Accept": "application/Users+json;q=1.0;charset=utf-8;version=2.0,application/json;q=1.0;version=2.0," + "*/*;q=1.0"} url = AUTHZ_PERMS_USER.format(deploy_config['aaf_url'], uid) rc = RestClient(userid=uid, passwd=passwd, headers=headers, url=url, log_func=debug_log.debug, req_id='aaf_user_id') diff --git a/osdf/adapters/aaf/sms.py b/osdf/adapters/aaf/sms.py index 25ae7f2..031fee4 100644 --- a/osdf/adapters/aaf/sms.py +++ b/osdf/adapters/aaf/sms.py @@ -1,6 +1,7 @@ # # ------------------------------------------------------------------------- # Copyright (c) 2018 Intel Corporation Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,10 +23,11 @@ from onapsmsclient import Client import osdf.config.base as cfg_base +from osdf.config.base import osdf_config import osdf.config.credentials as creds import osdf.config.loader as config_loader -from osdf.config.base import osdf_config from osdf.logging.osdf_logging import debug_log +from osdf.utils import cipherUtils config_spec = { "preload_secrets": "config/preload_secrets.yaml" @@ -33,9 +35,12 @@ config_spec = { def preload_secrets(): - """ This is intended to load the secrets required for testing Application - Actual deployment will have a preload script. Make sure the config is - in sync""" + """preload_secrets() + + This is intended to load the secrets required for testing Application + Actual deployment will have a preload script. Make sure the config is + in sync + """ preload_config = config_loader.load_config_file( config_spec.get("preload_secrets")) domain = preload_config.get("domain") @@ -43,6 +48,9 @@ def preload_secrets(): sms_url = config["aaf_sms_url"] timeout = config["aaf_sms_timeout"] cacert = config["aaf_ca_certs"] + if not sms_url: + debug_log.debug("SMS Disabled") + return sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) domain_uuid = sms_client.createDomain(domain) debug_log.debug( @@ -62,60 +70,80 @@ def retrieve_secrets(): timeout = config["aaf_sms_timeout"] cacert = config["aaf_ca_certs"] domain = config["secret_domain"] - sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) - secrets = sms_client.getSecretNames(domain) - for secret in secrets: - values = sms_client.getSecret(domain, secret) - secret_dict[secret] = values - debug_log.debug("Secret Dictionary Retrieval Success") + if sms_url: + sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) + secrets = sms_client.getSecretNames(domain) + for secret in secrets: + values = sms_client.getSecret(domain, secret) + secret_dict[secret] = values + debug_log.debug("Secret Dictionary Retrieval Success") + else: + debug_log.debug("SMS Disabled. Secrets not loaded") return secret_dict def load_secrets(): config = osdf_config.deployment secret_dict = retrieve_secrets() - config['soUsername'] = secret_dict['so']['UserName'] - config['soPassword'] = secret_dict['so']['Password'] - config['conductorUsername'] = secret_dict['conductor']['UserName'] - config['conductorPassword'] = secret_dict['conductor']['Password'] - config['policyPlatformUsername'] = secret_dict['policyPlatform']['UserName'] - config['policyPlatformPassword'] = secret_dict['policyPlatform']['Password'] - config['policyClientUsername'] = secret_dict['policyClient']['UserName'] - config['policyClientPassword'] = secret_dict['policyClient']['Password'] - config['messageReaderAafUserId'] = secret_dict['dmaap']['UserName'] - config['messageReaderAafPassword'] = secret_dict['dmaap']['Password'] - config['sdcUsername'] = secret_dict['sdc']['UserName'] - config['sdcPassword'] = secret_dict['sdc']['Password'] - config['osdfPlacementUsername'] = secret_dict['osdfPlacement']['UserName'] - config['osdfPlacementPassword'] = secret_dict['osdfPlacement']['Password'] - config['osdfPlacementSOUsername'] = secret_dict['osdfPlacementSO']['UserName'] - config['osdfPlacementSOPassword'] = secret_dict['osdfPlacementSO']['Password'] - config['osdfPlacementVFCUsername'] = secret_dict['osdfPlacementVFC']['UserName'] - config['osdfPlacementVFCPassword'] = secret_dict['osdfPlacementVFC']['Password'] - config['osdfCMSchedulerUsername'] = secret_dict['osdfCMScheduler']['UserName'] - config['osdfCMSchedulerPassword'] = secret_dict['osdfCMScheduler']['Password'] - config['configDbUserName'] = secret_dict['configDb']['UserName'] - config['configDbPassword'] = secret_dict['configDb']['Password'] - config['pciHMSUsername'] = secret_dict['pciHMS']['UserName'] - config['pciHMSPassword'] = secret_dict['pciHMS']['Password'] - config['osdfPCIOptUsername'] = secret_dict['osdfPCIOpt']['UserName'] - config['osdfPCIOptPassword'] = secret_dict['osdfPCIOpt']['Password'] + if secret_dict: + config['soUsername'] = secret_dict['so']['UserName'] + config['soPassword'] = decrypt_pass(secret_dict['so']['Password']) + config['conductorUsername'] = secret_dict['conductor']['UserName'] + config['conductorPassword'] = decrypt_pass(secret_dict['conductor']['Password']) + config['policyPlatformUsername'] = secret_dict['policyPlatform']['UserName'] + config['policyPlatformPassword'] = decrypt_pass(secret_dict['policyPlatform']['Password']) + config['policyClientUsername'] = secret_dict['policyPlatform']['UserName'] + config['policyClientPassword'] = decrypt_pass(secret_dict['policyPlatform']['Password']) + config['messageReaderAafUserId'] = secret_dict['dmaap']['UserName'] + config['messageReaderAafPassword'] = decrypt_pass(secret_dict['dmaap']['Password']) + config['sdcUsername'] = secret_dict['sdc']['UserName'] + config['sdcPassword'] = decrypt_pass(secret_dict['sdc']['Password']) + config['osdfPlacementUsername'] = secret_dict['osdfPlacement']['UserName'] + config['osdfPlacementPassword'] = decrypt_pass(secret_dict['osdfPlacement']['Password']) + config['osdfPlacementSOUsername'] = secret_dict['osdfPlacementSO']['UserName'] + config['osdfPlacementSOPassword'] = decrypt_pass(secret_dict['osdfPlacementSO']['Password']) + config['osdfPlacementVFCUsername'] = secret_dict['osdfPlacementVFC']['UserName'] + config['osdfPlacementVFCPassword'] = decrypt_pass(secret_dict['osdfPlacementVFC']['Password']) + config['osdfCMSchedulerUsername'] = secret_dict['osdfCMScheduler']['UserName'] + config['osdfCMSchedulerPassword'] = decrypt_pass(secret_dict['osdfCMScheduler']['Password']) + config['configDbUserName'] = secret_dict['configDb']['UserName'] + config['configDbPassword'] = decrypt_pass(secret_dict['configDb']['Password']) + config['pciHMSUsername'] = secret_dict['pciHMS']['UserName'] + config['pciHMSPassword'] = decrypt_pass(secret_dict['pciHMS']['Password']) + config['osdfPCIOptUsername'] = secret_dict['osdfPCIOpt']['UserName'] + config['osdfPCIOptPassword'] = decrypt_pass(secret_dict['osdfPCIOpt']['Password']) + config['osdfOptEngineUsername'] = secret_dict['osdfOptEngine']['UserName'] + config['osdfOptEnginePassword'] = decrypt_pass(secret_dict['osdfOptEngine']['Password']) cfg_base.http_basic_auth_credentials = creds.load_credentials(osdf_config) cfg_base.dmaap_creds = creds.dmaap_creds() +def decrypt_pass(passwd): + config = osdf_config.deployment + if not config.get('appkey') or passwd == '' or passwd == 'NA': + return passwd + else: + return cipherUtils.AESCipher.get_instance().decrypt(passwd) + + def delete_secrets(): - """ This is intended to delete the secrets for a clean initialization for - testing Application. Actual deployment will have a preload script. - Make sure the config is in sync""" + """delete_secrets() + + This is intended to delete the secrets for a clean initialization for + testing Application. Actual deployment will have a preload script. + Make sure the config is in sync + """ config = osdf_config.deployment sms_url = config["aaf_sms_url"] timeout = config["aaf_sms_timeout"] cacert = config["aaf_ca_certs"] domain = config["secret_domain"] - sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) - ret_val = sms_client.deleteDomain(domain) - debug_log.debug("Clean up complete") + if sms_url: + sms_client = Client(url=sms_url, timeout=timeout, cacert=cacert) + ret_val = sms_client.deleteDomain(domain) + debug_log.debug("Clean up complete") + else: + debug_log.debug("SMS Disabled. Secrets delete skipped") return ret_val diff --git a/osdf/adapters/aai/_init_.py b/osdf/adapters/aai/_init_.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/osdf/adapters/aai/_init_.py diff --git a/osdf/adapters/aai/fetch_aai_data.py b/osdf/adapters/aai/fetch_aai_data.py new file mode 100644 index 0000000..170d5e5 --- /dev/null +++ b/osdf/adapters/aai/fetch_aai_data.py @@ -0,0 +1,90 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 requests +from requests.auth import HTTPBasicAuth +from requests import RequestException + +from osdf.logging.osdf_logging import debug_log + +AAI_HEADERS = { + "X-TransactionId": "9999", + "X-FromAppId": "OOF", + "Accept": "application/json", + "Content-Type": "application/json", +} + +AUTH = HTTPBasicAuth("AAI", "AAI") + + +class AAIException(Exception): + pass + + +def get_aai_data(request_json, osdf_config): + + """Get the response from AAI + + :param request_json: requestjson + :param osdf_config: configuration specific to OSDF app + :return:response body from AAI + """ + + nxi_id = request_json["NxIId"] + config = osdf_config.deployment + aai_url = config["aaiUrl"] + aai_req_url = aai_url + config["aaiServiceInstanceUrl"] + nxi_id + "?depth=2" + + try: + debug_log.debug("aai request: {}".format(aai_req_url)) + response = requests.get(aai_req_url, headers=AAI_HEADERS, auth=AUTH, verify=False) + debug_log.debug("aai response: {}".format(response.json())) + except RequestException as e: + raise AAIException("Request exception was encountered {}".format(e)) + + if response.status_code == 200: + return response.json() + else: + raise AAIException("Error response recieved from AAI for the request {}".format(aai_req_url)) + + +def execute_dsl_query(query, format, osdf_config): + """Get the response from AAI + + :param query: dsl query to be executed + :param format format of the output + :param osdf_config: configuration specific to OSDF app + :return:response body from AAI + """ + config = osdf_config.deployment + dsl_url = config["aaiUrl"] + config["dslQueryPath"] + format + + data = json.dumps({'dsl': query}) + debug_log.debug("aai dsl request: {}".format(data)) + try: + response = requests.put(dsl_url, data=data, headers=AAI_HEADERS, auth=AUTH, verify=False) + debug_log.debug("aai dsl response: {}".format(response)) + except RequestException as ex: + raise AAIException("Request exception was encountered {}".format(ex)) + + if response.status_code == 200: + return response.json() + else: + raise AAIException("Response code other than 200 from AAI: {} {}".format(response.status_code, + response.content)) diff --git a/osdf/optimizers/licenseopt/__init__.py b/osdf/adapters/conductor/__init__.py index 4b25e5b..6156206 100644 --- a/osdf/optimizers/licenseopt/__init__.py +++ b/osdf/adapters/conductor/__init__.py @@ -14,4 +14,4 @@ # limitations under the License. # # ------------------------------------------------------------------------- -# +#
\ No newline at end of file diff --git a/osdf/adapters/conductor/api_builder.py b/osdf/adapters/conductor/api_builder.py new file mode 100644 index 0000000..f3b0798 --- /dev/null +++ b/osdf/adapters/conductor/api_builder.py @@ -0,0 +1,123 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 jinja2 import Template +import json + +import osdf.adapters.conductor.translation as tr +from osdf.adapters.policy.utils import group_policies_gen +from osdf.utils.programming_utils import list_flatten + + +def _build_parameters(group_policies, service_info, request_parameters): + """Function prepares parameters section for has request + + :param group_policies: filtered policies + :param service_info: service info + :param request_parameters: request parameters + :return: + """ + initial_params = tr.get_opt_query_data(request_parameters, + group_policies['onap.policies.optimization.service.QueryPolicy']) + params = dict() + params.update({"REQUIRED_MEM": initial_params.pop("requiredMemory", "")}) + params.update({"REQUIRED_DISK": initial_params.pop("requiredDisk", "")}) + params.update({"customer_lat": initial_params.pop("customerLatitude", 0.0)}) + params.update({"customer_long": initial_params.pop("customerLongitude", 0.0)}) + params.update({"service_name": service_info.get('serviceName', "")}) + params.update({"service_id": service_info.get('serviceInstanceId', "")}) + + for key, val in initial_params.items(): + if val and val != "": + params.update({key: val}) + return params + + +def conductor_api_builder(req_info, demands, request_parameters, service_info, + template_fields, flat_policies: list, local_config, + template="osdf/adapters/conductor/templates/conductor_interface.json"): + """Build an OSDF southbound API call for HAS-Conductor/Placement optimization + + :param req_info: parameter data received from a client + :param demands: list of demands + :param request_parameters: request parameters + :param service_info: service info object + :param template_fields: Fields that has to be passed to the template to render + :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 + :return: json to be sent to Conductor/placement optimization + """ + + templ = Template(open(template).read()) + gp = group_policies_gen(flat_policies, local_config) + demand_name_list = [] + for demand in demands: + demand_name_list.append(demand['resourceModuleName'].lower()) + demand_list = tr.gen_demands(demands, gp['onap.policies.optimization.resource.VnfPolicy']) + attribute_policy_list = tr.gen_attribute_policy( + demand_name_list, gp['onap.policies.optimization.resource.AttributePolicy']) + + distance_to_location_policy_list = tr.gen_distance_to_location_policy( + demand_name_list, gp['onap.policies.optimization.resource.DistancePolicy']) + inventory_policy_list = tr.gen_inventory_group_policy( + demand_name_list, gp['onap.policies.optimization.resource.InventoryGroupPolicy']) + resource_instance_policy_list = tr.gen_resource_instance_policy( + demand_name_list, gp['onap.policies.optimization.resource.ResourceInstancePolicy']) + resource_region_policy_list = tr.gen_resource_region_policy( + demand_name_list, gp['onap.policies.optimization.resource.ResourceRegionPolicy']) + zone_policy_list = tr.gen_zone_policy( + demand_name_list, gp['onap.policies.optimization.resource.AffinityPolicy']) + optimization_policy_list = tr.gen_optimization_policy( + demand_name_list, gp['onap.policies.optimization.resource.OptimizationPolicy']) + reservation_policy_list = tr.gen_reservation_policy( + demand_name_list, gp['onap.policies.optimization.resource.InstanceReservationPolicy']) + capacity_policy_list = tr.gen_capacity_policy( + demand_name_list, gp['onap.policies.optimization.resource.Vim_fit']) + hpa_policy_list = tr.gen_hpa_policy( + demand_name_list, gp['onap.policies.optimization.resource.HpaPolicy']) + threshold_policy_list = tr.gen_threshold_policy(demand_name_list, + gp['onap.policies.optimization.resource.' + 'ThresholdPolicy']) + aggregation_policy_list = tr.gen_aggregation_policy(demand_name_list, + gp['onap.policies.optimization.resource.' + 'AggregationPolicy']) + req_params_dict = _build_parameters(gp, service_info, request_parameters) + conductor_policies = [attribute_policy_list, distance_to_location_policy_list, + inventory_policy_list, resource_instance_policy_list, + resource_region_policy_list, zone_policy_list, reservation_policy_list, + capacity_policy_list, hpa_policy_list, threshold_policy_list, aggregation_policy_list] + filtered_policies = [x for x in conductor_policies if len(x) > 0] + policy_groups = list_flatten(filtered_policies) + request_type = req_info.get('requestType', None) + rendered_req = templ.render( + requestType=request_type, + 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'], + request_params=req_params_dict, + location_enabled=template_fields.get('location_enabled'), + version=template_fields.get('version'), + json=json) + json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! + return json_payload diff --git a/osdf/adapters/conductor/conductor.py b/osdf/adapters/conductor/conductor.py new file mode 100644 index 0000000..49c123d --- /dev/null +++ b/osdf/adapters/conductor/conductor.py @@ -0,0 +1,120 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +import time + +from osdf.adapters.conductor.api_builder import conductor_api_builder +from osdf.logging.osdf_logging import debug_log +from osdf.operation.exceptions import BusinessException +from osdf.utils.interfaces import RestClient + + +def request(req_info, demands, request_parameters, service_info, template_fields, + osdf_config, flat_policies): + config = osdf_config.deployment + local_config = osdf_config.core + uid, passwd = config['conductorUsername'], config['conductorPassword'] + conductor_url = config['conductorUrl'] + req_id = req_info["requestId"] + transaction_id = req_info['transactionId'] + headers = dict(transaction_id=transaction_id) + placement_ver_enabled = config.get('placementVersioningEnabled', False) + + if placement_ver_enabled: + cond_minor_version = config.get('conductorMinorVersion', None) + if cond_minor_version is not None: + x_minor_version = str(cond_minor_version) + headers.update({'X-MinorVersion': x_minor_version}) + debug_log.debug("Versions set in HTTP header to " + "conductor: X-MinorVersion: {} ".format(x_minor_version)) + + 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_info, demands, request_parameters, + service_info, template_fields, flat_policies, + local_config) + 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_info['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", "solved"]: + return resp + new_url = resp['plans'][0]['links'][0][0]['href'] # TODO(krishna): 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("Attempting to read the plan from " + "the conductor provided url {}".format(plan_url)) + raw_resp = rc.request(raw_response=True, + url=plan_url) + 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 diff --git a/osdf/templates/conductor_interface.json b/osdf/adapters/conductor/templates/conductor_interface.json index 030d6a0..c0e28dc 100755 --- a/osdf/templates/conductor_interface.json +++ b/osdf/adapters/conductor/templates/conductor_interface.json @@ -4,19 +4,21 @@ "timeout": {{ timeout }}, "num_solution": "{{ limit }}", "template": { - "homing_template_version": "2017-10-10", + "homing_template_version": "{{ version }}", "parameters": { {% set comma=joiner(",") %} {% for key, value in request_params.items() %} {{ comma() }} "{{key}}": {{ json.dumps(value) }} {% endfor %} }, + {% if location_enabled %} "locations": { "customer_loc": { "latitude": { "get_param": "customer_lat" }, "longitude": { "get_param": "customer_long" } } }, + {% endif %} "demands": {{ json.dumps(demand_list) }}, {% set comma_main = joiner(",") %} "constraints": { @@ -31,7 +33,7 @@ {% set comma=joiner(",") %} {% for elem in optimization_policies %} {{ comma() }} {% for key, value in elem.items() %} - "{{key}}": {{ json.dumps(value) }} + "{{key}}": {{ json.dumps(value) }} {{ ", " if not loop.last }} {% endfor %} {% endfor %} } diff --git a/osdf/adapters/conductor/translation.py b/osdf/adapters/conductor/translation.py new file mode 100644 index 0000000..f44f27f --- /dev/null +++ b/osdf/adapters/conductor/translation.py @@ -0,0 +1,376 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +import re +import yaml + +from osdf.utils.programming_utils import dot_notation + +policy_config_mapping = yaml.safe_load(open('config/has_config.yaml')).get('policy_config_mapping') + +CONSTRAINT_TYPE_MAP = {"onap.policies.optimization.resource.AttributePolicy": "attribute", + "onap.policies.optimization.resource.DistancePolicy": "distance_to_location", + "onap.policies.optimization.resource.InventoryGroupPolicy": "inventory_group", + "onap.policies.optimization.resource.ResourceInstancePolicy": "instance_fit", + "onap.policies.optimization.resource.ResourceRegionPolicy": "region_fit", + "onap.policies.optimization.resource.AffinityPolicy": "zone", + "onap.policies.optimization.resource.InstanceReservationPolicy": + "instance_reservation", + "onap.policies.optimization.resource.Vim_fit": "vim_fit", + "onap.policies.optimization.resource.HpaPolicy": "hpa", + "onap.policies.optimization.resource.ThresholdPolicy": "threshold", + "onap.policies.optimization.resource.AggregationPolicy": "aggregation" + } + + +def get_opt_query_data(request_parameters, policies): + """Fetch service and order specific details from the requestParameters field of a request. + + :param request_parameters: A list of request parameters + :param policies: A set of policies + :return: A dictionary with service and order-specific attributes. + """ + req_param_dict = {} + if request_parameters: + for policy in policies: + for queryProp in policy[list(policy.keys())[0]]['properties']['queryProperties']: + attr_val = queryProp['value'] if 'value' in queryProp and queryProp['value'] != "" \ + else dot_notation(request_parameters, queryProp['attribute_location']) + if attr_val is not None: + req_param_dict.update({queryProp['attribute']: attr_val}) + return req_param_dict + + +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 objective policy information provided in the incoming request + :return: List of optimization objective policies in a format required by Conductor + """ + if len(optimization_policy) == 1: + policy = optimization_policy[0] + policy_content = policy[list(policy.keys())[0]] + if policy_content['type_version'] == '2.0.0': + properties = policy_content['properties'] + objective = {'goal': properties['goal'], + 'operation_function': properties['operation_function']} + return [objective] + + optimization_policy_list = [] + for policy in optimization_policy: + content = policy[list(policy.keys())[0]]['properties'] + parameter_list = [] + parameters = ["cloud_version", "hpa_score"] + + for attr in content['objectiveParameter']['parameterAttributes']: + parameter = attr['parameter'] if attr['parameter'] in parameters else attr['parameter'] + "_between" + default, vnfs = get_matching_vnfs(attr['resources'], vnf_list) + for vnf in vnfs: + value = [vnf] if attr['parameter'] in parameters 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_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 vnfs 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 + """ + # Check if it is a default policy + default = True if resources == [] else False + resources_lcase = [x.lower() for x in resources] if not default else [x.lower() for x in vnf_list] + if match_type == "all": # don't bother with any comparisons + return default, resources if set(resources_lcase) <= set(vnf_list) else None + common_vnfs = set(vnf_list) & set(resources_lcase) if not default else set(vnf_list) + common_resources = [x for x in resources if x.lower() in common_vnfs] if not default else list(common_vnfs) + if match_type == "intersection": # specifically requested intersection + return default, list(common_resources) + return default, resources if common_vnfs else None # "any" match => all resources to be returned + + +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[list(policy.keys())[0]] + default, demands = get_matching_vnfs(pc['properties']['resources'], vnf_list, match_type=match_type) + resource = {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), 'demands': demands}} + + if rtype: + resource[pc['properties']['identity']]['properties'] = {'controller': pc[rtype]['controller'], + 'request': json.loads(pc[rtype]['request'])} + if demands and len(demands) != 0: + # The default policy shall not override the specific policy that already appended + if default: + for d in demands: + resource_repeated = True \ + if {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), + 'demands': d}} in resource_policy_list else False + if resource_repeated: + continue + else: + resource_policy_list.append( + {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), + 'demands': d}}) + policy[list(policy.keys())[0]]['properties']['resources'] = d + related_policies.append(policy) + # Need to override the default policies, here delete the outdated policy stored in the db + if resource in resource_policy_list: + for pc in related_policies: + if pc[list(pc.keys()[0])]['properties']['resources'] == resource: + related_policies.remove(pc) + resource_policy_list.remove(resource) + related_policies.append(policy) + resource_policy_list.append(resource) + + 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[list(p_main.keys())[0]]['properties']['distanceProperties'] + pcp_d = properties['distance'] + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = { + 'distance': pcp_d['operator'] + " " + pcp_d['value'].lower() + " " + pcp_d['unit'].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[list(p_main.keys())[0]]['properties']['attributeProperties'] + attribute_mapping = policy_config_mapping['filtering_attributes'] # wanted attributes and mapping + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = { + 'evaluate': dict((attribute_mapping[k], properties.get(k) + if k != "cloudRegion" else gen_cloud_region(properties)) + for k in attribute_mapping.keys()) + } + return cur_policies # cur_policies gets updated in place... + + +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, match_type="all", rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy + pmz = p_main[list(p_main.keys())[0]]['properties']['affinityProperties'] + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = \ + {'category': pmz['category'], 'qualifier': pmz['qualifier']} + return cur_policies + + +def gen_capacity_policy(vnf_list, capacity_policy): + """Get zone policies in order to populate the Conductor API call""" + cur_policies, related_policies = gen_policy_instance(vnf_list, capacity_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy + pmz = p_main[list(p_main.keys())[0]]['properties']['capacityProperty'] + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = \ + {"controller": pmz['controller'], 'request': json.loads(pmz['request'])} + return cur_policies + + +def gen_hpa_policy(vnf_list, hpa_policy): + """Get zone policies in order to populate the Conductor API call""" + cur_policies, related_policies = gen_policy_instance(vnf_list, hpa_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = \ + {'evaluate': p_main[list(p_main.keys())[0]]['properties']['flavorFeatures']} + return cur_policies + + +def gen_threshold_policy(vnf_list, threshold_policy): + cur_policies, related_policies = gen_policy_instance(vnf_list, threshold_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): + pmz = p_main[list(p_main.keys())[0]]['properties']['thresholdProperties'] + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = {'evaluate': pmz} + return cur_policies + + +def gen_aggregation_policy(vnf_list, cross_policy): + cur_policies, related_policies = gen_policy_instance(vnf_list, cross_policy, rtype=None) + for p_new, p_main in zip(cur_policies, related_policies): + pmz = p_main[list(p_main.keys())[0]]['properties']['aggregationProperties'] + p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = {'evaluate': pmz} + return cur_policies + + +def get_augmented_policy_attributes(policy_property, demand): + """Get policy attributes and augment them using policy_config_mapping and demand information""" + attributes = copy.copy(policy_property['attributes']) + remapping = policy_config_mapping['remapping'] + extra = dict((x, demand['resourceModelInfo'][remapping[x]]) for x in attributes if x in remapping) + attributes.update(extra) + return attributes + + +def get_candidates_demands(demand): + """Get demands related to candidates; e.g. excluded/required""" + res = {} + for k, v in policy_config_mapping['candidates'].items(): + if k not in demand: + continue + res[v] = [{'inventory_type': x['identifierType'], 'candidate_id': x['identifiers']} for x in demand[k]] + return res + + +def get_policy_properties(demand, policies): + """Get policy_properties for cases where there is a match with the demand""" + for policy in policies: + policy_demands = set([x.lower() for x in policy[list(policy.keys())[0]]['properties']['resources']]) + if policy_demands and demand['resourceModuleName'].lower() not in policy_demands: + continue # no match for this policy + elif policy_demands == set(): # Append resource name for default policy + policy[list(policy.keys())[0]]['properties'].update(resources=list(demand.get('resourceModuleName'))) + for policy_property in policy[list(policy.keys())[0]]['properties']['vnfProperties']: + yield policy_property + + +def get_demand_properties(demand, policies): + """Get list demand properties objects (named tuples) from policy""" + demand_properties = [] + for policy_property in get_policy_properties(demand, policies): + prop = dict(inventory_provider=policy_property['inventoryProvider'], + inventory_type=policy_property['inventoryType'], + service_type=demand.get('serviceResourceId', ''), + service_resource_id=demand.get('serviceResourceId', '')) + policy_property_mapping = {'filtering_attributes': 'attributes', + 'passthrough_attributes': 'passthroughAttributes', + 'default_attributes': 'defaultAttributes'} + + prop.update({'unique': policy_property['unique']} if 'unique' in policy_property and + policy_property['unique'] else {}) + prop['filtering_attributes'] = dict() + for key, value in policy_property_mapping.items(): + get_demand_attributes(prop, policy_property, key, value) + + prop['filtering_attributes'].update({'global-customer-id': policy_property['customerId']} + if 'customerId' in policy_property and policy_property['customerId'] + else {}) + prop['filtering_attributes'].update({'model-invariant-id': demand['resourceModelInfo']['modelInvariantId']} + if 'modelInvariantId' in demand['resourceModelInfo'] + and demand['resourceModelInfo']['modelInvariantId'] else {}) + prop['filtering_attributes'].update({'model-version-id': demand['resourceModelInfo']['modelVersionId']} + if 'modelVersionId' in demand['resourceModelInfo'] + and demand['resourceModelInfo']['modelVersionId'] else {}) + prop['filtering_attributes'].update({'equipment-role': policy_property['equipmentRole']} + if 'equipmentRole' in policy_property and policy_property['equipmentRole'] + else {}) + prop.update(get_candidates_demands(demand)) + demand_properties.append(prop) + return demand_properties + + +def get_demand_attributes(prop, policy_property, attribute_type, key): + if policy_property.get(key): + prop[attribute_type] = dict() + for attr_key, attr_val in policy_property[key].items(): + update_converted_attribute(attr_key, attr_val, prop, attribute_type) + + +def update_converted_attribute(attr_key, attr_val, properties, attribute_type): + """Updates dictonary of attributes with one specified in the arguments. + + Automatically translates key namr from camelCase to hyphens + :param attribute_type: attribute section name + :param attr_key: key of the attribute + :param attr_val: value of the attribute + :param properties: dictionary with attributes to update + :return: + """ + if attr_val: + remapping = policy_config_mapping[attribute_type] + if remapping.get(attr_key): + key_value = remapping.get(attr_key) + else: + key_value = re.sub('(.)([A-Z][a-z]+)', r'\1-\2', attr_key) + key_value = re.sub('([a-z0-9])([A-Z])', r'\1-\2', key_value).lower() + properties[attribute_type].update({key_value: attr_val}) + + +def gen_demands(demands, vnf_policies): + """Generate list of demands based on request and VNF policies + + :param demands: A List of demands + :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 demand in demands: + prop = get_demand_properties(demand, vnf_policies) + if len(prop) > 0: + demand_dictionary.update({demand['resourceModuleName']: prop}) + return demand_dictionary + + +def gen_cloud_region(property): + prop = {"cloud_region_attributes": dict()} + if 'cloudRegion' in property: + for k, v in property['cloudRegion'].items(): + update_converted_attribute(k, v, prop, 'cloud_region_attributes') + return prop["cloud_region_attributes"] diff --git a/osdf/adapters/dcae/des.py b/osdf/adapters/dcae/des.py new file mode 100644 index 0000000..57cb128 --- /dev/null +++ b/osdf/adapters/dcae/des.py @@ -0,0 +1,47 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +from osdf.utils.interfaces import RestClient + + +class DESException(Exception): + pass + + +def extract_data(service_id, request_data): + """Extracts data from the data lake via DES. + + param: service_id: kpi data identifier + param: request_data: data to send + param: osdf_config: osdf config to retrieve api info + """ + + config = osdf_config.deployment + user, password = config['desUsername'], config['desPassword'] + headers = config["desHeaders"] + req_url = config["desUrl"] + config["desApiPath"] + service_id + rc = RestClient(userid=user, passwd=password, url=req_url, headers=headers, method="POST") + + try: + response_json = rc.request(data=request_data) + return response_json.get("result") + except requests.RequestException as e: + raise DESException("Request exception was encountered {}".format(e)) diff --git a/osdf/adapters/dcae/message_router.py b/osdf/adapters/dcae/message_router.py index caf04a4..0968812 100755 --- a/osdf/adapters/dcae/message_router.py +++ b/osdf/adapters/dcae/message_router.py @@ -17,8 +17,10 @@ # import requests -from osdf.utils.data_types import list_like + from osdf.operation.exceptions import MessageBusConfigurationException +from osdf.utils.data_types import list_like +from osdf.utils.interfaces import RestClient class MessageRouterClient(object): @@ -27,7 +29,8 @@ class MessageRouterClient(object): consumer_group_id=':', timeout_ms=15000, fetch_limit=1000, userid_passwd=':'): - """ + """Class initializer + :param dmaap_url: protocol, host and port; can also be a list of URLs (e.g. https://dmaap-host.onapdemo.onap.org:3905/events/org.onap.dmaap.MULTICLOUD.URGENT), can also be a list of such URLs @@ -44,14 +47,14 @@ class MessageRouterClient(object): self.topic_urls = [dmaap_url] if not list_like(dmaap_url) else dmaap_url self.timeout_ms = timeout_ms self.fetch_limit = fetch_limit - userid, passwd = userid_passwd.split(':') - self.auth = (userid, passwd) if userid and passwd else None + self.userid, self.passwd = userid_passwd.split(':') consumer_group, consumer_id = consumer_group_id.split(':') 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 """ @@ -61,7 +64,7 @@ class MessageRouterClient(object): for url in urls[:-1]: try: return self.http_request(method='GET', url=url, outputjson=outputjson) - except: + except Exception: pass return self.http_request(method='GET', url=urls[-1], outputjson=outputjson) @@ -69,13 +72,13 @@ class MessageRouterClient(object): for url in self.topic_urls[:-1]: try: return self.http_request(method='POST', url=url, inputjson=inputjson, msg=msg) - except: + except Exception: 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 + """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) @@ -83,9 +86,15 @@ class MessageRouterClient(object): :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)) + + rc = RestClient(userid=self.userid, passwd=self.passwd, url=url, method=method) + try: + res = rc.request(raw_response=True, data=msg, **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)) + + except requests.RequestException as ex: + raise Exception("Request Exception occurred {}".format(str(ex))) diff --git a/osdf/adapters/policy/interface.py b/osdf/adapters/policy/interface.py index 7c3118c..6799c6b 100644 --- a/osdf/adapters/policy/interface.py +++ b/osdf/adapters/policy/interface.py @@ -1,4 +1,4 @@ - # ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- # Copyright (c) 2015-2017 AT&T Intellectual Property # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,21 +17,23 @@ # import base64 -import itertools import json -import yaml import os +from requests import RequestException import uuid +import yaml - -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 policy_name_as_regex, retrieve_node -from osdf.utils.programming_utils import list_flatten, dot_notation +from osdf.adapters.policy.utils import policy_name_as_regex from osdf.config.base import osdf_config -from osdf.logging.osdf_logging import audit_log, MH, metrics_log, debug_log +from osdf.logging.osdf_logging import audit_log +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import metrics_log +from osdf.logging.osdf_logging import MH +from osdf.operation.exceptions import BusinessException from osdf.utils.interfaces import RestClient +from osdf.utils.programming_utils import dot_notation +from osdf.utils.programming_utils import list_flatten def get_by_name(rest_client, policy_name_list, wildcards=True): @@ -49,7 +51,8 @@ def get_by_name(rest_client, policy_name_list, wildcards=True): def get_by_scope(rest_client, req, config_local, type_service): - """ Get policies by scopes as defined in the configuration file. + """Get policies by scopes as defined in the configuration file. + :param rest_client: a rest client object to make a call. :param req: an optimization request. :param config_local: application configuration file. @@ -58,28 +61,39 @@ def get_by_scope(rest_client, req, config_local, type_service): """ scope_policies = [] references = config_local.get('references', {}) - pscope = config_local.get('policy_info', {}).get(type_service, {}).get('policy_scope', {}) - service_name = dot_notation(req, references.get('service_name', {}).get('value', None)) - primary_scope = pscope['{}_scope'.format(service_name.lower() if pscope.get(service_name + "_scope", None) - else "default")] - for sec_scope in pscope.get('secondary_scopes', []): - policies, scope_fields = [], [] - for field in sec_scope: - scope_fields.extend([get_scope_fields(field, references, req, list_flatten(scope_policies)) - if 'get_param' in field else field]) - scope_fields = set(list_flatten(scope_fields)) - scope_fields = set([x.lower() for x in scope_fields]) - for scope in scope_fields: - policies.extend(policy_api_call(rest_client, primary_scope, scope)) - scope_policies.append([policy for policy in policies - if scope_fields <= set(json.loads(policy['config'])['content']['policyScope'])]) + pscope = config_local.get('policy_info', {}).get(type_service, {}).get('policy_scope', []) + scope_fields = {} + policies = {} + for scopes in pscope: + for key in scopes.keys(): + for field in scopes[key]: + scope_fields[key] = list_flatten([get_scope_fields(field, references, req, policies) + if 'get_param' in field else field]) + if scope_fields.get('resources') and len(scope_fields['resources']) > 1: + for s in scope_fields['resources']: + scope_fields['resources'] = [s] + policies.update(policy_api_call(rest_client, scope_fields).get('policies', {})) + else: + policies.update(policy_api_call(rest_client, scope_fields).get('policies', {})) + for policyName in policies.keys(): + keys = scope_fields.keys() & policies[policyName]['properties'].keys() + policy = {} + policy[policyName] = policies[policyName] + for k in keys: + if set(policies.get(policyName, {}).get('properties', {}).get(k)) >= set(scope_fields[k]) \ + and policy not in scope_policies: + scope_policies.append(policy) + return scope_policies -def get_scope_fields(field, references, req, policy_info): - """ Retrieve the values for scope fields from a request and policies as per the configuration - and references defined in a configuration file. If the value of a scope field missing in a request or +def get_scope_fields(field, references, req, policies): + """Retrieve the values for scope fields from a request and policies + + They are derived as per the configuration and references defined in a + configuration file. If the value of a scope field missing in a request or policies, throw an exception since correct policies cannot be retrieved. + :param field: details on a scope field from a configuration file. :param references: references defined in a configuration file. :param req: an optimization request. @@ -95,9 +109,9 @@ def get_scope_fields(field, references, req, policy_info): raise BusinessException("Field {} is missing a value in a request".format(ref_value.split('.')[-1])) else: scope_fields = [] - for policy in policy_info: - policy_content = json.loads(policy.get('config', "{}")) - if policy_content.get('content', {}).get('policyType', "invalid_policy") == ref_source: + for policyName in policies.keys(): + policy_content = policies.get(policyName) + if policy_content.get('type', "invalid_policy") == ref_source: scope_fields.append(dot_notation(policy_content, ref_value)) scope_values = list_flatten(scope_fields) if len(scope_values) > 0: @@ -106,31 +120,35 @@ def get_scope_fields(field, references, req, policy_info): ref_value.split('.')[-1], ref_source)) -def policy_api_call(rest_client, primary_scope, scope_field): - """ Makes a getConfig API call to the policy system to retrieve policies matching a scope. - :param rest_client: rest client object to make a call - :param primary_scope: the primary scope of policies, which is a folder in the policy system - where policies are stored. - :param scope_field: the secondary scope of policies, which is a collection of domain values. - :return: a list of policies matching both primary and secondary scopes. +def policy_api_call(rest_client, scope_fields): + """Makes the api call to policy and return the response if policies are present + + :param rest_client: rest client to make a call + :param scope_fields: a collection of scopes to be used for filtering + :return: a list of policies matching all filters """ - api_call_body = {"policyName": "{}.*".format(primary_scope), - "configAttributes": {"policyScope": "{}".format(scope_field)}} - return rest_client.request(json=api_call_body) + api_call_body = {"ONAPName": "OOF", + "ONAPComponent": "OOF_Component", + "ONAPInstance": "OOF_Component_Instance", + "action": "optimize", + "resource": scope_fields} + response = rest_client.request(json=api_call_body) + if not response.get("policies"): + raise Exception("Policies not found for the scope {}".format(scope_fields)) + return response 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 """ config = osdf_config.deployment + headers = {"Content-type": "application/json"} 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) @@ -142,17 +160,17 @@ def remote_api(req_json, osdf_config, service_type="placement"): policies = get_by_scope(rc, req_json, osdf_config.core, service_type) 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']) + for x in policies: + if x[list(x.keys())[0]].get('properties') is None: + raise BusinessException("Properties not found for policy with name %s" % x[list(x.keys()[0])]) else: - formatted_policies.append(json.loads(x['config'])) + formatted_policies.append(x) return formatted_policies def local_policies_location(req_json, osdf_config, service_type): - """ - Get folder and list of policy_files if "local policies" option is enabled + """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 """ @@ -164,15 +182,16 @@ def local_policies_location(req_json, osdf_config, service_type): if service_type == "scheduling": return lp.get('{}_policy_dir'.format(service_type)), lp.get('{}_policy_files'.format(service_type)) else: - service_name = req_json['serviceInfo']['serviceName'] # TODO: data_mapping.get_service_type(model_name) + service_name = req_json['serviceInfo']['serviceName'] debug_log.debug('Loading local policies for service name: {}'.format(service_name)) return lp.get('{}_policy_dir_{}'.format(service_type, service_name.lower())), \ - lp.get('{}_policy_files_{}'.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 @@ -194,6 +213,7 @@ def get_policies(request_json, service_type): return policies + def upload_policy_models(): """Upload all the policy models reside in the folder""" requestId = uuid.uuid4() diff --git a/osdf/adapters/policy/utils.py b/osdf/adapters/policy/utils.py index 2f873af..79047eb 100644 --- a/osdf/adapters/policy/utils.py +++ b/osdf/adapters/policy/utils.py @@ -33,11 +33,11 @@ def group_policies_gen(flat_policies, config): """ filtered_policies = defaultdict(list) policy_name = [] - policies = [x for x in flat_policies if x['content'].get('policyType')] # drop ones without 'policy_type' + policies = [x for x in flat_policies if x[list(x.keys())[0]]["type"]] # drop ones without 'type' priority = config.get('policy_info', {}).get('prioritization_attributes', {}) aggregated_policies = dict() for plc in policies: - attrs = [dot_notation(plc, dot_path) for key in priority.keys() for dot_path in priority[key]] + attrs = [dot_notation(plc[list(plc.keys())[0]], dot_path) for key in priority.keys() for dot_path in priority[key]] attrs_list = [x if isinstance(x, list) else [x] for x in attrs] attributes = [list_flatten(x) if isinstance(x, list) else x for x in attrs_list] for y in itertools.product(*attributes): @@ -45,12 +45,12 @@ def group_policies_gen(flat_policies, config): aggregated_policies[y].append(plc) for key in aggregated_policies.keys(): - aggregated_policies[key].sort(key=lambda x: x['priority'], reverse=True) + #aggregated_policies[key].sort(key=lambda x: x['priority'], reverse=True) prioritized_policy = aggregated_policies[key][0] - if prioritized_policy['policyName'] not in policy_name: + if list(prioritized_policy.keys())[0] not in policy_name: # TODO: Check logic here... should policy appear only once across all groups? - filtered_policies[prioritized_policy['content']['policyType']].append(prioritized_policy) - policy_name.append(prioritized_policy['policyName']) + filtered_policies[prioritized_policy[list(prioritized_policy.keys())[0]]['type']].append(prioritized_policy) + policy_name.append(list(prioritized_policy.keys())[0]) return filtered_policies diff --git a/osdf/apps/__init__.py b/osdf/apps/__init__.py new file mode 100644 index 0000000..370bc06 --- /dev/null +++ b/osdf/apps/__init__.py @@ -0,0 +1,2 @@ +import yaml +yaml.warnings({'YAMLLoadWarning': False})
\ No newline at end of file diff --git a/osdf/apps/baseapp.py b/osdf/apps/baseapp.py new file mode 100644 index 0000000..1ad7556 --- /dev/null +++ b/osdf/apps/baseapp.py @@ -0,0 +1,222 @@ +# ------------------------------------------------------------------------- +# 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. +# +# ------------------------------------------------------------------------- +# + +""" +OSDF Manager Main Flask Application +""" + +import json +from optparse import OptionParser +import ssl +import sys +import time +import traceback + +from flask import Flask +from flask import g +from flask import request +from flask import Response +from onaplogging.mdcContext import MDC +from requests import RequestException +from schematics.exceptions import DataError + +import osdf.adapters.aaf.sms as sms +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import audit_log +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log +from osdf.operation.error_handling import internal_error_message +from osdf.operation.error_handling import request_exception_to_json_body +from osdf.operation.exceptions import BusinessException +import osdf.operation.responses +from osdf.utils.mdc_utils import clear_mdc +from osdf.utils.mdc_utils import get_request_id +from osdf.utils.mdc_utils import populate_default_mdc +from osdf.utils.mdc_utils import populate_mdc +from osdf.utils.mdc_utils import set_error_details + +ERROR_TEMPLATE = osdf.ERROR_TEMPLATE + +app = Flask(__name__) + +BAD_CLIENT_REQUEST_MESSAGE = 'Client sent an invalid request' + + +@app.errorhandler(BusinessException) +def handle_business_exception(e): + """An exception explicitly raised due to some business rule + + """ + error_log.error("Synchronous error for request id {} {}" + .format(g.request_id, traceback.format_exc())) + err_msg = ERROR_TEMPLATE.render(description=str(e)) + response = Response(err_msg, content_type='application/json; charset=utf-8') + response.status_code = 400 + return response + + +@app.errorhandler(RequestException) +def handle_request_exception(e): + """Returns a detailed synchronous message to the calling client when osdf fails due to a remote call to another system + + """ + error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) + err_msg = request_exception_to_json_body(e) + response = Response(err_msg, content_type='application/json; charset=utf-8') + response.status_code = 400 + return response + + +@app.errorhandler(DataError) +def handle_data_error(e): + """Returns a detailed message to the calling client when the initial synchronous message is invalid + + """ + error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) + + body_dictionary = { + "serviceException": { + "text": BAD_CLIENT_REQUEST_MESSAGE, + "exceptionMessage": str(e.errors), + "errorType": "InvalidClientRequest" + } + } + + body_as_json = json.dumps(body_dictionary) + response = Response(body_as_json, content_type='application/json; charset=utf-8') + response.status_code = 400 + return response + + +@app.before_request +def log_request(): + clear_mdc() + if request.content_type and 'json' in request.content_type: + populate_mdc(request) + g.request_id = get_request_id(request.get_json()) + log_message(json.dumps(request.get_json()), "INPROGRESS", 'ENTRY') + else: + populate_default_mdc(request) + log_message('', "INPROGRESS", 'ENTRY') + + +@app.after_request +def log_response(response): + log_response_data(response) + return response + + +def log_response_data(response): + status_value = '' + try: + status_value = map_status_value(response) + log_message(response.get_data(as_text=True), status_value, 'EXIT') + except Exception: + try: + set_default_audit_mdc(request, status_value, 'EXIT') + audit_log.info(response.get_data(as_text=True)) + except Exception: + set_error_details(300, 'Internal Error') + error_log.error("Error logging the response data due to {}".format(traceback.format_exc())) + + +def set_default_audit_mdc(request, status_value, p_marker): + MDC.put('partnerName', 'internal') + MDC.put('serviceName', request.path) + MDC.put('statusCode', status_value) + MDC.put('requestID', 'internal') + MDC.put('timer', int((time.process_time() - g.request_start) * 1000)) + MDC.put('customField1', p_marker) + + +def log_message(message, status_value, p_marker='INVOKE'): + MDC.put('statusCode', status_value) + MDC.put('customField1', p_marker) + MDC.put('timer', int((time.process_time() - g.request_start) * 1000)) + audit_log.info(message) + + +def map_status_value(response): + if 200 <= response.status_code < 300: + status_value = "COMPLETE" + else: + status_value = "ERROR" + return status_value + + +@app.errorhandler(500) +def internal_failure(error): + """Returned when unexpected coding errors occur during initial synchronous processing + + """ + error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) + response = Response(internal_error_message, content_type='application/json; charset=utf-8') + response.status_code = 500 + return response + + +def get_options(argv): + program_version_string = '%%prog %s' % "v1.0" + program_longdesc = "" + program_license = "" + + parser = OptionParser(version=program_version_string, epilog=program_longdesc, description=program_license) + parser.add_option("-l", "--local", dest="local", help="run locally", action="store_true", default=False) + parser.add_option("-t", "--devtest", dest="devtest", help="run in dev/test environment", action="store_true", + default=False) + parser.add_option("-d", "--debughost", dest="debughost", + help="IP Address of host running debug server", default='') + parser.add_option("-p", "--debugport", dest="debugport", + help="Port number of debug server", type=int, default=5678) + opts, args = parser.parse_args(argv) + + if opts.debughost: + debug_log.debug('pydevd.settrace({}, port={})'.format(opts.debughost, opts.debugport)) + # pydevd.settrace(opts.debughost, port=opts.debugport) + return opts + + +def build_ssl_context(): + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + ssl_context.set_ciphers('ECDHE-RSA-AES128-SHA256:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH') + ssl_context.load_cert_chain(sys_conf['ssl_context'][0], sys_conf['ssl_context'][1]) + return ssl_context + + +def run_app(): + global sys_conf + sys_conf = osdf_config['core']['osdf_system'] + ports = sys_conf['osdf_ports'] + internal_port, external_port = ports['internal'], ports['external'] + local_host = sys_conf['osdf_ip_default'] + common_app_opts = dict(host=local_host, threaded=True, use_reloader=False) + ssl_opts = sys_conf.get('ssl_context') + if ssl_opts: + common_app_opts.update({'ssl_context': build_ssl_context()}) + opts = get_options(sys.argv) + # Load secrets from SMS + sms.load_secrets() + if not opts.local and not opts.devtest: # normal deployment + app.run(port=internal_port, debug=False, **common_app_opts) + else: + port = internal_port if opts.local else external_port + app.run(port=port, debug=True, **common_app_opts) + + +if __name__ == "__main__": + run_app() diff --git a/osdf/cmd/encryptionUtil.py b/osdf/cmd/encryptionUtil.py new file mode 100644 index 0000000..6c0cae2 --- /dev/null +++ b/osdf/cmd/encryptionUtil.py @@ -0,0 +1,50 @@ +# +# ------------------------------------------------------------------------- +# Copyright (c) 2015-2018 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 sys +from osdf.utils import cipherUtils + + +def main(): + + if len(sys.argv) != 4: + print("Invalid input - usage --> (options(encrypt/decrypt) input-value with-key)") + return + + enc_dec = sys.argv[1] + valid_option_values = ['encrypt', 'decrypt'] + if enc_dec not in valid_option_values: + print("Invalid input - usage --> (options(encrypt/decrypt) input-value with-key)") + print("Option value can only be one of {}".format(valid_option_values)) + print("You entered '{}'".format(enc_dec)) + return + + input_string = sys.argv[2] + with_key = sys.argv[3] + + print("You've requested '{}' to be '{}ed' using key '{}'".format(input_string, enc_dec, with_key)) + print("You can always perform the reverse operation (encrypt/decrypt) using the same key" + "to be certain you get the same results back'") + + util = cipherUtils.AESCipher.get_instance(with_key) + if enc_dec.lower() == 'encrypt': + result = util.encrypt(input_string) + else: + result = util.decrypt(input_string) + + print("Your resultt: {}".format(result))
\ No newline at end of file diff --git a/osdf/config/__init__.py b/osdf/config/__init__.py index 86156a1..e32d44e 100644 --- a/osdf/config/__init__.py +++ b/osdf/config/__init__.py @@ -27,6 +27,6 @@ class CoreConfig(metaclass=MetaSingleton): def get_core_config(self, config_file=None): if self.core_config is None: - self.core_config = yaml.load(open(config_file)) + self.core_config = yaml.safe_load(open(config_file)) return self.core_config diff --git a/osdf/config/base.py b/osdf/config/base.py index fbe9315..be693cb 100644 --- a/osdf/config/base.py +++ b/osdf/config/base.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,16 +18,21 @@ # import os - +from osdf.config.consulconfig import call_consul_kv import osdf.config.credentials as creds import osdf.config.loader as config_loader from osdf.utils.programming_utils import DotDict +from threading import Thread config_spec = { "deployment": os.environ.get("OSDF_CONFIG_FILE", "config/osdf_config.yaml"), "core": "config/common_config.yaml" } +slicing_spec = "config/slicing_config.yaml" + +slice_config = config_loader.load_config_file(slicing_spec) + osdf_config = DotDict(config_loader.all_configs(**config_spec)) http_basic_auth_credentials = creds.load_credentials(osdf_config) @@ -34,3 +40,10 @@ http_basic_auth_credentials = creds.load_credentials(osdf_config) dmaap_creds = creds.dmaap_creds() creds_prefixes = {"so": "so", "cm": "cmPortal", "pcih": "pciHMS"} + +osdf_config_deployment = osdf_config.deployment + + +if osdf_config.deployment.get('activateConsulConfig'): + consulthread = Thread(target=call_consul_kv, args=(osdf_config,)) + consulthread.start() diff --git a/osdf/config/consulconfig.py b/osdf/config/consulconfig.py new file mode 100644 index 0000000..fc5b3fe --- /dev/null +++ b/osdf/config/consulconfig.py @@ -0,0 +1,52 @@ +from consul.base import Timeout +from consul.tornado import Consul +import json +from osdf.logging.osdf_logging import debug_log +from tornado.gen import coroutine +from tornado.ioloop import IOLoop + + +class Config(object): + def __init__(self, loop, osdf_final_config): + self.config = osdf_final_config + osdf_config = self.config['osdf_config'] + self.consul = Consul(host=osdf_config['consulHost'], port=osdf_config['consulPort'], + scheme=osdf_config['consulScheme'], verify=osdf_config['consulVerify'], + cert=osdf_config['consulCert']) + result = json.dumps(self.config) + self.consul.kv.put("osdfconfiguration", result) + loop.add_callback(self.watch) + + @coroutine + def watch(self): + index = None + while True: + try: + index, data = yield self.consul.kv.get('osdfconfiguration', index=index) + if data is not None: + self.update_config(data) + except Timeout: + pass + except Exception as e: + debug_log.debug('Exception Encountered {}'.format(e)) + + def update_config(self, data): + new_config = json.loads(data['Value'].decode('utf-8')) + osdf_deployment = new_config['osdf_config'] + osdf_core = new_config['common_config'] + self.config['osdf_config'].update(osdf_deployment) + self.config['common_config'].update(osdf_core) + debug_log.debug("updated config {}".format(new_config)) + debug_log.debug("value changed") + + +def call_consul_kv(osdf_config): + osdf_final_config = { + 'osdf_config': osdf_config.deployment, + 'common_config': osdf_config.core + } + io_loop = IOLoop() + io_loop.make_current() + IOLoop.current(instance=False) + _ = Config(io_loop, osdf_final_config) + io_loop.start() diff --git a/osdf/config/loader.py b/osdf/config/loader.py index 7cb363a..dca0033 100644 --- a/osdf/config/loader.py +++ b/osdf/config/loader.py @@ -31,7 +31,7 @@ def load_config_file(config_file: str, child_name="dockerConfiguration") -> dict with open(config_file, 'r') as fid: res = {} if config_file.endswith(".yaml"): - res = yaml.load(fid) + res = yaml.safe_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 diff --git a/osdf/logging/__init__.py b/osdf/logging/__init__.py index 4b25e5b..df7613d 100644 --- a/osdf/logging/__init__.py +++ b/osdf/logging/__init__.py @@ -15,3 +15,7 @@ # # ------------------------------------------------------------------------- # + +import yaml + +yaml.warnings({'YAMLLoadWarning': False}) diff --git a/osdf/logging/monkey.py b/osdf/logging/monkey.py new file mode 100644 index 0000000..f6041bc --- /dev/null +++ b/osdf/logging/monkey.py @@ -0,0 +1,35 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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. +# +# ------------------------------------------------------------------------- +# + + +__all__ = ["patch_all"] + +from onaplogging.logWatchDog import patch_loggingYaml + +from osdf.logging.oof_mdc_context import patch_logging_mdc + + +def patch_all(mdc=True, yaml=True): + """monkey patch osdf logging to enable mdc + + """ + if mdc is True: + patch_logging_mdc() + + if yaml is True: + patch_loggingYaml() diff --git a/osdf/logging/onap_common_v1/CommonLogger.py b/osdf/logging/onap_common_v1/CommonLogger.py deleted file mode 100755 index 6572d6f..0000000 --- a/osdf/logging/onap_common_v1/CommonLogger.py +++ /dev/null @@ -1,900 +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. -# -# ------------------------------------------------------------------------- -# - -"""ONAP Common Logging library in Python.""" - -#!/usr/bin/python -# -*- indent-tabs-mode: nil -*- vi: set expandtab: - - -from __future__ import print_function -import os, sys, getopt, logging, logging.handlers, time, re, uuid, socket, threading - -class CommonLogger: - """ONAP Common Logging object. - - Public methods: - __init__ - setFields - debug - info - warn - error - fatal - """ - - UnknownFile = -1 - ErrorFile = 0 - DebugFile = 1 - AuditFile = 2 - MetricsFile = 3 - DateFmt = '%Y-%m-%dT%H:%M:%S' - verbose = False - - def __init__(self, configFile, logKey, **kwargs): - """Construct a Common Logger for one Log File. - - Arguments: - configFile -- configuration filename. - logKey -- the keyword in configFile that identifies the log filename. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages, - one of CommonLogger.ErrorFile, CommonLogger.DebugFile, - CommonLogger.AuditFile and CommonLogger.MetricsFile, or - one of the strings "error", "debug", "audit" or "metrics". - May also be set in the config file using a field named - <logKey>Style (where <logKey> is the value of the logKey - parameter). The keyword value overrides the value in the - config file. - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - self._monitorFlag = False - - # Get configuration parameters - self._logKey = str(logKey) - self._configFile = str(configFile) - self._rotateMethod = 'time' - self._timeRotateIntervalType = 'midnight' - self._timeRotateInterval = 1 - self._sizeMaxBytes = 0 - self._sizeRotateMode = 'a' - self._socketHost = None - self._socketPort = 0 - self._typeLogger = 'filelogger' - self._backupCount = 6 - self._logLevelThreshold = self._intLogLevel('') - self._logFile = None - self._begTime = None - self._begMsec = 0 - self._fields = {} - self._fields["style"] = CommonLogger.UnknownFile - try: - self._configFileModified = os.path.getmtime(self._configFile) - for line in open(self._configFile): - line = line.split('#',1)[0] # remove comments - if '=' in line: - key, value = [x.strip() for x in line.split('=',1)] - if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none']: - self._rotateMethod = value.lower() - elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']: - self._timeRotateIntervalType = value - elif key == 'timeRotateInterval' and int( value ) > 0: - self._timeRotateInterval = int( value ) - elif key == 'sizeMaxBytes' and int( value ) >= 0: - self._sizeMaxBytes = int( value ) - elif key == 'sizeRotateMode' and value in ['a']: - self._sizeRotateMode = value - elif key == 'backupCount' and int( value ) >= 0: - self._backupCount = int( value ) - elif key == self._logKey + 'SocketHost': - self._socketHost = value - elif key == self._logKey + 'SocketPort' and int( value ) == 0: - self._socketPort = int( value ) - elif key == self._logKey + 'LogType' and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']: - self._typeLogger = value.lower() - elif key == self._logKey + 'LogLevel': - self._logLevelThreshold = self._intLogLevel(value.upper()) - elif key == self._logKey + 'Style': - self._fields["style"] = value - elif key == self._logKey: - self._logFile = value - except Exception as x: - print("exception reading '%s' configuration file: %s" %(self._configFile, str(x)), file=sys.stderr) - sys.exit(2) - except: - print("exception reading '%s' configuration file" %(self._configFile), file=sys.stderr) - sys.exit(2) - - if self._logFile is None: - print('configuration file %s is missing definition %s for log file' %(self._configFile, self._logKey), file=sys.stderr) - sys.exit(2) - - - # initialize default log fields - # timestamp will automatically be generated - for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \ - 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \ - 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \ - 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \ - 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \ - 'errorDescription' ]: - if key in kwargs and kwargs[key] != None: - self._fields[key] = kwargs[key] - - self._resetStyleField() - - # Set up logger - self._logLock = threading.Lock() - with self._logLock: - self._logger = logging.getLogger(self._logKey) - self._logger.propagate = False - self._createLogger() - - self._defaultServerInfo() - - # spawn a thread to monitor configFile for logLevel and logFile changes - self._monitorFlag = True - self._monitorThread = threading.Thread(target=self._monitorConfigFile, args=()) - self._monitorThread.daemon = True - self._monitorThread.start() - - - def _createLogger(self): - if self._typeLogger == 'filelogger': - self._mkdir_p(self._logFile) - if self._rotateMethod == 'time': - self._logHandler = logging.handlers.TimedRotatingFileHandler(self._logFile, \ - when=self._timeRotateIntervalType, interval=self._timeRotateInterval, \ - backupCount=self._backupCount, encoding=None, delay=False, utc=True) - elif self._rotateMethod == 'size': - self._logHandler = logging.handlers.RotatingFileHandler(self._logFile, \ - mode=self._sizeRotateMode, maxBytes=self._sizeMaxBytes, \ - backupCount=self._backupCount, encoding=None, delay=False) - - else: - self._logHandler = logging.handlers.WatchedFileHandler(self._logFile, \ - mode=self._sizeRotateMode, \ - encoding=None, delay=False) - elif self._typeLogger == 'stderrlogger': - self._logHandler = logging.handlers.StreamHandler(sys.stderr) - elif self._typeLogger == 'stdoutlogger': - self._logHandler = logging.handlers.StreamHandler(sys.stdout) - elif self._typeLogger == 'socketlogger': - self._logHandler = logging.handlers.SocketHandler(self._socketHost, self._socketPort) - elif self._typeLogger == 'nulllogger': - self._logHandler = logging.handlers.NullHandler() - - if self._fields["style"] == CommonLogger.AuditFile or self._fields["style"] == CommonLogger.MetricsFile: - self._logFormatter = logging.Formatter(fmt='%(begtime)s,%(begmsecs)03d+00:00|%(endtime)s,%(endmsecs)03d+00:00|%(message)s', datefmt=CommonLogger.DateFmt) - else: - self._logFormatter = logging.Formatter(fmt='%(asctime)s,%(msecs)03d+00:00|%(message)s', datefmt='%Y-%m-%dT%H:%M:%S') - self._logFormatter.converter = time.gmtime - self._logHandler.setFormatter(self._logFormatter) - self._logger.addHandler(self._logHandler) - - def _resetStyleField(self): - styleFields = ["error", "debug", "audit", "metrics"] - if self._fields['style'] in styleFields: - self._fields['style'] = styleFields.index(self._fields['style']) - - def __del__(self): - if self._monitorFlag == False: - return - - self._monitorFlag = False - - if self._monitorThread is not None and self._monitorThread.is_alive(): - self._monitorThread.join() - - self._monitorThread = None - - - def _defaultServerInfo(self): - - # If not set or purposely set = None, then set default - if self._fields.get('server') is None: - try: - self._fields['server'] = socket.getfqdn() - except Exception as err: - try: - self._fields['server'] = socket.gethostname() - except Exception as err: - self._fields['server'] = "" - - # If not set or purposely set = None, then set default - if self._fields.get('serverIPAddress') is None: - try: - self._fields['serverIPAddress'] = socket.gethostbyname(self._fields['server']) - except Exception as err: - self._fields['serverIPAddress'] = "" - - - def _monitorConfigFile(self): - while self._monitorFlag: - try: - fileTime = os.path.getmtime(self._configFile) - if fileTime > self._configFileModified: - self._configFileModified = fileTime - ReopenLogFile = False - logFile = self._logFile - with open(self._configFile) as fp: - for line in fp: - line = line.split('#',1)[0] # remove comments - if '=' in line: - key, value = [x.strip() for x in line.split('=',1)] - if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none'] and self._rotateMethod != value: - self._rotateMethod = value.lower() - ReopenLogFile = True - elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']: - self._timeRotateIntervalType = value - ReopenLogFile = True - elif key == 'timeRotateInterval' and int( value ) > 0: - self._timeRotateInterval = int( value ) - ReopenLogFile = True - elif key == 'sizeMaxBytes' and int( value ) >= 0: - self._sizeMaxBytes = int( value ) - ReopenLogFile = True - elif key == 'sizeRotateMode' and value in ['a']: - self._sizeRotateMode = value - ReopenLogFile = True - elif key == 'backupCount' and int( value ) >= 0: - self._backupCount = int( value ) - ReopenLogFile = True - elif key == self._logKey + 'SocketHost' and self._socketHost != value: - self._socketHost = value - ReopenLogFile = True - elif key == self._logKey + 'SocketPort' and self._socketPort > 0 and self._socketPort != int( value ): - self._socketPort = int( value ) - ReopenLogFile = True - elif key == self._logKey + 'LogLevel' and self._logLevelThreshold != self._intLogLevel( value.upper() ): - self._logLevelThreshold = self._intLogLevel(value.upper()) - elif key == self._logKey + 'LogType' and self._typeLogger != value and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']: - self._typeLogger = value.lower() - ReopenLogFile = True - elif key == self._logKey + 'Style': - self._fields["style"] = value - self._resetStyleField() - elif key == self._logKey and self._logFile != value: - logFile = value - ReopenLogFile = True - if ReopenLogFile: - with self._logLock: - self._logger.removeHandler(self._logHandler) - self._logFile = logFile - self._createLogger() - except Exception as err: - pass - - time.sleep(5) - - - def setFields(self, **kwargs): - """Set default values for log fields. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \ - 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \ - 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \ - 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \ - 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \ - 'errorDescription' ]: - if key in kwargs: - if kwargs[key] != None: - self._fields[key] = kwargs[key] - elif key in self._fields: - del self._fields[key] - - self._defaultServerInfo() - - - def debug(self, message, **kwargs): - """Write a DEBUG level message to the log file. - - Arguments: - message -- value for the last log record field. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - self._log('DEBUG', message, errorCategory = 'DEBUG', **kwargs) - - def info(self, message, **kwargs): - """Write an INFO level message to the log file. - - Arguments: - message -- value for the last log record field. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - self._log('INFO', message, errorCategory = 'INFO', **kwargs) - - def warn(self, message, **kwargs): - """Write a WARN level message to the log file. - - Arguments: - message -- value for the last log record field. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - self._log('WARN', message, errorCategory = 'WARN', **kwargs) - - def error(self, message, **kwargs): - """Write an ERROR level message to the log file. - - Arguments: - message -- value for the last log record field. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - self._log('ERROR', message, errorCategory = 'ERROR', **kwargs) - - def fatal(self, message, **kwargs): - """Write a FATAL level message to the log file. - - Arguments: - message -- value for the last log record field. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - self._log('FATAL', message, errorCategory = 'FATAL', **kwargs) - - def _log(self, logLevel, message, **kwargs): - """Write a message to the log file. - - Arguments: - logLevel -- value ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', ...) for the log record. - message -- value for the last log record field. - - Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error - style -- the log file format (style) to use when writing log messages - requestID (dame) -- optional default value for this log record field. - serviceInstanceID (am) -- optional default value for this log record field. - threadID (am) -- optional default value for this log record field. - serverName (am) -- optional default value for this log record field. - serviceName (am) -- optional default value for this log record field. - instanceUUID (am) -- optional default value for this log record field. - severity (am) -- optional default value for this log record field. - serverIPAddress (am) -- optional default value for this log record field. - server (am) -- optional default value for this log record field. - IPAddress (am) -- optional default value for this log record field. - className (am) -- optional default value for this log record field. - timer (am) -- (ElapsedTime) optional default value for this log record field. - partnerName (ame) -- optional default value for this log record field. - targetEntity (me) -- optional default value for this log record field. - targetServiceName (me) -- optional default value for this log record field. - statusCode (am) -- optional default value for this log record field. - responseCode (am) -- optional default value for this log record field. - responseDescription (am) -- optional default value for this log record field. - processKey (am) -- optional default value for this log record field. - targetVirtualEntity (m) -- optional default value for this log record field. - customField1 (am) -- optional default value for this log record field. - customField2 (am) -- optional default value for this log record field. - customField3 (am) -- optional default value for this log record field. - customField4 (am) -- optional default value for this log record field. - errorCategory (e) -- optional default value for this log record field. - errorCode (e) -- optional default value for this log record field. - errorDescription (e) -- optional default value for this log record field. - - Note: the pipe '|' character is not allowed in any log record field. - """ - - # timestamp will automatically be inserted - style = int(self._getVal('style', '', **kwargs)) - requestID = self._getVal('requestID', '', **kwargs) - serviceInstanceID = self._getVal('serviceInstanceID', '', **kwargs) - threadID = self._getVal('threadID', threading.currentThread().getName(), **kwargs) - serverName = self._getVal('serverName', '', **kwargs) - serviceName = self._getVal('serviceName', '', **kwargs) - instanceUUID = self._getVal('instanceUUID', '', **kwargs) - upperLogLevel = self._noSep(logLevel.upper()) - severity = self._getVal('severity', '', **kwargs) - serverIPAddress = self._getVal('serverIPAddress', '', **kwargs) - server = self._getVal('server', '', **kwargs) - IPAddress = self._getVal('IPAddress', '', **kwargs) - className = self._getVal('className', '', **kwargs) - timer = self._getVal('timer', '', **kwargs) - partnerName = self._getVal('partnerName', '', **kwargs) - targetEntity = self._getVal('targetEntity', '', **kwargs) - targetServiceName = self._getVal('targetServiceName', '', **kwargs) - statusCode = self._getVal('statusCode', '', **kwargs) - responseCode = self._getVal('responseCode', '', **kwargs) - responseDescription = self._noSep(self._getVal('responseDescription', '', **kwargs)) - processKey = self._getVal('processKey', '', **kwargs) - targetVirtualEntity = self._getVal('targetVirtualEntity', '', **kwargs) - customField1 = self._getVal('customField1', '', **kwargs) - customField2 = self._getVal('customField2', '', **kwargs) - customField3 = self._getVal('customField3', '', **kwargs) - customField4 = self._getVal('customField4', '', **kwargs) - errorCategory = self._getVal('errorCategory', '', **kwargs) - errorCode = self._getVal('errorCode', '', **kwargs) - errorDescription = self._noSep(self._getVal('errorDescription', '', **kwargs)) - - detailMessage = self._noSep(message) - if bool(re.match(r" *$", detailMessage)): - return # don't log empty messages - - useLevel = self._intLogLevel(upperLogLevel) - if CommonLogger.verbose: print("logger STYLE=%s" % style) - if useLevel < self._logLevelThreshold: - if CommonLogger.verbose: print("skipping because of level") - pass - else: - with self._logLock: - if style == CommonLogger.ErrorFile: - if CommonLogger.verbose: print("using CommonLogger.ErrorFile") - self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ - %(requestID, threadID, serviceName, partnerName, targetEntity, targetServiceName, - errorCategory, errorCode, errorDescription, detailMessage)) - elif style == CommonLogger.DebugFile: - if CommonLogger.verbose: print("using CommonLogger.DebugFile") - self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ - %(requestID, threadID, serverName, serviceName, instanceUUID, upperLogLevel, - severity, serverIPAddress, server, IPAddress, className, timer, detailMessage)) - elif style == CommonLogger.AuditFile: - if CommonLogger.verbose: print("using CommonLogger.AuditFile") - endAuditTime, endAuditMsec = self._getTime() - if self._begTime is not None: - d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec } - else: - d = { 'begtime': endAuditTime, 'begmsecs': endAuditMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec } - self._begTime = None - unused = "" - self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ - %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName, - statusCode, responseCode, responseDescription, instanceUUID, upperLogLevel, - severity, serverIPAddress, timer, server, IPAddress, className, unused, - processKey, customField1, customField2, customField3, customField4, detailMessage), extra=d) - elif style == CommonLogger.MetricsFile: - if CommonLogger.verbose: print("using CommonLogger.MetricsFile") - endMetricsTime, endMetricsMsec = self._getTime() - if self._begTime is not None: - d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec } - else: - d = { 'begtime': endMetricsTime, 'begmsecs': endMetricsMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec } - self._begTime = None - unused = "" - self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \ - %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName, - targetEntity, targetServiceName, statusCode, responseCode, responseDescription, - instanceUUID, upperLogLevel, severity, serverIPAddress, timer, server, IPAddress, - className, unused, processKey, targetVirtualEntity, customField1, customField2, - customField3, customField4, detailMessage), extra=d) - else: - print("!!!!!!!!!!!!!!!! style not set: %s" % self._fields["style"]) - - def _getTime(self): - ct = time.time() - lt = time.localtime(ct) - return (time.strftime(CommonLogger.DateFmt, lt), (ct - int(ct)) * 1000) - - def setStartRecordEvent(self): - """ - Set the start time to be saved for both audit and metrics records - """ - self._begTime, self._begMsec = self._getTime() - - def _getVal(self, key, default, **kwargs): - val = self._fields.get(key) - if key in kwargs: val = kwargs[key] - if val is None: val = default - return self._noSep(val) - - def _noSep(self, message): - if message is None: return '' - return re.sub(r'[\|\n]', ' ', str(message)) - - def _intLogLevel(self, logLevel): - if logLevel == 'FATAL': useLevel = 50 - elif logLevel == 'ERROR': useLevel = 40 - elif logLevel == 'WARN': useLevel = 30 - elif logLevel == 'INFO': useLevel = 20 - elif logLevel == 'DEBUG': useLevel = 10 - else: useLevel = 0 - return useLevel - - def _mkdir_p(self, filename): - """Create missing directories from a full filename path like mkdir -p""" - - if filename is None: - return - - folder=os.path.dirname(filename) - - if folder == "": - return - - if not os.path.exists(folder): - try: - os.makedirs(folder) - except OSError as err: - print("error number %d creating %s directory to hold %s logfile: %s" %(err.errno, err.filename, filename, err.strerror), file=sys.stderr) - sys.exit(2) - except Exception as err: - print("error creating %s directory to hold %s logfile: %s" %(folder, filename, str(err)), file=sys.stderr) - sys.exit(2) - -def __checkTime1(line): - format = r'[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9][+]00:00[|]' - format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[+]00:00[|]' - m = re.match(format, line) - if not m: - print("ERROR: time string did not match proper time format, %s" %line) - print("\t: format=%s" % format) - return 1 - return 0 - -def __checkTime2(line, different): - format = '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|][0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|]' - format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|][0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|]' - m = re.match(format, line) - if not m: - print("ERROR: time strings did not match proper time format, %s" %line) - print("\t: format=%s" % format) - return 1 - second1 = int(m.group(1)) - msec1 = int(m.group(2)) - second2 = int(m.group(3)) - msec2 = int(m.group(4)) - if second1 > second2: second2 += 60 - t1 = second1 * 1000 + msec1 - t2 = second2 * 1000 + msec2 - diff = t2 - t1 - # print("t1=%d (%d,%d) t2=%d (%d,%d), diff = %d" % (t1, second1, msec1, t2, second2, msec2, diff)) - if different: - if diff < 500: - print("ERROR: times did not differ enough: %s" % line) - return 1 - else: - if diff > 10: - print("ERROR: times were too far apart: %s" % line) - return 1 - return 0 - -def __checkLog(logfile, numLines, numFields): - lineCount = 0 - errorCount = 0 - with open(logfile, "r") as fp: - for line in fp: - # print("saw line %s" % line) - lineCount += 1 - c = line.count('|') - if c != numFields: - print("ERROR: wrong number of fields. Expected %d, got %d: %s" % (numFields, c, line)) - errorCount += 1 - if re.search("should not appear", line): - print("ERROR: a line appeared that should not have appeared, %s" % line) - errorCount += 1 - elif re.search("single time", line): - errorCount += __checkTime1(line) - elif re.search("time should be the same", line): - errorCount += __checkTime2(line, different=False) - elif re.search("time should be different", line): - errorCount += __checkTime2(line, different=True) - else: - print("ERROR: an unknown message appeared, %s" % line) - errorCount += 1 - - if lineCount != numLines: - print("ERROR: expected %d lines, but got %d lines" % (numLines, lineCount)) - errorCount += 1 - return errorCount - -if __name__ == "__main__": - import os, argparse - parser = argparse.ArgumentParser(description="test the CommonLogger functions") - parser.add_argument("-k", "--keeplogs", help="Keep the log files after finishing the tests", action="store_true") - parser.add_argument("-v", "--verbose", help="Print debugging messages", action="store_true") - args = parser.parse_args() - - spid = str(os.getpid()) - if args.keeplogs: - spid = "" - logcfg = "/tmp/cl.log" + spid + ".cfg" - errorLog = "/tmp/cl.error" + spid + ".log" - metricsLog = "/tmp/cl.metrics" + spid + ".log" - auditLog = "/tmp/cl.audit" + spid + ".log" - debugLog = "/tmp/cl.debug" + spid + ".log" - if args.verbose: CommonLogger.verbose = True - - import atexit - def cleanupTmps(): - for f in [ logcfg, errorLog, metricsLog, auditLog, debugLog ]: - try: - os.remove(f) - except: - pass - if not args.keeplogs: - atexit.register(cleanupTmps) - - with open(logcfg, "w") as o: - o.write("error = " + errorLog + "\n" + - "errorLogLevel = WARN\n" + - "metrics = " + metricsLog + "\n" + - "metricsLogLevel = INFO\n" + - "audit = " + auditLog + "\n" + - "auditLogLevel = INFO\n" + - "debug = " + debugLog + "\n" + - "debugLogLevel = DEBUG\n") - - import uuid - instanceUUID = uuid.uuid1() - serviceName = "testharness" - errorLogger = CommonLogger(logcfg, "error", style=CommonLogger.ErrorFile, instanceUUID=instanceUUID, serviceName=serviceName) - debugLogger = CommonLogger(logcfg, "debug", style=CommonLogger.DebugFile, instanceUUID=instanceUUID, serviceName=serviceName) - auditLogger = CommonLogger(logcfg, "audit", style=CommonLogger.AuditFile, instanceUUID=instanceUUID, serviceName=serviceName) - metricsLogger = CommonLogger(logcfg, "metrics", style=CommonLogger.MetricsFile, instanceUUID=instanceUUID, serviceName=serviceName) - - testsRun = 0 - errorCount = 0 - errorLogger.debug("error calling debug (should not appear)") - errorLogger.info("error calling info (should not appear)") - errorLogger.warn("error calling warn (single time)") - errorLogger.error("error calling error (single time)") - errorLogger.setStartRecordEvent() - time.sleep(1) - errorLogger.fatal("error calling fatal, after setStartRecordEvent and sleep (start should be ignored, single time)") - testsRun += 6 - errorCount += __checkLog(errorLog, 3, 10) - - auditLogger.debug("audit calling debug (should not appear)") - auditLogger.info("audit calling info (time should be the same)") - auditLogger.warn("audit calling warn (time should be the same)") - auditLogger.error("audit calling error (time should be the same)") - auditLogger.setStartRecordEvent() - time.sleep(1) - auditLogger.fatal("audit calling fatal, after setStartRecordEvent and sleep, time should be different)") - testsRun += 6 - errorCount += __checkLog(auditLog, 4, 25) - - debugLogger.debug("debug calling debug (single time)") - debugLogger.info("debug calling info (single time)") - debugLogger.warn("debug calling warn (single time)") - debugLogger.setStartRecordEvent() - time.sleep(1) - debugLogger.error("debug calling error, after SetStartRecordEvent and sleep (start should be ignored, single time)") - debugLogger.fatal("debug calling fatal (single time)") - errorCount += __checkLog(debugLog, 5, 13) - testsRun += 6 - - metricsLogger.debug("metrics calling debug (should not appear)") - metricsLogger.info("metrics calling info (time should be the same)") - metricsLogger.warn("metrics calling warn (time should be the same)") - metricsLogger.setStartRecordEvent() - time.sleep(1) - metricsLogger.error("metrics calling error, after SetStartRecordEvent and sleep, time should be different") - metricsLogger.fatal("metrics calling fatal (time should be the same)") - testsRun += 6 - errorCount += __checkLog(metricsLog, 4, 28) - - print("%d tests run, %d errors found" % (testsRun, errorCount)) diff --git a/osdf/logging/onap_common_v1/CommonLogger_test.config b/osdf/logging/onap_common_v1/CommonLogger_test.config deleted file mode 100755 index 584fb5e..0000000 --- a/osdf/logging/onap_common_v1/CommonLogger_test.config +++ /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. -# -# ------------------------------------------------------------------------- -# - -# You may change this file while your program is running and CommonLogger will automatically reconfigure accordingly. -# Changing these parameters may leave old log files lying around. - - -#--- Parameters that apply to all logs -# -# rotateMethod: time, size, stdout, stderr, none -#... Note: the following two parameters apply only when rotateMethod=time -# timeRotateIntervalType: S, M, H, D, W0 - W6, or midnight (seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC) -# timeRotateInterval: >= 1 (1 means every timeRotateIntervalType, 2 every other, 3 every third, etc.) -#... Note: the following parameter applies only when rotateMethod=size -# sizeMaxBytes: >= 0 (0 means no limit, else maximum filesize in Bytes) -# backupCount: >= 0 (Number of rotated backup files to retain. If rotateMethod=time, 0 retains *all* backups. If rotateMethod=size, 0 retains *no* backups.) -# -rotateMethod = time -timeRotateIntervalType = midnight -timeRotateInterval = 1 -sizeMaxBytes = 0 -backupCount = 6 - - -#--- Parameters that define log filenames and their initial LogLevel threshold -#... Note: CommonLogger will exit if your process does not have permission to write to the file. -# - -error = /opt/logs/oof/error.log -errorLogLevel = WARN -errorStyle = error - -metrics = /opt/logs/oof/metrics.log -metricsLogLevel = INFO -metricsStyle = metrics - -audit = /opt/logs/oof/audit.log -auditLogLevel = INFO -auditStyle = audit - -debug = /opt/logs/oof/debug.log -debugLogLevel = DEBUG -debugStyle = debug diff --git a/osdf/logging/onap_common_v1/CommonLogger_testing.py b/osdf/logging/onap_common_v1/CommonLogger_testing.py deleted file mode 100755 index 43e0ec3..0000000 --- a/osdf/logging/onap_common_v1/CommonLogger_testing.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/python - -# ------------------------------------------------------------------------- -# 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. -# -# ------------------------------------------------------------------------- -# -""" -Test the ONAP Common Logging library in Python. -CommonLogger_test.py -""" - - -from __future__ import print_function # for the example code below parsing command line options -import os, sys, getopt # for the example code below parsing command line options - -from osdf.logging.onap_common_v1.CommonLogger import CommonLogger # all that is needed to import the CommonLogger library - -import uuid # to create UUIDs for our log records -import time # to create elapsed time for our log records - - -#----- A client might want to allow specifying the configFile as a command line option -usage="usage: %s [ -c <configFile> ]" % ( os.path.basename(__file__) ) -try: - opts, args = getopt.getopt(sys.argv[1:], "c:") -except getopt.GetoptError: - print(usage, file=sys.stderr) - sys.exit(2) - -configFile = "CommonLogger_test.config" -for opt, arg in opts: - if opt == "-c": - configFile = arg - else: - print(usage, file=sys.stderr) - sys.exit(2) - - -#----- Instantiate the loggers - -# The client's top-level program (e.g., vPRO.py) can create a unique identifier UUID to differentiate between multiple instances of itself. -instanceUUID = uuid.uuid1() - -# The client should identify its ONAP component -- and if applicable -- its ONAP sub-component -serviceName = "DCAE/vPRO" - -# Instantiate using a configuration file with a key specifying the log file name and set fields' default values -errorLog = CommonLogger.CommonLogger(configFile, "error", instanceUUID=instanceUUID, serviceName=serviceName) -metricsLog = CommonLogger.CommonLogger(configFile, "metrics", instanceUUID=instanceUUID, serviceName=serviceName) -auditLog = CommonLogger.CommonLogger(configFile, "audit", instanceUUID=instanceUUID, serviceName=serviceName) -debugLog = CommonLogger.CommonLogger(configFile, "debug", instanceUUID=instanceUUID, serviceName=serviceName) - - -#----- use the loggers - -# both metrics and audit logs can have an event starting time. This only affects the next log message. -metricsLog.setStartRecordEvent() -auditLog.setStartRecordEvent() - -# Simple log messages -debugLog.debug("a DEBUG message for the debug log") -metricsLog.info("an INFO message for the metrics log") -auditLog.info("an INFO message for the audit log") -errorLog.warn("a WARN message for the error log") -errorLog.error("an ERROR message for the error log") -errorLog.fatal("a FATAL message for the error log") - - -# Can override any of the other fields when writing each log record -debugLog.debug("demonstrating overriding all fields with atypical values", requestID="2", serviceInstanceID="3", threadID="4", serverName="5", serviceName="6", instanceUUID="7", severity="9", serverIPAddress="10", server="11", IPAddress="12", className="13", timer="14") - - -# The is an example of an interaction between two ONAP components: - -# vPRO generates Closed Loop RESTful API requests to App-C, knowing this information: -requestClient = "netman@localdcae.att.com:~/vPRO_trinity/vPRO.py:905" # uniquely identifies the requester -requestTime = "2015-08-20 20:57:14.463426" # unique ID of the request within the requester's scope -request = "Restart" - -# Form the value for Common Logging's requestID field: -requestID = requestClient + "+" + requestTime # vPRO will use this as the unique requestID -# requestID = uuid.uuid1() # other services might generate a UUID as their requestID - -# Form the value for Common Logging's serviceName field when an interaction between two ONAP components: -ourONAP = serviceName -peerONAP = "App-C" -operation = request -interaction = ourONAP + ":" + peerONAP + "." + operation - -# Let's calculate and report elapsed times -start = time.time() - -# Log the request -auditLog.info("Requesting %s to %s" %(peerONAP, operation), requestID=requestID, serviceName=interaction) - -# Wait for first response -time.sleep(1) # simulate processing the action, e.g., waiting for response from App-C - -# Form the value for Common Logging's serviceName field when an interaction between two ONAP components: -operation = 'PENDING' -interaction = peerONAP + ":" + ourONAP + "." + operation - -# Log the response with elapsed time -ms = int(round(1000 * (time.time() - start))) # Calculate elapsed time in ms -auditLog.info("%s acknowledged receiving request for %s" %(peerONAP, operation), requestID=requestID, serviceName=interaction, timer=ms) - -# Wait for next response -time.sleep(1) # simulate processing the action, e.g., waiting for response from App-C - -# Form the value for Common Logging's serviceName field when an interaction between two ONAP components: -operation = 'SUCCESS' -interaction = peerONAP + ":" + ourONAP + "." + operation - -# Log the response with elapsed time -ms = int(round(1000 * (time.time() - start))) # Calculate elapsed time in ms -auditLog.info("%s finished %s" %(peerONAP, operation), requestID=requestID, serviceName=interaction, timer=ms) - - -# Can change the fields' default values for a logger after instantiation if desired -debugLog.setFields(serviceName="DCAE", threadID='thread-2') - -# Then subsequent logging will have the new default field values -debugLog.info("Something happened") -debugLog.warn("Something happened again") - - -# Unset (set=None) a field so the Common Logger will use the default value -debugLog.info("threadID should be default", threadID=None) -debugLog.setFields(threadID=None) -debugLog.info("threadID should be default") diff --git a/osdf/logging/onap_common_v1/README.md b/osdf/logging/onap_common_v1/README.md deleted file mode 100755 index 596cd7f..0000000 --- a/osdf/logging/onap_common_v1/README.md +++ /dev/null @@ -1,214 +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. -# -# ------------------------------------------------------------------------- -# - -# Common Logging Wrapper for Python - -* CommonLogger.py is the module (library) to import -* CommonLogger_test.config is an example configuration file used by CommonLogger_test.py -* CommonLogger_test.py is an example of how to import and use the CommonLogger module - -## Configuration File - -Configure common logging for a python application in a configuration file. -In the file, put key = value assignments - -* defining the filename for each log file you will create, such as -'error=/path/error.log', 'metrics=/path/metrics.log', 'audit=/path/audit.log', -and 'debug=/path/debug.log'. -The name used (shown here as 'error', 'metrics', etc.) is chosen in the program, allowing a single configuration file to be -used by numerous different programs. -(It will be referred to below as <logKey>.) -* defining the style of the log messages to be produced, -using <logKey> suffixed with 'Style', as in 'errorStyle=', and one of the -words 'error', 'metrics', 'audit' and 'debug'. -* defining the minimum level of log messages to be retained in a log file, -using <logKey> suffixed with 'LogLevel', as in 'errorLogLevel=WARN'. -The levels are DEBUG, INFO, WARN, ERROR, and FATAL. -So specifying WARN will retain only WARN, ERROR, and FATAL level -log messages, while specifying DEBUG will retain all levels of log messages: -DEBUG, INFO, WARN, ERROR, and FATAL. - -Comments may be included on any line following a '#' character. - -Common logging monitors the configuration file so if the file is edited -and any its values change, then common logging will implement the changes -in the running application. -This enables operations to change log levels or even log filenames without -interrupting the running application. - -By default, log files are rotated daily at midnight UTC, retaining 6 backup versions by default. - -Other strategies can be specified within the configuration file using the keywords: - -* rotateMethod = one of 'time', 'size', and 'none' (case insensitive) - -If rotateMethod is 'time', the following keywords apply: -* backupCount = Number of rotated backup files to retain, >= 0. 0 retains *all* backups. -* timeRotateIntervalType = one of 'S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight' -(seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC) -* timeRotateInterval = number of seconds/minutes/hours/days between rotations. (Ignored for W#.) - -If rotateMethod is 'size', the following keywords apply: -* backupCount = Number of rotated backup files to retain, >= 0. 0 retains *no* backups. -* sizeMaxBytes = maximum number of bytes allowed in the file before rotation -* sizeRotateMode = for now, this defaults to 'a' and may only be specified as 'a'. -It is passed to the underlying Python Logging methods. - - -Besides logging to a file, it is also possible to send log messages elsewhere, -using <logKey> suffixed with 'LogType'. -You can set <logKey>LogType to any of 'filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger' orlogger 'null' (case insensitive). - -* 'filelogger' is the default specifying logging to a file. -* 'stdoutlogger' and 'stderrlogger' send the output to the corresponding output streams. -* 'socketlogger' will send the output to the corresponding socket host. -* 'nulllogger' turns off logging. - -If <logKey>LogType is 'socket', the following keywords apply: -* <logKey>SocketHost = FQDN or IP address for a host to sent the logs to -* <logKey>SocketPort = the port (> 0) to open on that host - -This is an example configuration file: - - error = /var/log/DCAE/vPRO/error.log - errorLogLevel = WARN - errorStyle = error - - metrics = /var/log/DCAE/vPRO/metrics.log - metricsLogLevel = INFO - metricsStyle = metrics - - audit = /var/log/DCAE/vPRO/audit.log - auditLogLevel = INFO - auditStyle = audit - - debug = /var/log/DCAE/vPRO/debug.log - debugLogLevel = DEBUG - debugStyle = debug - -## Coding Python Applications to Produce ONAP Common Logging - -A python application uses common logging by importing the CommonLogger -module, instantiating a CommonLogger object for each log file, and then -invoking each object's debug, info, warn, error, or fatal methods to log -messages to the file. There are four styles of logging: -error/info logs, debug logs, audit logs, and metrics logs. -The difference between the types of logs is in the list of fields that -are printed out. - -### Importing the CommonLogger Module - -Importing the CommonLogger module is typical: - - sys.path.append("/opt/app/dcae-commonlogging/python") - import CommonLogger - -### Creating a CommonLogger object: - -When creating a CommonLogger object, three arguments are required: - -1. The configuration filename. -2. The keyword name in the configuration file that -defines the log filename and parameters controlling rotation of the logfiles. -(This is the <logKey> referred to above.) -3. The keyword arguments for style and to set default values for the log record fields. - -The style of the log (one of CommonLoger.DebugFile, CommonLogger.AuditFile, -CommonLogger.MetricsFile and CommonLogger.ErrorFile), must be specified either -in the configuration file (e.g., errorStyle=error or metricsStyle=metrics) or -using a style= keyword and one of the values: CommonLoger.DebugFile, -CommonLogger.AuditFile, CommonLogger.MetricsFile and CommonLogger.ErrorFile. - -Keyword arguments for log record fields are as follows. -The annotation indicates whether the field is included in -(d) debug logs, (a) audit logs, (m) metrics logs, and (e) error logs. - -* requestID (dame) -* serviceInstanceID (am) -* threadID (am) -* serverName (am) -* serviceName (am) -* instanceUUID (am) -* severity (am) -* serverIPAddress (am) -* server (am) -* IPAddress (am) -* className (am) -* timer (am) -* partnerName (ame) -* targetEntity (me) -* targetServiceName (me) -* statusCode (am) -* responseCode (am) -* responseDescription (am) -* processKey (am) -* targetVirtualEntity (m) -* customField1 (am) -* customField2 (am) -* customField3 (am) -* customField4 (am) -* errorCategory (e) -* errorCode (e) -* errorDescription (e) - -Sample code: - - """ The style can be specified here or in the config file using errorStyle. """ - errorLog = CommonLogger.CommonLogger("my.config", "error", style=CommonLogger.ErrorFile, serviceName="DCAE/vPRO") - infoLog = CommonLogger.CommonLogger("my.config", "info", serviceName="DCAE/vPRO") - -### Setting default values for fields: - -The object's setFields method allows keyword arguments changing default values for the log record fields. - - errorLog.setFields(serviceName="DCAE/vPRO", threadID="thread-2") - -### Calling Methods - -The object's debug(), info(), warn(), error(), and fatal() methods require a detailMessage argument -(which can be a zero-length string) and allow the keyword arguments for setting log record field -values for just that one message. -Any newlines or '|' characters in the message will be changed to a single space. - - infoLog.info("Something benign happened.") - errorLog.fatal("Something very bad happened.", threadID="thread-4") - -### Output - -Note that no field may contain the '|' (pipe) field separation character, as that -character is used as the separator between fields. -Here is a possible example of a produced log record: - - 2015-10-12T15:56:43,182+00:00|netman@localdcae.att.com:~/vPRO_trinity/vPRO.py:905+2015-08-20 20:57:14.463426||||DCAE/vPRO:App-C.Restart|d4d5fc66-70f9-11e5-b0b1-005056866a82|INFO||135.16.76.33|mtvpro01dev1.dev.att.com|||1001|Finished Restart - 2016-12-09T23:06:02,314+00:00||MainThread|DCAE/vPRO|||||||a FATAL message for the error log - -### Example Code - -The main within CommonLogger.py contains a regression test of the CommonLogger methods. - -CommonLogger_test.py contains a complete demonstration of a python application -using the python CommonLogging wrapper module, including creating UUIDs, -setting default log field values, and timing operations. - -## Upgrading from Previous Versions of CommonLogger - -The current version of CommonLogger is 99% compatible with earlier versions of CommonLogger. -The key change, due to update ONAP logging requirements, is the choice to use different lists -of fields in different types of log files. -This required adding a mandatory "style" to be given, which we chose to do using either a -new keyword in the configuration file, or using a new parameter keyword when creating the logger. diff --git a/osdf/logging/oof_mdc_context.py b/osdf/logging/oof_mdc_context.py new file mode 100644 index 0000000..9c9b52c --- /dev/null +++ b/osdf/logging/oof_mdc_context.py @@ -0,0 +1,170 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 logging +import re +import sys + +from onaplogging.marker import Marker +from onaplogging.marker import MARKER_TAG +from onaplogging.mdcContext import _replace_func_name +from onaplogging.mdcContext import fetchkeys +from onaplogging.mdcContext import findCaller as fc +from onaplogging.mdcContext import MDC + +from osdf.utils.mdc_utils import set_error_details + + +def findCaller(self, stack_info=False, stacklevel=1): + """replacing onaplogging.mdcContext with this method to work with py3.8 + + """ + return fc(stack_info) + + +def mdc_mapper(): + """Convert the MDC dict into comma separated, name=value string + + :return: string format + """ + return ','.join(f'{k}={v}' for (k, v) in MDC.result().items() if k not in ['customField2']) + + +@fetchkeys +def info(self, msg, *args, **kwargs): + """Wrapper method for log.info is called + + """ + if self.isEnabledFor(logging.INFO): + MDC.put('customField2', mdc_mapper()) + self._log(logging.INFO, no_sep(msg), args, **kwargs) + + +@fetchkeys +def debug(self, msg, *args, **kwargs): + """Wrapper method for log.debug is called + + msg: log message + args: logging args + kwargs: all the optional args + """ + if self.isEnabledFor(logging.DEBUG): + self._log(logging.DEBUG, no_sep(msg), args, **kwargs) + + +@fetchkeys +def warning(self, msg, *args, **kwargs): + """Wrapper method for log.warning is called + + msg: log message + args: logging args + kwargs: all the optional args + """ + if self.isEnabledFor(logging.WARNING): + self._log(logging.WARNING, no_sep(msg), args, **kwargs) + + +@fetchkeys +def exception(self, msg, *args, **kwargs): + """Wrapper method for log.exception is called + + msg: log message + args: logging args + kwargs: all the optional args + """ + kwargs['exc_info'] = 1 + self.error(no_sep(msg), *args, **kwargs) + + +@fetchkeys +def critical(self, msg, *args, **kwargs): + """Wrapper method for log.critical + + msg: log message + args: logging args + kwargs: all the optional args + """ + if self.isEnabledFor(logging.CRITICAL): + self._log(logging.CRITICAL, no_sep(msg), args, **kwargs) + + +@fetchkeys +def error(self, msg, *args, **kwargs): + """Wrapper method for log.error is called + + msg: log message + args: logging args + kwargs: all the optional args + """ + if self.isEnabledFor(logging.ERROR): + if not MDC.get('errorCode'): + set_error_details(400, 'Internal Error') + MDC.put('customField2', mdc_mapper()) + self._log(logging.ERROR, no_sep(msg), args, **kwargs) + + +@fetchkeys +def log(self, level, msg, *args, **kwargs): + """Wrapper method for log.log is called + + msg: log message + args: logging args + kwargs: all the optional args + """ + if not isinstance(level, int): + if logging.raiseExceptions: + raise TypeError("level must be an integer") + else: + return + + if self.isEnabledFor(level): + self._log(level, no_sep(msg), args, **kwargs) + + +def handle(self, record): + """Wrapper method for log.handle is called + + """ + c_marker = getattr(self, MARKER_TAG, None) + + if isinstance(c_marker, Marker): + setattr(record, MARKER_TAG, c_marker) + + if (not self.disabled) and self.filter(record): + self.callHandlers(record) + + +def no_sep(message): + """This method will remove newline, | from the message + + """ + if message is None: + return '' + return re.sub(r'[\|\n]', ' ', str(message)) + + +def patch_logging_mdc(): + """The patch to add MDC ability in logging Record instance at runtime + + """ + local_module = sys.modules[__name__] + for attr in dir(logging.Logger): + if attr in _replace_func_name: + new_func = getattr(local_module, attr, None) + if new_func: + setattr(logging.Logger, attr, new_func) diff --git a/osdf/logging/oof_mdc_formatter.py b/osdf/logging/oof_mdc_formatter.py new file mode 100644 index 0000000..6272a18 --- /dev/null +++ b/osdf/logging/oof_mdc_formatter.py @@ -0,0 +1,51 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 time + +from onaplogging.colorFormatter import RESET +from onaplogging.mdcformatter import MDCFormatter + + +class OOFMDCFormatter(MDCFormatter): + """ONAP MDC formatter + + """ + + def __init__(self, fmt=None, mdcfmt=None, + datefmt=None, colorfmt=None, style="%"): + super().__init__(fmt, mdcfmt, datefmt, colorfmt, style) + self.converter = time.gmtime + + def _replaceStr(self, keys): + """overriding the default behavior + + keys: keys that needs to be substituted + + """ + fmt = self._mdcFmt + for i in keys: + fmt = fmt.replace(i, i) + + return fmt + + def format(self, record): + """Removing the color format end of line character. + + """ + return super(OOFMDCFormatter, self).format(record).replace(RESET, '') diff --git a/osdf/logging/osdf_logging.py b/osdf/logging/osdf_logging.py index a54d426..7ebaa99 100755 --- a/osdf/logging/osdf_logging.py +++ b/osdf/logging/osdf_logging.py @@ -15,38 +15,23 @@ # # ------------------------------------------------------------------------- # - +import logging +from logging import config +import os import traceback -import uuid -from .onap_common_v1.CommonLogger import CommonLogger -from osdf.utils.programming_utils import MetaSingleton +import yaml +from osdf.logging import monkey +from osdf.utils.programming_utils import MetaSingleton -def log_handlers_pre_onap(config_file="config/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") - """ - main_params = dict(instanceUUID=uuid.uuid1(), serviceName=service_name, configFile=config_file) - return dict((x, CommonLogger(logKey=x, **main_params)) - for x in ["error", "metrics", "audit", "debug"]) +BASE_DIR = os.path.dirname(__file__) +LOGGING_FILE = os.path.join(BASE_DIR, '..', '..', 'config', 'log.yml') 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__)) @@ -56,188 +41,279 @@ def format_exception(err, prefix=None): return exception_desc if not prefix else prefix + ": " + exception_desc -class OOF_OSDFLogMessageHelper(metaclass=MetaSingleton): +def create_log_dirs(): + with open(LOGGING_FILE, 'r') as fid: + yaml_config = yaml.full_load(fid) + for key in yaml_config['handlers']: + a = yaml_config['handlers'][key] + if a.get('filename'): + os.makedirs(os.path.dirname(a['filename']), exist_ok=True) + + +class OOFOSDFLogMessageHelper(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): + def get_handlers(self, levels=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) + create_log_dirs() + monkey.patch_all() + config.yamlConfig(filepath=LOGGING_FILE, watchDog=False) wanted_levels = self.default_levels if levels is None else levels - return [self.log_handlers.get(x) for x in wanted_levels] + return [logging.getLogger(x) for x in wanted_levels] -class OOF_OSDFLogMessageFormatter(object): +class OOFOSDFLogMessageFormatter(object): @staticmethod def accepted_valid_request(req_id, request): + """valid request message formatter + + """ return "Accepted valid request for ID: {} for endpoint: {}".format( req_id, request.url) @staticmethod def invalid_request(req_id, err): + """invalid request message formatter + + """ return "Invalid request for request ID: {}; cause: {}".format( req_id, format_exception(err)) @staticmethod def invalid_response(req_id, err): + """invalid response message formatter + + """ return "Invalid response for request ID: {}; cause: {}".format( req_id, format_exception(err)) @staticmethod def malformed_request(request, err): + """Malformed request message formatter + + """ return "Malformed request for URL {}, from {}; cause: {}".format( request.url, request.remote_address, format_exception(err)) @staticmethod def malformed_response(response, client, err): + """Malformed response message formatter + + """ return "Malformed response {} for client {}; cause: {}".format( response, client, format_exception(err)) @staticmethod def need_policies(req_id): + """Policies needed message formatter + + """ return "Policies required but found no policies for request ID: {}".format(req_id) @staticmethod def policy_service_error(url, req_id, err): + """policy service error message formatter + + """ return "Unable to call policy for {} for request ID: {}; cause: {}".format( url, req_id, format_exception(err)) @staticmethod def requesting_url(url, req_id): + """Message formatter for requesting url + + """ return "Making a call to URL {} for request ID: {}".format(url, req_id) @staticmethod def requesting(service_name, req_id): + """Message formatter for requesting a service + + """ return "Making a call to service {} for request ID: {}".format(service_name, req_id) @staticmethod def error_requesting(service_name, req_id, err): + """Message formatter on error requesting a service + + """ 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): + """Message formatter when a callback url is invoked + + """ 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): + """Message formatter when a callback url with body is invoked + + """ 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): + """Message formatter on an error while posting result to callback url + + """ 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): + """Message when a call is received + + """ return "Received a call to {} from {} {}".format(url, remote_addr, json_body) @staticmethod def new_worker_thread(req_id, extra_msg=""): + """Message on invoking a new worker thread + + """ res = "Initiating new worker thread for request ID: {}".format(req_id) return res + extra_msg @staticmethod def inside_worker_thread(req_id, extra_msg=""): + """Message when inside a worker thread + + """ res = "Inside worker thread for request ID: {}".format(req_id) return res + extra_msg @staticmethod def processing(req_id, desc): + """Processing a request + + """ return "Processing request ID: {} -- {}".format(req_id, desc) @staticmethod def processed(req_id, desc): + """Processed the request + + """ return "Processed request ID: {} -- {}".format(req_id, desc) @staticmethod def error_while_processing(req_id, desc, err): + """Error while processing the request + + """ return "Error while processing request ID: {} -- {}; cause: {}".format( req_id, desc, format_exception(err)) @staticmethod def creating_local_env(req_id): + """Creating a local environment + + """ return "Creating local environment request ID: {}".format( req_id) @staticmethod def error_local_env(req_id, desc, err): + """Error creating a local env + + """ 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=""): + """Inside a new thread + + """ 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): + """Error while posting a response + + """ return "Error while posting a response for a request ID: {} -- {}; cause: {}".format( req_id, desc, err.__traceback__) @staticmethod def received_http_response(resp): + """Received a http response + + """ return "Received response [code: {}, headers: {}, data: {}]".format( resp.status_code, resp.headers, resp.__dict__) @staticmethod def sending_response(req_id, desc): + """sending a response + + """ return "Response is sent for request ID: {} -- {}".format( req_id, desc) @staticmethod def listening_response(req_id, desc): + """Resposne is sent for a request ID + + """ return "Response is sent for request ID: {} -- {}".format( req_id, desc) @staticmethod def items_received(item_num, item_type, desc="Received"): + """Items received + + """ return "{} {} {}".format(desc, item_num, item_type) @staticmethod def items_sent(item_num, item_type, desc="Published"): + """items published + + """ return "{} {} {}".format(desc, item_num, item_type) -MH = OOF_OSDFLogMessageFormatter -error_log, metrics_log, audit_log, debug_log = OOF_OSDFLogMessageHelper().get_handlers() +MH = OOFOSDFLogMessageFormatter + +error_log, metrics_log, audit_log, debug_log = OOFOSDFLogMessageHelper().get_handlers() def warn_audit_error(msg): - """Log the message to error_log.warn and audit_log.warn""" + """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. """ diff --git a/osdf/models/policy/placement/tosca/vnfPolicy-v20181031.yml b/osdf/models/policy/placement/tosca/vnfPolicy-v20181031.yml index 4ce3b9f..8eaf178 100644 --- a/osdf/models/policy/placement/tosca/vnfPolicy-v20181031.yml +++ b/osdf/models/policy/placement/tosca/vnfPolicy-v20181031.yml @@ -66,3 +66,20 @@ data_types: customerId: type: string required: true + unique: + type: string + required: false + attributes: + type: list + required: false + entry_schema: + type:policy.data.vnfProperties_filteringAttributes + passthroughAttributes: + type: list + required: false + entry_schema: + type:policy.data.vnfProperties_passthroughAttributes + policy.data.vnfProperties_filteringAttributes: + derived_from: tosca.nodes.Root + policy.data.vnfProperties_passthroughAttributes: + derived_from: tosca.nodes.Root diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.AffinityPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.AffinityPolicy.yaml deleted file mode 100644 index c2fd504..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.AffinityPolicy.yaml +++ /dev/null @@ -1,62 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.AffinityPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - zone - identity: - type: string - required: true - applicableResources: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - any - - all - affinityProperties: - type: policy.data.affinityProperties_properties - required: true - resources: - type: list - required: true - entry_schema: - type: string -data_types: - policy.data.affinityProperties_properties: - derived_from: tosca.nodes.Root - properties: - qualifier: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - same - - different - category: - type: string - required: true diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.DistancePolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.DistancePolicy.yaml deleted file mode 100644 index 93ddd63..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.DistancePolicy.yaml +++ /dev/null @@ -1,82 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.DistancePolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - distance_to_location - identity: - type: string - required: true - resources: - type: list - required: true - entry_schema: - type: string - applicableResources: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - any - - all - distanceProperties: - type: policy.data.distanceProperties_properties - required: true -data_types: - policy.data.distanceProperties_properties: - derived_from: tosca.nodes.Root - properties: - locationInfo: - type: string - required: true - distance: - type: policy.data.distance_properties - required: true - policy.data.distance_properties: - derived_from: tosca.nodes.Root - properties: - value: - type: string - required: true - operator: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - < - - <= - - '>' - - '>=' - - '=' - unit: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - km diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.HpaPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.HpaPolicy.yaml deleted file mode 100644 index 63f0d8a..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.HpaPolicy.yaml +++ /dev/null @@ -1,131 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.HpaPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - hpa - resources: - type: list - required: true - entry_schema: - type: string - identity: - type: string - required: true - flavorFeatures: - type: list - required: true - entry_schema: - type:policy.data.flavorFeatures_properties -data_types: - policy.data.flavorFeatures_properties: - derived_from: tosca.nodes.Root - properties: - id: - type: string - required: true - type: - type: string - required: true - directives: - type: list - required: true - entry_schema: - type: policy.data.directives_properties - flavorProperties: - type: list - required: true - entry_schema: - type: policy.data.flavorProperties_properties - policy.data.directives_properties: - derived_from: tosca.nodes.Root - properties: - type: - type: string - attributes: - type: list - entry_schema: - type: policy.data.directives_attributes_properties - policy.data.directives_attributes_properties: - derived_from: tosca.nodes.Root - properties: - attribute_name: - type: string - attribute_value: - type: string - policy.data.flavorProperties_properties: - derived_from: tosca.nodes.Root - properties: - hpa-feature: - type: string - required: true - mandatory: - type: string - required: true - score: - type: string - required: false - architecture: - type: string - required: true - hpa-version: - type: string - required: true - directives: - type: list - required: true - entry_schema: - type: policy.data.directives_properties - hpa-feature-attributes: - type: list - required: true - entry_schema: - type: policy.data.hpa-feature-attributes_properties - policy.data.hpa-feature-attributes_properties: - derived_from: tosca.nodes.Root - properties: - hpa-attribute-key: - type: string - required: true - hpa-attribute-value: - type: string - required: true - operator: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - < - - <= - - '>' - - '>=' - - '=' - - '!=' - - any - - all - - subset - unit: - type: string - required: false diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.OptimizationPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.OptimizationPolicy.yaml deleted file mode 100644 index edfac14..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.OptimizationPolicy.yaml +++ /dev/null @@ -1,89 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.OptimizationPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - placement_optimization - identity: - type: string - required: true - objective: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - minimize - - maximize - objectiveParameter: - type: policy.data.objectiveParameter_properties - required: true -data_types: - policy.data.objectiveParameter_properties: - derived_from: tosca.nodes.Root - properties: - parameterAttributes: - type: list - required: true - entry_schema: - type: policy.data.parameterAttributes_properties - operator: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - '*' - - + - - '-' - - / - - '%' - policy.data.parameterAttributes_properties: - derived_from: tosca.nodes.Root - properties: - resources: - type: string - required: true - customerLocationInfo: - type: string - required: true - parameter: - type: string - required: true - weight: - type: string - required: true - operator: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - '*' - - + - - '-' - - / - - '%' diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.PciPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.PciPolicy.yaml deleted file mode 100644 index 1355eb0..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.PciPolicy.yaml +++ /dev/null @@ -1,58 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.PciPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - pciPolicy - identity: - type: string - required: true - resources: - type: list - required: true - entry_schema: - type: string - pciProperties: - type: list - required: false - entry_schema: - - type:policy.data.pciProperties_properties -data_types: - policy.data.pciProperties_properties: - derived_from: tosca.nodes.Root - properties: - algoCategory: - type: string - required: false - pciOptmizationAlgoName: - type: string - required: false - pciOptimizationNwConstraint: - type: string - required: false - pciOptimizationPriority: - type: string - required: false - pciOptimizationTimeConstraint: - type: string - required: false diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.QueryPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.QueryPolicy.yaml deleted file mode 100644 index f7036dc..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.QueryPolicy.yaml +++ /dev/null @@ -1,47 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.QueryPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - request_param_query - identity: - type: string - required: true - queryProperties: - type: list - required: true - entry_schema: - type:policy.data.queryProperties_properties -data_types: - policy.data.queryProperties_properties: - derived_from: tosca.nodes.Root - properties: - attribute: - type: string - required: true - value: - type: string - required: true - attribute_location: - type: string - required: true diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.SubscriberPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.SubscriberPolicy.yaml deleted file mode 100644 index 3c2c2b2..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.SubscriberPolicy.yaml +++ /dev/null @@ -1,51 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.SubscriberPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - subscriberPolicy - identity: - type: string - required: true - properties: - type: policy.data.properties_properties - required: true -data_types: - policy.data.properties_properties: - derived_from: tosca.nodes.Root - properties: - subscriberName: - type: list - required: true - entry_schema: - type: string - subscriberRole: - type: list - required: true - entry_schema: - type: string - provStatus: - type: list - required: true - entry_schema: - type: string diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.Vim_fit.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.Vim_fit.yaml deleted file mode 100644 index 860c37f..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.Vim_fit.yaml +++ /dev/null @@ -1,56 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.Vim_fit: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - vim_fit - identity: - type: string - required: true - applicableResources: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - any - - all - resources: - type: list - required: true - entry_schema: - type: string - capacityProperties: - type: policy.data.capacityProperties_properties - required: true -data_types: - policy.data.capacityProperties_properties: - derived_from: tosca.nodes.Root - properties: - controller: - type: string - required: true - request: - type: string - required: true diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.VnfPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.VnfPolicy.yaml deleted file mode 100644 index 13d4f13..0000000 --- a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.VnfPolicy.yaml +++ /dev/null @@ -1,73 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0_0 -policy_types: - - onap.policies.Optimization: - derived_from: tosca.policies.Root - version: 1.0.0 - description: a base policy type for all policies that govern optimization - - onap.policies.optimization.VnfPolicy: - derived_from: onap.policies.Optimization - properties: - policyScope: - type: list - description: scope where the policy is applicable - required: true - matchable: true - entry_schema: - type: string - policyType: - type: list - description: type of a policy - required: true - matchable: true - entry_schema: - type: string - consraints: - - valid_values: - - vnfPolicy - identity: - type: string - required: true - resources: - type: list - required: true - entry_schema: - type: string - applicableResources: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - any - - all - vnfProperties: - type: list - required: true - entry_schema: - type:policy.data.vnfProperties_properties -data_types: - policy.data.vnfProperties_properties: - derived_from: tosca.nodes.Root - properties: - inventoryProvider: - type: string - required: true - serviceType: - type: string - required: true - inventoryType: - type: list - required: true - entry_schema: - type: string - constraints: - - valid_values: - - serviceInstanceId - - vnfName - - cloudRegionId - - vimId - customerId: - type: string - required: true - diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.AffinityPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.AffinityPolicy.yaml new file mode 100644 index 0000000..3fb8525 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.AffinityPolicy.yaml @@ -0,0 +1,31 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.AffinityPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + applicableResources: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - any + - all + affinityProperties: + type: policy.data.affinityProperties_properties + required: true +data_types: + policy.data.affinityProperties_properties: + derived_from: tosca.nodes.Root + properties: + qualifier: + type: string + constraints: + - valid_values: + - same + - different + category: + type: string + required: true
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.AggregationPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.AggregationPolicy.yaml new file mode 100644 index 0000000..1e3c813 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.AggregationPolicy.yaml @@ -0,0 +1,42 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.AggregationPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + applicableResources: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - any + - all + aggregationProperties: + type: list + required: true + entry_schema: + type: policy.data.aggregationProperties_properties +data_types: + policy.data.aggregationProperties_properties: + derived_from: tosca.nodes.Root + properties: + attribute: + type: string + required: true + operator: + type: string + required: true + threshold: + type: policy.data.thresh_properties + required: true + unit: + type: string + required: false + function: + type: string + required: true + policy.data.thresh_properties: + derived_from: tosca.nodes.Root + diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.DistancePolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.DistancePolicy.yaml new file mode 100644 index 0000000..196ba9e --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.DistancePolicy.yaml @@ -0,0 +1,56 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.DistancePolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + applicableResources: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - any + - all + distanceProperties: + type: policy.data.distanceProperties_properties + required: true +data_types: + policy.data.distanceProperties_properties: + derived_from: tosca.nodes.Root + properties: + locationInfo: + type: string + required: true + distance: + type: policy.data.distance_properties + required: true + entry_schema: + type: policy.data.distance_properties + policy.data.distance_properties: + derived_from: tosca.nodes.Root + properties: + value: + type: string + required: true + operator: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - < + - <= + - '>' + - '>=' + - = + unit: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - km
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.HpaPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.HpaPolicy.yaml new file mode 100644 index 0000000..fe7b864 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.HpaPolicy.yaml @@ -0,0 +1,103 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.HpaPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + flavorFeatures: + type: list + required: true + entry_schema: + type: policy.data.flavorFeatures_properties +data_types: + policy.data.flavorFeatures_properties: + derived_from: tosca.nodes.Root + properties: + id: + type: string + required: true + type: + type: string + required: true + directives: + type: list + required: true + entry_schema: + type: policy.data.directives_properties + flavorProperties: + type: list + required: true + entry_schema: + type: policy.data.flavorProperties_properties + policy.data.directives_properties: + derived_from: tosca.nodes.Root + properties: + type: + type: string + attributes: + type: list + entry_schema: + type: policy.data.directives_attributes_properties + policy.data.directives_attributes_properties: + derived_from: tosca.nodes.Root + properties: + attribute_name: + type: string + attribute_value: + type: string + policy.data.flavorProperties_properties: + derived_from: tosca.nodes.Root + properties: + hpa-feature: + type: string + required: true + mandatory: + type: string + required: true + score: + type: string + required: false + architecture: + type: string + required: true + hpa-version: + type: string + required: true + directives: + type: list + required: true + entry_schema: + type: policy.data.directives_properties + hpa-feature-attributes: + type: list + required: true + entry_schema: + type: policy.data.hpa-feature-attributes_properties + policy.data.hpa-feature-attributes_properties: + derived_from: tosca.nodes.Root + properties: + hpa-attribute-key: + type: string + required: true + hpa-attribute-value: + type: string + required: true + operator: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - < + - <= + - '>' + - '>=' + - = + - '!=' + - any + - all + - subset + unit: + type: string + required: false diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.OptimizationPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.OptimizationPolicy.yaml new file mode 100644 index 0000000..fae050b --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.OptimizationPolicy.yaml @@ -0,0 +1,66 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.OptimizationPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + objective: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - minimize + - maximize + objectiveParameter: + type: policy.data.objectiveParameter_properties + required: true +data_types: + policy.data.objectiveParameter_properties: + derived_from: tosca.nodes.Root + properties: + parameterAttributes: + type: list + required: true + entry_schema: + type: policy.data.parameterAttributes_properties + operator: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - '*' + - + + - '-' + - / + - '%' + policy.data.parameterAttributes_properties: + derived_from: tosca.nodes.Root + properties: + resources: + type: string + required: true + customerLocationInfo: + type: string + required: true + parameter: + type: string + required: true + weight: + type: string + required: true + operator: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - '*' + - + + - '-' + - / + - '%'
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.PciPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.PciPolicy.yaml new file mode 100644 index 0000000..021cff9 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.PciPolicy.yaml @@ -0,0 +1,30 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.PciPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + pciProperties: + type: list + required: false + entry_schema: + type: policy.data.pciProperties_properties +data_types: + policy.data.pciProperties_properties: + derived_from: tosca.nodes.Root + properties: + algoCategory: + type: string + required: false + pciOptmizationAlgoName: + type: string + required: false + pciOptimizationNwConstraint: + type: string + required: false + pciOptimizationPriority: + type: string + required: false + pciOptimizationTimeConstraint: + type: string + required: false
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.ThresholdPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.ThresholdPolicy.yaml new file mode 100644 index 0000000..ab400dd --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.ThresholdPolicy.yaml @@ -0,0 +1,37 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.ThresholdPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + applicableResources: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - any + - all + thresholdProperties: + type: list + required: true + entry_schema: + type: policy.data.thresholdProperties_properties +data_types: + policy.data.thresholdProperties_properties: + derived_from: tosca.nodes.Root + properties: + attribute: + type: string + required: true + operator: + type: string + required: true + threshold: + type: float + required: true + unit: + type: string + required: false + diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.Vim_fit.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.Vim_fit.yaml new file mode 100644 index 0000000..6ba2ae1 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.Vim_fit.yaml @@ -0,0 +1,28 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.Vim_fit: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + applicableResources: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - any + - all + capacityProperties: + type: policy.data.capacityProperties_properties + required: true +data_types: + policy.data.capacityProperties_properties: + derived_from: tosca.nodes.Root + properties: + controller: + type: string + required: true + request: + type: string + required: true
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.VnfPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.VnfPolicy.yaml new file mode 100644 index 0000000..1c7d3b6 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.resource.VnfPolicy.yaml @@ -0,0 +1,44 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.resource.VnfPolicy: + derived_from: onap.policies.optimization.Resource + version: 1.0.0 + properties: + applicableResources: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - any + - all + vnfProperties: + type: list + required: true + entry_schema: + type: policy.data.vnfProperties_properties +data_types: + policy.data.vnfProperties_properties: + derived_from: tosca.nodes.Root + properties: + inventoryProvider: + type: string + required: true + serviceType: + type: string + required: true + inventoryType: + type: list + required: true + entry_schema: + type: string + constraints: + - valid_values: + - serviceInstanceId + - vnfName + - cloudRegionId + - vimId + customerId: + type: string + required: true
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.service.QueryPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.service.QueryPolicy.yaml new file mode 100644 index 0000000..2a615ab --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.service.QueryPolicy.yaml @@ -0,0 +1,24 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.service.QueryPolicy: + derived_from: onap.policies.optimization.Service + version: 1.0.0 + properties: + queryProperties: + type: list + required: true + entry_schema: + type: policy.data.queryProperties_properties +data_types: + policy.data.queryProperties_properties: + derived_from: tosca.nodes.Root + properties: + attribute: + type: string + required: true + value: + type: string + required: true + attribute_location: + type: string + required: true
\ No newline at end of file diff --git a/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.service.SubscriberPolicy.yaml b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.service.SubscriberPolicy.yaml new file mode 100644 index 0000000..60da742 --- /dev/null +++ b/osdf/models/policy/placement/tosca_upload/onap.policies.optimization.service.SubscriberPolicy.yaml @@ -0,0 +1,34 @@ +tosca_definitions_version: tosca_simple_yaml_1_1_0 +policy_types: + onap.policies.optimization.service.SubscriberPolicy: + derived_from: onap.policies.optimization.Service + version: 1.0.0 + properties: + subscriberProperties: + type: policy.data.subscriberProperties_properties + required: true +data_types: + policy.data.subscriberProperties_properties: + derived_from: tosca.nodes.Root + properties: + subscriberName: + type: list + required: true + metadata: + contextProvider: true + entry_schema: + type: string + subscriberRole: + type: list + required: true + metadata: + contextMatchable: scope + entry_schema: + type: string + provStatus: + type: list + required: true + metadata: + contextAttribute: true + entry_schema: + type: string
\ No newline at end of file diff --git a/osdf/optimizers/placementopt/conductor/api_builder.py b/osdf/optimizers/placementopt/conductor/api_builder.py deleted file mode 100644 index 08a7460..0000000 --- a/osdf/optimizers/placementopt/conductor/api_builder.py +++ /dev/null @@ -1,99 +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 jinja2 import Template - -import osdf.optimizers.placementopt.conductor.translation as tr -from osdf.adapters.policy.utils import group_policies_gen -from osdf.utils.programming_utils import list_flatten - - -def _build_parameters(group_policies, request_json): - """ - Function prepares parameters section for has request - :param group_policies: filtered policies - :param request_json: parameter data received from a client - :return: - """ - initial_params = tr.get_opt_query_data(request_json, group_policies['request_param_query']) - params = dict() - params.update({"REQUIRED_MEM": initial_params.pop("requiredMemory", "")}) - params.update({"REQUIRED_DISK": initial_params.pop("requiredDisk", "")}) - params.update({"customer_lat": initial_params.pop("customerLatitude", 0.0)}) - params.update({"customer_long": initial_params.pop("customerLongitude", 0.0)}) - params.update({"service_name": request_json['serviceInfo']['serviceName']}) - params.update({"service_id": request_json['serviceInfo']['serviceInstanceId']}) - - for key, val in initial_params.items(): - if val and val != "": - params.update({key: val}) - - return params - - -def conductor_api_builder(request_json, flat_policies: list, local_config, - template="osdf/templates/conductor_interface.json"): - """Build an OSDF southbound API call for HAS-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_gen(flat_policies, local_config) - demand_vnf_name_list = [] - - for placementDemand in request_json['placementInfo']['placementDemands']: - demand_vnf_name_list.append(placementDemand['resourceModuleName'].lower()) - demand_list = tr.gen_demands(request_json, 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['placement_optimization']) - reservation_policy_list = tr.gen_reservation_policy(demand_vnf_name_list, gp['instance_reservation']) - capacity_policy_list = tr.gen_capacity_policy(demand_vnf_name_list, gp['vim_fit']) - hpa_policy_list = tr.gen_hpa_policy(demand_vnf_name_list, gp['hpa']) - req_params_dict = _build_parameters(gp, request_json) - conductor_policies = [attribute_policy_list, distance_to_location_policy_list, inventory_policy_list, - resource_instance_policy_list, resource_region_policy_list, zone_policy_list, - reservation_policy_list, capacity_policy_list, hpa_policy_list] - filtered_policies = [x for x in conductor_policies if len(x) > 0] - policy_groups = list_flatten(filtered_policies) - req_info = request_json['requestInfo'] - request_type = req_info.get('requestType', None) - rendered_req = templ.render( - requestType=request_type, - 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'], - request_params=req_params_dict, - json=json) - json_payload = json.dumps(json.loads(rendered_req)) # need this because template's JSON is ugly! - return json_payload diff --git a/osdf/optimizers/placementopt/conductor/conductor.py b/osdf/optimizers/placementopt/conductor/conductor.py deleted file mode 100755 index 357efd1..0000000 --- a/osdf/optimizers/placementopt/conductor/conductor.py +++ /dev/null @@ -1,202 +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, flat_policies):
- """
- 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 flat_policies: policies related to placement (fetched based on request)
- :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)
- placement_ver_enabled = config.get('placementVersioningEnabled', False)
-
- if placement_ver_enabled:
- cond_minor_version = config.get('conductorMinorVersion', None)
- if cond_minor_version is not None:
- x_minor_version = str(cond_minor_version)
- headers.update({'X-MinorVersion': x_minor_version})
- debug_log.debug("Versions set in HTTP header to conductor: X-MinorVersion: {} ".format(x_minor_version))
-
- 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, flat_policies, local_config)
- 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("Attempting 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",
- "cloud": "cloudRegionId", "service": "serviceInstanceId", "is_rehome": "isRehome",
- "location_id": "locationId", "location_type": "locationType", "directives": "oof_directives"}
- for reco in conductor_response['plans'][0]['recommendations']:
- for resource in reco.keys():
- c = reco[resource]['candidate']
- solution = {
- 'resourceModuleName': resource,
- 'serviceResourceId': reco[resource].get('service_resource_id', ""),
- 'solution': {"identifierType": name_map.get(c['inventory_type'], c['inventory_type']),
- 'identifiers': [c['candidate_id']],
- 'cloudOwner': c.get('cloud_owner', "")},
- 'assignmentInfo': []
- }
- for key, value in c.items():
- if key in ["location_id", "location_type", "is_rehome", "host_id"]:
- try:
- solution['assignmentInfo'].append({"key": name_map.get(key, key), "value": value})
- except KeyError:
- debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info".format(key))
-
- for key, value in reco[resource]['attributes'].items():
- try:
- solution['assignmentInfo'].append({"key": name_map.get(key, key), "value": value})
- except KeyError:
- debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info".format(key))
- composite_solutions.append(solution)
-
- request_status = "completed" if conductor_response['plans'][0]['status'] == "done" \
- else 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.setdefault('placementSolutions', [])
- solution_info['placementSolutions'].append(composite_solutions)
-
- resp = {
- "transactionId": transaction_id,
- "requestId": req_id,
- "requestStatus": request_status,
- "statusMessage": status_message,
- "solutions": 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, license_solutions=[],
- transactionId=raw_response.headers.get('transaction_id', ""),
- requestStatus="completed", 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 deleted file mode 100644 index 614eca3..0000000 --- a/osdf/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): - """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 request_json.get('licenseInfo', {}).get('licenseDemands'): - license_info = license_optim(request_json) - - # Conductor only handles placement, only call Conductor if placementDemands exist - if request_json.get('placementInfo', {}).get('placementDemands'): - metrics_log.info(MH.requesting("placement/conductor", req_id)) - placement_response = conductor.request(request_json, osdf_config, policies) - 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, - "requestStatus": "completed", - "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 deleted file mode 100644 index e09ecd3..0000000 --- a/osdf/optimizers/placementopt/conductor/translation.py +++ /dev/null @@ -1,285 +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 -import yaml -import re - -from osdf.utils.data_conversion import text_to_symbol -from osdf.utils.programming_utils import dot_notation - -policy_config_mapping = yaml.load(open('config/has_config.yaml')).get('policy_config_mapping') - - -def get_opt_query_data(req_json, policies): - """ - Fetch service and order specific details from the requestParameters field of a request. - :param req_json: a request file - :param policies: A set of policies - :return: A dictionary with service and order-specific attributes. - """ - req_param_dict = {} - if 'requestParameters' in req_json["placementInfo"]: - req_params = req_json["placementInfo"]["requestParameters"] - for policy in policies: - for queryProp in policy['content']['queryProperties']: - attr_val = queryProp['value'] if 'value' in queryProp and queryProp['value'] != "" \ - else dot_notation(req_params, queryProp['attribute_location']) - if attr_val is not None: - req_param_dict.update({queryProp['attribute']: attr_val}) - return req_param_dict - - -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 objective policy information provided in the incoming request - :return: List of optimization objective policies in a format required by Conductor - """ - optimization_policy_list = [] - for policy in optimization_policy: - content = policy['content'] - parameter_list = [] - parameters = ["cloud_version", "hpa_score"] - - for attr in content['objectiveParameter']['parameterAttributes']: - parameter = attr['parameter'] if attr['parameter'] in parameters else attr['parameter']+"_between" - vnfs = get_matching_vnfs(attr['resources'], vnf_list) - for vnf in vnfs: - value = [vnf] if attr['parameter'] in parameters 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_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 vnfs 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 - """ - resources_lcase = [x.lower() for x in resources] - if match_type == "all": # don't bother with any comparisons - return resources if set(resources_lcase) <= set(vnf_list) else None - common_vnfs = set(vnf_list) & set(resources_lcase) - common_resources = [x for x in resources if x.lower() in common_vnfs] - if match_type == "intersection": # specifically requested intersection - return list(common_resources) - return resources if common_vnfs else None # "any" match => all resources to be returned - - -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['resources'], vnf_list, match_type=match_type) - resource = {pc['identity']: {'type': pc['policyType'], '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']['distanceProperties'] - pcp_d = properties['distance'] - p_new[p_main['content']['identity']]['properties'] = { - 'distance': pcp_d['operator'] + " " + pcp_d['value'].lower() + " " + pcp_d['unit'].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'] - attribute_mapping = policy_config_mapping['attributes'] # wanted attributes and mapping - p_new[p_main['content']['identity']]['properties'] = { - 'evaluate': dict((k, properties.get(attribute_mapping.get(k))) for k in attribute_mapping.keys()) - } - return cur_policies # cur_policies gets updated in place... - - -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, match_type="all", rtype=None) - for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy - pmz = p_main['content']['affinityProperty'] - p_new[p_main['content']['identity']]['properties'] = {'category': pmz['category'], 'qualifier': pmz['qualifier']} - return cur_policies - - -def gen_capacity_policy(vnf_list, capacity_policy): - """Get zone policies in order to populate the Conductor API call""" - cur_policies, related_policies = gen_policy_instance(vnf_list, capacity_policy, rtype=None) - for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy - pmz = p_main['content']['capacityProperty'] - p_new[p_main['content']['identity']]['properties'] = \ - {"controller": pmz['controller'], 'request': json.loads(pmz['request'])} - return cur_policies - - -def gen_hpa_policy(vnf_list, hpa_policy): - """Get zone policies in order to populate the Conductor API call""" - cur_policies, related_policies = gen_policy_instance(vnf_list, hpa_policy, rtype=None) - for p_new, p_main in zip(cur_policies, related_policies): # add additional fields to each policy - p_new[p_main['content']['identity']]['properties'] = {'evaluate': p_main['content']['flavorFeatures']} - return cur_policies - - -def get_augmented_policy_attributes(policy_property, demand): - """Get policy attributes and augment them using policy_config_mapping and demand information""" - attributes = copy.copy(policy_property['attributes']) - remapping = policy_config_mapping['remapping'] - extra = dict((x, demand['resourceModelInfo'][remapping[x]]) for x in attributes if x in remapping) - attributes.update(extra) - return attributes - - -def get_candidates_demands(demand): - """Get demands related to candidates; e.g. excluded/required""" - res = {} - for k, v in policy_config_mapping['candidates'].items(): - if k not in demand: - continue - res[v] = list() - for x in demand[k]: - for candidate_id in x['identifiers']: - res[v].append({'inventory_type': x['identifierType'], 'candidate_id': candidate_id}) - return res - - -def get_policy_properties(demand, policies): - """Get policy_properties for cases where there is a match with the demand""" - for policy in policies: - policy_demands = set([x.lower() for x in policy['content'].get('resources', [])]) - if demand['resourceModuleName'].lower() not in policy_demands: - continue # no match for this policy - for policy_property in policy['content']['vnfProperties']: - yield policy_property - - -def get_demand_properties(demand, policies): - """Get list demand properties objects (named tuples) from policy""" - demand_properties = [] - for policy_property in get_policy_properties(demand, policies): - prop = dict(inventory_provider=policy_property['inventoryProvider'], - inventory_type=policy_property['inventoryType'], - service_type=demand['serviceResourceId'], - service_resource_id=demand['serviceResourceId']) - - prop.update({'unique': demand['unique']} if demand.get('unique') else {}) - prop['attributes'] = dict() - prop['attributes'].update({'global-customer-id': policy_property['customerId']} - if policy_property['customerId'] else {}) - prop['attributes'].update({'model-invariant-id': demand['resourceModelInfo']['modelInvariantId']} - if demand['resourceModelInfo']['modelInvariantId'] else {}) - prop['attributes'].update({'model-version-id': demand['resourceModelInfo']['modelVersionId']} - if demand['resourceModelInfo']['modelVersionId'] else {}) - prop['attributes'].update({'equipment-role': policy_property['equipmentRole']} - if policy_property['equipmentRole'] else {}) - - if policy_property.get('attributes'): - for attr_key, attr_val in policy_property['attributes'].items(): - update_converted_attribute(attr_key, attr_val, prop) - - prop.update(get_candidates_demands(demand)) - demand_properties.append(prop) - return demand_properties - - -def update_converted_attribute(attr_key, attr_val, properties): - """ - Updates dictonary of attributes with one specified in the arguments. - Automatically translates key namr from camelCase to hyphens - :param attr_key: key of the attribute - :param attr_val: value of the attribute - :param properties: dictionary with attributes to update - :return: - """ - if attr_val: - remapping = policy_config_mapping['attributes'] - if remapping.get(attr_key): - key_value = remapping.get(attr_key) - else: - key_value = re.sub('(.)([A-Z][a-z]+)', r'\1-\2', attr_key) - key_value = re.sub('([a-z0-9])([A-Z])', r'\1-\2', key_value).lower() - properties['attributes'].update({key_value: attr_val}) - - -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 demand in req_json['placementInfo']['placementDemands']: - prop = get_demand_properties(demand, vnf_policies) - if len(prop) > 0: - demand_dictionary.update({demand['resourceModuleName']: prop}) - return demand_dictionary diff --git a/osdf/optimizers/routeopt/simple_route_opt.py b/osdf/optimizers/routeopt/simple_route_opt.py deleted file mode 100644 index 060e1ed..0000000 --- a/osdf/optimizers/routeopt/simple_route_opt.py +++ /dev/null @@ -1,150 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) 2018 Huawei 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 requests.auth import HTTPBasicAuth - - -class RouteOpt: - - """ - This values will need to deleted.. - only added for the debug purpose - """ - # DNS server and standard port of AAI.. - # TODO: read the port from the configuration and add to DNS - aai_host = "https://aai.api.simpledemo.onap.org:8443" - aai_headers = { - "X-TransactionId": "9999", - "X-FromAppId": "OOF", - "Accept": "application/json", - "Content-Type": "application/json", - "Real-Time": "true" - } - - def isCrossONAPLink(self, logical_link): - """ - This method checks if cross link is cross onap - :param logical_link: - :return: - """ - for relationship in logical_link["relationship-list"]["relationship"]: - if relationship["related-to"] == "ext-aai-network": - return True - return False - - def getRoute(self, request): - """ - This method checks - :param logical_link: - :return: - """ - - src_access_node_id = request["srcPort"]["src-access-node-id"] - dst_access_node_id = request["dstPort"]["dst-access-node-id"] - - - ingress_p_interface = None - egress_p_interface = None - - # for the case of request_json for same domain, return the same node with destination update - if src_access_node_id == dst_access_node_id: - data = '{'\ - '"vpns":['\ - '{'\ - '"access-topology-id": "' + request["srcPort"]["src-access-topology-id"] + '",'\ - '"access-client-id": "' + request["srcPort"]["src-access-client-id"] + '",'\ - '"access-provider-id": "' + request["srcPort"]["src-access-provider-id"]+ '",'\ - '"access-node-id": "' + request["srcPort"]["src-access-node-id"]+ '",'\ - '"src-access-ltp-id": "' + request["srcPort"]["src-access-ltp-id"]+ '",'\ - '"dst-access-ltp-id": "' + request["dstPort"]["dst-access-ltp-id"] +'"'\ - '}'\ - ']'\ - '}' - return data - else: - logical_links = self.get_logical_links() - - # take the logical link where both the p-interface in same onap - if logical_links != None: - for logical_link in logical_links.get("logical-link"): - if not self.isCrossONAPLink(logical_link): - # link is in local ONAP - for relationship in logical_link["relationship-list"]["relationship"]: - if relationship["related-to"] == "p-interface": - if src_access_node_id in relationship["related-link"]: - i_interface = relationship["related-link"].split("/")[-1] - ingress_p_interface = i_interface.split("-")[-1] - if dst_access_node_id in relationship["related-link"]: - e_interface = relationship["related-link"].split("/")[-1] - egress_p_interface = e_interface.split("-")[-1] - data = '{'\ - '"vpns":['\ - '{'\ - '"access-topology-id": "' + request["srcPort"]["src-access-topology-id"] + '",'\ - '"access-client-id": "' + request["srcPort"]["src-access-client-id"] + '",'\ - '"access-provider-id": "' + request["srcPort"]["src-access-provider-id"]+ '",'\ - '"access-node-id": "' + request["srcPort"]["src-access-node-id"]+ '",'\ - '"src-access-ltp-id": "' + request["srcPort"]["src-access-ltp-id"]+ '",'\ - '"dst-access-ltp-id": "' + ingress_p_interface +'"'\ - '},'\ - '{' \ - '"access-topology-id": "' + request["dstPort"]["dst-access-topology-id"] + '",' \ - '"access-topology-id": "' + request["dstPort"]["dst-access-topology-id"]+ '",' \ - '"access-provider-id": "' + request["dstPort"]["dst-access-provider-id"]+ '",' \ - '"access-node-id": "' + request["dstPort"]["dst-access-node-id"]+ '",' \ - '"src-access-ltp-id": "' + egress_p_interface + '",' \ - '"dst-access-ltp-id": "' + request["dstPort"]["dst-access-ltp-id"] + '"' \ - '}'\ - ']'\ - '}' - return data - - - def get_pinterface(self, url): - """ - This method returns details for p interface - :return: details of p interface - """ - aai_req_url = self.aai_host + url - response = requests.get(aai_req_url, - headers=self.aai_headers, - auth=HTTPBasicAuth("AAI", "AAI"), - verify=False) - - if response.status_code == 200: - return response.json() - - - def get_logical_links(self): - """ - This method returns list of all cross ONAP links - from /aai/v14/network/logical-links?operation-status="Up" - :return: logical-links[] - """ - logical_link_url = "/aai/v13/network/logical-links?operational-status=up" - aai_req_url = self.aai_host + logical_link_url - - response = requests.get(aai_req_url, - headers=self.aai_headers, - auth=HTTPBasicAuth("AAI", "AAI"), - verify=False) - - logical_links = None - if response.status_code == 200: - return response.json()
\ No newline at end of file diff --git a/osdf/utils/cipherUtils.py b/osdf/utils/cipherUtils.py new file mode 100644 index 0000000..169f1a1 --- /dev/null +++ b/osdf/utils/cipherUtils.py @@ -0,0 +1,59 @@ +# +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 Crypto.Cipher import AES +from osdf.config.base import osdf_config +from Crypto.Util.Padding import unpad +from Crypto.Util.Padding import pad + + +class AESCipher(object): + __instance = None + + @staticmethod + def get_instance(key = None): + if AESCipher.__instance is None: + print("Creating the singleton instance") + AESCipher(key) + return AESCipher.__instance + + def __init__(self, key=None): + if AESCipher.__instance is not None: + raise Exception("This class is a singleton!") + else: + AESCipher.__instance = self + + self.bs = 32 + if key is None: + key = osdf_config.deployment["appkey"] + + self.key = key.encode() + + def encrypt(self, data): + data = data.encode() + cipher = AES.new(self.key, AES.MODE_CBC) + ciphered_data = cipher.encrypt(pad(data, AES.block_size)) + enc = (cipher.iv.hex())+(ciphered_data.hex()) + return enc + + def decrypt(self, enc): + iv = bytes.fromhex(enc[:32]) + ciphered_data = bytes.fromhex(enc[32:]) + cipher = AES.new(self.key, AES.MODE_CBC, iv=iv) + original_data = unpad(cipher.decrypt(ciphered_data), AES.block_size).decode() + return original_data diff --git a/osdf/utils/data_conversion.py b/osdf/utils/data_conversion.py index 2f678fa..08af3e4 100644 --- a/osdf/utils/data_conversion.py +++ b/osdf/utils/data_conversion.py @@ -16,15 +16,16 @@ # ------------------------------------------------------------------------- # -import itertools from collections import defaultdict +import itertools -from dateutil import tz from dateutil.parser import parse +from dateutil import tz 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}} @@ -38,6 +39,7 @@ def tuples_to_multi_val_dict(kvw_tuples, colnums=(0, 1)): 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) @@ -46,13 +48,17 @@ def tuples_to_dict(kvw_tuples, colnums=(0, 1)): def utc_time_from_ts(timestamp): - """Return corresponding UTC timestamp for a given ISO timestamp (or anything that parse accepts)""" + """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]) +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 = { @@ -60,3 +66,10 @@ text_to_symbol = { 'less': "<", 'equal': "=" } + + +def decode_data(data): + """Decode bytes to string + + """ + return data.decode(encoding='utf-8') if isinstance(data, bytes) else data diff --git a/osdf/utils/file_utils.py b/osdf/utils/file_utils.py new file mode 100644 index 0000000..b12c17d --- /dev/null +++ b/osdf/utils/file_utils.py @@ -0,0 +1,34 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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. +# +# ------------------------------------------------------------------------- +# + +# File related utilities + +import os +from shutil import rmtree + +from osdf.logging.osdf_logging import debug_log + + +def delete_file_folder(p): + if not p: + return + debug_log.debug('Deleting folder/file {}'.format(p)) + if os.path.isfile(p): + os.remove(p) + else: + rmtree(p, ignore_errors=True) diff --git a/osdf/utils/interfaces.py b/osdf/utils/interfaces.py index a869d6d..4fa4730 100644 --- a/osdf/utils/interfaces.py +++ b/osdf/utils/interfaces.py @@ -20,12 +20,15 @@ import json import requests import yaml -from osdf.config.base import osdf_config, creds_prefixes -from osdf.logging.osdf_logging import MH, debug_log +from osdf.config.base import creds_prefixes +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import MH 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 @@ -53,21 +56,23 @@ 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): + method="POST", req_id=None, verify=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.timeout = (30, 120) if timeout is None else timeout self.req_id = req_id + self.verify = verify 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): - """ + """Sends http request to the specified url + :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 @@ -83,16 +88,23 @@ class RestClient(object): else: debug_log.debug("Requesting URL: {} for request ID: {}".format(url or self.url, self.req_id)) + if not url: + url = self.url + if not self.verify and url.startswith("https"): + verify = osdf_config.deployment["aaf_ca_certs"] + else: + verify = self.verify + 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) + timeout=timeout or self.timeout, verify=verify, **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() + raise BaseException(res.raise_for_status()) if raw_response: return res diff --git a/osdf/utils/mdc_utils.py b/osdf/utils/mdc_utils.py new file mode 100644 index 0000000..c74a161 --- /dev/null +++ b/osdf/utils/mdc_utils.py @@ -0,0 +1,155 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 socket +import threading +import time +import uuid + +from flask import g +from onaplogging.mdcContext import MDC + +EMPTY = "EMPTY" + +DATE_FMT = '%Y-%m-%dT%H:%M:%S' + + +def default_server_info(): + """Populate server & server_ip_address MDC fields + + """ + # If not set or purposely set = None, then set default + if MDC.get('server') is None: + try: + server = socket.getfqdn() + except Exception: + try: + server = socket.gethostname() + except Exception: + server = '' + MDC.put('server', server) + if MDC.get('serverIPAddress') is None: + try: + server_ip_address = socket.gethostbyname(MDC.get('server')) + except Exception: + server_ip_address = "" + MDC.put('serverIPAddress', server_ip_address) + + +def default_mdc(): + """Populate default MDC fields + + """ + MDC.put('instanceUUID', generate_uuid()) + MDC.put('InvocationID', generate_uuid()) + MDC.put('serviceName', 'OOF_OSDF') + MDC.put('threadID', threading.currentThread().getName()) + default_server_info() + MDC.put('requestID', generate_uuid()) + MDC.put('partnerName', 'N/A') + MDC.put('entryTimestamp', get_time()) + + +def mdc_from_json(request_json): + """Populate MDC fields given a request in json format + + """ + if MDC.get("instanceUUID") is None: + default_mdc() + MDC.put('requestID', get_request_id(request_json)) + MDC.put('partnerName', get_partner_name(request_json)) + + +def populate_default_mdc(request): + """Populate default MDC fields given the request + + """ + if MDC.get("instanceUUID") is None: + default_mdc() + g.request_start = time.process_time() + g.empty_value = "EMPTY" + g.request_id = MDC.get("requestID") + MDC.put('serviceName', request.path) + MDC.put('IPAddress', request.headers.get('X-Forwarded-For', request.remote_addr)) + + +def populate_mdc(request): + """Populate MDC fields from the request headers + + """ + populate_default_mdc(request) + req_id = request.headers.get('X-ONAP-RequestID', g.empty_value) + request_json = request.get_json() + if req_id == g.empty_value: + req_id = get_request_id(request_json) + g.request_id = req_id + MDC.put('requestID', req_id) + MDC.put('partnerName', get_partner_name(request_json)) + + +def get_request_id(request_json): + """Get the request_id from the request + + """ + request_id = request_json['requestInfo'].get('requestId') + if not request_id: + request_id = request_json['requestInfo'].get('requestID') + return request_id + + +def generate_uuid(): + """Generate an unique uuid + + """ + return f'{uuid.uuid1()}' + + +def get_partner_name(request_json): + """Get the partnerName + + """ + partner_name = EMPTY + if 'requestInfo' in request_json: + partner_name = request_json['requestInfo'].get('sourceId', EMPTY) + return partner_name + + +def clear_mdc(): + """Clear MDC + + """ + MDC.clear() + + +def get_time(): + """Generate the invocation time string + + The dateformat is %Y-%m-%dT%H:%M:%S.sss + """ + ct = time.time() + lt = time.gmtime(ct) + msec = int((ct - int(ct)) * 1000) + return f'{time.strftime(DATE_FMT, lt)}.{msec:0>3}' + + +def set_error_details(code, desc): + """set errorCode and description + + """ + MDC.put('errorCode', code) + MDC.put('errorDescription', desc) diff --git a/osdf/webapp/appcontroller.py b/osdf/webapp/appcontroller.py index 9714fb5..5db879a 100644 --- a/osdf/webapp/appcontroller.py +++ b/osdf/webapp/appcontroller.py @@ -16,14 +16,16 @@ # ------------------------------------------------------------------------- # +import json + +from flask import Response from flask import request from flask_httpauth import HTTPBasicAuth -from flask import Response -import json + import osdf import osdf.config.base as cfg_base -from osdf.config.base import osdf_config from osdf.adapters.aaf import aaf_authentication as aaf_auth +from osdf.config.base import osdf_config auth_basic = HTTPBasicAuth() @@ -35,12 +37,15 @@ error_body = { 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 cfg_base.http_basic_auth_credentials[auth_group].get( - username) if auth_group else None + auth_group = '' + for k in osdf.end_point_auth_mapping: + if k in request.url: + auth_group = osdf.end_point_auth_mapping.get(k) + return cfg_base.http_basic_auth_credentials[auth_group].get(username) if auth_group else None + @auth_basic.error_handler def auth_error(): @@ -58,4 +63,3 @@ def verify_pw(username, password): else: pw = get_pw(username) return pw == password - return False
\ No newline at end of file @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2015-2017 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,79 +22,35 @@ OSDF Manager Main Flask Application """ import json -import ssl -import sys -import traceback -from optparse import OptionParser -from threading import Thread # for scaling up, may need celery with RabbitMQ or redis -import pydevd -from flask import Flask, request, Response, g -from requests import RequestException -from schematics.exceptions import DataError +from threading import Thread # for scaling up, may need celery with RabbitMQ or redis -import osdf.adapters.aaf.sms as sms -import osdf.operation.responses +from flask import request, g + +from osdf.apps.baseapp import app, run_app +from apps.nst.models.api.nstSelectionRequest import NSTSelectionAPI +from apps.nsst.models.api.nsstSelectionRequest import NSSTSelectionAPI +from apps.pci.models.api.pciOptimizationRequest import PCIOptimizationAPI +from apps.nst.optimizers.nst_select_processor import NstSelection +from apps.nsst.optimizers.nsst_select_processor import NsstSelection +from apps.pci.optimizers.pci_opt_processor import process_pci_optimation +from apps.placement.models.api.placementRequest import PlacementAPI +from apps.placement.optimizers.conductor.remote_opt_processor import process_placement_opt +from apps.route.optimizers.inter_domain_route_opt import InterDomainRouteOpt +from apps.route.optimizers.simple_route_opt import RouteOpt +from apps.slice_selection.models.api.nsi_selection_request import NSISelectionAPI +from apps.slice_selection.models.api.nssi_selection_request import NSSISelectionAPI +from apps.slice_selection.optimizers.conductor.remote_opt_processor import SliceSelectionOptimizer from osdf.adapters.policy.interface import get_policies from osdf.adapters.policy.interface import upload_policy_models from osdf.config.base import osdf_config -from osdf.logging.osdf_logging import MH, audit_log, error_log, debug_log -from osdf.models.api.pciOptimizationRequest import PCIOptimizationAPI -from osdf.models.api.placementRequest import PlacementAPI -from osdf.operation.error_handling import request_exception_to_json_body, internal_error_message -from osdf.operation.exceptions import BusinessException +from osdf.config.base import slice_config +from osdf.logging.osdf_logging import MH, audit_log from osdf.operation.responses import osdf_response_for_request_accept as req_accept -from osdf.optimizers.pciopt.pci_opt_processor import process_pci_optimation -from osdf.optimizers.placementopt.conductor.remote_opt_processor import process_placement_opt -from osdf.optimizers.routeopt.simple_route_opt import RouteOpt from osdf.utils import api_data_utils from osdf.webapp.appcontroller import auth_basic - -ERROR_TEMPLATE = osdf.ERROR_TEMPLATE - -app = Flask(__name__) - -BAD_CLIENT_REQUEST_MESSAGE = 'Client sent an invalid request' - - -@app.errorhandler(BusinessException) -def handle_business_exception(e): - """An exception explicitly raised due to some business rule""" - error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) - err_msg = ERROR_TEMPLATE.render(description=str(e)) - response = Response(err_msg, content_type='application/json; charset=utf-8') - response.status_code = 400 - return response - - -@app.errorhandler(RequestException) -def handle_request_exception(e): - """Returns a detailed synchronous message to the calling client - when osdf fails due to a remote call to another system""" - error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) - err_msg = request_exception_to_json_body(e) - response = Response(err_msg, content_type='application/json; charset=utf-8') - response.status_code = 400 - return response - - -@app.errorhandler(DataError) -def handle_data_error(e): - """Returns a detailed message to the calling client when the initial synchronous message is invalid""" - error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) - - body_dictionary = { - "serviceException": { - "text": BAD_CLIENT_REQUEST_MESSAGE, - "exceptionMessage": str(e.errors), - "errorType": "InvalidClientRequest" - } - } - - body_as_json = json.dumps(body_dictionary) - response = Response(body_as_json, content_type='application/json; charset=utf-8') - response.status_code = 400 - return response +from apps.nxi_termination.optimizers.remote_opt_processor import process_nxi_termination_opt +from apps.nxi_termination.models.api.nxi_termination_request import NxiTerminationApi @app.route("/api/oof/v1/healthcheck", methods=["GET"]) @@ -145,22 +102,67 @@ def placement_rest_api(): version_info=api_version_info, request_status="accepted", status_message="") -@app.route("/api/oof/v1/route", methods=["POST"]) +@app.route("/api/oof/route/v1", methods=["POST"]) def do_route_calc(): """ Perform the basic route calculations and returnn the vpn-bindings """ request_json = request.get_json() audit_log.info("Calculate Route request received!") - return RouteOpt().getRoute(request_json) + response = RouteOpt().get_route(request_json, osdf_config) + return response + + +@app.route("/api/oof/mdons/route/v1", methods=["POST"]) +def do_mdons_route_calc(): + """ + Perform the inter domain route calculation + """ + request_json = request.get_json() + audit_log.info("Inter Domain Calculation Route request received!") + response = InterDomainRouteOpt().get_route(request_json, osdf_config) + return response + + +@app.route("/api/oof/v1/selection/nst", methods=["POST"]) +def do_nst_selection(): + request_json = request.get_json() + req_id = request_json['requestInfo']['requestId'] + audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json))) + NSTSelectionAPI(request_json).validate() + audit_log.info(MH.new_worker_thread(req_id, "[for NST selection]")) + nst_selection = NstSelection(osdf_config, request_json) + nst_selection.start() + return req_accept(request_id=req_id, + transaction_id=request_json['requestInfo']['transactionId'], + request_status="accepted", status_message="") +@app.route("/api/oof/v1/selection/nsst", methods=["POST"]) +def do_nsst_selection(): + request_json = request.get_json() + req_id = request_json['requestInfo']['requestId'] + audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json))) + NSSTSelectionAPI(request_json).validate() + audit_log.info(MH.new_worker_thread(req_id, "[for NSST selection]")) + nsst_selection = NsstSelection(osdf_config, request_json) + nsst_selection.start() + return req_accept(request_id=req_id, + transaction_id=request_json['requestInfo']['transactionId'], + request_status="accepted", status_message="") + @app.route("/api/oof/v1/pci", methods=["POST"]) @app.route("/api/oof/pci/v1", methods=["POST"]) @auth_basic.login_required def do_pci_optimization(): request_json = request.get_json() + audit_log.info('request json obtained==>') + audit_log.info(request_json) + req_id = request_json['requestInfo']['requestId'] + audit_log.info('requestID obtained==>') + audit_log.info(req_id) + g.request_id = req_id audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json))) PCIOptimizationAPI(request_json).validate() @@ -170,64 +172,51 @@ def do_pci_optimization(): t = Thread(target=process_pci_optimation, args=(request_json, osdf_config, None)) t.start() audit_log.info(MH.accepted_valid_request(req_id, request)) + audit_log.info('reached upto return') return req_accept(request_id=req_id, transaction_id=request_json['requestInfo']['transactionId'], request_status="accepted", status_message="") -@app.errorhandler(500) -def internal_failure(error): - """Returned when unexpected coding errors occur during initial synchronous processing""" - error_log.error("Synchronous error for request id {} {}".format(g.request_id, traceback.format_exc())) - response = Response(internal_error_message, content_type='application/json; charset=utf-8') - response.status_code = 500 - return response - - -def get_options(argv): - program_version_string = '%%prog %s' % "v1.0" - program_longdesc = "" - program_license = "" +@app.route("/api/oof/selection/nsi/v1", methods=["POST"]) +def do_nsi_selection(): + request_json = request.get_json() + req_id = request_json['requestInfo']['requestId'] + g.request_id = req_id + audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json))) + NSISelectionAPI(request_json).validate() + audit_log.info(MH.new_worker_thread(req_id, "[for NSI selection]")) + slice_opt = SliceSelectionOptimizer(osdf_config, slice_config, request_json, 'NSI') + slice_opt.start() + return req_accept(request_id=req_id, + transaction_id=request_json['requestInfo']['transactionId'], + request_status="accepted", status_message="") - parser = OptionParser(version=program_version_string, epilog=program_longdesc, description=program_license) - parser.add_option("-l", "--local", dest="local", help="run locally", action="store_true", default=False) - parser.add_option("-t", "--devtest", dest="devtest", help="run in dev/test environment", action="store_true", - default=False) - parser.add_option("-d", "--debughost", dest="debughost", help="IP Address of host running debug server", default='') - parser.add_option("-p", "--debugport", dest="debugport", help="Port number of debug server", type=int, default=5678) - opts, args = parser.parse_args(argv) - if opts.debughost: - debug_log.debug('pydevd.settrace({}, port={})'.format(opts.debughost, opts.debugport)) - pydevd.settrace(opts.debughost, port=opts.debugport) - return opts +@app.route("/api/oof/selection/nssi/v1", methods=["POST"]) +def do_nssi_selection(): + request_json = request.get_json() + req_id = request_json['requestInfo']['requestId'] + g.request_id = req_id + audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json))) + NSSISelectionAPI(request_json).validate() + audit_log.info(MH.new_worker_thread(req_id, "[for NSSI selection]")) + slice_opt = SliceSelectionOptimizer(osdf_config, slice_config, request_json, 'NSSI') + slice_opt.start() + return req_accept(request_id=req_id, + transaction_id=request_json['requestInfo']['transactionId'], + request_status="accepted", status_message="") -def build_ssl_context(): - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) - ssl_context.set_ciphers('ECDHE-RSA-AES128-SHA256:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH') - ssl_context.load_cert_chain(sys_conf['ssl_context'][0], sys_conf['ssl_context'][1]) - return ssl_context +@app.route("/api/oof/terminate/nxi/v1",methods=["POST"]) +def do_nxi_terminaton(): + request_json = request.get_json() + req_id = request_json['requestInfo']['requestId'] + g.request_id = req_id + audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json))) + NxiTerminationApi(request_json).validate() + return process_nxi_termination_opt(request_json,osdf_config) if __name__ == "__main__": - - sys_conf = osdf_config['core']['osdf_system'] - ports = sys_conf['osdf_ports'] - internal_port, external_port = ports['internal'], ports['external'] - - local_host = sys_conf['osdf_ip_default'] - common_app_opts = dict(host=local_host, threaded=True, use_reloader=False) - - ssl_opts = sys_conf.get('ssl_context') - if ssl_opts: - common_app_opts.update({'ssl_context': build_ssl_context()}) - - opts = get_options(sys.argv) - # Load secrets from SMS - sms.load_secrets() - if not opts.local and not opts.devtest: # normal deployment - app.run(port=internal_port, debug=False, **common_app_opts) - else: - port = internal_port if opts.local else external_port - app.run(port=port, debug=True, **common_app_opts) + run_app() @@ -18,27 +18,60 @@ # ------------------------------------------------------------------------- # +usage() { + echo "Usage:" + echo " $0 -h Display this help message." + echo " $0 -c configfile_path(optional) -x app.py file" + exit 0 +} + cd $(dirname $0) # bash ../etc/make-certs.sh # create the https certificates if they are not present +while getopts ":hc:x:" opt; do + case ${opt} in + h ) + usage + ;; + c ) + # process option configuration + export OSDF_CONFIG_FILE=$OPTARG + ;; + x ) + # process executable file + export EXEC_FILE=$OPTARG + ;; + ? ) + usage + ;; + : ) + echo "Invalid Option: -$OPTARG requires an argument" 1>&2 + exit 1 + ;; + esac +done +shift $(( OPTIND - 1 )) + +set -e + LOGS=logs mkdir -p $LOGS -export OSDF_CONFIG_FILE=${1:-/opt/app/config/osdf_config.yaml} # this file may be passed by invoker -[ ! -e "$OSDF_CONFIG_FILE" ] && unset OSDF_CONFIG_FILE +#if [ -e /opt/app/ssl_cert/aaf_root_ca.cer ]; then +# #assuming that this would be an ubuntu vm. +# cp /opt/app/ssl_cert/aaf_root_ca.cer /usr/local/share/ca-certificates/aafcacert.crt +# chmod 444 /usr/local/share/ca-certificates/aafcacert.crt +# update-ca-certificates +#fi -if [ -e /opt/app/ssl_cert/aaf_root_ca.cer ]; then - #assuming that this would be an ubuntu vm. - cp /opt/app/ssl_cert/aaf_root_ca.cer /usr/local/share/ca-certificates/aafcacert.crt - chmod 444 /usr/local/share/ca-certificates/aafcacert.crt - update-ca-certificates -fi +export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt -if [ -e /etc/ssl/certs/aafcacert.pem ]; then - export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt +if [ ! -z "$EXEC_FILE" ] +then + # flask run + echo "Running $EXEC_FILE" + python $EXEC_FILE # running the app else - export REQUESTS_CA_BUNDLE=/opt/app/ssl_cert/aaf_root_ca.cer + usage fi - -python osdfapp.py 2>$LOGS/err.log 1>$LOGS/out.log < /dev/null # running the app @@ -14,71 +14,201 @@ License for the specific language governing permissions and limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <packaging>pom</packaging> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd +http://maven.apache.org/POM/4.0.0 "> + <modelVersion>4.0.0</modelVersion> + <packaging>pom</packaging> - <parent> - <groupId>org.onap.oparent</groupId> - <artifactId>oparent-python</artifactId> - <version>2.0.0</version> - </parent> + <parent> + <groupId>org.onap.oparent</groupId> + <artifactId>oparent-python</artifactId> + <version>3.0.0</version> + </parent> - <groupId>org.onap.optf.osdf</groupId> - <artifactId>optf-osdf</artifactId> - <name>optf-osdf</name> - <version>1.3.2-SNAPSHOT</version> - <description>Optimization Service Design Framework</description> + <groupId>org.onap.optf.osdf</groupId> + <artifactId>optf-osdf</artifactId> + <name>optf-osdf</name> + <version>3.0.8-SNAPSHOT</version> + <description>Optimization Service Design Framework</description> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <sonar.sources>.</sonar.sources> - <sonar.junit.reportsPath>xunit-results.xml</sonar.junit.reportsPath> - <sonar.python.coverage.reportPath>coverage.xml</sonar.python.coverage.reportPath> - <sonar.language>py</sonar.language> - <sonar.pluginname>python</sonar.pluginname> - <sonar.inclusions>**/**.py,osdfapp.py</sonar.inclusions> - <sonar.exclusions>test/**.py</sonar.exclusions> - </properties> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <sonar.sources>.</sonar.sources> + <sonar.junit.reportsPath>xunit-results.xml</sonar.junit.reportsPath> + <sonar.python.coverage.reportPaths>coverage.xml</sonar.python.coverage.reportPaths> + <sonar.language>py</sonar.language> + <sonar.pluginname>python</sonar.pluginname> + <sonar.inclusions>**/**.py,osdfapp.py</sonar.inclusions> + <sonar.exclusions>test/**.py,docs/**.py</sonar.exclusions> + <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format> + <osdf.build.timestamp>${maven.build.timestamp}</osdf.build.timestamp> + <osdf.project.version>${project.version}</osdf.project.version> + <osdf.docker.repository>nexus3.onap.org:10003</osdf.docker.repository> + <osdf.base.image>osdf-base</osdf.base.image> + <image.namespace>${osdf.docker.repository}/onap/optf-osdf</image.namespace> + <opteng.namespace>${osdf.docker.repository}/onap/optf-opteng</opteng.namespace> + </properties> - <build> - <plugins> - <!-- triggers tox test for sonar --> - <plugin> - <artifactId>exec-maven-plugin</artifactId> - <groupId>org.codehaus.mojo</groupId> - </plugin> - <plugin> - <artifactId>maven-assembly-plugin</artifactId> - <configuration> - <appendAssemblyId>false</appendAssemblyId> - <descriptors> - <descriptor>assembly.xml</descriptor> - </descriptors> - </configuration> - <executions> - <execution> - <id>make-assembly</id> - <phase>package</phase> - <goals> - <goal>single</goal> - </goals> - </execution> - </executions> - </plugin> + <build> + <plugins> + <!-- triggers tox test for sonar --> + <plugin> + <artifactId>exec-maven-plugin</artifactId> + <groupId>org.codehaus.mojo</groupId> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <appendAssemblyId>false</appendAssemblyId> + <descriptors> + <descriptor>assembly.xml</descriptor> + </descriptors> + </configuration> + <executions> + <execution> + <id>make-assembly</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-release-plugin</artifactId> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-deploy-plugin</artifactId> - <version>2.8</version> - <configuration> - <retryFailedDeploymentCount>2</retryFailedDeploymentCount> - </configuration> - </plugin> - </plugins> - </build> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>2.8</version> + <configuration> + <retryFailedDeploymentCount>2</retryFailedDeploymentCount> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.groovy.maven</groupId> + <artifactId>gmaven-plugin</artifactId> + <version>1.0</version> + <executions> + <execution> + <phase>validate</phase> + <goals> + <goal>execute</goal> + </goals> + <configuration> + <source>${project.basedir}/script/TagVersion.groovy</source> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>io.fabric8</groupId> + <artifactId>docker-maven-plugin</artifactId> + <version>0.26.0</version> + <configuration> + <verbose>true</verbose> + <apiVersion>1.23</apiVersion> + <images> + <image> + <name>${osdf.base.image}</name> + <alias>optf-base</alias> + <build> + <cleanup>true</cleanup> + <tags> + <tag>latest</tag> + </tags> + + <dockerFile>${project.basedir}/docker/osdf-lib-base/Dockerfile</dockerFile> + <assembly> + <descriptor>${project.basedir}/docker/osdf-lib-base/assembly/osdf-lib-files.xml</descriptor> + <name>onap-osdf-tm</name> + </assembly> + <args> + <MVN_ARTIFACT_VERSION>${project.version}</MVN_ARTIFACT_VERSION> + <REPO>${project.repo}</REPO> + </args> + </build> + </image> + <image> + <name>${image.namespace}</name> + <alias>optf-osdf</alias> + <build> + <cleanup>true</cleanup> + <tags> + <tag>latest</tag> + <tag>${project.docker.latesttagtimestamp.version}</tag> + <tag>${project.docker.latesttag.version}</tag> + </tags> + + <dockerFile>${project.basedir}/docker/osdf/Dockerfile</dockerFile> + <assembly> + <descriptor>${project.basedir}/docker/osdf/assembly/osdf-files.xml</descriptor> + <name>onap-osdf-tm</name> + </assembly> + <args> + <MVN_ARTIFACT_VERSION>${project.version}</MVN_ARTIFACT_VERSION> + <REPO>${project.repo}</REPO> + + <!-- plugin cannot handle empty (no proxy) arguments + <http_proxy_arg>${docker.http_proxy}</http_proxy_arg> + <https_proxy_arg>${docker.https_proxy}</https_proxy_arg> + --> + </args> + </build> + </image> + <image> + <name>${opteng.namespace}</name> + <alias>optf-opteng</alias> + <build> + <cleanup>true</cleanup> + <tags> + <tag>latest</tag> + <tag>${project.docker.latesttagtimestamp.version}</tag> + <tag>${project.docker.latesttag.version}</tag> + </tags> + + <dockerFile>${project.basedir}/docker/opteng/Dockerfile</dockerFile> + <assembly> + <descriptor>${project.basedir}/docker/opteng/assembly/osdf-files.xml</descriptor> + <name>onap-osdf-tm</name> + </assembly> + <args> + <MVN_ARTIFACT_VERSION>${project.version}</MVN_ARTIFACT_VERSION> + <REPO>${project.repo}</REPO> + + <!-- plugin cannot handle empty (no proxy) arguments + <http_proxy_arg>${docker.http_proxy}</http_proxy_arg> + <https_proxy_arg>${docker.https_proxy}</https_proxy_arg> + --> + </args> + </build> + </image> + </images> + </configuration> + <executions> + <execution> + <id>generate-images</id> + <phase>install</phase> + <goals> + <goal>build</goal> + </goals> + </execution> + <execution> + <id>push-images</id> + <phase>deploy</phase> + <goals> + <goal>build</goal> + <goal>push</goal> + </goals> + <configuration> + <image>${image.namespace}:%l</image> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> </project> diff --git a/releases/1.3.3.yaml b/releases/1.3.3.yaml new file mode 100644 index 0000000..bcff0af --- /dev/null +++ b/releases/1.3.3.yaml @@ -0,0 +1,6 @@ +distribution_type: 'maven' +version: '1.3.3' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/97/' +maven_central_url: 'oss.sonatype.org' + diff --git a/releases/1.3.4.yaml b/releases/1.3.4.yaml new file mode 100644 index 0000000..c7da426 --- /dev/null +++ b/releases/1.3.4.yaml @@ -0,0 +1,5 @@ +distribution_type: 'maven' +version: '1.3.4' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/106/' + diff --git a/releases/2.0.0-container.yaml b/releases/2.0.0-container.yaml new file mode 100644 index 0000000..4f458d2 --- /dev/null +++ b/releases/2.0.0-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '2.0.0' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/150/' +ref: 6e84d892b5f8e3a2f90fb36b6c7a642c800c2f9c +containers: + - name: 'optf-osdf' + version: '2.0.0-STAGING-20200316T162605Z' + - name: 'optf-opteng' + version: '2.0.0-STAGING-20200316T162605Z'
\ No newline at end of file diff --git a/releases/2.0.0.yaml b/releases/2.0.0.yaml new file mode 100644 index 0000000..fee446c --- /dev/null +++ b/releases/2.0.0.yaml @@ -0,0 +1,4 @@ +distribution_type: 'maven' +version: '2.0.0' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/329/'
\ No newline at end of file diff --git a/releases/2.0.1-container.yaml b/releases/2.0.1-container.yaml new file mode 100644 index 0000000..b2fe40c --- /dev/null +++ b/releases/2.0.1-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '2.0.1' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/160/' +ref: fcb37e97e37137d3111924e993e75fdb83c2a0a0 +containers: + - name: 'optf-osdf' + version: '2.0.1-STAGING-20200325T192323Z' + - name: 'optf-opteng' + version: '2.0.1-STAGING-20200325T192323Z'
\ No newline at end of file diff --git a/releases/2.0.1.yaml b/releases/2.0.1.yaml new file mode 100644 index 0000000..86f3f47 --- /dev/null +++ b/releases/2.0.1.yaml @@ -0,0 +1,4 @@ +distribution_type: 'maven' +version: '2.0.1' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/340/'
\ No newline at end of file diff --git a/releases/2.0.2-container.yaml b/releases/2.0.2-container.yaml new file mode 100644 index 0000000..9e44d61 --- /dev/null +++ b/releases/2.0.2-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '2.0.2' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/163/' +ref: ddac631c097167df890cf6814df301b61a0815c7 +containers: + - name: 'optf-osdf' + version: '2.0.2-STAGING-20200327T232752Z' + - name: 'optf-opteng' + version: '2.0.2-STAGING-20200327T232752Z'
\ No newline at end of file diff --git a/releases/2.0.3-container.yaml b/releases/2.0.3-container.yaml new file mode 100644 index 0000000..30d8603 --- /dev/null +++ b/releases/2.0.3-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '2.0.3' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/185/' +ref: d79e6bda8b636d77d51ddb63dcd3d91ddda34d83 +containers: + - name: 'optf-osdf' + version: '2.0.3-STAGING-20200415T161453Z' + - name: 'optf-opteng' + version: '2.0.3-STAGING-20200415T161453Z'
\ No newline at end of file diff --git a/releases/2.0.3.yaml b/releases/2.0.3.yaml new file mode 100644 index 0000000..5a2d322 --- /dev/null +++ b/releases/2.0.3.yaml @@ -0,0 +1,4 @@ +distribution_type: 'maven' +version: '2.0.3' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/365/'
\ No newline at end of file diff --git a/releases/2.0.4-container.yaml b/releases/2.0.4-container.yaml new file mode 100644 index 0000000..b792d55 --- /dev/null +++ b/releases/2.0.4-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '2.0.4' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/222/' +ref: acb4f63122e205acf0bc4f8649f746bd5bccd486 +containers: + - name: 'optf-osdf' + version: '2.0.4-STAGING-20200521T185101Z' + - name: 'optf-opteng' + version: '2.0.4-STAGING-20200521T185101Z'
\ No newline at end of file diff --git a/releases/2.0.4.yaml b/releases/2.0.4.yaml new file mode 100644 index 0000000..b96f810 --- /dev/null +++ b/releases/2.0.4.yaml @@ -0,0 +1,4 @@ +distribution_type: 'maven' +version: '2.0.4' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/402/'
\ No newline at end of file diff --git a/releases/3.0.0-container.yaml b/releases/3.0.0-container.yaml new file mode 100644 index 0000000..fb9e198 --- /dev/null +++ b/releases/3.0.0-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.0' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/346/' +ref: c9b6e78051ea314bc7f20d1e273f51c9017e2e5b +containers: + - name: 'optf-osdf' + version: '3.0.0-STAGING-20200921T105356Z' + - name: 'optf-opteng' + version: '3.0.0-STAGING-20200921T105356Z' diff --git a/releases/3.0.0.yaml b/releases/3.0.0.yaml new file mode 100644 index 0000000..85993d0 --- /dev/null +++ b/releases/3.0.0.yaml @@ -0,0 +1,4 @@ +distribution_type: 'maven' +version: '3.0.0' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-stage-master/525/' diff --git a/releases/3.0.1-container.yaml b/releases/3.0.1-container.yaml new file mode 100644 index 0000000..f065265 --- /dev/null +++ b/releases/3.0.1-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.1' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/364/' +ref: e5a3e67f106f88a1143da7ee2f7248fe8dc1920d +containers: + - name: 'optf-osdf' + version: '3.0.1-STAGING-20201008T142633Z' + - name: 'optf-opteng' + version: '3.0.1-STAGING-20201008T142633Z' diff --git a/releases/3.0.2-container.yaml b/releases/3.0.2-container.yaml new file mode 100644 index 0000000..c6f66c3 --- /dev/null +++ b/releases/3.0.2-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.2' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/404/' +ref: dbdc2d26632175ab01d345b88ebf02ee8f95454a +containers: + - name: 'optf-osdf' + version: '3.0.2-STAGING-20201116T133002Z' + - name: 'optf-opteng' + version: '3.0.2-STAGING-20201116T133002Z' diff --git a/releases/3.0.3-container.yaml b/releases/3.0.3-container.yaml new file mode 100644 index 0000000..268c71c --- /dev/null +++ b/releases/3.0.3-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.3' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/504/' +ref: 818489f9580f62c71ee6d6ad7a9e6e080e4c2107 +containers: + - name: 'optf-osdf' + version: '3.0.3-STAGING-20210223T072953Z' + - name: 'optf-opteng' + version: '3.0.3-STAGING-20210223T072953Z' diff --git a/releases/3.0.4-container.yaml b/releases/3.0.4-container.yaml new file mode 100644 index 0000000..6c32fb5 --- /dev/null +++ b/releases/3.0.4-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.4' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/520/' +ref: e5182a3ed5333c7da2e043a58d8df8d76e7f6e2f +containers: + - name: 'optf-osdf' + version: '3.0.4-STAGING-20210310T054208Z' + - name: 'optf-opteng' + version: '3.0.4-STAGING-20210310T054208Z' diff --git a/releases/3.0.5-container.yaml b/releases/3.0.5-container.yaml new file mode 100644 index 0000000..4d58a70 --- /dev/null +++ b/releases/3.0.5-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.5' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/663/' +ref: 9f6fccf6a2609fbd2c9e50cb7f27d15b040133bb +containers: + - name: 'optf-osdf' + version: '3.0.5-STAGING-20210730T065718Z' + - name: 'optf-opteng' + version: '3.0.5-STAGING-20210730T065718Z' diff --git a/releases/3.0.6-container.yaml b/releases/3.0.6-container.yaml new file mode 100644 index 0000000..0dcd634 --- /dev/null +++ b/releases/3.0.6-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.6' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/732/' +ref: 93997d04f30539b8947f3084d2397b34d9d75e27 +containers: + - name: 'optf-osdf' + version: '3.0.6-STAGING-20211007T114744Z' + - name: 'optf-opteng' + version: '3.0.6-STAGING-20211007T114744Z' diff --git a/releases/3.0.7-container.yaml b/releases/3.0.7-container.yaml new file mode 100644 index 0000000..2dbab8f --- /dev/null +++ b/releases/3.0.7-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.7' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/1078/' +ref: ba5deeadc1eef53a2f44ba92e6ede9026b56bc57 +containers: + - name: 'optf-osdf' + version: '3.0.7-STAGING-20220922T095920Z' + - name: 'optf-opteng' + version: '3.0.7-STAGING-20220922T095920Z' diff --git a/releases/3.0.8-container.yaml b/releases/3.0.8-container.yaml new file mode 100644 index 0000000..e1969a1 --- /dev/null +++ b/releases/3.0.8-container.yaml @@ -0,0 +1,10 @@ +distribution_type: 'container' +container_release_tag: '3.0.8' +project: 'optf-osdf' +log_dir: 'optf-osdf-maven-docker-stage-master/1226/' +ref: dee386685502c63d64959e0f2324bf52440e9655 +containers: + - name: 'optf-osdf' + version: '3.0.8-STAGING-20230217T053828Z' + - name: 'optf-opteng' + version: '3.0.8-STAGING-20230217T053828Z' diff --git a/requirements-opteng.txt b/requirements-opteng.txt new file mode 100644 index 0000000..a20435b --- /dev/null +++ b/requirements-opteng.txt @@ -0,0 +1 @@ +mysql-connector-python==8.0.31 diff --git a/requirements-osdf.txt b/requirements-osdf.txt new file mode 100644 index 0000000..c095258 --- /dev/null +++ b/requirements-osdf.txt @@ -0,0 +1 @@ +scikit-learn>=0.22.0
\ No newline at end of file diff --git a/requirements-sim.txt b/requirements-sim.txt new file mode 100644 index 0000000..56a8931 --- /dev/null +++ b/requirements-sim.txt @@ -0,0 +1,18 @@ +cryptography==3.3.2 +docutils>=0.12 +docopt>=0.6.2 +Flask>=0.11.1 +Flask-HTTPAuth>=3.2.2 +jsonschema>=2.5.1 +lxml>=3.6.4 +python-dateutil>=2.5.3 +PyYAML==5.4.1 +requests>=2.14.2 +schematics>=2.0.0 +onapsmsclient>=0.0.4 +pymzn>=0.18.3 +onappylog>=1.0.9 +pathtools>=0.1.2 +pycryptodome>=3.9.6 +python-consul>=1.1.0 +tornado>=6.1 diff --git a/requirements.txt b/requirements.txt index 241392b..56a8931 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,18 @@ +cryptography==3.3.2 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 +PyYAML==5.4.1 requests>=2.14.2 schematics>=2.0.0 -docopt>=0.6.2 -pydevd==1.4.0 onapsmsclient>=0.0.4 -pymzn==0.17.1 +pymzn>=0.18.3 +onappylog>=1.0.9 +pathtools>=0.1.2 +pycryptodome>=3.9.6 +python-consul>=1.1.0 +tornado>=6.1 diff --git a/osdf/optimizers/placementopt/conductor/__init__.py b/runtime/__init__.py index 4b25e5b..2aa67d8 100644 --- a/osdf/optimizers/placementopt/conductor/__init__.py +++ b/runtime/__init__.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (c) 2020 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. diff --git a/runtime/model_api.py b/runtime/model_api.py new file mode 100644 index 0000000..b0492f2 --- /dev/null +++ b/runtime/model_api.py @@ -0,0 +1,221 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 traceback + +from flask import Flask +from flask import g +from flask import Response +import mysql.connector + +from osdf.config.base import osdf_config +from osdf.logging.osdf_logging import debug_log +from osdf.logging.osdf_logging import error_log +from osdf.operation.exceptions import BusinessException +from osdf.utils.data_conversion import decode_data + + +def init_db(): + if is_db_enabled(): + get_db() + + +def get_db(): + """Opens a new database connection if there is none yet for the current application context. + + """ + if not hasattr(g, 'pg'): + properties = osdf_config['deployment'] + host, db_port, db = properties["osdfDatabaseHost"], properties["osdfDatabasePort"], properties.get( + "osdfDatabaseSchema") + user, password = properties["osdfDatabaseUsername"], properties["osdfDatabasePassword"] + g.pg = mysql.connector.connect(host=host, port=db_port, user=user, password=password, database=db) + return g.pg + + +def close_db(): + """Closes the database again at the end of the request. + + """ + if hasattr(g, 'pg'): + g.pg.close() + + +app = Flask(__name__) + + +def create_model_data(model_api): + with app.app_context(): + try: + model_info = model_api['modelInfo'] + model_id = model_info['modelId'] + debug_log.debug( + "persisting model_api {}".format(model_id)) + connection = get_db() + cursor = connection.cursor(buffered=True) + query = "SELECT model_id FROM optim_model_data WHERE model_id = %s" + values = (model_id,) + cursor.execute(query, values) + if cursor.fetchone() is None: + query = "INSERT INTO optim_model_data (model_id, model_content, description, solver_type) VALUES " \ + "(%s, %s, %s, %s)" + values = (model_id, model_info['modelContent'], model_info.get('description'), model_info['solver']) + cursor.execute(query, values) + g.pg.commit() + + debug_log.debug("A record successfully inserted for request_id: {}".format(model_id)) + return retrieve_model_data(model_id) + close_db() + else: + query = "UPDATE optim_model_data SET model_content = %s, description = %s, solver_type = %s where " \ + "model_id = %s " + values = (model_info['modelContent'], model_info.get('description'), model_info['solver'], model_id) + cursor.execute(query, values) + g.pg.commit() + + return retrieve_model_data(model_id) + close_db() + except Exception as err: + error_log.error("error for request_id: {} - {}".format(model_id, traceback.format_exc())) + close_db() + raise BusinessException(err) + + +def retrieve_model_data(model_id): + status, resp_data = get_model_data(model_id) + + if status == 200: + resp = json.dumps(build_model_dict(resp_data)) + return build_response(resp, status) + else: + resp = json.dumps({ + 'modelId': model_id, + 'statusMessage': "Error retrieving the model data for model {} due to {}".format(model_id, resp_data) + }) + return build_response(resp, status) + + +def build_model_dict(resp_data, content_needed=True): + resp = {'modelId': resp_data[0], 'description': resp_data[2] if resp_data[2] else '', + 'solver': resp_data[3]} + if content_needed: + resp.update({'modelContent': decode_data(resp_data[1])}) + return resp + + +def build_response(resp, status): + response = Response(resp, content_type='application/json; charset=utf-8') + response.headers.add('content-length', len(resp)) + response.status_code = status + return response + + +def delete_model_data(model_id): + with app.app_context(): + try: + debug_log.debug("deleting model data given model_id = {}".format(model_id)) + connection = get_db() + cursor = connection.cursor(buffered=True) + query = "delete from optim_model_data WHERE model_id = %s" + values = (model_id,) + cursor.execute(query, values) + g.pg.commit() + close_db() + resp = { + "statusMessage": "model data for modelId {} deleted".format(model_id) + } + return build_response(json.dumps(resp), 200) + except Exception as err: + error_log.error("error deleting model_id: {} - {}".format(model_id, traceback.format_exc())) + close_db() + raise BusinessException(err) + + +def get_model_data(model_id): + with app.app_context(): + try: + debug_log.debug("getting model data given model_id = {}".format(model_id)) + d = dict() + connection = get_db() + cursor = connection.cursor(buffered=True) + query = "SELECT model_id, model_content, description, " \ + "solver_type FROM optim_model_data WHERE model_id = %s" + values = (model_id,) + cursor.execute(query, values) + if cursor is None: + return 400, "FAILED" + else: + rows = cursor.fetchone() + if rows is not None: + index = 0 + for row in rows: + d[index] = row + index = index + 1 + return 200, d + else: + close_db() + return 500, "NOT_FOUND" + except Exception: + error_log.error("error for request_id: {} - {}".format(model_id, traceback.format_exc())) + close_db() + return 500, "FAILED" + + +def retrieve_all_models(): + status, resp_data = get_all_models() + model_list = [] + if status == 200: + for r in resp_data: + model_list.append(build_model_dict(r, False)) + resp = json.dumps(model_list) + return build_response(resp, status) + + else: + resp = json.dumps({ + 'statusMessage': "Error retrieving all the model data due to {}".format(resp_data) + }) + return build_response(resp, status) + + +def get_all_models(): + with app.app_context(): + try: + debug_log.debug("getting all model data".format()) + connection = get_db() + cursor = connection.cursor(buffered=True) + query = "SELECT model_id, model_content, description, solver_type FROM optim_model_data" + + cursor.execute(query) + if cursor is None: + return 400, "FAILED" + else: + rows = cursor.fetchall() + if rows is not None: + return 200, rows + else: + close_db() + return 500, "NOT_FOUND" + except Exception: + error_log.error("error for request_id: {}".format(traceback.format_exc())) + close_db() + return 500, "FAILED" + + +def is_db_enabled(): + return osdf_config['deployment'].get('isDatabaseEnabled', False) diff --git a/runtime/models/__init__.py b/runtime/models/__init__.py new file mode 100644 index 0000000..2aa67d8 --- /dev/null +++ b/runtime/models/__init__.py @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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/runtime/models/api/__init__.py b/runtime/models/api/__init__.py new file mode 100644 index 0000000..2aa67d8 --- /dev/null +++ b/runtime/models/api/__init__.py @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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/runtime/models/api/model_request.py b/runtime/models/api/model_request.py new file mode 100644 index 0000000..710da4b --- /dev/null +++ b/runtime/models/api/model_request.py @@ -0,0 +1,48 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 schematics.types import StringType +from schematics.types.compound import ModelType + +from osdf.models.api.common import OSDFModel + + +class RequestInfo(OSDFModel): + """Info for northbound request from client such as PCI-mS Handler""" + transactionId = StringType(required=True) + requestID = StringType(required=True) + sourceId = StringType(required=True) + + +class OptimModelInfo(OSDFModel): + """Optimizer request info details.""" + # ModelId from the database + modelId = StringType() + # type of solver (mzn, or-tools, etc.) + solver = StringType(required=True) + # Description of the model + description = StringType() + # a large blob string containing the model (which is not that + # problematic since models are fairly small). + modelContent = StringType() + + +class OptimModelRequestAPI(OSDFModel): + """Request for Optimizer API (specific to optimization and additional metadata""" + requestInfo = ModelType(RequestInfo, required=True) + modelInfo = ModelType(OptimModelInfo, required=True) diff --git a/runtime/models/api/model_response.py b/runtime/models/api/model_response.py new file mode 100644 index 0000000..e4a41a5 --- /dev/null +++ b/runtime/models/api/model_response.py @@ -0,0 +1,31 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 schematics.types import StringType + +from osdf.models.api.common import OSDFModel + + +class OptimModelResponse(OSDFModel): + modelId = StringType() + # type of solver (mzn, or-tools, etc.) + solver = StringType() + # a large blob string containing the model + modelContent = StringType() + # statusMessage + statusMessage = StringType() diff --git a/runtime/models/api/optim_request.py b/runtime/models/api/optim_request.py new file mode 100644 index 0000000..4a046d2 --- /dev/null +++ b/runtime/models/api/optim_request.py @@ -0,0 +1,60 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 schematics.types import BaseType, DictType, StringType, IntType +from schematics.types.compound import ModelType + +from osdf.models.api.common import OSDFModel + +""" +""" +class RequestInfo(OSDFModel): + """Info for northbound request from client """ + transactionId = StringType(required=True) + requestID = StringType(required=True) + callbackUrl = StringType() + sourceId = StringType(required=True) + timeout = IntType() + + +class DataInfo(OSDFModel): + """Optimization data info""" + text = StringType() + json = DictType(BaseType) + + +class OptimInfo(OSDFModel): + """Optimizer request info details.""" + # ModelId from the database, if its not populated, + # assume that solverModel will be populated. + modelId = StringType() + # type of solver (mzn, or-tools, etc.) + solver = StringType() + # Arguments for solver + solverArgs = DictType(BaseType) + # NOTE: a large blob string containing the model (which is not that + # problematic since models are fairly small). + modelContent = StringType() + # Data Payload, input data for the solver + optData = ModelType(DataInfo) + + +class OptimizationAPI(OSDFModel): + """Request for Optimizer API (specific to optimization and additional metadata""" + requestInfo = ModelType(RequestInfo, required=True) + optimInfo = ModelType(OptimInfo, required=True) diff --git a/runtime/models/api/optim_response.py b/runtime/models/api/optim_response.py new file mode 100644 index 0000000..6fd0f6b --- /dev/null +++ b/runtime/models/api/optim_response.py @@ -0,0 +1,30 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 schematics.types import StringType, BaseType +from schematics.types.compound import DictType + +from osdf.models.api.common import OSDFModel + + +class OptimResponse(OSDFModel): + transactionId = StringType(required=True) + requestID = StringType(required=True) + requestStatus = StringType(required=True) + statusMessage = StringType() + solutions = DictType(BaseType) diff --git a/runtime/optim_engine.py b/runtime/optim_engine.py new file mode 100644 index 0000000..b303bbf --- /dev/null +++ b/runtime/optim_engine.py @@ -0,0 +1,80 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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.operation.exceptions import BusinessException +from osdf.utils.data_conversion import decode_data +from runtime.model_api import get_model_data +from runtime.models.api.optim_request import OptimizationAPI +from runtime.solvers.mzn.mzn_solver import solve as mzn_solve +from runtime.solvers.py.py_solver import solve as py_solve + + +def is_valid_optim_request(request_json): + # Method to check whether the requestinfo/optimizer value is valid. + opt_info = request_json['optimInfo'] + if not opt_info.get('modelId'): + if not opt_info.get('modelContent') or not opt_info.get('solver'): + raise BusinessException('modelContent and solver needs to be populated if model_id is not set') + if not opt_info.get('optData'): + raise BusinessException('optimInfo.optData needs to be populated to solve for a problem') + + return True + + +def validate_request(request_json): + OptimizationAPI(request_json).validate() + if not is_valid_optim_request(request_json): + raise BusinessException('Invalid optim request ') + return True + + +def process_request(request_json): + response_code, response_message = run_optimizer(request_json) + 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 + + +def run_optimizer(request_json): + validate_request(request_json) + + model_content, solver = get_model_content(request_json) + + if solver == 'mzn': + return mzn_solve(request_json, model_content) + elif solver == 'py': + return py_solve(request_json, model_content) + raise BusinessException('Unsupported optimization solver requested {} '.format(solver)) + + +def get_model_content(request_json): + model_id = request_json['optimInfo'].get('modelId') + if model_id: + status, data = get_model_data(model_id) + if status == 200: + model_content = decode_data(data[1]) + solver = data[3] + else: + raise BusinessException('model_id [{}] not found in the model database'.format(model_id)) + else: + model_content = request_json['optimInfo']['modelContent'] + solver = request_json['optimInfo']['solver'] + return model_content, solver diff --git a/runtime/solvers/__init__.py b/runtime/solvers/__init__.py new file mode 100644 index 0000000..2aa67d8 --- /dev/null +++ b/runtime/solvers/__init__.py @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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/runtime/solvers/mzn/__init__.py b/runtime/solvers/mzn/__init__.py new file mode 100644 index 0000000..2aa67d8 --- /dev/null +++ b/runtime/solvers/mzn/__init__.py @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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/runtime/solvers/mzn/mzn_solver.py b/runtime/solvers/mzn/mzn_solver.py new file mode 100644 index 0000000..f3daa2b --- /dev/null +++ b/runtime/solvers/mzn/mzn_solver.py @@ -0,0 +1,132 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 datetime import datetime +import json + +from pymzn import cbc +from pymzn import chuffed +from pymzn import gecode +from pymzn import minizinc +from pymzn import or_tools +from pymzn import Status + +from osdf.utils.file_utils import delete_file_folder + +error_status_map = { + Status.INCOMPLETE: "incomplete", + Status.COMPLETE: "complete", + Status.UNSATISFIABLE: "unsatisfiable", + Status.UNKNOWN: "unknown", + Status.UNBOUNDED: "unbounded", + Status.UNSATorUNBOUNDED: "unsat_or_unbounded", + Status.ERROR: "error" +} + +solver_dict = { + 'cbc': cbc, + 'geocode': gecode, + 'chuffed': chuffed, + 'cp': chuffed, + 'or_tools': or_tools +} + + +def map_status(status): + return error_status_map.get(status, "failed") + + +def solve(request_json, mzn_content): + """Given the request and minizinc content. Translates the json request to the format minizinc understands + + return: returns the optimized solution. + """ + req_info = request_json['requestInfo'] + opt_info = request_json['optimInfo'] + try: + mzn_solution = mzn_solver(mzn_content, opt_info) + + response = { + 'transactionId': req_info['transactionId'], + 'requestID': req_info['requestID'], + 'requestStatus': 'done', + 'statusMessage': map_status(mzn_solution.status), + 'solutions': mzn_solution[0] if mzn_solution else {} + } + return 200, json.dumps(response) + except Exception as e: + response = { + 'transactionId': req_info['transactionId'], + 'requestID': req_info['requestID'], + 'requestStatus': 'failed', + 'statusMessage': 'Failed due to {}'.format(e) + } + return 400, json.dumps(response) + + +def mzn_solver(mzn_content, opt_info): + """Calls the minizinc optimizer. + + """ + args = opt_info['solverArgs'] + solver = get_mzn_solver(args.pop('solver')) + mzn_opts = dict() + + try: + file_name = persist_opt_data(opt_info) + mzn_opts.update(args) + return minizinc(mzn_content, file_name, **mzn_opts, solver=solver) + + finally: + delete_file_folder(file_name) + + +def persist_opt_data(opt_info): + """Persist the opt data, if included as part of the request. + + return: file_name path of the optim_data + returns None if no optData is part of the request + """ + file_name = None + if 'optData' in opt_info: + if opt_info['optData'].get('json'): + data_content = json.dumps(opt_info['optData']['json']) + file_name = '/tmp/optim_engine_{}.json'.format(datetime.timestamp(datetime.now())) + persist_data(data_content, file_name) + elif opt_info['optData'].get('text'): + data_content = opt_info['optData']['text'] + file_name = '/tmp/optim_engine_{}.dzn'.format(datetime.timestamp(datetime.now())) + persist_data(data_content, file_name) + return file_name + + +def persist_data(data_content, file_name): + """Save the dzn data into a file + + """ + with open(file_name, "wt") as data: + data.write(data_content) + + +def get_mzn_solver(solver): + """Returns a solver type object for minizinc optimizers + + solver: solver that is part of the request + return: solver mapped object + """ + return solver_dict.get(solver) diff --git a/runtime/solvers/py/__init__.py b/runtime/solvers/py/__init__.py new file mode 100644 index 0000000..a8aa582 --- /dev/null +++ b/runtime/solvers/py/__init__.py @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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. +# +# ------------------------------------------------------------------------- +#
\ No newline at end of file diff --git a/runtime/solvers/py/py_solver.py b/runtime/solvers/py/py_solver.py new file mode 100644 index 0000000..6b200ab --- /dev/null +++ b/runtime/solvers/py/py_solver.py @@ -0,0 +1,92 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 subprocess +import traceback +from datetime import datetime + +from osdf.logging.osdf_logging import error_log, debug_log +from osdf.utils.file_utils import delete_file_folder + + +def py_solver(py_content, opt_info): + py_file = '/tmp/custom_heuristics_{}.py'.format(datetime.timestamp(datetime.now())) + with open(py_file, "wt") as f: + f.write(py_content) + if opt_info['optData'].get('json'): + data_content = json.dumps(opt_info['optData']['json']) + input_file = '/tmp/optim_engine_{}.json'.format(datetime.timestamp(datetime.now())) + elif opt_info['optData'].get('text'): + data_content = opt_info['optData']['text'] + input_file = '/tmp/optim_engine_{}.txt'.format(datetime.timestamp(datetime.now())) + with open(input_file, "wt") as f: + f.write(data_content) + + output_file = '/tmp/opteng_output_{}.json'.format(datetime.timestamp(datetime.now())) + + command = ['python', py_file, input_file, output_file] + + try: + p = subprocess.run(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + + debug_log.debug('Process return code {}'.format(p.returncode)) + if p.returncode > 0: + error_log.error('Process return code {} {}'.format(p.returncode, p.stdout)) + return 'error', {} + with open(output_file) as file: + data = file.read() + return 'success', json.loads(data) + + except Exception as e: + error_log.error("Error running optimizer {}".format(traceback.format_exc())) + return 'error', {} + finally: + cleanup((input_file, output_file, py_file)) + + +def cleanup(file_tup): + for f in file_tup: + try: + delete_file_folder(f) + except Exception as e: + error_log.error("Failed deleting the file {} - {}".format(f, traceback.format_exc())) + + +def solve(request_json, py_content): + req_info = request_json['requestInfo'] + opt_info = request_json['optimInfo'] + try: + status, solution = py_solver(py_content, opt_info) + + response = { + 'transactionId': req_info['transactionId'], + 'requestID': req_info['requestID'], + 'requestStatus': status, + 'statusMessage': "completed", + 'solutions': solution if solution else {} + } + return 200, json.dumps(response) + except Exception as e: + response = { + 'transactionId': req_info['transactionId'], + 'requestID': req_info['requestID'], + 'requestStatus': 'failed', + 'statusMessage': 'Failed due to {}'.format(e) + } + return 400, json.dumps(response) diff --git a/script/TagVersion.groovy b/script/TagVersion.groovy new file mode 100644 index 0000000..01bc840 --- /dev/null +++ b/script/TagVersion.groovy @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP OSDF + * ================================================================================ + * 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. + * ============LICENSE_END============================================ + * =================================================================== + * + */ + +package org.onap.osdf.maven.scripts + +println project.properties['osdf.project.version'] + +def versionTag +if ( project.properties['osdf.project.version'] != null ) { + versionArray = project.properties['osdf.project.version'].split('\\.|-'); + versionTag = versionArray[0] + '.' + versionArray[1] + '.' + versionArray[2] + timestamp = project.properties['osdf.build.timestamp'] +} + +if ( project.properties['osdf.project.version'].endsWith("-SNAPSHOT") ) { + project.properties['project.docker.latesttag.version']=versionTag + "-SNAPSHOT-latest"; + project.properties['project.docker.latesttagtimestamp.version']=versionTag + "-SNAPSHOT-"+timestamp; + project.properties['project.repo'] = 'snapshots' +} else { + project.properties['project.docker.latesttag.version']=versionTag + "-STAGING-latest"; + project.properties['project.docker.latesttagtimestamp.version']=versionTag + "-STAGING-"+timestamp; + project.properties['project.repo'] = 'releases' +} + +println 'New Tag for docker: ' + project.properties['project.docker.latesttag.version'];
\ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1dffa77 --- /dev/null +++ b/setup.py @@ -0,0 +1,53 @@ +# -*- encoding: utf-8 -*- +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +'''Setup''' + +import setuptools + +setuptools.setup(name='of-osdf', + version='1.0', + description='Python Distribution Utilities', + author='xyz', + author_email='xyz@wipro.com', + url='https://wiki.onap.org/display/DW/Optimization+Service+Design+Framework', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: ONAP', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3' + 'Programming Language :: Python :: 3.5' + 'Topic :: Communications :: Email', + 'Topic :: Office/Business', + 'Topic :: Software Development :: Bug Tracking',], + keywords=['onap','osdf'], + packages=['osdf'], + entry_points = { + 'console_scripts': [ + 'cipher-utility = osdf.cmd.encryptionUtil:main', + ], + 'oslo.config.opts': [ + 'osdf = osdf.opts:list_opts', + ], + } + ) diff --git a/solverapp.py b/solverapp.py new file mode 100644 index 0000000..a2df317 --- /dev/null +++ b/solverapp.py @@ -0,0 +1,82 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 markupsafe import Markup + +from osdf.apps.baseapp import app, run_app +from osdf.logging.osdf_logging import audit_log +from osdf.webapp.appcontroller import auth_basic +from runtime.model_api import create_model_data, retrieve_model_data, retrieve_all_models, delete_model_data +from runtime.models.api.model_request import OptimModelRequestAPI +from runtime.optim_engine import process_request + + +@app.route("/api/oof/optengine/v1", methods=["POST"]) +@auth_basic.login_required +def opt_engine_rest_api(): + """Perform OptimEngine optimization after validating the request + """ + request_json = request.get_json() + return process_request(request_json) + + +@app.route("/api/oof/optmodel/v1", methods=["PUT", "POST"]) +@auth_basic.login_required +def opt_model_create_rest_api(): + """Perform OptimEngine optimization after validating the request + """ + request_json = request.get_json() + OptimModelRequestAPI(request_json).validate() + return create_model_data(request_json) + + +@app.route("/api/oof/optmodel/v1/<model_id>", methods=["GET"]) +@auth_basic.login_required +def opt_get_model_rest_api(model_id): + """Retrieve model data + """ + model_id = Markup.escape(model_id) + return retrieve_model_data(model_id) + + +@app.route("/api/oof/optmodel/v1", methods=["GET"]) +@auth_basic.login_required +def opt_get_all_models_rest_api(): + """Retrieve all models data + """ + return retrieve_all_models() + + +@app.route("/api/oof/optmodel/v1/<model_id>", methods=["DELETE"]) +@auth_basic.login_required +def opt_delete_model_rest_api(model_id): + """Perform OptimEngine optimization after validating the request + """ + return delete_model_data(model_id) + + +@app.route("/api/oof/optengine/healthcheck/v1", methods=["GET"]) +def do_health_check(): + """Simple health check""" + audit_log.info("A OptimEngine health check v1 request is processed!") + return "OK" + + +if __name__ == "__main__": + run_app() diff --git a/ssl_certs/oof.crt b/ssl_certs/oof.crt deleted file mode 100644 index dc61a43..0000000 --- a/ssl_certs/oof.crt +++ /dev/null @@ -1,59 +0,0 @@ -Bag Attributes - localKeyID: F5 64 7B F8 32 67 FD CE 81 5E 0D 13 36 B7 67 35 47 33 B8 9B - friendlyName: oof@oof.onap.org -subject=/C=US/O=ONAP/OU=oof@oof.onap.org/OU=OSAAF/CN=oof.api.simpledemo.onap.org -issuer=/C=US/O=ONAP/OU=OSAAF/CN=intermediateCA_1 ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIBHjANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEN -MAsGA1UECgwET05BUDEOMAwGA1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVk -aWF0ZUNBXzEwHhcNMTgwNDI1MTIxMzAxWhcNMTkwNDIwMTIxMzAxWjBtMQswCQYD -VQQGEwJVUzENMAsGA1UECgwET05BUDEZMBcGA1UECwwQb29mQG9vZi5vbmFwLm9y -ZzEOMAwGA1UECwwFT1NBQUYxJDAiBgNVBAMMG29vZi5hcGkuc2ltcGxlZGVtby5v -bmFwLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGpQUtgLXG3 -dVikd/QC2Q24wzeTOeZzbx3PnidNYZT5K0sJ/TdnZF6O/4+9gXQ6AQS2Q8wfQ009 -MQAA5vhUaq5yZ2K+XAtEFGln1TxTFpGu3WDOwQ800Vw18Dk8WidrkzDJv489Bn1f -SSaPC0IaRB0K1d8BD63ZHgsuEY8lt31DX2wFWJcfN9mxNDzuLTZoLxtxKsedoZKH -rsOOILwXOhwuunfx40i6RQN/pFX6C2i8dtOA5OwUm9Q1RrZ2Tv1Uf4IURriH6bfZ -5n50yxTuL22TMYXsF/ohrdgwacuC0aV9ZSGhIZUJPyHVg7+QTBioHmoUJInVKuIx -kkC4lENbLYUCAwEAAaOB+jCB9zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG -wDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp -ZmljYXRlMB0GA1UdDgQWBBQwbU5oHU2iYHCoVz4hFCvBW59cdTBUBgNVHSMETTBL -gBQd5lldG54KOKRipsGF8/PP1vGX6qEwpC4wLDEOMAwGA1UECwwFT1NBQUYxDTAL -BgNVBAoMBE9OQVAxCzAJBgNVBAYTAlVTggEBMA4GA1UdDwEB/wQEAwIF4DAdBgNV -HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBADEa -0VuxoFIygeQTqlizpHNwfApPmlAVSKDTWuEu4rhJs8GT61EuWZQPygXEUHCYmGvJ -GMwEGGIDGiQqxMqlqng46gksNJbi1ktXr6Du18qW7gziUd84ve8KcecjZru1Sk1e -UJ/6WEQVE17CHKcnzQZsMDakgP+61VgKbk5NlkeF/Qh4L6/3jY7g+xoXqaId5RT9 -BetmH/cMsj33lxQTs0fcXTbAQd6BX5ug854OJ1mU4ngJnNBdmn9Ow1bB71ohf5Xv -OEYX8+khjgjlmM0u1hBRL4qViv3y2Gzhpm1M8cETMDj4g0zIJytzIYMxO8XvDPCF -YmVZHXJDLsCogSOmmh0= ------END CERTIFICATE----- -Bag Attributes: <No Attributes> -subject=/C=US/O=ONAP/OU=OSAAF/CN=intermediateCA_1 -issuer=/OU=OSAAF/O=ONAP/C=US ------BEGIN CERTIFICATE----- -MIIEVDCCAjygAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMQ4wDAYDVQQLDAVPU0FB -RjENMAsGA1UECgwET05BUDELMAkGA1UEBhMCVVMwHhcNMTgwNDA1MTQxNTQwWhcN -MTgwNjA0MTQxNTQwWjBHMQswCQYDVQQGEwJVUzENMAsGA1UECgwET05BUDEOMAwG -A1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVkaWF0ZUNBXzEwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCY3YPA/YQdz4kaZQzdRzWNjmn33WYAWZ8+ -EIz3PhkEzk7M1q9N7Icx2LvozMj4VH0yGz/HYlliHhw26ZRsjYMSR8zATsXl4oW9 -w9BrjuyvM3w8Ptxe8WbUFF9LJDGyXPeVvcXVo0iyh3QYPWC/AWmomN19MvBFN5vH -AvEG/7qtonViNfISW9Gr9LpXB0foCmUDBu/lV+SwRGajoCPqdZhZ6/L6/yqDvha2 -wsML/UZXlGhXAedt/xOKmT/dSXx/I0vWBVp6Tq4zu87yCvd+I6Tpa5HjttA2I5EV -zdHX+JYBPBBcVCyO9YQOYjJuoVDE4D5etY6dEipKG/KZF/rqAoqZAgMBAAGjZjBk -MB0GA1UdDgQWBBQd5lldG54KOKRipsGF8/PP1vGX6jAfBgNVHSMEGDAWgBRTVTPy -S+vQUbHBeJrBKDF77+rtSTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQE -AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAmgeiitBDi/YEqFh2Cqp0VIEqw8hiuV87 -rADQWMK4hv5WXl3KJTjFAnWsYFUKrm6s1jNH16FyGExUQgwggob0Vt+MHiUs36jU -kyret/uE5qrjz+/J+i2XG6s1oKcDRVD/jU4qBygZWFBMuwl7sz8IEvaYXGM43s96 -Du3UF9E+V3aMppqkGWz6MnrTmANnWAlDAMeifcoexjrpxiKbp8f49HX1UzwFoeEg -RnVwNqgDWT66yGV6mbNl6FpE/U81RpCRY1ZJDeVTxbqIaG/UPV4hpQ+BEVBDF+cb -rGsvsNYYpWx5srIQ7WtGKIlaDFbfWPwnHDHegzr8ypAS3KNWULE+QXCbHWtB+b0Y -WhP/2F6Jjb+ByvJqQoE+nHEYBeUOZUUZC4IuQFNJ5Wy5P0CNXdheiWhdrBmG02Gy -KMi0FJx6BEoWM2xcdl6bn5j9mhF4TX7zgepNWlgTra4Z8Oz8iqbQk33/s2OKM4ic -6ZezUYhNp+MuUt4Se+ufNcGV65jnUKeROtWzNLwP+xwglEFlG8aNiAORthd7QJuT -Ey2cX7H7f38ENQ5YCriUk1nVLO9F66l/rNRzYZgQzRI3IvDW8vyM2TLW2mcZNsaf -qjFMcCDweV2FRb8eTbmWzzB2/xTVpGzVJqzwgE+U7UtJx5CZS3wPkvXuEgvcg1tY -m1r4NGYFvLM= ------END CERTIFICATE----- diff --git a/ssl_certs/oof.crt.pem b/ssl_certs/oof.crt.pem deleted file mode 100644 index 4c6eb91..0000000 --- a/ssl_certs/oof.crt.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE-----
-MIIEKjCCAxKgAwIBAgIBHjANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEN
-MAsGA1UECgwET05BUDEOMAwGA1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVk
-aWF0ZUNBXzEwHhcNMTgwNDI1MTIxMzAxWhcNMTkwNDIwMTIxMzAxWjBtMQswCQYD
-VQQGEwJVUzENMAsGA1UECgwET05BUDEZMBcGA1UECwwQb29mQG9vZi5vbmFwLm9y
-ZzEOMAwGA1UECwwFT1NBQUYxJDAiBgNVBAMMG29vZi5hcGkuc2ltcGxlZGVtby5v
-bmFwLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGpQUtgLXG3
-dVikd/QC2Q24wzeTOeZzbx3PnidNYZT5K0sJ/TdnZF6O/4+9gXQ6AQS2Q8wfQ009
-MQAA5vhUaq5yZ2K+XAtEFGln1TxTFpGu3WDOwQ800Vw18Dk8WidrkzDJv489Bn1f
-SSaPC0IaRB0K1d8BD63ZHgsuEY8lt31DX2wFWJcfN9mxNDzuLTZoLxtxKsedoZKH
-rsOOILwXOhwuunfx40i6RQN/pFX6C2i8dtOA5OwUm9Q1RrZ2Tv1Uf4IURriH6bfZ
-5n50yxTuL22TMYXsF/ohrdgwacuC0aV9ZSGhIZUJPyHVg7+QTBioHmoUJInVKuIx
-kkC4lENbLYUCAwEAAaOB+jCB9zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG
-wDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp
-ZmljYXRlMB0GA1UdDgQWBBQwbU5oHU2iYHCoVz4hFCvBW59cdTBUBgNVHSMETTBL
-gBQd5lldG54KOKRipsGF8/PP1vGX6qEwpC4wLDEOMAwGA1UECwwFT1NBQUYxDTAL
-BgNVBAoMBE9OQVAxCzAJBgNVBAYTAlVTggEBMA4GA1UdDwEB/wQEAwIF4DAdBgNV
-HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBADEa
-0VuxoFIygeQTqlizpHNwfApPmlAVSKDTWuEu4rhJs8GT61EuWZQPygXEUHCYmGvJ
-GMwEGGIDGiQqxMqlqng46gksNJbi1ktXr6Du18qW7gziUd84ve8KcecjZru1Sk1e
-UJ/6WEQVE17CHKcnzQZsMDakgP+61VgKbk5NlkeF/Qh4L6/3jY7g+xoXqaId5RT9
-BetmH/cMsj33lxQTs0fcXTbAQd6BX5ug854OJ1mU4ngJnNBdmn9Ow1bB71ohf5Xv
-OEYX8+khjgjlmM0u1hBRL4qViv3y2Gzhpm1M8cETMDj4g0zIJytzIYMxO8XvDPCF
-YmVZHXJDLsCogSOmmh0=
------END CERTIFICATE-----
\ No newline at end of file diff --git a/ssl_certs/oof_new.key b/ssl_certs/oof_new.key deleted file mode 100644 index b3208c1..0000000 --- a/ssl_certs/oof_new.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA0alBS2Atcbd1WKR39ALZDbjDN5M55nNvHc+eJ01hlPkrSwn9 -N2dkXo7/j72BdDoBBLZDzB9DTT0xAADm+FRqrnJnYr5cC0QUaWfVPFMWka7dYM7B -DzTRXDXwOTxaJ2uTMMm/jz0GfV9JJo8LQhpEHQrV3wEPrdkeCy4RjyW3fUNfbAVY -lx832bE0PO4tNmgvG3Eqx52hkoeuw44gvBc6HC66d/HjSLpFA3+kVfoLaLx204Dk -7BSb1DVGtnZO/VR/ghRGuIfpt9nmfnTLFO4vbZMxhewX+iGt2DBpy4LRpX1lIaEh -lQk/IdWDv5BMGKgeahQkidUq4jGSQLiUQ1sthQIDAQABAoIBAHeHah1B6MajE/iE -U4q+sOYcxtcBTYovl1LEkeLQP+jBoUf3mvAiNtud5N8a6BnOE9SO4NoXnLQFRdE9 -snAzGFr6CC0IX8tgdc6eDriEmiJWMgnF9dTohM9wRNMssC03LEQtUNOls/R4BWlB -NebquJhiHAo2Pa0cUf+HtSUKGLEFVqyGyf/psqw+y38VP5ZVv5BvlPGRsSyExbwD -uZ7QNC5szL7k1kqsiQ0nRxHBZxTI9gBQr2LKM8TY4TAmFr2JIoFDr9BDZ5GANzGR -aglyQWERRuNhGDkS9Okn/vfxjhUcuaNciULUyIMt0RT3IlgmgWyWqk75xueaCiMr -kpFWRWECgYEA72WwP+rqv6gM88kD+zBKcyianW6TYSN6TDBpzX4StcPr32KYqvXW -CXgUUjfZQduyNrfxxI7C/6fGWT6oj3G7I3dI+GXMQ6TYWUIos0uhL4SBPZa04hKf -Y3P6PBFGOqv301/mwS5MI2sMOBrpJH/hig0ExXrzM2EAQi7V6adji5kCgYEA4DOg -NTuLaB0FinHzPCySiujjcAWBsvjhpF+C3g3RMOFC0EKCy3snPnxyLYQENcIueE9r -9y68pnpqNqFWOJqLINc727cU2+becFfpinGQEnZuC/48FbiDDR2uTv/vd4OT8+ng -tuNGXbBz/XP9nvjS5t06MDOrOrseBSpo3ZfmBM0CgYEAxQCOgJrl4R/+wKL75rp/ -mbKhQcqb94UFgCsa9iK4bOG0ehid/5ncL+CkAGC7JWoQhtzqVNESgOXk4M4iUiDK -Wk4wO1EyPbwq2ZELAzjKhNrqq+8YHS4sAeCP3NxuSZv4jfZOY0yhFUhjPsxObV3b -EQrTkVszRWWem9gE6ol37okCgYEAhEeRb7b5Em2FFmES/N7je1fa0P4+vuS+5OeB -ZBhM44UUkaGcYAgCaIiuKRKqFTnDhzJ85fNKVQMG5cKdB3qPOcojxAeqI/B8L1Z/ -MTK9qVb8qNDQjJQ3piZr8KpqlF4qjg/giKdhned9F/42lnQCoznFmijyDw3VsYCL -LKrxiMUCgYAvq51mzXuGRGEJp8QmVBJGfSwIlqB9F5zdkVfWADP6X99MSH0PGpvU -SJOYO9gQJA31v3AECLUXYjYFlEX4PcAhMCwVONm2AAok0EXIc1UgJrpNkdRIjhJW -81NkKznllRF7LownV1zoOl9CcIn8u9XoRd1OjRTzU8QTZ1QfLkexoQ== ------END RSA PRIVATE KEY----- diff --git a/test/adapters/dcae/des_response.json b/test/adapters/dcae/des_response.json new file mode 100644 index 0000000..c8595eb --- /dev/null +++ b/test/adapters/dcae/des_response.json @@ -0,0 +1,47 @@ +{ + "result": [ + { + "additionalMeasurements": [ + { + "hashMap":{ + "networkId":"plmnid1", + "InterEnbOutAtt_X2HO":"300", + "InterEnbOutSucc_X2HO":"290" + }, + "name":"Chn0004" + }, + { + "hashMap":{ + "InterEnbOutAtt_X2HO":"250", + "InterEnbOutSucc_X2HO":"170" + }, + "name":"Chn0001" + } + ] + }, + { + "additionalMeasurements": [ + { + "hashMap":{ + "networkId":"plmnid1", + "InterEnbOutAtt_X2HO":"300", + "InterEnbOutSucc_X2HO":"290" + }, + "name":"Chn0004" + }, + { + "hashMap":{ + "InterEnbOutAtt_X2HO":"250", + "InterEnbOutSucc_X2HO":"170" + }, + "name":"Chn0001" + } + ] + } + ], + "request": { + "cell_id": "Chn0002", + "interval": 2 + }, + "result_count": 2 +}
\ No newline at end of file diff --git a/test/adapters/dcae/test_des.py b/test/adapters/dcae/test_des.py new file mode 100644 index 0000000..6e2520a --- /dev/null +++ b/test/adapters/dcae/test_des.py @@ -0,0 +1,71 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +import mock +from mock import patch +from requests import RequestException +from requests.exceptions import HTTPError +import unittest +from osdf.adapters.dcae import des +from osdf.adapters.dcae.des import DESException +import osdf.config.loader as config_loader +from osdf.utils.interfaces import json_from_file +from osdf.utils.programming_utils import DotDict + + +class TestDes(unittest.TestCase): + + def setUp(self): + self.config_spec = { + "deployment": "config/osdf_config.yaml", + "core": "config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + + def tearDown(self): + pass + + def test_extract_data(self): + response_file = 'test/adapters/dcae/des_response.json' + response_json = json_from_file(response_file) + + des_config = self.osdf_config.core['PCI']['DES'] + service_id = des_config['service_id'] + data = des_config['filter'] + expected = response_json['result'] + response = mock.MagicMock() + response.status_code = 200 + response.ok = True + response.json.return_value = response_json + self.patcher_req = patch('requests.request', return_value=response) + self.Mock_req = self.patcher_req.start() + self.assertEqual(expected, des.extract_data(service_id, data)) + self.patcher_req.stop() + + response = mock.MagicMock() + response.status_code = 404 + response.raise_for_status.side_effect = HTTPError("404") + self.patcher_req = patch('requests.request', return_value=response) + self.Mock_req = self.patcher_req.start() + self.assertRaises(DESException, des.extract_data, service_id, data) + self.patcher_req.stop() + + self.patcher_req = patch('requests.request', side_effect=RequestException("error")) + self.Mock_req = self.patcher_req.start() + self.assertRaises(DESException, des.extract_data, service_id, data) + self.patcher_req.stop() diff --git a/test/apps/nxi_termination/_init_.py b/test/apps/nxi_termination/_init_.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/apps/nxi_termination/_init_.py diff --git a/test/apps/nxi_termination/aai_exception_response.json b/test/apps/nxi_termination/aai_exception_response.json new file mode 100644 index 0000000..56f61df --- /dev/null +++ b/test/apps/nxi_termination/aai_exception_response.json @@ -0,0 +1,4 @@ +{ + "status-code": 404, + "status-response": "NOT FOUND" +}
\ No newline at end of file diff --git a/test/apps/nxi_termination/aai_response.json b/test/apps/nxi_termination/aai_response.json new file mode 100644 index 0000000..b7ef43b --- /dev/null +++ b/test/apps/nxi_termination/aai_response.json @@ -0,0 +1,64 @@ +{"service-instance": [{ + "service-instance-id": "1a636c4d-5e76-427e-bfd6-241a947224b0", + "service-instance-name": "nssi_test_0211", + "service-type": "embb", + "service-role": "nssi", + "environment-context": "cn", + "model-invariant-id": "21d57d4b-52ad-4d3c-a798-248b5bb9124a", + "model-version-id": "bfba363e-e39c-4bd9-a9d5-1371c28f4d22", + "resource-version": "1581418601616", + "orchestration-status": "active", + "relationship-list": { + "relationship": [ + { + "related-to": "service-instance", + "relationship-label": "org.onap.relationships.inventory.ComposedOf", + "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b518" + } + ], + "related-to-property": [ + { + "property-key": "service-instance.service-instance-name", + "property-value": "nsi_test_0211" + } + ] + } + ] + }, + "slice-profiles": { + "slice-profile": [ + { + "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299", + "latency": 20, + "max-number-of-UEs": 0, + "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "ue-mobility-level": "stationary", + "resource-sharing-level": "0", + "exp-data-rate-UL": 100, + "exp-data-rate-DL": 100, + "activity-factor": 0, + "e2e-latency": 0, + "jitter": 0, + "survival-time": 0, + "exp-data-rate": 0, + "payload-size": 0, + "traffic-density": 0, + "conn-density": 0, + "reliability": 99.999, + "resource-version": "1581418602494" + } + ] + } +}]} diff --git a/test/apps/nxi_termination/exception_response1.json b/test/apps/nxi_termination/exception_response1.json new file mode 100644 index 0000000..cde603f --- /dev/null +++ b/test/apps/nxi_termination/exception_response1.json @@ -0,0 +1,7 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "failure", + "reason": "Error response recieved from AAI for the request" + +}
\ No newline at end of file diff --git a/test/apps/nxi_termination/failure_relationship_list.json b/test/apps/nxi_termination/failure_relationship_list.json new file mode 100644 index 0000000..392f4db --- /dev/null +++ b/test/apps/nxi_termination/failure_relationship_list.json @@ -0,0 +1,66 @@ +[ + { + "related-to": "allotted-resource", + "relationship-label": "org.onap.relationships.inventory.Uses", + "related-link": "/aai/v23/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/d88b6ce6-19be-439d-8553-4f9d6cce0494/allotted-resources/allotted-resource/07138106-f535-413b-b002-40ba24f95937", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "07138106-f535-413b-b002-40ba24f95937" + }, + { + "relationship-key": "allotted-resource.id", + "relationship-value": "07138106-f535-413b-b002-40ba24f95937" + } + ], + "related-to-property": [ + { + "property-key": "allotted-resource.description" + }, + { + "property-key": "allotted-resource.allotted-resource-name", + "property-value": "Allotted_coe" + } + ] + }, + { + "related-to": "allotted-resource", + "relationship-label": "org.onap.relationships.inventory.Uses", + "related-link": "/aai/v23/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/d88b6ce6-19be-439d-8553-4f9d6cce0494/allotted-resources/allotted-resource/07138106-f535-413b-b002-40ba24f95937", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "d290f1ee-6c54-4b01-90e6-d701748f0851" + }, + { + "relationship-key": "allotted-resource.id", + "relationship-value": "d290f1ee-6c54-4b01-90e6-d701748f0851" + } + ], + "related-to-property": [ + { + "property-key": "allotted-resource.description" + }, + { + "property-key": "allotted-resource.allotted-resource-name", + "property-value": "Allotted_terminate" + } + ] + } +]
\ No newline at end of file diff --git a/test/apps/nxi_termination/failure_relationship_list2.json b/test/apps/nxi_termination/failure_relationship_list2.json new file mode 100644 index 0000000..624448a --- /dev/null +++ b/test/apps/nxi_termination/failure_relationship_list2.json @@ -0,0 +1,52 @@ +[ + { + "related-to": "service-instance", + "relationship-label": "org.onap.relationships.inventory.ComposedOf", + "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b567" + } + ], + "related-to-property": [ + { + "property-key": "service-instance.service-instance-name", + "property-value": "nsi_test_0211" + } + ] + }, + { + "related-to": "service-instance", + "relationship-label": "org.onap.relationships.inventory.ComposedOf", + "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b567" + } + ], + "related-to-property": [ + { + "property-key": "service-instance.service-instance-name", + "property-value": "nsi_test_0211" + } + ] + } + ]
\ No newline at end of file diff --git a/test/apps/nxi_termination/failure_service_profiles.json b/test/apps/nxi_termination/failure_service_profiles.json new file mode 100644 index 0000000..d10a818 --- /dev/null +++ b/test/apps/nxi_termination/failure_service_profiles.json @@ -0,0 +1,24 @@ +[ + { + "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c29", + "latency": 20, + "max-number-of-UEs": 0, + "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "ue-mobility-level": "stationary", + "resource-sharing-level": "0", + "exp-data-rate-UL": 100, + "exp-data-rate-DL": 100, + "activity-factor": 0, + "e2e-latency": 0, + "jitter": 0, + "survival-time": 0, + "exp-data-rate": 0, + "payload-size": 0, + "traffic-density": 0, + "conn-density": 0, + "reliability": 99.999, + "resource-version": "1581418602494" + } +] + + diff --git a/test/apps/nxi_termination/failure_service_profiles2.json b/test/apps/nxi_termination/failure_service_profiles2.json new file mode 100644 index 0000000..1740758 --- /dev/null +++ b/test/apps/nxi_termination/failure_service_profiles2.json @@ -0,0 +1,42 @@ +[ + { + "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299", + "latency": 20, + "max-number-of-UEs": 0, + "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "ue-mobility-level": "stationary", + "resource-sharing-level": "0", + "exp-data-rate-UL": 100, + "exp-data-rate-DL": 100, + "activity-factor": 0, + "e2e-latency": 0, + "jitter": 0, + "survival-time": 0, + "exp-data-rate": 0, + "payload-size": 0, + "traffic-density": 0, + "conn-density": 0, + "reliability": 99.999, + "resource-version": "1581418602494" + }, + { + "profile-id": "abcd9f49-4201-4e3a-aac1-b0f27902c299", + "latency": 20, + "max-number-of-UEs": 0, + "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "ue-mobility-level": "stationary", + "resource-sharing-level": "0", + "exp-data-rate-UL": 100, + "exp-data-rate-DL": 100, + "activity-factor": 0, + "e2e-latency": 0, + "jitter": 0, + "survival-time": 0, + "exp-data-rate": 0, + "payload-size": 0, + "traffic-density": 0, + "conn-density": 0, + "reliability": 99.999, + "resource-version": "1581418602494" + } +] diff --git a/test/apps/nxi_termination/invalid_request.json b/test/apps/nxi_termination/invalid_request.json new file mode 100644 index 0000000..72eafd7 --- /dev/null +++ b/test/apps/nxi_termination/invalid_request.json @@ -0,0 +1,17 @@ +{ + "requestInfo": { + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "sourceId": "SO", + "timeout": 5, + "addtnlArgs": { + "serviceInstanceId":"cdad9f49-4201-4e3a-aac1-b0f27902c299" + } + }, + "type":"NST", + "NxIId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "UUID":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "invariantUUID":"d290f1ee-6c54-4b01-90e6-d701748f0851" + +} diff --git a/test/apps/nxi_termination/nsi_success_output.json b/test/apps/nxi_termination/nsi_success_output.json new file mode 100644 index 0000000..e25a272 --- /dev/null +++ b/test/apps/nxi_termination/nsi_success_output.json @@ -0,0 +1,7 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "success", + "terminateResponse": true, + "reason": "" +}
\ No newline at end of file diff --git a/test/apps/nxi_termination/nssi_failure_output.json b/test/apps/nxi_termination/nssi_failure_output.json new file mode 100644 index 0000000..f300c53 --- /dev/null +++ b/test/apps/nxi_termination/nssi_failure_output.json @@ -0,0 +1,8 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "success", + "terminateResponse": false, + "reason": "" + +}
\ No newline at end of file diff --git a/test/apps/nxi_termination/nssi_termination.json b/test/apps/nxi_termination/nssi_termination.json new file mode 100644 index 0000000..b4e3711 --- /dev/null +++ b/test/apps/nxi_termination/nssi_termination.json @@ -0,0 +1,17 @@ +{ + "requestInfo": { + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "sourceId": "SO", + "timeout": 5, + "addtnlArgs": { + "serviceInstanceId":"4115d3c8-dd59-45d6-b09d-e756dee9b518" + } + }, + "type":"NSSI", + "NxIId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "UUID":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "invariantUUID":"d290f1ee-6c54-4b01-90e6-d701748f0851" + +}
\ No newline at end of file diff --git a/test/apps/nxi_termination/nxi_failure_output1.json b/test/apps/nxi_termination/nxi_failure_output1.json new file mode 100644 index 0000000..4cce5eb --- /dev/null +++ b/test/apps/nxi_termination/nxi_failure_output1.json @@ -0,0 +1,10 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "success", + "terminateResponse": false, + "reason": "" + +} + + diff --git a/test/apps/nxi_termination/nxi_failure_output2.json b/test/apps/nxi_termination/nxi_failure_output2.json new file mode 100644 index 0000000..f18b73c --- /dev/null +++ b/test/apps/nxi_termination/nxi_failure_output2.json @@ -0,0 +1,7 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "success", + "terminateResponse": false, + "reason": "" +}
\ No newline at end of file diff --git a/test/apps/nxi_termination/nxi_termination.json b/test/apps/nxi_termination/nxi_termination.json new file mode 100644 index 0000000..1e25f2e --- /dev/null +++ b/test/apps/nxi_termination/nxi_termination.json @@ -0,0 +1,17 @@ +{ + "requestInfo": { + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "sourceId": "SO", + "timeout": 5, + "addtnlArgs": { + "serviceInstanceId":"cdad9f49-4201-4e3a-aac1-b0f27902c299" + } + }, + "type":"NSI", + "NxIId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "UUID":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "invariantUUID":"d290f1ee-6c54-4b01-90e6-d701748f0851" + +} diff --git a/test/apps/nxi_termination/service_profiles.json b/test/apps/nxi_termination/service_profiles.json new file mode 100644 index 0000000..899acb4 --- /dev/null +++ b/test/apps/nxi_termination/service_profiles.json @@ -0,0 +1,23 @@ +[ + { + "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299", + "latency": 20, + "max-number-of-UEs": 0, + "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "ue-mobility-level": "stationary", + "resource-sharing-level": "0", + "exp-data-rate-UL": 100, + "exp-data-rate-DL": 100, + "activity-factor": 0, + "e2e-latency": 0, + "jitter": 0, + "survival-time": 0, + "exp-data-rate": 0, + "payload-size": 0, + "traffic-density": 0, + "conn-density": 0, + "reliability": 99.999, + "resource-version": "1581418602494" + } +] + diff --git a/test/apps/nxi_termination/success_relationship_list.json b/test/apps/nxi_termination/success_relationship_list.json new file mode 100644 index 0000000..608418d --- /dev/null +++ b/test/apps/nxi_termination/success_relationship_list.json @@ -0,0 +1,34 @@ +[ + { + "related-to": "allotted-resource", + "relationship-label": "org.onap.relationships.inventory.Uses", + "related-link": "/aai/v23/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/d88b6ce6-19be-439d-8553-4f9d6cce0494/allotted-resources/allotted-resource/07138106-f535-413b-b002-40ba24f95937", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "cdad9f49-4201-4e3a-aac1-b0f27902c299" + }, + { + "relationship-key": "allotted-resource.id", + "relationship-value": "07138106-f535-413b-b002-40ba24f95937" + } + ], + "related-to-property": [ + { + "property-key": "allotted-resource.description" + }, + { + "property-key": "allotted-resource.allotted-resource-name", + "property-value": "Allotted_coe" + } + ] + } +]
\ No newline at end of file diff --git a/test/apps/nxi_termination/test_fetch_aai_data.py b/test/apps/nxi_termination/test_fetch_aai_data.py new file mode 100644 index 0000000..241b24b --- /dev/null +++ b/test/apps/nxi_termination/test_fetch_aai_data.py @@ -0,0 +1,70 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +import unittest +import mock +from unittest.mock import patch +from osdf.config.base import osdf_config +import osdf.config.loader as config_loader +from osdf.utils.programming_utils import DotDict +from osdf.utils.interfaces import json_from_file +from osdf.adapters.aai.fetch_aai_data import get_aai_data,AAIException + +class TestRemoteOptProcessor(unittest.TestCase): + def setUp(self): + self.config_spec = { + "deployment": "config/osdf_config.yaml", + "core": "config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + + def tearDown(self): + + patch.stopall() + + + def test_get_aai_data(self): + main_dir = "" + response_file = main_dir + 'test/apps/nxi_termination/aai_response.json' + exception_response_file = main_dir + 'test/apps/nxi_termination/aai_exception_response.json' + request_file = main_dir + 'test/apps/nxi_termination/nxi_termination.json' + response_json = json_from_file(response_file) + request_json = json_from_file(request_file) + exception_json = json_from_file(exception_response_file) + response = mock.MagicMock() + response.status_code = 200 + response.ok = True + response.json.return_value = response_json + self.patcher_req = patch('requests.get', + return_value = response) + self.Mock_req = self.patcher_req.start() + self.assertEquals(response_json, get_aai_data(request_json,osdf_config)) + self.patcher_req.stop() + + responsenew=mock.MagicMock() + responsenew.status_code=404 + responsenew.json.return_value = exception_json + self.patcher_req = patch('requests.get', + return_value=responsenew) + self.Mock_req = self.patcher_req.start() + self.assertRaises( AAIException,get_aai_data,request_json,osdf_config) + self.patcher_req.stop() + + +if __name__ == "__main__": + unittest.main()
\ No newline at end of file diff --git a/test/apps/nxi_termination/test_remote_opt_processor_termination.py b/test/apps/nxi_termination/test_remote_opt_processor_termination.py new file mode 100644 index 0000000..555f2e8 --- /dev/null +++ b/test/apps/nxi_termination/test_remote_opt_processor_termination.py @@ -0,0 +1,136 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# + +import unittest +from unittest.mock import patch +import osdf.config.loader as config_loader +import pytest +from apps.nxi_termination.optimizers.remote_opt_processor import process_nxi_termination_opt +from osdf.adapters.aai.fetch_aai_data import AAIException + +from osdf.config.base import osdf_config +from osdf.utils.programming_utils import DotDict +from osdf.utils.interfaces import json_from_file + +class TestRemoteOptProcessor(unittest.TestCase): + def setUp(self): + self.config_spec = { + "deployment": "config/osdf_config.yaml", + "core": "config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + + def tearDown(self): + + patch.stopall() + + def test_process_nxi_termination_opt(self): + main_dir = "" + request_file = main_dir + 'test/apps/nxi_termination/nxi_termination.json' + nssi_request_file=main_dir + 'test/apps/nxi_termination/nssi_termination.json' + service_file = main_dir + 'test/apps/nxi_termination/service_profiles.json' + failure_service_file = main_dir + 'test/apps/nxi_termination/failure_service_profiles.json' + failure_service_file2 = main_dir + 'test/apps/nxi_termination/failure_service_profiles2.json' + nsi_success=main_dir + 'test/apps/nxi_termination/nsi_success_output.json' + nxi_failure1 = main_dir + 'test/apps/nxi_termination/nxi_failure_output1.json' + nxi_failure2 = main_dir + 'test/apps/nxi_termination/nxi_failure_output2.json' + nssi_failure = main_dir + 'test/apps/nxi_termination/nssi_failure_output.json' + success_rel_file = main_dir + 'test/apps/nxi_termination/success_relationship_list.json' + failure_rel_file1 = main_dir + 'test/apps/nxi_termination/failure_relationship_list.json' + failure_rel_file2 = main_dir + 'test/apps/nxi_termination/failure_relationship_list2.json' + exception_response_file1 = main_dir + 'test/apps/nxi_termination/exception_response1.json' + request_json=json_from_file(request_file) + nssi_request_json = json_from_file(nssi_request_file) + service_profile_json = json_from_file(service_file) + failure_service_profile_json = json_from_file(failure_service_file) + failure_service_profile_json2 = json_from_file(failure_service_file2) + success_rel_json=json_from_file(success_rel_file) + failure_rel_json = json_from_file(failure_rel_file1) + failure_rel_json2 = json_from_file(failure_rel_file2) + success_output_json=json_from_file(nsi_success) + nxi_failure_output_json1 = json_from_file(nxi_failure1) + nxi_failure_output_json2 = json_from_file(nxi_failure2) + nssi_failure_output_json = json_from_file(nssi_failure) + exception_response_json1 = json_from_file(exception_response_file1) + + #nsi success scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_allotted_resources', return_value=success_rel_json) + self.Mock_req = self.patcher_req.start() + self.assertEquals(success_output_json, process_nxi_termination_opt(request_json, osdf_config)) + self.patcher_req.stop() + + #nsi failure scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_allotted_resources', return_value=failure_rel_json) + self.Mock_req = self.patcher_req.start() + self.assertEquals(nxi_failure_output_json1, process_nxi_termination_opt(request_json, osdf_config)) + self.patcher_req.stop() + + request_json["requestInfo"]["addtnlArgs"] = {} + + #nsi success scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_allotted_resources', + return_value=[]) + self.Mock_req = self.patcher_req.start() + self.assertEquals(success_output_json, process_nxi_termination_opt(request_json, osdf_config)) + self.patcher_req.stop() + + # # + # nssi success scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_resource_count', return_value=1) + self.Mock_req = self.patcher_req.start() + self.assertEquals(success_output_json, process_nxi_termination_opt(nssi_request_json, osdf_config)) + self.patcher_req.stop() + + # nssi failure scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_resource_count', + return_value=2) + self.Mock_req = self.patcher_req.start() + self.assertEquals(nssi_failure_output_json, process_nxi_termination_opt(nssi_request_json, osdf_config)) + self.patcher_req.stop() + + nssi_request_json["requestInfo"]["addtnlArgs"] = {} + + # nssi success scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_resource_count', + return_value=0) + self.Mock_req = self.patcher_req.start() + self.assertEquals(success_output_json, process_nxi_termination_opt(nssi_request_json, osdf_config)) + self.patcher_req.stop() + + # nssi failure scenario + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_resource_count', + return_value=1) + self.Mock_req = self.patcher_req.start() + self.assertEquals(nxi_failure_output_json2, process_nxi_termination_opt(nssi_request_json, osdf_config)) + self.patcher_req.stop() + + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_resource_count', + side_effect=AAIException("Error response recieved from AAI for the request")) + self.Mock_req = self.patcher_req.start() + self.assertEquals("failure", process_nxi_termination_opt(nssi_request_json, osdf_config).get('requestStatus')) + self.patcher_req.stop() + + self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_resource_count', + side_effect=AAIException("Request exception was encountered")) + self.Mock_req = self.patcher_req.start() + self.assertEquals("failure", process_nxi_termination_opt(nssi_request_json, osdf_config).get('requestStatus')) + self.patcher_req.stop() + + +if __name__ == "__main__": + unittest.main()
\ No newline at end of file diff --git a/test/apps/pci_optimization/des_result.json b/test/apps/pci_optimization/des_result.json new file mode 100644 index 0000000..9209acf --- /dev/null +++ b/test/apps/pci_optimization/des_result.json @@ -0,0 +1,18 @@ +[ + [ + { + "overallHoAtt": 1300 + }, + { + "overallHoAtt": 550 + } + ], + [ + { + "overallHoAtt": 450 + }, + { + "overallHoAtt": 400 + } + ] +] diff --git a/test/apps/pci_optimization/test_ml_model.py b/test/apps/pci_optimization/test_ml_model.py new file mode 100644 index 0000000..cf0dee5 --- /dev/null +++ b/test/apps/pci_optimization/test_ml_model.py @@ -0,0 +1,87 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2020 Wipro Limited. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +from mock import patch +import unittest +from apps.pci.optimizers.solver.ml_model import MlModel +from osdf.adapters.dcae.des import DESException +import osdf.config.loader as config_loader +from osdf.utils.interfaces import json_from_file +from osdf.utils.programming_utils import DotDict + + +class TestMlModel(unittest.TestCase): + def setUp(self): + self.config_spec = { + "deployment": "config/osdf_config.yaml", + "core": "config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + + def tearDown(self): + pass + + def test_ml_model(self): + des_result_file = 'test/apps/pci_optimization/des_result.json' + results = json_from_file(des_result_file) + + dzn_data = { + 'NUM_NODES': 4, + 'NUM_PCIS': 4, + 'NUM_NEIGHBORS': 4, + 'NEIGHBORS': [], + 'NUM_SECOND_LEVEL_NEIGHBORS': 1, + 'SECOND_LEVEL_NEIGHBORS': [], + 'PCI_UNCHANGEABLE_CELLS': {}, + 'ORIGINAL_PCIS': [] + } + + network_cell_info = { + 'cell_list': [ + { + 'cell_id': 'Chn0001', + 'id': 1, + 'nbr_list': [] + }, + { + 'cell_id': 'Chn0002', + 'id': 2, + 'nbr_list': [] + } + ] + } + self.patcher_req = patch('osdf.adapters.dcae.des.extract_data', side_effect=results) + self.Mock_req = self.patcher_req.start() + mlmodel = MlModel() + mlmodel.get_additional_inputs(dzn_data, network_cell_info) + self.assertEqual({1}, dzn_data['PCI_UNCHANGEABLE_CELLS']) + self.patcher_req.stop() + + dzn_data['PCI_UNCHANGEABLE_CELLS'] = [] + self.patcher_req = patch('osdf.adapters.dcae.des.extract_data', side_effect=DESException('error')) + self.Mock_req = self.patcher_req.start() + mlmodel.get_additional_inputs(dzn_data, network_cell_info) + self.assertEqual(set() , dzn_data['PCI_UNCHANGEABLE_CELLS']) + self.patcher_req.stop() + + self.patcher_req = patch('osdf.adapters.dcae.des.extract_data', return_value=[]) + self.Mock_req = self.patcher_req.start() + mlmodel.get_additional_inputs(dzn_data, network_cell_info) + self.assertEqual(set() , dzn_data['PCI_UNCHANGEABLE_CELLS']) + self.patcher_req.stop() diff --git a/test/apps/slice_selection/conductor_error_response.json b/test/apps/slice_selection/conductor_error_response.json new file mode 100644 index 0000000..95a9750 --- /dev/null +++ b/test/apps/slice_selection/conductor_error_response.json @@ -0,0 +1,18 @@ +{ + "plans": [ + { + "status": "error", + "message": "Some error message", + "name": "Plan Name 1", + "links": [ + [ + { + "href": "http://conductor:8091/v1/plans/plan_id", + "rel": "self" + } + ] + ], + "id": "plan_id" + } + ] +} diff --git a/test/apps/slice_selection/new_solution_conductor_response.json b/test/apps/slice_selection/new_solution_conductor_response.json new file mode 100644 index 0000000..897aa2b --- /dev/null +++ b/test/apps/slice_selection/new_solution_conductor_response.json @@ -0,0 +1,47 @@ +{ + "plans":[ + { + "status":"done", + "id":"plan_id", + "name":"Plan Name 1", + "links":[ + [ + { + "href":"http://conductor:8091/v1/plans/plan_id", + "rel":"self" + } + ] + ], + "recommendations":[ + { + "embb-nst":{ + "inventory_provider":"generator", + "candidate": { + "candidate_id":"1a636c4d-5e76-427e-bfd6-241a947224b0", + "latency":20, + "inventory_provider":"generator", + "max_number_of_ues":100, + "ue_mobility_level":"stationary", + "candidate_type":"slice_profiles", + "resource_sharing_level":"shared", + "inventory_type":"slice_profiles", + "reliability": 99.99, + "AN_latency": 10, + "AN_ue_mobility_level": "stationary", + "AN_max_number_of_ues": 100, + "AN_reliability": 99.99, + "AN_resource_sharing_level":"shared", + "CN_latency": 5, + "CN_reliability": 99.99, + "CN_resource_sharing_level":"shared", + "TN-BH_reliability": 99.99, + "TN-BH_latency": 5, + "TN-BH_resource_sharing_level":"shared", + "cost":1.0 + } + } + } + ] + } + ] +} diff --git a/test/apps/slice_selection/new_solution_nsi_response.json b/test/apps/slice_selection/new_solution_nsi_response.json new file mode 100644 index 0000000..52624b6 --- /dev/null +++ b/test/apps/slice_selection/new_solution_nsi_response.json @@ -0,0 +1,35 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "completed", + "statusMessage": "", + "solutions": [ + { + "existingNSI": false, + "newNSISolution": { + "sliceProfiles": [ + { + "domainType": "AN", + "resourceSharingLevel": "shared", + "latency": 10, + "reliability": 99.99, + "uEMobilityLevel": "stationary", + "maxNumberofUEs": 100 + }, + { + "domainType": "CN", + "resourceSharingLevel": "shared", + "latency": 5, + "reliability": 99.99 + }, + { + "domainType": "TN-BH", + "resourceSharingLevel": "shared", + "latency": 5, + "reliability": 99.99 + } + ] + } + } + ] +} diff --git a/test/apps/slice_selection/no_rec.json b/test/apps/slice_selection/no_rec.json new file mode 100644 index 0000000..855afac --- /dev/null +++ b/test/apps/slice_selection/no_rec.json @@ -0,0 +1,18 @@ +{ + "plans": [ + { + "id": "d8c07237-5f66-4aa6-871c-a04221d99458", + "links": [ + [ + { + "href": "https://oof-has-api:8091/v1/plans/d8c07237-5f66-4aa6-871c-a04221d99458", + "rel": "self" + } + ] + ], + "message": "Plan d8c07237-5f66-4aa6-871c-a04221d99458 search failed, no recommendations found by machine dev-oof-has-solver-65d478b6d-ql5fp", + "name": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "status": "not found" + } + ] +} diff --git a/test/apps/slice_selection/no_recomm_conductor_response.json b/test/apps/slice_selection/no_recomm_conductor_response.json new file mode 100644 index 0000000..f23af87 --- /dev/null +++ b/test/apps/slice_selection/no_recomm_conductor_response.json @@ -0,0 +1,18 @@ +{
+ "plans":[
+ {
+ "status":"done",
+ "id":"plan_id",
+ "name":"Plan Name 1",
+ "links":[
+ [
+ {
+ "href":"http://conductor:8091/v1/plans/plan_id",
+ "rel":"self"
+ }
+ ]
+ ],
+ "recommendations": []
+ }
+ ]
+}
diff --git a/test/apps/slice_selection/no_recomm_nsi_response.json b/test/apps/slice_selection/no_recomm_nsi_response.json new file mode 100644 index 0000000..e36a243 --- /dev/null +++ b/test/apps/slice_selection/no_recomm_nsi_response.json @@ -0,0 +1,7 @@ +{
+ "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+ "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+ "requestStatus":"completed",
+ "statusMessage":"",
+ "solutions": []
+}
diff --git a/test/apps/slice_selection/nsi_error_response.json b/test/apps/slice_selection/nsi_error_response.json new file mode 100644 index 0000000..9dc5300 --- /dev/null +++ b/test/apps/slice_selection/nsi_error_response.json @@ -0,0 +1,6 @@ +{ + "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus":"error", + "statusMessage":"Some error message" +} diff --git a/test/apps/slice_selection/nsi_request.json b/test/apps/slice_selection/nsi_request.json new file mode 100644 index 0000000..72b2c8b --- /dev/null +++ b/test/apps/slice_selection/nsi_request.json @@ -0,0 +1,31 @@ +{ + "serviceProfile": { + "latency": 2, + "security": "High", + "reliability": 99.9999, + "trafficDensity": 1, + "connDensity": 100000, + "expDataRate": 50, + "jitter": 1, + "survivalTime": 0, + "resourceSharingLevel":"shared" + }, + "serviceInfo":{ + "serviceInstanceId": "209fb01e-60ca-4325-b074-c5ad4e0499f8", + "serviceName": "" + }, + "requestInfo": { + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "sourceId": "SO", + "timeout": 5 + }, + "NSTInfoList": [ + { + "modelInvariantId": "fda3c1e8-7653-4acd-80ef-f5755c1d3859", + "modelVersionId": "a6906768-1cae-4e78-acd1-d753ac61f3e8", + "modelName": "URLLC_1" + } + ] +} diff --git a/test/apps/slice_selection/nsi_selection_invalid_request.json b/test/apps/slice_selection/nsi_selection_invalid_request.json new file mode 100644 index 0000000..3ecd1e3 --- /dev/null +++ b/test/apps/slice_selection/nsi_selection_invalid_request.json @@ -0,0 +1,80 @@ +{ + "serviceProfile": { + "latency": 2, + "security": "High", + "reliability": 99.9999, + "trafficDensity": 1, + "connDensity": 100000, + "expDataRate": 50, + "jitter": 1, + "survivalTime": 0, + "resourceSharingLevel":"shared" + }, + "requestInfo": { + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "sourceId": "SO", + "timeout": 5 + }, + "NSSTInfo":[ + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa2", + "invariantUUID":"2fa85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-an-nf" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa3", + "invariantUUID":"4fa85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-cn" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa4", + "invariantUUID":"5ta85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-tn-fh" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa5", + "invariantUUID":"6ya85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-tn-mh" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa7", + "invariantUUID":"7ua85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-tn-bh" + } + ], + "preferReuse":true, + "subnetCapabilities":[ + { + "domainType":"AN-NF", + "capabilityDetails":{ + "blob":"content" + } + }, + { + "domainType":"CN", + "capabilityDetails":{ + "blob":"content" + } + }, + { + "domainType":"TN-FH", + "capabilityDetails":{ + "blob":"content" + } + }, + { + "domainType":"TN-MH", + "capabilityDetails":{ + "blob":"content" + } + }, + { + "domainType":"TN-BH", + "capabilityDetails":{ + "blob":"content" + } + } + ] +} diff --git a/test/apps/slice_selection/nsi_selection_request.json b/test/apps/slice_selection/nsi_selection_request.json new file mode 100644 index 0000000..39fb925 --- /dev/null +++ b/test/apps/slice_selection/nsi_selection_request.json @@ -0,0 +1,84 @@ +{ + "serviceProfile":{ + "latency":5, + "security":"High", + "reliability":99.999, + "resourceSharingLevel":"shared" + }, + "requestInfo":{ + "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "callbackHeader":{ + "blob":"content" + }, + "sourceId":"d290f1ee-6c54-4b01-90e6-d701748f0851", + "timeout":5, + "numSolutions":1 + }, + "NSTInfo":{ + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa1", + "invariantUUID":"7ua85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-nst" + }, + "NSSTInfo":[ + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa2", + "invariantUUID":"2fa85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-an-nf" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa3", + "invariantUUID":"4fa85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-cn" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa4", + "invariantUUID":"5ta85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-tn-fh" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa5", + "invariantUUID":"6ya85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-tn-mh" + }, + { + "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa7", + "invariantUUID":"7ua85f64-5717-4562-b3fc-2c963f66afa6", + "name":"embb-tn-bh" + } + ], + "preferReuse":false, + "subnetCapabilities":[ + { + "domainType":"AN", + "capabilityDetails":{ + "latency": "4", + "reliability": "99.9", + "maxNumberofUEs": "10", + "maxThroughput": "50", + "termDensity": "60" + } + }, + { + "domainType":"CN", + "capabilityDetails":{ + "latency": "3", + "reliability": "99.9", + "maxNumberofUEs": "10", + "maxThroughput": "50", + "termDensity": "60" + } + }, + { + "domainType":"TN-BH", + "capabilityDetails":{ + "latency": "2", + "reliability": "99.9", + "maxNumberofUEs": "10", + "maxThroughput": "50", + "termDensity": "60" + } + } + ] +} diff --git a/test/apps/slice_selection/nssi_conductor_response.json b/test/apps/slice_selection/nssi_conductor_response.json new file mode 100644 index 0000000..d2edcf5 --- /dev/null +++ b/test/apps/slice_selection/nssi_conductor_response.json @@ -0,0 +1,53 @@ +{ + "plans":[ + { + "status":"done", + "id":"plan_id", + "name":"Plan Name 1", + "links":[ + [ + { + "href":"http://conductor:8091/v1/plans/plan_id", + "rel":"self" + } + ] + ], + "recommendations":[ + { + "embb-cn": { + "inventory_provider": "aai", + "candidate": { + "exp_data_rate": 0, + "conn_density": 0, + "coverage_area_ta_list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "activity_factor": 0, + "cs_availability": null, + "candidate_id": "1a636c4d-5e76-427e-bfd6-241a947224b0", + "area_traffic_cap_dl": null, + "latency": 20, + "service_area_dimension": null, + "e2e_latency": 0, + "area_traffic_cap_ul": null, + "inventory_provider": "aai", + "exp_data_rate_ul": 100, + "max_number_of_ues": 0, + "ue_mobility_level": "stationary", + "candidate_type": "nssi", + "traffic_density": 0, + "payload_size": 0, + "exp_data_rate_dl": 100, + "jitter": 0, + "survival_time": 0, + "resource_sharing_level": "shared", + "inventory_type": "nssi", + "reliability": null, + "cost": 1.0, + "instance_id": "e1041cdc-12da-4f36-b84e-68c380e9cd47", + "instance_name": "nssi_test_0211" + } + } + } + ] + } + ] +} diff --git a/test/apps/slice_selection/nssi_error_response.json b/test/apps/slice_selection/nssi_error_response.json new file mode 100644 index 0000000..70e0596 --- /dev/null +++ b/test/apps/slice_selection/nssi_error_response.json @@ -0,0 +1,7 @@ +{ + "requestId":"r450f1ee-6c54-4b01-90e6-d701748f0851", + "transactionId":"t670f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus":"completed", + "statusMessage":"", + "solutions": [] +} diff --git a/test/apps/slice_selection/nssi_selection_invalid_request.json b/test/apps/slice_selection/nssi_selection_invalid_request.json new file mode 100644 index 0000000..57e0184 --- /dev/null +++ b/test/apps/slice_selection/nssi_selection_invalid_request.json @@ -0,0 +1,23 @@ +{ + "sliceProfile": { + "blob": "content" + }, + "requestInfo": { + "transactionId": "t670f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "r450f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "callbackHeader": { + "blob": "content" + }, + "sourceId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "timeout": 5, + "numSolutions": 1, + "addtnlArgs": { + "blob": "content" + } + }, + "NSSTInfo": { + "UUID": "y7785f64-5717-4562-b3fc-2c963f66afa6", + "name": "embb-cn" + } +} diff --git a/test/apps/slice_selection/nssi_selection_request.json b/test/apps/slice_selection/nssi_selection_request.json new file mode 100644 index 0000000..61ee563 --- /dev/null +++ b/test/apps/slice_selection/nssi_selection_request.json @@ -0,0 +1,27 @@ +{ + "sliceProfile": { + "latency":5, + "security":"High", + "reliability":99.999, + "resourceSharingLevel":"shared" + }, + "requestInfo": { + "transactionId": "t670f1ee-6c54-4b01-90e6-d701748f0851", + "requestId": "r450f1ee-6c54-4b01-90e6-d701748f0851", + "callbackUrl": "http://0.0.0.0:9000/osdfCallback/", + "callbackHeader": { + "blob": "content" + }, + "sourceId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "timeout": 5, + "numSolutions": 1, + "addtnlArgs": { + "blob": "content" + } + }, + "NSSTInfo": { + "UUID": "a7785f64-5717-4562-b3fc-2c963f66afa6", + "invariantUUID": "9fh85f64-5717-4562-b3fc-2c963f66afa6", + "name": "embb-cn" + } +} diff --git a/test/apps/slice_selection/shared_solution_conductor_response.json b/test/apps/slice_selection/shared_solution_conductor_response.json new file mode 100644 index 0000000..a187fef --- /dev/null +++ b/test/apps/slice_selection/shared_solution_conductor_response.json @@ -0,0 +1,53 @@ +{ + "plans":[ + { + "status":"done", + "id":"plan_id", + "name":"Plan Name 1", + "links":[ + [ + { + "href":"http://conductor:8091/v1/plans/plan_id", + "rel":"self" + } + ] + ], + "recommendations":[ + { + "embb-nst": { + "inventory_provider": "aai", + "candidate": { + "exp_data_rate": 0, + "conn_density": 0, + "coverage_area_ta_list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]", + "activity_factor": 0, + "cs_availability": null, + "candidate_id": "1a636c4d-5e76-427e-bfd6-241a947224b0", + "area_traffic_cap_dl": null, + "latency": 20, + "service_area_dimension": null, + "e2e_latency": 0, + "area_traffic_cap_ul": null, + "inventory_provider": "aai", + "exp_data_rate_ul": 100, + "max_number_of_ues": 0, + "ue_mobility_level": "stationary", + "candidate_type": "nsi", + "traffic_density": 0, + "payload_size": 0, + "exp_data_rate_dl": 100, + "jitter": 0, + "survival_time": 0, + "resource_sharing_level": "shared", + "inventory_type": "nsi", + "reliability": null, + "cost": 1.0, + "instance_id": "f1041cdc-12da-4f36-b84e-68c380e9cd47", + "instance_name": "nsi_test_0211" + } + } + } + ] + } + ] +} diff --git a/test/apps/slice_selection/shared_solution_nsi_response.json b/test/apps/slice_selection/shared_solution_nsi_response.json new file mode 100644 index 0000000..ff83dfc --- /dev/null +++ b/test/apps/slice_selection/shared_solution_nsi_response.json @@ -0,0 +1,17 @@ +{ + "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "completed", + "solutions": [ + { + "existingNSI": true, + "sharedNSISolution": { + "UUID": "3fa85f64-5717-4562-b3fc-2c963f66afa1", + "invariantUUID": "7ua85f64-5717-4562-b3fc-2c963f66afa6", + "NSIName": "nsi_test_0211", + "NSIId": "f1041cdc-12da-4f36-b84e-68c380e9cd47" + } + } + ], + "statusMessage": "", + "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851" +} diff --git a/test/apps/slice_selection/shared_solution_nssi_response.json b/test/apps/slice_selection/shared_solution_nssi_response.json new file mode 100644 index 0000000..f3468a4 --- /dev/null +++ b/test/apps/slice_selection/shared_solution_nssi_response.json @@ -0,0 +1,15 @@ +{ + "requestId": "r450f1ee-6c54-4b01-90e6-d701748f0851", + "requestStatus": "completed", + "solutions": [ + { + "UUID": "a7785f64-5717-4562-b3fc-2c963f66afa6", + "invariantUUID": "9fh85f64-5717-4562-b3fc-2c963f66afa6", + "NSSIName": "nssi_test_0211", + "NSSIId": "e1041cdc-12da-4f36-b84e-68c380e9cd47" + + } + ], + "statusMessage": "", + "transactionId": "t670f1ee-6c54-4b01-90e6-d701748f0851" +} diff --git a/test/apps/slice_selection/slice_policies.txt b/test/apps/slice_selection/slice_policies.txt new file mode 100644 index 0000000..7eb55de --- /dev/null +++ b/test/apps/slice_selection/slice_policies.txt @@ -0,0 +1,5 @@ +query_policy_nsi.json +threshold_policy_nsi.json +vnf_policy_nsi_shared_case.json +opt_policy_nsi_reuse.json + diff --git a/test/apps/slice_selection/subnet_policies.txt b/test/apps/slice_selection/subnet_policies.txt new file mode 100644 index 0000000..649672d --- /dev/null +++ b/test/apps/slice_selection/subnet_policies.txt @@ -0,0 +1,5 @@ +query_policy_nsi.json +threshold_policy_nsi.json +vnf_policy_nssi_shared.json +opt_policy_nssi.json + diff --git a/test/apps/slice_selection/test_remote_opt_processor.py b/test/apps/slice_selection/test_remote_opt_processor.py new file mode 100644 index 0000000..7c2d191 --- /dev/null +++ b/test/apps/slice_selection/test_remote_opt_processor.py @@ -0,0 +1,165 @@ +# -------------------------------------------------------------------------
+# Copyright (C) 2020 Wipro Limited.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+import json
+import unittest
+from requests import RequestException, Response
+
+from apps.slice_selection.optimizers.conductor.remote_opt_processor import SliceSelectionOptimizer
+from osdf.adapters.local_data import local_policies
+from osdf.utils.interfaces import json_from_file, yaml_from_file
+from osdf.utils.programming_utils import DotDict
+import osdf.config.loader as config_loader
+from mock import patch, MagicMock
+import json
+from osdf.logging.osdf_logging import error_log, debug_log
+from osdf.adapters.policy.interface import get_policies
+
+
+class TestRemoteOptProcessor(unittest.TestCase):
+ def setUp(self):
+ self.config_spec = {
+ "deployment": "config/osdf_config.yaml",
+ "core": "config/common_config.yaml"
+ }
+ slice_spec = "config/slicing_config.yaml"
+ self.slice_config = config_loader.load_config_file(slice_spec)
+ self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec))
+ self.patcher_RestClient = patch(
+ 'osdf.utils.interfaces.RestClient.request', return_value=MagicMock())
+ self.mock_rc = self.patcher_RestClient.start()
+
+ def tearDown(self):
+ patch.stopall()
+
+ def test_process_nsi_selection_opt(self):
+ main_dir = ""
+ request_file = main_dir + 'test/apps/slice_selection/nsi_selection_request.json'
+ not_shared_request_file = main_dir + 'test/apps/slice_selection/not_shared_nsi_request.json'
+ #response files
+ new_solution_response_file = main_dir + 'test/apps/slice_selection/new_solution_nsi_response.json'
+ shared_solution_response_file = main_dir + 'test/apps/slice_selection/shared_solution_nsi_response.json'
+ no_solution_response_file = main_dir + 'test/apps/slice_selection/no_recomm_nsi_response.json'
+ error_response_file = main_dir + 'test/apps/slice_selection/nsi_error_response.json'
+
+ request_json = json_from_file(request_file)
+ new_solution_response_json = json_from_file(new_solution_response_file)
+ shared_solution_response_json = json_from_file(shared_solution_response_file)
+ no_solution_response_json = json_from_file(no_solution_response_file)
+ error_response_json = json_from_file(error_response_file)
+
+ policies_path = main_dir + 'test/policy-local-files/slice-selection-files'
+ slice_policies_file = main_dir + 'test/apps/slice_selection/slice_policies.txt'
+
+ valid_policies_files = local_policies.get_policy_names_from_file(slice_policies_file)
+ policies = [json_from_file(policies_path + '/' + name) for name in valid_policies_files]
+ self.patcher_get_policies = patch('osdf.adapters.policy.interface.remote_api',
+ return_value=policies)
+ self.Mock_get_policies = self.patcher_get_policies.start()
+
+ # new solution
+ new_solution_conductor_response_file = 'test/apps/slice_selection/new_solution_conductor_response.json'
+ new_solution_conductor_response = json_from_file(new_solution_conductor_response_file)
+ self.patcher_req = patch('osdf.adapters.conductor.conductor.request',
+ return_value=new_solution_conductor_response)
+ self.Mock_req = self.patcher_req.start()
+ slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSI')
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=new_solution_response_json, noresponse=True)
+ self.patcher_req.stop()
+
+ # shared solution
+ request_json['preferReuse'] = True
+ shared_solution_conductor_response_file = 'test/apps/slice_selection/shared_solution_conductor_response.json'
+ shared_solution_conductor_response = json_from_file(shared_solution_conductor_response_file)
+ self.patcher_req = patch('osdf.adapters.conductor.conductor.request',
+ return_value=shared_solution_conductor_response)
+ self.Mock_req = self.patcher_req.start()
+ slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSI')
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=shared_solution_response_json, noresponse=True)
+ self.patcher_req.stop()
+
+ # no recommendation
+ no_solution_conductor_response_file = 'test/apps/slice_selection/no_rec.json'
+ no_solution_conductor_response = json_from_file(no_solution_conductor_response_file)
+ self.patcher_req = patch('osdf.adapters.conductor.conductor.request',
+ return_value=no_solution_conductor_response)
+ self.Mock_req = self.patcher_req.start()
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=no_solution_response_json, noresponse=True)
+ self.patcher_req.stop()
+
+ # Exception
+ conductor_error_response_file = 'test/apps/slice_selection/conductor_error_response.json'
+ conductor_error_response = json_from_file(conductor_error_response_file)
+
+ response = Response()
+ response._content = json.dumps(conductor_error_response).encode()
+ self.patcher_req = patch('osdf.adapters.conductor.conductor.request',
+ side_effect=RequestException(response=response))
+ self.Mock_req = self.patcher_req.start()
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=error_response_json, noresponse=True)
+ self.patcher_req.stop()
+
+ self.patcher_req = patch('osdf.adapters.conductor.conductor.request',
+ side_effect=Exception("Some error message"))
+ self.Mock_req = self.patcher_req.start()
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=error_response_json, noresponse=True)
+ self.patcher_req.stop()
+
+ def test_process_nssi_selection_opt(self):
+ main_dir = ""
+ request_file = main_dir + 'test/apps/slice_selection/nssi_selection_request.json'
+ # response files
+ shared_solution_response_file = main_dir + 'test/apps/slice_selection/shared_solution_nssi_response.json'
+ error_response_file = main_dir + 'test/apps/slice_selection/nssi_error_response.json'
+
+ request_json = json_from_file(request_file)
+ shared_solution_response_json = json_from_file(shared_solution_response_file)
+ error_response_json = json_from_file(error_response_file)
+
+ policies_path = main_dir + 'test/policy-local-files/slice-selection-files'
+ slice_policies_file = main_dir + 'test/apps/slice_selection/subnet_policies.txt'
+
+ valid_policies_files = local_policies.get_policy_names_from_file(slice_policies_file)
+ policies = [json_from_file(policies_path + '/' + name) for name in valid_policies_files]
+ self.patcher_get_policies = patch('osdf.adapters.policy.interface.remote_api',
+ return_value=policies)
+ self.Mock_get_policies = self.patcher_get_policies.start()
+
+ shared_solution_conductor_response_file = 'test/apps/slice_selection/nssi_conductor_response.json'
+ shared_solution_conductor_response = json_from_file(shared_solution_conductor_response_file)
+ self.patcher_req = patch('osdf.adapters.conductor.conductor.request',
+ return_value=shared_solution_conductor_response)
+ self.Mock_req = self.patcher_req.start()
+ slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSSI')
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=shared_solution_response_json, noresponse=True)
+ self.patcher_req.stop()
+
+ request_json['sliceProfile']['resourceSharingLevel'] = "not-shared"
+ slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSSI')
+ slice_select_opt.process_slice_selection_opt()
+ self.mock_rc.assert_called_with(json=error_response_json, noresponse=True)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/test/conductor/test_conductor_calls.py b/test/conductor/test_conductor_calls.py index 77d9a72..8b4411d 100644 --- a/test/conductor/test_conductor_calls.py +++ b/test/conductor/test_conductor_calls.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ # import unittest -from osdf.optimizers.placementopt.conductor import conductor +from osdf.adapters.conductor import conductor import osdf.config.loader as config_loader from osdf.utils.interfaces import json_from_file from osdf.utils.programming_utils import DotDict @@ -34,6 +35,10 @@ class TestConductorCalls(unittest.TestCase): self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) self.lp = self.osdf_config.core.get('osdf_temp', {}).get('local_policies', {} ).get('placement_policy_files_vcpe') + self.template_fields = { + 'location_enabled': True, + 'version': '2017-10-10' + } def tearDown(self): pass @@ -41,12 +46,22 @@ class TestConductorCalls(unittest.TestCase): def test_request(self): req_json = json_from_file("./test/placement-tests/request.json") policies = pol.get_local_policies("test/policy-local-files/", self.lp) - conductor.request(req_json, self.osdf_config, policies) + req_info = req_json['requestInfo'] + demands = req_json['placementInfo']['placementDemands'] + request_parameters = req_json['placementInfo']['requestParameters'] + service_info = req_json['serviceInfo'] + conductor.request(req_info, demands, request_parameters, service_info, self.template_fields, + self.osdf_config, policies) def test_request_vfmod(self): req_json = json_from_file("./test/placement-tests/request_vfmod.json") policies = pol.get_local_policies("test/policy-local-files/", self.lp) - conductor.request(req_json, self.osdf_config, policies) + req_info = req_json['requestInfo'] + demands = req_json['placementInfo']['placementDemands'] + request_parameters = req_json['placementInfo']['requestParameters'] + service_info = req_json['serviceInfo'] + conductor.request(req_info, demands, request_parameters, service_info, self.template_fields, + self.osdf_config, policies) if __name__ == "__main__": diff --git a/test/conductor/test_conductor_translation.py b/test/conductor/test_conductor_translation.py index 27711f5..2a600e4 100644 --- a/test/conductor/test_conductor_translation.py +++ b/test/conductor/test_conductor_translation.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,14 +16,11 @@ # # ------------------------------------------------------------------------- # -import mock import unittest -from flask import Response -from mock import patch from osdf.adapters.local_data import local_policies -from osdf.optimizers.placementopt.conductor import translation as tr -from osdf.utils.interfaces import json_from_file, yaml_from_file +from osdf.adapters.conductor import translation as tr +from osdf.utils.interfaces import json_from_file class TestConductorTranslation(unittest.TestCase): @@ -42,21 +40,47 @@ class TestConductorTranslation(unittest.TestCase): self.request_vfmod_json = json_from_file(parameter_data_file) self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + self.optimization_policies = [json_from_file(policy_data_path + '/' + + "slice-selection-files/opt_policy_nsi_reuse.json")] + def tearDown(self): pass def test_gen_demands(self): # need to run this only on vnf policies - vnf_policies = [x for x in self.policies if x["content"]["policyType"] == "vnfPolicy"] - res = tr.gen_demands(self.request_json, vnf_policies) + vnf_policies = [x for x in self.policies if x[list(x.keys())[0]]["type"] + == "onap.policies.optimization.VnfPolicy"] + res = tr.gen_demands(self.request_json['placementInfo']['placementDemands'], vnf_policies) + assert res is not None def test_gen_vfmod_demands(self): # need to run this only on vnf policies - vnf_policies = [x for x in self.policies if x["content"]["policyType"] == "vnfPolicy"] - res = tr.gen_demands(self.request_vfmod_json, vnf_policies) + vnf_policies = [x for x in self.policies if x[list(x.keys())[0]]["type"] + == "onap.policies.optimization.VnfPolicy"] + res = tr.gen_demands(self.request_vfmod_json['placementInfo']['placementDemands'], vnf_policies) assert res is not None + def test_gen_optimization_policy(self): + expected = [{ + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "creation_cost", + "demand": "embb-nst" + } + } + ] + } + }] + self.assertEqual(expected, + tr.gen_optimization_policy(self.request_vfmod_json['placementInfo']['placementDemands'], + self.optimization_policies)) + if __name__ == "__main__": unittest.main() diff --git a/test/config/common_config.yaml b/test/config/common_config.yaml index a3ef82e..560d707 100644 --- a/test/config/common_config.yaml +++ b/test/config/common_config.yaml @@ -61,4 +61,14 @@ policy_info: - get_param: subscriber_role default: # if no explicit service related information is needed policy_fetch: by_name - policy_scope: none
\ No newline at end of file + policy_scope: none + +PCI: + ML: + average_ho_threshold: 10000 + latest_ho_threshold: 500 + DES: + service_id: ho_metric + filter: + interval: 10 + ml_enabled: false diff --git a/test/config/log.yml b/test/config/log.yml new file mode 100644 index 0000000..ad0de21 --- /dev/null +++ b/test/config/log.yml @@ -0,0 +1,100 @@ +version: 1 +disable_existing_loggers: True + +loggers: + error: + handlers: [error_handler, console_handler] + level: "WARN" + propagate: True + debug: + handlers: [debug_handler, console_handler] + level: "DEBUG" + propagate: True + metrics: + handlers: [metrics_handler, console_handler] + level: "INFO" + propagate: True + audit: + handlers: [audit_handler, console_handler] + level: "INFO" + propagate: True +handlers: + debug_handler: + level: "DEBUG" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/debug.log" + formatter: "debugFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + error_handler: + level: "WARN" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/error.log" + formatter: "errorFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + metrics_handler: + level: "INFO" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/metrics.log" + formatter: "metricsFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + audit_handler: + level: "INFO" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/audit.log" + formatter: "auditFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + console_handler: + level: "DEBUG" + class: "logging.StreamHandler" + formatter: "metricsFormat" + +formatters: + standard: + format: "%(asctime)s|||||%(name)s||%(thread)||%(funcName)s||%(levelname)s||%(message)s" + debugFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "%(asctime)s.%(msecs)03d+00:00|{requestID}|%(threadName)s|{server}|%(levelname)s|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + errorFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "%(asctime)s.%(msecs)03d+00:00|{requestID}|%(threadName)s|{serviceName}|{partnerName}\ + |{targetEntity}|{targetServiceName}|%(levelname)s|{errorCode}|{errorDescription}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + auditFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "{entryTimestamp}+00:00|%(asctime)s.%(msecs)03d+00:00|{requestID}|{serviceInstanceID}\ + |%(threadName)s|{server}|{serviceName}|{partnerName}|{statusCode}|{responseCode}|{responseDescription}\ + |{instanceUUID}|%(levelname)s|{severity}|{serverIPAddress}|{timer}|{server}|{IPAddress}||{unused}\ + |{processKey}|{customField1}|{customField2}|{customField3}|{customField4}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + metricsFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "{entryTimestamp}+00:00|%(asctime)s.%(msecs)03d+00:00|{requestID}|{serviceInstanceID}\ + |%(threadName)s|{server}|{serviceName}|{partnerName}|{targetEntity}|{targetServiceName}|{statusCode}|{responseCode}|{responseDescription}\ + |{instanceUUID}|%(levelname)s|{severity}|{serverIPAddress}|{timer}|{server}|{IPAddress}||{unused}\ + |{processKey}|{TargetVirtualEntity}|{customField1}|{customField2}|{customField3}|{customField4}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + mdcFormat: + format: "%(asctime)s.%(msecs)03d+00:00|||||%(name)s||%(thread)s||%(funcName)s||%(levelname)s||%(message)s||||%(mdc)s" + mdcfmt: "{requestID} {invocationID} {serviceName} {serverIPAddress}" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter diff --git a/test/config/opteng_config.yaml b/test/config/opteng_config.yaml new file mode 100755 index 0000000..4a7e57d --- /dev/null +++ b/test/config/opteng_config.yaml @@ -0,0 +1,25 @@ +# Policy Platform -- requires Authorization +policyPlatformUrl: https://policy-xacml-pdp:6969/policy/pdpx/decision/v1 # Policy Dev platform URL + +# AAF Authentication config +is_aaf_enabled: False +aaf_cache_expiry_mins: 5 +aaf_url: https://aaftest.simpledemo.onap.org:8095 +aaf_user_roles: + - '/optmodel:org.onap.oof.access|*|read ALL' + - '/optengine:org.onap.oof.access|*|read ALL' + +# Secret Management Service from AAF +aaf_sms_url: https://aaf-sms.onap:10443 +aaf_sms_timeout: 30 +secret_domain: osdf +aaf_ca_certs: ssl_certs/aaf_root_ca.cer + +osdfDatabaseHost: localhost +osdfDatabaseSchema: osdf +osdfDatabaseUsername: osdf +osdfDatabasePassword: osdf +osdfDatabasePort: 3306 + +#key +appkey: os35@rrtky400fdntc#001t5
\ No newline at end of file diff --git a/test/config/osdf_config.yaml b/test/config/osdf_config.yaml index eaa31ff..05d7c6a 100755 --- a/test/config/osdf_config.yaml +++ b/test/config/osdf_config.yaml @@ -64,6 +64,35 @@ aaf_ca_certs: ssl_certs/aaf_root_ca.cer pciHMSUsername: "" # pcihandler username for call back. pciHMSPassword: "" # pcihandler password for call back. +configClientType: configdb + +# config db api +configDbUrl: http://127.0.0.1:5000/simulated/configdb +configDbUserName: osdf +configDbPassword: passwd +configDbGetCellListUrl: 'getCellList' +configDbGetNbrListUrl: 'getNbrList' + # Credentials for the OOF PCI Opt service osdfPCIOptUsername: PCI-OSDF-USER osdfPCIOptPassword: PCI-OSDF-PASSWD + +aaiUrl: "https://aai.url:30233" +aaiServiceInstanceUrl : "/aai/v20/nodes/service-instances/service-instance/" + +#DES api +desUrl: http://des.url:9000 +desApiPath: /datalake/v1/exposure/ +desHeaders: + Accept: application/json + Content-Type: application/json +desUsername: +desPassword: + +#consulconfig +consulHost: '127.0.0.1' +consulPort: 8500 +consulScheme: 'http' +consulVerify: True +consulCert: None +activateConsulConfig: False
\ No newline at end of file diff --git a/test/configdb/test_configdb_calls.py b/test/configdb/test_configdb_calls.py index eb799e7..3393991 100644 --- a/test/configdb/test_configdb_calls.py +++ b/test/configdb/test_configdb_calls.py @@ -16,7 +16,7 @@ # ------------------------------------------------------------------------- # -from osdf.optimizers.pciopt.configdb import request +from apps.pci.optimizers.config_request import request import osdf.config.loader as config_loader from osdf.utils.interfaces import json_from_file from osdf.utils.programming_utils import DotDict diff --git a/test/functest/scripts/start-simulators.sh b/test/functest/scripts/start-simulators.sh index a3f3db1..66507d4 100755 --- a/test/functest/scripts/start-simulators.sh +++ b/test/functest/scripts/start-simulators.sh @@ -20,7 +20,12 @@ # This script is in osdf/test/functest/scripts/ -SCRIPTDIR=$(dirname $(readlink -f $0)) +if [[ `uname` == "Darwin" ]] +then + SCRIPTDIR=$(dirname $(greadlink -f $0)) +else + SCRIPTDIR=$(dirname $(readlink -f $0)) +fi FUNC_TEST_DIR=$(dirname $SCRIPTDIR) TEST_DIR=$(dirname $FUNC_TEST_DIR) OSDF_DIR=$(dirname $TEST_DIR) diff --git a/test/functest/scripts/stop-simulators.sh b/test/functest/scripts/stop-simulators.sh index c9dd126..e80a080 100755 --- a/test/functest/scripts/stop-simulators.sh +++ b/test/functest/scripts/stop-simulators.sh @@ -23,7 +23,12 @@ # We don't need all the directory names here and the "cd", but it may be needed later on # Also, it will be a guard against some bad config where the directory doesn't exist -SCRIPTDIR=$(dirname $(readlink -f $0)) +if [[ `uname` == "Darwin" ]] +then + SCRIPTDIR=$(dirname $(greadlink -f $0)) +else + SCRIPTDIR=$(dirname $(readlink -f $0)) +fi FUNC_TEST_DIR=$(dirname $SCRIPTDIR) TEST_DIR=$(dirname $FUNC_TEST_DIR) OSDF_DIR=$(dirname $TEST_DIR) diff --git a/test/functest/simulators/Dockerfile b/test/functest/simulators/Dockerfile index ca32895..233503c 100644 --- a/test/functest/simulators/Dockerfile +++ b/test/functest/simulators/Dockerfile @@ -17,19 +17,22 @@ # ------------------------------------------------------------------------- # -FROM ubuntu:16.04 +FROM ubuntu:20.04 + +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -y \ - && apt-get install -y vim unzip wget libmpfr-dev \ - && apt-get install -y git libqt5printsupport5 build-essential \ - && apt-get install -y python3 python3-setuptools python3-dev \ - && easy_install3 pip \ - && pip install --upgrade virtualenv pip wheel + && apt-get install -y --no-install-recommends software-properties-common \ + curl ca-certificates vim binutils binfmt-support build-essential \ + python3 python3-setuptools python3-dev \ + && ln -s /usr/bin/python3.8 /usr/bin/python \ + && curl -s https://bootstrap.pypa.io/get-pip.py | python \ + && python -m pip --no-cache-dir install --upgrade pip wheel setuptools \ + && rm -rf /var/lib/apt/lists/* -RUN ln -s /usr/bin/python3.5 /usr/bin/python -ADD requirements.txt /requirements.txt +COPY requirements.txt /requirements.txt RUN pip install -r requirements.txt -ADD sim /sim -ADD start_sim.sh /start_sim.sh +COPY sim /sim +COPY start_sim.sh /start_sim.sh CMD ["/start_sim.sh"]
\ No newline at end of file diff --git a/test/functest/simulators/aai/response-payloads/nsi_instance.json b/test/functest/simulators/aai/response-payloads/nsi_instance.json new file mode 100644 index 0000000..b09d2e4 --- /dev/null +++ b/test/functest/simulators/aai/response-payloads/nsi_instance.json @@ -0,0 +1,13 @@ +{ + "service-instance-id": "9629e36c-a3d9-4aed-8368-f72b8be1cd34", + "service-instance-name": "nsi_test_0211", + "service-type": "embb", + "service-role": "nsi", + "environment-context": "cn", + "model-invariant-id": "21d57d4b-52ad-4d3c-a798-248b5bb9124a", + "model-version-id": "bfba363e-e39c-4bd9-a9d5-1371c28f4d22", + "resource-version": "1581418601616", + "orchestration-status": "active", + "relationship-list": { + "relationship": [ +]}} diff --git a/test/functest/simulators/aai/response-payloads/nsi_instance_with_args.json b/test/functest/simulators/aai/response-payloads/nsi_instance_with_args.json new file mode 100644 index 0000000..19e5ac1 --- /dev/null +++ b/test/functest/simulators/aai/response-payloads/nsi_instance_with_args.json @@ -0,0 +1,45 @@ +{ + "service-instance-id": "9629e36c-a3d9-4aed-8368-f72b8be1cd34", + "service-instance-name": "nsi_test_0211", + "service-type": "embb", + "service-role": "nsi", + "environment-context": "cn", + "model-invariant-id": "21d57d4b-52ad-4d3c-a798-248b5bb9124a", + "model-version-id": "bfba363e-e39c-4bd9-a9d5-1371c28f4d22", + "resource-version": "1581418601616", + "orchestration-status": "active", + "relationship-list": { + "relationship": [ + { + "related-to": "allotted-resource", + "relationship-label": "org.onap.relationships.inventory.Uses", + "related-link": "/aai/v23/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/d88b6ce6-19be-439d-8553-4f9d6cce0494/allotted-resources/allotted-resource/07138106-f535-413b-b002-40ba24f95937", + "relationship-data": [ + { + "relationship-key": "customer.global-customer-id", + "relationship-value": "5GCustomer" + }, + { + "relationship-key": "service-subscription.service-type", + "relationship-value": "5G" + }, + { + "relationship-key": "service-instance.service-instance-id", + "relationship-value": "cdad9f49-4201-4e3a-aac1-b0f27902c299" + }, + { + "relationship-key": "allotted-resource.id", + "relationship-value": "07138106-f535-413b-b002-40ba24f95937" + } + ], + "related-to-property": [ + { + "property-key": "allotted-resource.description" + }, + { + "property-key": "allotted-resource.allotted-resource-name", + "property-value": "Allotted_coe" + } + ] + } +]}} diff --git a/test/functest/simulators/build_sim_image.sh b/test/functest/simulators/build_sim_image.sh index 8efb273..561db19 100755 --- a/test/functest/simulators/build_sim_image.sh +++ b/test/functest/simulators/build_sim_image.sh @@ -18,13 +18,23 @@ # ------------------------------------------------------------------------- # -SCRIPTDIR=$(dirname $(readlink -f $0)) +if [[ `uname` == "Darwin" ]] +then + SCRIPTDIR=$(dirname $(greadlink -f $0)) +else + SCRIPTDIR=$(dirname $(readlink -f $0)) +fi + FUNC_TEST_DIR=$(dirname $SCRIPTDIR) TEST_DIR=$(dirname $FUNC_TEST_DIR) SIMULATORS_DIR=$FUNC_TEST_DIR/simulators OSDF_DIR=$(dirname $TEST_DIR) DOCKER_DIR=$SIMULATORS_DIR/tmp_docker +echo "Before Docker Build" +cat $OSDF_DIR/requirements-sim.txt +echo $OSDF_DIR + mkdir -p $DOCKER_DIR/sim/osdf/policy/response-payloads/pdp-has-vcpe-good cp $SIMULATORS_DIR/Dockerfile $DOCKER_DIR/. @@ -32,13 +42,16 @@ cp $SIMULATORS_DIR/Dockerfile $DOCKER_DIR/. cp -r $OSDF_DIR/osdf $DOCKER_DIR/sim mkdir -p $DOCKER_DIR/sim/config/ cp $SIMULATORS_DIR/simulated-config/*.yaml $DOCKER_DIR/sim/config/ +cp $SIMULATORS_DIR/simulated-config/*.yml $DOCKER_DIR/sim/config/ cp $SIMULATORS_DIR/simulated-config/*.config $DOCKER_DIR/sim/config/ cp -r $SIMULATORS_DIR/configdb $DOCKER_DIR/sim cp -r $SIMULATORS_DIR/has-api $DOCKER_DIR/sim cp -r $SIMULATORS_DIR/policy $DOCKER_DIR/sim +cp -r $SIMULATORS_DIR/aai $DOCKER_DIR/sim cp $TEST_DIR/policy-local-files/*.json $DOCKER_DIR/sim/policy/response-payloads/pdp-has-vcpe-good +cp $TEST_DIR/placement-tests/policy_response.json $DOCKER_DIR/sim/policy/response-payloads/ cp $SIMULATORS_DIR/oof_dependencies_simulators.py $DOCKER_DIR/sim/oof_dependencies_simulators.py -cp $OSDF_DIR/requirements.txt $DOCKER_DIR +cp $OSDF_DIR/requirements-sim.txt $DOCKER_DIR/requirements.txt cp -r $SIMULATORS_DIR/start_sim.sh $DOCKER_DIR/ cd $DOCKER_DIR diff --git a/test/functest/simulators/configdb/response-payloads/getCellList-1000.json b/test/functest/simulators/configdb/response-payloads/getCellList-netw1000.json index df23f6e..df23f6e 100644 --- a/test/functest/simulators/configdb/response-payloads/getCellList-1000.json +++ b/test/functest/simulators/configdb/response-payloads/getCellList-netw1000.json diff --git a/test/functest/simulators/configdb/response-payloads/getCellList-2000.json b/test/functest/simulators/configdb/response-payloads/getCellList-netw2000.json index 77cf7db..77cf7db 100644 --- a/test/functest/simulators/configdb/response-payloads/getCellList-2000.json +++ b/test/functest/simulators/configdb/response-payloads/getCellList-netw2000.json diff --git a/test/functest/simulators/oof_dependencies_simulators.py b/test/functest/simulators/oof_dependencies_simulators.py index 2995861..d7b1f04 100644 --- a/test/functest/simulators/oof_dependencies_simulators.py +++ b/test/functest/simulators/oof_dependencies_simulators.py @@ -22,6 +22,7 @@ Simulators for dependencies of OSDF (e.g. HAS-API, Policy, SO-callback, etc.) import glob import json import os + from flask import Flask, jsonify, request from osdf.utils.interfaces import json_from_file @@ -93,6 +94,14 @@ def get_policies(sub_component): return jsonify(list_json) +@app.route("/simulated/policy/pdpx/decision/v1", methods=["POST"]) +def get_pdx_policies(): + """ + get the pdpx policy + """ + return jsonify(json_from_file("policy/response-payloads/policy_response.json")) + + @app.route("/simulated/configdb/getCellList/<network_id>/<ts>", methods=["GET"]) def get_cell_list(network_id, ts): data, status = get_payload_for_simulated_component('configdb', @@ -109,6 +118,40 @@ def get_nbr_list(cell_id, ts): return jsonify(data) return jsonify(data), 503 +@app.route("/simulated/aai/v23/nodes/service-instances/service-instance/<service_id>", methods=["GET"]) +def get_aai_instances(service_id): + data, status = get_payload_for_simulated_component('aai', 'nsi_instance.json') + if not status: + return jsonify(data) + return jsonify(data), 503 + +@app.route("/simulated/aai/v23/dsl", methods=["PUT"]) +def dsl_query(): + + nssi_query = {"dsl": "service-instance*('service-instance-id','9629e36c-a3d9-4aed-8368-f72b8be1cd34')('workload-context', 'CN') > " + "service-instance*('service-role','nsi')"} + + nssi_with_nsi_query = {"dsl": "service-instance*('service-instance-id','9629e36c-a3d9-4aed-8368-f72b8be1cd34')('workload-context', 'CN') > " + "service-instance*('service-role','nsi')('service-instance-id'," + "'660ca85c-1a0f-4521-a559-65f23e794699')"} + + queries = { + "nssi_query": nssi_query, + "nssi_with_nsi": nssi_with_nsi_query + } + + count = { + "nssi_query": 1, + "nssi_with_nsi": 2 + } + + request_body = request.get_json() + service_count = 0 + for query_type, query in queries.items(): + if request_body == query: + service_count = count[query_type] + return {'results': [{'service-instance': service_count}]} + if __name__ == "__main__": app.run(debug=True, host='0.0.0.0') diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Affinity_vCPE_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Affinity_vCPE_1.json index 6f0ecb3..2953589 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Affinity_vCPE_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Affinity_vCPE_1.json @@ -1,7 +1,7 @@ { - "service": "zone", - "policyName": "OSDF_R2.Affinity_vCPE_1", - "description": "Optimization query policy for vCPE", + "service": "affinityPolicy", + "policyName": "OSDF_DUBLIN.Affinity_vCPE_1", + "description": "Zone policy for vCPE", "templateVersion": "OpenSource.version.1", "version": "test1", "priority": "3", diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vGMuxInfra.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vGMuxInfra.json index 2578544..010cf3f 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vGMuxInfra.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vGMuxInfra.json @@ -1,6 +1,6 @@ { "service": "vim_fit", - "policyName": "OSDF_R2.Capacity_vGMuxInfra", + "policyName": "OSDF_DUBLIN.Capacity_vGMuxInfra", "description": "Capacity policy for vGMuxInfra", "templateVersion": "OpenSource.version.1", "version": "test1", diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vG_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vG_1.json index c1682fa..fedcc4f 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vG_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Capacity_vG_1.json @@ -1,6 +1,6 @@ { "service": "vim_fit", - "policyName": "OSDF_R2.Capacity_vG_1", + "policyName": "OSDF_DUBLIN.Capacity_vG_1", "description": "Capacity policy for vG", "templateVersion": "OpenSource.version.1", "version": "test1", diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vGMuxInfra_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vGMuxInfra_1.json index 61ec500..e3ba83c 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vGMuxInfra_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vGMuxInfra_1.json @@ -1,6 +1,6 @@ { - "service": "distance_to_location", - "policyName": "OSDF_R2.Distance_vGMuxInfra", + "service": "distancePolicy", + "policyName": "OSDF_DUBLIN.Distance_vGMuxInfra", "description": "Distance Policy for vGMuxInfra", "templateVersion": "OpenSource.version.1", "version": "test1", @@ -10,13 +10,13 @@ "guard": "False", "content": { "distanceProperties": { - "locationInfo": "customer_location", + "locationInfo": "customer_loc", "distance": { "value": "500", "operator": "<", "unit": "km" } }, "identity": "distance-vGMuxInfra", "resources": ["vGMuxInfra"], "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], - "policyType": "distancePolicy", + "policyType": "distance_to_location", "applicableResources": "any" } } diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vG_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vG_1.json index 06c3ada..c498c7a 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vG_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Distance_vG_1.json @@ -1,6 +1,6 @@ { - "service": "distance_to_location", - "policyName": "OSDF_R2.Distance_vG_1", + "service": "distancePolicy", + "policyName": "OSDF_DUBLIN.Distance_vG_1", "description": "Distance Policy for vG", "templateVersion": "OpenSource.version.1", "version": "test1", @@ -10,13 +10,13 @@ "guard": "False", "content": { "distanceProperties": { - "locationInfo": "customer_location", + "locationInfo": "customer_loc", "distance": { "value": "1500", "operator": "<", "unit": "km" } }, "identity": "distance-vG", "resources": ["vG"], "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], - "policyType": "distancePolicy", + "policyType": "distance_to_location", "applicableResources": "any" } } diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Placement_Optimization_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Placement_Optimization_1.json index ab3c586..9b062b0 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Placement_Optimization_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/Placement_Optimization_1.json @@ -1,6 +1,6 @@ { - "service": "placementOptimization", - "policyName": "OSDF_R2.Placement_Optimization_1", + "service": "optimizationPolicy", + "policyName": "OSDF_DUBLIN.Placement_Optimization_1", "description": "Placement Optimization Policy for vGMuxInfra", "templateVersion": "OpenSource.version.1", "version": "test1", @@ -30,13 +30,26 @@ "parameter": "hpa_score", "weight": "200", "operator": "product" + }, + { + "resources": ["vFW"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "100", + "operator": "product" + }, + { + "resources": ["vFW"], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" } ], "operator": "sum" }, "identity": "optimization", "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], - "policyType": "placementOptimization", + "policyType": "placement_optimization", "objective": "minimize" } } diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE.json index 27f49d2..5097964 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE.json @@ -1,7 +1,7 @@ { - "service": "optimizationQueryPolicy", - "policyName": "OSDF_R2.QueryPolicy_vCPE", - "description": "Optimization query policy for vCPE", + "service": "queryPolicy", + "policyName": "OSDF_DUBLIN.QueryPolicy_vCPE", + "description": "Query policy for vCPE", "templateVersion": "OpenSource.version.1", "version": "test1", "priority": "3", @@ -14,7 +14,7 @@ {"attribute":"customerLongitude", "attribute_location": "customerLongitude"} ], "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], - "policyType": "optimizationQueryPolicy", + "policyType": "request_param_query", "serviceName": "vCPE", "identity": "vCPE_Query_Policy" } diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE_2.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE_2.json index 7f1db83..e398f39 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE_2.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/QueryPolicy_vCPE_2.json @@ -1,10 +1,10 @@ { - "service": "optimizationQueryPolicy", - "policyName": "oofBeijing.queryPolicy_vCPE", - "description": "Optimization query policy for vCPE", - "templateVersion": "0.0.1", - "version": "oofBeijing", - "priority": "5", + "service": "queryPolicy", + "policyName": "OSDF_DUBLIN.queryPolicy_vCPE", + "description": "Query policy for vCPE", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", "riskType": "test", "riskLevel": "2", "guard": "False", @@ -17,7 +17,8 @@ {"attribute":"customerLongitude", "attribute_location": "customerLongitude", "value": 2.2} ], "serviceName": "vCPE", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG", "optimizationQueryPolicy"], - "policyType": "optimizationQueryPolicy" + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "policyType": "request_param_query", + "identity": "vCPE_Query_Policy" } } diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vGMuxInfra_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vGMuxInfra_1.json index ce0b7e3..690f5dc 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vGMuxInfra_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vGMuxInfra_1.json @@ -1,6 +1,6 @@ { "service": "hpaPolicy", - "policyName": "OSDF_R2.hpa_policy_vGMuxInfra_1", + "policyName": "OSDF_DUBLIN.hpa_policy_vGMuxInfra_1", "description": "HPA policy for vGMuxInfra", "templateVersion": "OpenSource.version.1", "version": "test1", @@ -9,10 +9,10 @@ "riskLevel": "2", "guard": "False", "content": { - "resources": "vGMuxInfra", - "identity": "hpaPolicy_vGMuxInfra", + "resources": ["vGMuxInfra"], + "identity": "hpa-vGMuxInfra", "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], - "policyType": "hpaPolicy", + "policyType": "hpa", "flavorFeatures": [ { "id": "vgmux_1", diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vG_1.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vG_1.json index 5d2499f..b29c67d 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vG_1.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/hpa_policy_vG_1.json @@ -1,6 +1,6 @@ { "service": "hpaPolicy", - "policyName": "OSDF_R2.hpa_policy_vG_1", + "policyName": "OSDF_DUBLIN.hpa_policy_vG_1", "description": "HPA policy for vG", "templateVersion": "OpenSource.version.1", "version": "test1", @@ -9,10 +9,10 @@ "riskLevel": "2", "guard": "False", "content": { - "resources": "vG", - "identity": "hpaPolicy_vG", + "resources": ["vG"], + "identity": "hpa-vG", "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], - "policyType": "hpaPolicy", + "policyType": "hpa", "flavorFeatures": [ { "id": "vg_1", diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vG.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vG.json index d215078..b047686 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vG.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vG.json @@ -1,6 +1,6 @@ { "service": "vnfPolicy", - "policyName": "OSDF_R2.vnfPolicy_vG", + "policyName": "OSDF_DUBLIN.vnfPolicy_vG", "description": "vnfPolicy", "templateVersion": "OpenSource.version.1", "version": "test1", diff --git a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vGMuxInfra.json b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vGMuxInfra.json index 6849105..45d67f6 100644 --- a/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vGMuxInfra.json +++ b/test/functest/simulators/policy/response-payloads/pdp-has-vcpe-good/vnfPolicy_vGMuxInfra.json @@ -1,6 +1,6 @@ { "service": "vnfPolicy", - "policyName": "OSDF_R2.vnfPolicy_vGMuxInfra", + "policyName": "OSDF_DUBLIN.vnfPolicy_vGMuxInfra", "description": "vnfPolicy", "templateVersion": "OpenSource.version.1", "version": "test1", diff --git a/test/functest/simulators/simulated-config/common_config.yaml b/test/functest/simulators/simulated-config/common_config.yaml index 1249dc0..36c639f 100644 --- a/test/functest/simulators/simulated-config/common_config.yaml +++ b/test/functest/simulators/simulated-config/common_config.yaml @@ -62,3 +62,13 @@ policy_info: default: # if no explicit service related information is needed policy_fetch: by_name policy_scope: none + +PCI: + ML: + average_ho_threshold: 10000 + latest_ho_threshold: 500 + DES: + service_id: ho_metric + filter: + interval: 10 + ml_enabled: false diff --git a/test/functest/simulators/simulated-config/log.yml b/test/functest/simulators/simulated-config/log.yml new file mode 100644 index 0000000..ad0de21 --- /dev/null +++ b/test/functest/simulators/simulated-config/log.yml @@ -0,0 +1,100 @@ +version: 1 +disable_existing_loggers: True + +loggers: + error: + handlers: [error_handler, console_handler] + level: "WARN" + propagate: True + debug: + handlers: [debug_handler, console_handler] + level: "DEBUG" + propagate: True + metrics: + handlers: [metrics_handler, console_handler] + level: "INFO" + propagate: True + audit: + handlers: [audit_handler, console_handler] + level: "INFO" + propagate: True +handlers: + debug_handler: + level: "DEBUG" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/debug.log" + formatter: "debugFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + error_handler: + level: "WARN" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/error.log" + formatter: "errorFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + metrics_handler: + level: "INFO" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/metrics.log" + formatter: "metricsFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + audit_handler: + level: "INFO" + class: "logging.handlers.TimedRotatingFileHandler" + filename: "logs/audit.log" + formatter: "auditFormat" + when: midnight + interval: 1 + utc: True + delay: False + backupCount: 10 + console_handler: + level: "DEBUG" + class: "logging.StreamHandler" + formatter: "metricsFormat" + +formatters: + standard: + format: "%(asctime)s|||||%(name)s||%(thread)||%(funcName)s||%(levelname)s||%(message)s" + debugFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "%(asctime)s.%(msecs)03d+00:00|{requestID}|%(threadName)s|{server}|%(levelname)s|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + errorFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "%(asctime)s.%(msecs)03d+00:00|{requestID}|%(threadName)s|{serviceName}|{partnerName}\ + |{targetEntity}|{targetServiceName}|%(levelname)s|{errorCode}|{errorDescription}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + auditFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "{entryTimestamp}+00:00|%(asctime)s.%(msecs)03d+00:00|{requestID}|{serviceInstanceID}\ + |%(threadName)s|{server}|{serviceName}|{partnerName}|{statusCode}|{responseCode}|{responseDescription}\ + |{instanceUUID}|%(levelname)s|{severity}|{serverIPAddress}|{timer}|{server}|{IPAddress}||{unused}\ + |{processKey}|{customField1}|{customField2}|{customField3}|{customField4}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + metricsFormat: + format: "%(mdc)s" + datefmt: "%Y-%m-%dT%H:%M:%S" + mdcfmt: "{entryTimestamp}+00:00|%(asctime)s.%(msecs)03d+00:00|{requestID}|{serviceInstanceID}\ + |%(threadName)s|{server}|{serviceName}|{partnerName}|{targetEntity}|{targetServiceName}|{statusCode}|{responseCode}|{responseDescription}\ + |{instanceUUID}|%(levelname)s|{severity}|{serverIPAddress}|{timer}|{server}|{IPAddress}||{unused}\ + |{processKey}|{TargetVirtualEntity}|{customField1}|{customField2}|{customField3}|{customField4}|%(message)s" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter + mdcFormat: + format: "%(asctime)s.%(msecs)03d+00:00|||||%(name)s||%(thread)s||%(funcName)s||%(levelname)s||%(message)s||||%(mdc)s" + mdcfmt: "{requestID} {invocationID} {serviceName} {serverIPAddress}" + (): osdf.logging.oof_mdc_formatter.OOFMDCFormatter diff --git a/test/functest/simulators/simulated-config/opteng_config.yaml b/test/functest/simulators/simulated-config/opteng_config.yaml new file mode 100755 index 0000000..0dfb536 --- /dev/null +++ b/test/functest/simulators/simulated-config/opteng_config.yaml @@ -0,0 +1,25 @@ +# Policy Platform -- requires Authorization +policyPlatformUrl: https://policy-xacml-pdp:6969/policy/pdpx/decision/v1 # Policy Dev platform URL + +# AAF Authentication config +is_aaf_enabled: False +aaf_cache_expiry_mins: 5 +aaf_url: https://aaftest.simpledemo.onap.org:8095 +aaf_user_roles: + - '/optmodel:org.onap.oof.access|*|read ALL' + - '/optengine:org.onap.oof.access|*|read ALL' + +# Secret Management Service from AAF +aaf_sms_url: https://aaf-sms.onap:10443 +aaf_sms_timeout: 30 +secret_domain: osdf +aaf_ca_certs: ssl_certs/aaf_root_ca.cer + +osdfDatabaseHost: localhost +osdfDatabaseSchema: osdf +osdfDatabaseUsername: osdf +osdfDatabasePassword: osdf +osdfDatabasePort: 3306 + +#key +appkey: os35@rrtky400fdntc#001t5 diff --git a/test/functest/simulators/simulated-config/osdf_config.yaml b/test/functest/simulators/simulated-config/osdf_config.yaml index eccad14..eb02b33 100755 --- a/test/functest/simulators/simulated-config/osdf_config.yaml +++ b/test/functest/simulators/simulated-config/osdf_config.yaml @@ -60,6 +60,8 @@ aaf_sms_timeout: 30 secret_domain: osdf aaf_ca_certs: ssl_certs/aaf_root_ca.cer +configClientType: configdb + # config db api configDbUrl: http://127.0.0.1:5000/simulated/configdb configDbUserName: osdf @@ -71,3 +73,16 @@ configDbGetNbrListUrl: 'getNbrList' pciHMSUsername: "" # pcihandler username for call back. pciHMSPassword: "" # pcihandler password for call back. +aaiUrl: "https://api.url:30233" +aaiGetLinksUrl: "/aai/v16/network/logical-links" +aaiGetControllersUrl: /aai/v19/external-system/esr-thirdparty-sdnc-list +controllerQueryUrl: /aai/v19/query?format=resource +aaiGetInterDomainLinksUrl: /aai/v19/network/logical-links?link-type=inter-domain&operational-status=up + +#consulconfig +consulHost: '127.0.0.1' +consulPort: 8500 +consulScheme: 'http' +consulVerify: True +consulCert: None +activateConsulConfig: False diff --git a/test/functest/simulators/simulated-config/slicing_config.yaml b/test/functest/simulators/simulated-config/slicing_config.yaml new file mode 100644 index 0000000..179f54a --- /dev/null +++ b/test/functest/simulators/simulated-config/slicing_config.yaml @@ -0,0 +1,96 @@ +app_info: + NSI: + app_name: slice_selection + requirements_field: serviceProfile + model_info: NSTInfo + NSSI: + app_name: subnet_selection + requirements_field: sliceProfile + model_info: NSSTInfo + +attribute_mapping: + camel_to_snake: + maxBandwidth: max_bandwidth + jitter: jitter + sST: sst + latency: latency + resourceSharingLevel: resource_sharing_level + uEMobilityLevel: ue_mobility_level + maxNumberofUEs: max_number_of_ues + dLThptPerUE: dl_thpt_per_ue + uLThptPerUE: ul_thpt_per_ue + sNSSAI: s_nssai + pLMNIdList: plmn_id_list + activityFactor: activity_factor + coverageAreaTAList: coverage_area_ta_list + availability: availability + cSAvailabilityTarget: cs_availability_target + reliability: reliability + cSReliabilityMeanTime: cs_reliability_mean_time + dLThptPerSlice: dl_thpt_per_slice + expDataRateDL: exp_data_rate_dl + uLThptPerSlice: ul_thpt_per_slice + expDataRateUL: exp_data_rate_ul + maxPktSize: max_pkt_size + msgSizeByte: msg_size_byte + maxNumberofConns: max_number_of_conns + maxNumberofPDUSession: max_number_of_pdu_session + termDensity: terminal_density + survivalTime: survival_time + areaTrafficCapDL: area_traffic_cap_dl + areaTrafficCapUL: area_traffic_cap_ul + overallUserDensity: overall_user_density + transferIntervalTarget: transfer_interval_target + expDataRate: exp_data_rate + security: security + maxThroughput: max_throughput + sliceProfileId: slice_profile_id + snssaiList: s_nssai_list + domainType: domain_type + logicInterfaceId: logical_interface_id + ipAddress: ip_address + nextHopInfo: next_hop_info + perfReq: perf_req + + snake_to_camel: + max_bandwidth: maxBandwidth + jitter: jitter + sst: sST + latency: latency + resource_sharing_level: resourceSharingLevel + ue_mobility_level: uEMobilityLevel + max_number_of_ues: maxNumberofUEs + dl_thpt_per_ue: dLThptPerUE + ul_thpt_per_ue: uLThptPerUE + s_nssai: sNSSAI + plmn_id_list: pLMNIdList + activity_factor: activityFactor + coverage_area_ta_list: coverageAreaTAList + availability: availability + cs_availability_target: cSAvailabilityTarget + reliability: reliability + cs_reliability_mean_time: cSReliabilityMeanTime + dl_thpt_per_slice: dLThptPerSlice + exp_data_rate_dl: expDataRateDL + ul_thpt_per_slice: uLThptPerSlice + exp_data_rate_ul: expDataRateUL + max_pkt_size: maxPktSize + msg_size_byte: msgSizeByte + max_number_of_conns: maxNumberofConns + max_number_of_pdu_session: maxNumberofPDUSession + terminal_density: termDensity + survival_time: survivalTime + area_traffic_cap_dl: areaTrafficCapDL + area_traffic_cap_ul: areaTrafficCapUL + overall_user_density: overallUserDensity + transfer_interval_target: transferIntervalTarget + exp_data_rate: expDataRate + security: security + max_throughput: maxThroughput + slice_profile_id: sliceProfileId + s_nssai_list: snssaiList + domain_type: domainType + logical_interface_id: logicInterfaceId + ip_address: ipAddress + next_hop_info: nextHopInfo + perf_req: perfReq diff --git a/test/functest/simulators/start_sim.sh b/test/functest/simulators/start_sim.sh index 1d17d65..f5935da 100755 --- a/test/functest/simulators/start_sim.sh +++ b/test/functest/simulators/start_sim.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash cd /sim -python oof_dependencies_simulators.py > simulator-logs 2>&1
\ No newline at end of file +python oof_dependencies_simulators.py diff --git a/test/inter_domain_route_opt/bandwidth_attributes.json b/test/inter_domain_route_opt/bandwidth_attributes.json new file mode 100644 index 0000000..0de7e51 --- /dev/null +++ b/test/inter_domain_route_opt/bandwidth_attributes.json @@ -0,0 +1,176 @@ +{ + "int-1-bw":{ + "interface-name":"int1", + "bandwidth-attributes":{ + "bandwidth-attribute":[ + { + "bwa-id":"bw6", + "resource-version":"1596387588545", + "available-bandwidth-map":{ + "available-bandwidth":[ + { + "ab-id":"ab226", + "odu-type":"ODU2", + "number":1, + "resource-version":"1596387588545" + }, + { + "ab-id":"ab112", + "odu-type":"ODU4", + "number":8, + "resource-version":"1596387588545" + } + ] + } + } + ] + }, + "resource-version":"1596387588545", + "in-maint":false + }, + "int-3-bw":{ + "interface-name":"int3", + "bandwidth-attributes":{ + "bandwidth-attribute":[ + { + "bwa-id":"bw6", + "resource-version":"1596387588545", + "available-bandwidth-map":{ + "available-bandwidth":[ + { + "ab-id":"ab226", + "odu-type":"ODU2", + "number":1, + "resource-version":"1596387588545" + }, + { + "ab-id":"ab112", + "odu-type":"ODU4", + "number":8, + "resource-version":"1596387588545" + } + ] + } + } + ] + }, + "resource-version":"1596387588545", + "in-maint":false + }, + "int-4-bw":{ + "interface-name":"int4", + "bandwidth-attributes":{ + "bandwidth-attribute":[ + { + "bwa-id":"bw6", + "resource-version":"1596387588545", + "available-bandwidth-map":{ + "available-bandwidth":[ + { + "ab-id":"ab226", + "odu-type":"ODU2", + "number":1, + "resource-version":"1596387588545" + }, + { + "ab-id":"ab112", + "odu-type":"ODU4", + "number":8, + "resource-version":"1596387588545" + } + ] + } + } + ] + }, + "resource-version":"1596387588545", + "in-maint":false + }, + "int-5-bw":{ + "interface-name":"int5", + "bandwidth-attributes":{ + "bandwidth-attribute":[ + { + "bwa-id":"bw6", + "resource-version":"1596387588545", + "available-bandwidth-map":{ + "available-bandwidth":[ + { + "ab-id":"ab226", + "odu-type":"ODU2", + "number":1, + "resource-version":"1596387588545" + }, + { + "ab-id":"ab112", + "odu-type":"ODU4", + "number":8, + "resource-version":"1596387588545" + } + ] + } + } + ] + }, + "resource-version":"1596387588545", + "in-maint":false + }, + "int-6-bw":{ + "interface-name":"int6", + "bandwidth-attributes":{ + "bandwidth-attribute":[ + { + "bwa-id":"bw6", + "resource-version":"1596387588545", + "available-bandwidth-map":{ + "available-bandwidth":[ + { + "ab-id":"ab226", + "odu-type":"ODU2", + "number":1, + "resource-version":"1596387588545" + }, + { + "ab-id":"ab112", + "odu-type":"ODU4", + "number":8, + "resource-version":"1596387588545" + } + ] + } + } + ] + }, + "resource-version":"1596387588545", + "in-maint":false + }, + "int-7-bw":{ + "interface-name":"int7", + "bandwidth-attributes":{ + "bandwidth-attribute":[ + { + "bwa-id":"bw6", + "resource-version":"1596387588545", + "available-bandwidth-map":{ + "available-bandwidth":[ + { + "ab-id":"ab226", + "odu-type":"ODU2", + "number":1, + "resource-version":"1596387588545" + }, + { + "ab-id":"ab112", + "odu-type":"ODU4", + "number":8, + "resource-version":"1596387588545" + } + ] + } + } + ] + }, + "resource-version":"1596387588545", + "in-maint":false + } +} diff --git a/test/inter_domain_route_opt/controllers_for_interfaces.json b/test/inter_domain_route_opt/controllers_for_interfaces.json new file mode 100644 index 0000000..3de47d1 --- /dev/null +++ b/test/inter_domain_route_opt/controllers_for_interfaces.json @@ -0,0 +1,62 @@ +{ + "int-1-cont":{ + "results":[ + { + "esr-thirdparty-sdnc":{ + "thirdparty-sdnc-id":"Controller1", + "resource-version":"1593421890494" + } + } + ] + }, + "int-3-cont":{ + "results":[ + { + "esr-thirdparty-sdnc":{ + "thirdparty-sdnc-id":"Controller2", + "resource-version":"1593421890494" + } + } + ] + }, + "int-4-cont":{ + "results":[ + { + "esr-thirdparty-sdnc":{ + "thirdparty-sdnc-id":"Controller2", + "resource-version":"1593421890494" + } + } + ] + }, + "int-5-cont":{ + "results":[ + { + "esr-thirdparty-sdnc":{ + "thirdparty-sdnc-id":"Controller3", + "resource-version":"1593421890494" + } + } + ] + }, + "int-6-cont":{ + "results":[ + { + "esr-thirdparty-sdnc":{ + "thirdparty-sdnc-id":"Controller3", + "resource-version":"1593421890494" + } + } + ] + }, + "int-7-cont":{ + "results":[ + { + "esr-thirdparty-sdnc":{ + "thirdparty-sdnc-id":"Controller4", + "resource-version":"1593421890494" + } + } + ] + } +} diff --git a/test/inter_domain_route_opt/controllers_list.json b/test/inter_domain_route_opt/controllers_list.json new file mode 100644 index 0000000..158f530 --- /dev/null +++ b/test/inter_domain_route_opt/controllers_list.json @@ -0,0 +1,16 @@ +{ + "esr-thirdparty-sdnc":[ + { + "thirdparty-sdnc-id":"Controller1" + }, + { + "thirdparty-sdnc-id":"Controller2" + }, + { + "thirdparty-sdnc-id":"Controller3" + }, + { + "thirdparty-sdnc-id":"Controller4" + } + ] +} diff --git a/test/inter_domain_route_opt/get_links.json b/test/inter_domain_route_opt/get_links.json new file mode 100644 index 0000000..0e70523 --- /dev/null +++ b/test/inter_domain_route_opt/get_links.json @@ -0,0 +1,157 @@ +{ + "logical-link":[ + { + "link-name":"link1", + "in-maint":false, + "link-type":"inter-domain", + "resource-version":"1588952379221", + "operational-status":"up", + "relationship-list":{ + "relationship":[ + { + "related-to":"p-interface", + "relationship-label":"tosca.relationships.network.LinksTo", + "related-link":"/aai/v19/network/pnfs/pnf/pnf1/p-interfaces/p-interface/int1", + "relationship-data":[ + { + "relationship-key":"pnf.pnf-name", + "relationship-value":"pnf1" + }, + { + "relationship-key":"p-interface.interface-name", + "relationship-value":"int1" + } + ], + "related-to-property":[ + { + "property-key":"p-interface.prov-status" + } + ] + }, + { + "related-to":"p-interface", + "relationship-label":"tosca.relationships.network.LinksTo", + "related-link":"/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int3", + "relationship-data":[ + { + "relationship-key":"pnf.pnf-name", + "relationship-value":"pnf2" + }, + { + "relationship-key":"p-interface.interface-name", + "relationship-value":"int3" + } + ], + "related-to-property":[ + { + "property-key":"p-interface.prov-status" + } + ] + } + ] + } + }, + { + "link-name":"link2", + "in-maint":false, + "link-type":"inter-domain", + "resource-version":"1588952379221", + "operational-status":"up", + "relationship-list":{ + "relationship":[ + { + "related-to":"p-interface", + "relationship-label":"tosca.relationships.network.LinksTo", + "related-link":"/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int4", + "relationship-data":[ + { + "relationship-key":"pnf.pnf-name", + "relationship-value":"pnf2" + }, + { + "relationship-key":"p-interface.interface-name", + "relationship-value":"int4" + } + ], + "related-to-property":[ + { + "property-key":"p-interface.prov-status" + } + ] + }, + { + "related-to":"p-interface", + "relationship-label":"tosca.relationships.network.LinksTo", + "related-link":"/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int5", + "relationship-data":[ + { + "relationship-key":"pnf.pnf-name", + "relationship-value":"pnf3" + }, + { + "relationship-key":"p-interface.interface-name", + "relationship-value":"int5" + } + ], + "related-to-property":[ + { + "property-key":"p-interface.prov-status" + } + ] + } + ] + } + }, + { + "link-name":"link3", + "in-maint":false, + "link-type":"inter-domain", + "resource-version":"1588952379221", + "operational-status":"up", + "relationship-list":{ + "relationship":[ + { + "related-to":"p-interface", + "relationship-label":"tosca.relationships.network.LinksTo", + "related-link":"/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int6", + "relationship-data":[ + { + "relationship-key":"pnf.pnf-name", + "relationship-value":"pnf3" + }, + { + "relationship-key":"p-interface.interface-name", + "relationship-value":"int6" + } + ], + "related-to-property":[ + { + "property-key":"p-interface.prov-status" + } + ] + }, + { + "related-to":"p-interface", + "relationship-label":"tosca.relationships.network.LinksTo", + "related-link":"/aai/v19/network/pnfs/pnf/pnf4/p-interfaces/p-interface/int7", + "relationship-data":[ + { + "relationship-key":"pnf.pnf-name", + "relationship-value":"pnf4" + }, + { + "relationship-key":"p-interface.interface-name", + "relationship-value":"int7" + } + ], + "related-to-property":[ + { + "property-key":"p-interface.prov-status" + } + ] + } + ] + } + } + ] +} diff --git a/test/inter_domain_route_opt/request.json b/test/inter_domain_route_opt/request.json new file mode 100644 index 0000000..041a32f --- /dev/null +++ b/test/inter_domain_route_opt/request.json @@ -0,0 +1,30 @@ +{ + "requestInfo":{ + "transactionId":"123456", + "requestId":"789456", + "callbackUrl":"", + "callbackHeader": "", + "sourceId":"SDNC", + "requestType":"create", + "numSolutions":1, + "optimizers":[ + "route" + ], + "timeout":600 + }, + "routeInfo":{ + "routeRequest":{ + "srcDetails":{ + "interfaceId":"int19", + "nodeId":"pnf1", + "controllerId":"Controller1" + }, + "dstDetails":{ + "interfaceId":"int20", + "nodeId":"pnf4", + "controllerId":"Controller3" + }, + "serviceRate":"ODU2" + } + } +} diff --git a/test/logging/test_osdf_logging.py b/test/logging/test_osdf_logging.py index 982ef0b..c8fa3ae 100755 --- a/test/logging/test_osdf_logging.py +++ b/test/logging/test_osdf_logging.py @@ -16,13 +16,11 @@ # ------------------------------------------------------------------------- # import unittest -import json -import yaml +from unittest import mock from osdf.logging import osdf_logging as L1 -from osdf.logging.osdf_logging import OOF_OSDFLogMessageHelper as MH -from osdf.logging.osdf_logging import OOF_OSDFLogMessageFormatter as formatter -from unittest import mock +from osdf.logging.osdf_logging import OOFOSDFLogMessageFormatter as formatter +from osdf.logging.osdf_logging import OOFOSDFLogMessageHelper as MH class TestOSDFLogging(unittest.TestCase): @@ -45,12 +43,8 @@ class TestOSDFLogging(unittest.TestCase): self.json_body = mock.MagicMock() self.F = formatter - def test_log_handlers_pre_onap(self): - res = L1.log_handlers_pre_onap() - assert type(res) == dict - def test_format_exception(self): - res = L1.format_exception(Exception("Some error")) + L1.format_exception(Exception("Some error")) def test_accepted_valid_request(self): res = formatter.accepted_valid_request(self.req_id, self.request) @@ -95,7 +89,7 @@ class TestOSDFLogging(unittest.TestCase): def test_error_calling_back(self): res = formatter.error_calling_back(self.service_name, self.callback_url, self.err) assert res.startswith("Error while posting result to callback URL") - + def test_calling_back(self): res = formatter.calling_back(self.req_id, self.callback_url) assert res.startswith("Posting result to callback URL") @@ -168,15 +162,6 @@ class TestOSDFLogging(unittest.TestCase): """Log the message to error_log.warn and audit_log.warn""" L1.warn_audit_error("Some warning message") - def test_log_message_multi(msg): - X = L1.log_handlers_pre_onap() - wanted_methods = [ - X["error"].error, X["error"].warn, X["audit"].info, - X["metrics"].info, X["debug"].debug, X["error"].fatal - ] - L1.log_message_multi("Some log message", *wanted_methods) - if __name__ == "__main__": unittest.main() - diff --git a/test/mainapp/test_osdfapp.py b/test/mainapp/test_osdfapp.py index 7fbe707..e4f12e6 100644 --- a/test/mainapp/test_osdfapp.py +++ b/test/mainapp/test_osdfapp.py @@ -15,21 +15,27 @@ # # ------------------------------------------------------------------------- # -import osdfapp + import unittest +from unittest import TestCase +from unittest import mock +from unittest.mock import patch -from osdf.operation.exceptions import BusinessException -from requests import Request, RequestException +from requests import Request +from requests import RequestException from schematics.exceptions import DataError -from unittest import mock, TestCase -from unittest.mock import patch + +from osdf.apps import baseapp +from osdf.apps.baseapp import app +from osdf.operation.exceptions import BusinessException class TestOSDFApp(TestCase): def setUp(self): - self.patcher_g = patch('osdfapp.g', return_value={'request_id':'DUMMY-REQ'}) - self.Mock_g = self.patcher_g.start() + with app.app_context(): + self.patcher_g = patch('osdf.apps.baseapp.g', return_value={'request_id': 'DUMMY-REQ'}) + self.Mock_g = self.patcher_g.start() # self.patcher2 = patch('package.module.Class2') # self.MockClass2 = self.patcher2.start() @@ -44,31 +50,30 @@ class TestOSDFApp(TestCase): e.response.content = "Some request exception occurred" # request().raise_for_status.side_effect = e return e - + def test_handle_business_exception(self): e = BusinessException("Business Exception Description") - resp = osdfapp.handle_business_exception(e) + resp = baseapp.handle_business_exception(e) assert resp.status_code == 400 def test_handle_request_exception(self): e = self.dummy_request_exception() - resp = osdfapp.handle_request_exception(e) + resp = baseapp.handle_request_exception(e) assert resp.status_code == 400 def test_handle_data_error(self): e = DataError({"A1": "A1 Data Error"}) - resp = osdfapp.handle_data_error(e) + resp = baseapp.handle_data_error(e) assert resp.status_code == 400 def test_internal_failure(self): e = Exception("An Internal Error") - resp = osdfapp.internal_failure(e) + resp = baseapp.internal_failure(e) assert resp.status_code == 500 def test_get_options_default(self): - opts = osdfapp.get_options(["PROG"]) # ensure nothing breaks + baseapp.get_options(["PROG"]) # ensure nothing breaks if __name__ == "__main__": unittest.main() - diff --git a/test/optengine-tests/test_modelapi_invalid.json b/test/optengine-tests/test_modelapi_invalid.json new file mode 100644 index 0000000..a58258e --- /dev/null +++ b/test/optengine-tests/test_modelapi_invalid.json @@ -0,0 +1,13 @@ +{ + "requestInfo": { + "transactinId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt" + }, + "modelInfo": { + "modelId": "model2", + "solver": "mzn", + "description": "graph coloring problem for australia", + "modelContent": "int: nc;\r\nvar 1 .. nc: wa; var 1 .. nc: nt; var 1 .. nc: sa; var 1 .. nc: q;\r\nvar 1 .. nc: nsw; var 1 .. nc: v; var 1 .. nc: t;\r\nconstraint wa != nt;\r\nconstraint wa != sa;\r\nconstraint nt != sa;\r\nconstraint nt != q;\r\nconstraint sa != q;\r\nconstraint sa != nsw;\r\nconstraint sa != v;\r\nconstraint q != nsw;\r\nconstraint nsw != v;\r\nsolve satisfy;\r\noutput [\r\n \"wa=\\(wa)\\t nt=\\(nt)\\t sa=\\(sa)\\n\",\r\n \"q=\\(q)\\t nsw=\\(nsw)\\t v=\\(v)\\n\",\r\n \"t=\", show(t), \"\\n\"\r\n];" + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_modelapi_valid.json b/test/optengine-tests/test_modelapi_valid.json new file mode 100644 index 0000000..1fbca5b --- /dev/null +++ b/test/optengine-tests/test_modelapi_valid.json @@ -0,0 +1,13 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt" + }, + "modelInfo": { + "modelId": "model2", + "solver": "mzn", + "description": "graph coloring problem for australia", + "modelContent": "int: nc;\r\nvar 1 .. nc: wa; var 1 .. nc: nt; var 1 .. nc: sa; var 1 .. nc: q;\r\nvar 1 .. nc: nsw; var 1 .. nc: v; var 1 .. nc: t;\r\nconstraint wa != nt;\r\nconstraint wa != sa;\r\nconstraint nt != sa;\r\nconstraint nt != q;\r\nconstraint sa != q;\r\nconstraint sa != nsw;\r\nconstraint sa != v;\r\nconstraint q != nsw;\r\nconstraint nsw != v;\r\nsolve satisfy;\r\noutput [\r\n \"wa=\\(wa)\\t nt=\\(nt)\\t sa=\\(sa)\\n\",\r\n \"q=\\(q)\\t nsw=\\(nsw)\\t v=\\(v)\\n\",\r\n \"t=\", show(t), \"\\n\"\r\n];" + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_invalid.json b/test/optengine-tests/test_optengine_invalid.json new file mode 100644 index 0000000..9a0267a --- /dev/null +++ b/test/optengine-tests/test_optengine_invalid.json @@ -0,0 +1,18 @@ +{ + "requestInfo": { + "transactioId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "mzn", + "solverArgs": { + "solver": "geocode" + }, + "modelContent": "int: nc;\r\nvar 1 .. nc: wa; var 1 .. nc: nt; var 1 .. nc: sa; var 1 .. nc: q;\r\nvar 1 .. nc: nsw; var 1 .. nc: v; var 1 .. nc: t;\r\nconstraint wa != nt;\r\nconstraint wa != sa;\r\nconstraint nt != sa;\r\nconstraint nt != q;\r\nconstraint sa != q;\r\nconstraint sa != nsw;\r\nconstraint sa != v;\r\nconstraint q != nsw;\r\nconstraint nsw != v;\r\nsolve satisfy;\r\noutput [\r\n \"wa=\\(wa)\\t nt=\\(nt)\\t sa=\\(sa)\\n\",\r\n \"q=\\(q)\\t nsw=\\(nsw)\\t v=\\(v)\\n\",\r\n \"t=\", show(t), \"\\n\"\r\n];", + "optData": { + "nc": 3 + } + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_invalid2.json b/test/optengine-tests/test_optengine_invalid2.json new file mode 100644 index 0000000..23c5a8e --- /dev/null +++ b/test/optengine-tests/test_optengine_invalid2.json @@ -0,0 +1,15 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + + "solverArgs": { + "solver": "cbc" + }, + "modelContent": "% Baking cakes for the school fete (with data file)\r\n\r\nint: flour; %no. grams of flour available\r\nint: banana; %no. of bananas available\r\nint: sugar; %no. grams of sugar available\r\nint: butter; %no. grams of butter available\r\nint: cocoa; %no. grams of cocoa available\r\n\r\nconstraint assert(flour >= 0,\"Invalid datafile: \" ++\r\n \"Amount of flour should be non-negative\");\r\nconstraint assert(banana >= 0,\"Invalid datafile: \" ++\r\n \"Amount of banana should be non-negative\");\r\nconstraint assert(sugar >= 0,\"Invalid datafile: \" ++\r\n \"Amount of sugar should be non-negative\");\r\nconstraint assert(butter >= 0,\"Invalid datafile: \" ++\r\n \"Amount of butter should be non-negative\");\r\nconstraint assert(cocoa >= 0,\"Invalid datafile: \" ++\r\n \"Amount of cocoa should be non-negative\");\r\n\r\nvar 0..100: b; % no. of banana cakes\r\nvar 0..100: c; % no. of chocolate cakes\r\n\r\n% flour\r\nconstraint 250*b + 200*c <= flour;\r\n% bananas\r\nconstraint 2*b <= banana;\r\n% sugar\r\nconstraint 75*b + 150*c <= sugar;\r\n% butter\r\nconstraint 100*b + 150*c <= butter;\r\n% cocoa\r\nconstraint 75*c <= cocoa;\r\n\r\n% maximize our profit\r\nsolve maximize 400*b + 450*c;\r\n\r\noutput [\"no. of banana cakes = \\(b)\\n\",\r\n \"no. of chocolate cakes = \\(c)\\n\"];" + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_invalid_solver.json b/test/optengine-tests/test_optengine_invalid_solver.json new file mode 100644 index 0000000..a967c16 --- /dev/null +++ b/test/optengine-tests/test_optengine_invalid_solver.json @@ -0,0 +1,15 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "apy", + "modelContent": "import sys\r\n\r\nif __name__ == \"__main__\":\r\n print(sys.argv[1],sys.argv[2])\r\n\r\n with open(sys.argv[2], \"wt\") as f:\r\n f.write('{\"hello\":\"world\",\"another\":\"string\"}')\r\n\r\n", + "optData": { + "text": "flour = 8000; \r\nbanana = 11; \r\nsugar = 3000; \r\nbutter = 1500; \r\ncocoa = 800; " + } + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_modelId.json b/test/optengine-tests/test_optengine_modelId.json new file mode 100644 index 0000000..b676d91 --- /dev/null +++ b/test/optengine-tests/test_optengine_modelId.json @@ -0,0 +1,19 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "modelId": "test", + "solverArgs": { + "solver": "geocode" + }, + "optData": { + "json": { + "nc": 3 + } + } + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_no_modelid.json b/test/optengine-tests/test_optengine_no_modelid.json new file mode 100644 index 0000000..9a8c3a4 --- /dev/null +++ b/test/optengine-tests/test_optengine_no_modelid.json @@ -0,0 +1,24 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "mzn", + "solverArgs": { + "solver": "cbc" + }, + "modelContent": "% Baking cakes for the school fete (with data file)\r\n\r\nint: flour; %no. grams of flour available\r\nint: banana; %no. of bananas available\r\nint: sugar; %no. grams of sugar available\r\nint: butter; %no. grams of butter available\r\nint: cocoa; %no. grams of cocoa available\r\n\r\nconstraint assert(flour >= 0,\"Invalid datafile: \" ++\r\n \"Amount of flour should be non-negative\");\r\nconstraint assert(banana >= 0,\"Invalid datafile: \" ++\r\n \"Amount of banana should be non-negative\");\r\nconstraint assert(sugar >= 0,\"Invalid datafile: \" ++\r\n \"Amount of sugar should be non-negative\");\r\nconstraint assert(butter >= 0,\"Invalid datafile: \" ++\r\n \"Amount of butter should be non-negative\");\r\nconstraint assert(cocoa >= 0,\"Invalid datafile: \" ++\r\n \"Amount of cocoa should be non-negative\");\r\n\r\nvar 0..100: b; % no. of banana cakes\r\nvar 0..100: c; % no. of chocolate cakes\r\n\r\n% flour\r\nconstraint 250*b + 200*c <= flour;\r\n% bananas\r\nconstraint 2*b <= banana;\r\n% sugar\r\nconstraint 75*b + 150*c <= sugar;\r\n% butter\r\nconstraint 100*b + 150*c <= butter;\r\n% cocoa\r\nconstraint 75*c <= cocoa;\r\n\r\n% maximize our profit\r\nsolve maximize 400*b + 450*c;\r\n\r\noutput [\"no. of banana cakes = \\(b)\\n\",\r\n \"no. of chocolate cakes = \\(c)\\n\"];", + "optData": { + "json": { + "flour": 4000, + "banana": 6, + "sugar": 2000, + "butter": 500, + "cocoa": 500 + } + } + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_no_optdata.json b/test/optengine-tests/test_optengine_no_optdata.json new file mode 100644 index 0000000..f6645c8 --- /dev/null +++ b/test/optengine-tests/test_optengine_no_optdata.json @@ -0,0 +1,15 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "mzn", + "solverArgs": { + "solver": "geocode" + }, + "modelContent": "int: nc;\r\nvar 1 .. nc: wa; var 1 .. nc: nt; var 1 .. nc: sa; var 1 .. nc: q;\r\nvar 1 .. nc: nsw; var 1 .. nc: v; var 1 .. nc: t;\r\nconstraint wa != nt;\r\nconstraint wa != sa;\r\nconstraint nt != sa;\r\nconstraint nt != q;\r\nconstraint sa != q;\r\nconstraint sa != nsw;\r\nconstraint sa != v;\r\nconstraint q != nsw;\r\nconstraint nsw != v;\r\nsolve satisfy;\r\noutput [\r\n \"wa=\\(wa)\\t nt=\\(nt)\\t sa=\\(sa)\\n\",\r\n \"q=\\(q)\\t nsw=\\(nsw)\\t v=\\(v)\\n\",\r\n \"t=\", show(t), \"\\n\"\r\n];" + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_solverid.json b/test/optengine-tests/test_optengine_solverid.json new file mode 100644 index 0000000..bfd446c --- /dev/null +++ b/test/optengine-tests/test_optengine_solverid.json @@ -0,0 +1,15 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "py", + "modelContent": "import sys\r\n\r\nif __name__ == \"__main__\":\r\n print(sys.argv[1],sys.argv[2])\r\n\r\n with open(sys.argv[2], \"wt\") as f:\r\n f.write('{\"hello\":\"world\",\"another\":\"string\"}')\r\n\r\n", + "optData": { + "text": "flour = 8000; \r\nbanana = 11; \r\nsugar = 3000; \r\nbutter = 1500; \r\ncocoa = 800; " + } + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_optengine_valid.json b/test/optengine-tests/test_optengine_valid.json new file mode 100644 index 0000000..8de2b71 --- /dev/null +++ b/test/optengine-tests/test_optengine_valid.json @@ -0,0 +1,20 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "mzn", + "solverArgs": { + "solver": "geocode" + }, + "modelContent": "int: nc;\r\nvar 1 .. nc: wa; var 1 .. nc: nt; var 1 .. nc: sa; var 1 .. nc: q;\r\nvar 1 .. nc: nsw; var 1 .. nc: v; var 1 .. nc: t;\r\nconstraint wa != nt;\r\nconstraint wa != sa;\r\nconstraint nt != sa;\r\nconstraint nt != q;\r\nconstraint sa != q;\r\nconstraint sa != nsw;\r\nconstraint sa != v;\r\nconstraint q != nsw;\r\nconstraint nsw != v;\r\nsolve satisfy;\r\noutput [\r\n \"wa=\\(wa)\\t nt=\\(nt)\\t sa=\\(sa)\\n\",\r\n \"q=\\(q)\\t nsw=\\(nsw)\\t v=\\(v)\\n\",\r\n \"t=\", show(t), \"\\n\"\r\n];", + "optData": { + "json": { + "nc": 3 + } + } + } +}
\ No newline at end of file diff --git a/test/optengine-tests/test_py_optengine_valid.json b/test/optengine-tests/test_py_optengine_valid.json new file mode 100644 index 0000000..bfd446c --- /dev/null +++ b/test/optengine-tests/test_py_optengine_valid.json @@ -0,0 +1,15 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestID": "yyy-yyy-yyyy", + "sourceId": "cmopt", + "timeout": 600 + }, + "optimInfo": { + "solver": "py", + "modelContent": "import sys\r\n\r\nif __name__ == \"__main__\":\r\n print(sys.argv[1],sys.argv[2])\r\n\r\n with open(sys.argv[2], \"wt\") as f:\r\n f.write('{\"hello\":\"world\",\"another\":\"string\"}')\r\n\r\n", + "optData": { + "text": "flour = 8000; \r\nbanana = 11; \r\nsugar = 3000; \r\nbutter = 1500; \r\ncocoa = 800; " + } + } +}
\ No newline at end of file diff --git a/test/osdf/utils/test_interfaces.py b/test/osdf/utils/test_interfaces.py index cdb3d7a..8d66e16 100644 --- a/test/osdf/utils/test_interfaces.py +++ b/test/osdf/utils/test_interfaces.py @@ -16,12 +16,13 @@ # ------------------------------------------------------------------------- # import requests -import unittest - from requests.models import Response -from osdf.utils.interfaces import RestClient, get_rest_client +import unittest from unittest.mock import patch +from osdf.utils.interfaces import get_rest_client +from osdf.utils.interfaces import RestClient + m1 = Response() m1._content = b'{"msg": "OK"}' @@ -39,7 +40,7 @@ class TestOsdfUtilsInterfaces(unittest.TestCase): def test_rc_request(self, mock_good_response): rc = RestClient() rc.add_headers({}) - rc.request(req_id="testReq") + rc.request(url="http://localhost", req_id="testReq") @patch('requests.request', return_value=mock_good_response) def test_rc_request_v1(self, mock_good_response): @@ -53,7 +54,7 @@ class TestOsdfUtilsInterfaces(unittest.TestCase): def test_rc_request_v2(self, mock_bad_response): rc = RestClient() try: - rc.request() + rc.request(url="http://localhost") except requests.RequestException: return raise Exception("Allows bad requests instead of raising exception") @@ -66,4 +67,3 @@ class TestOsdfUtilsInterfaces(unittest.TestCase): if __name__ == "__main__": unittest.main() - diff --git a/test/pci-optimization-tests/fixed_pci.json b/test/pci-optimization-tests/fixed_pci.json new file mode 100644 index 0000000..0038d04 --- /dev/null +++ b/test/pci-optimization-tests/fixed_pci.json @@ -0,0 +1,42 @@ +{ + + "requestInfo": { + + "transactionId": "xxx-xxx-xxxx", + + "requestId": "yyy-yyy-yyyy", + + "callbackUrl": "https://wiki.onap.org:5000/callbackUrl/", + + "sourceId": "SO", + + "requestType": "create", + + "numSolutions": 1, + + "optimizers": [ + + "pci-anr" + + ], + + "timeout": 600 + + }, + + "cellInfo": { + + "networkId": "netw2000", + + "cellIdList": [ + + "cell0" + + ], + + "fixedPCICells": ["cell21","cell22"], + "anrInputList": [{"cellId": "cell20", "removeableNeighbors" :["cell23"]}], + "trigger": "NbrListChange" + } + +} diff --git a/test/pci-optimization-tests/pci_anr_request.json b/test/pci-optimization-tests/pci_anr_request.json index e8947b1..34e2409 100644 --- a/test/pci-optimization-tests/pci_anr_request.json +++ b/test/pci-optimization-tests/pci_anr_request.json @@ -12,7 +12,7 @@ "timeout": 600 }, "cellInfo": { - "networkId": "2000", + "networkId": "netw2000", "cellIdList": [ "cell20" ] diff --git a/test/pci-optimization-tests/request.json b/test/pci-optimization-tests/request.json index 517ee10..a8eb4f6 100644 --- a/test/pci-optimization-tests/request.json +++ b/test/pci-optimization-tests/request.json @@ -12,9 +12,9 @@ "timeout": 600 }, "cellInfo": { - "networkId": "1000", + "networkId": "netw1000", "cellIdList": [ "cell0" ] } -}
\ No newline at end of file +} diff --git a/test/placement-tests/policy_response.json b/test/placement-tests/policy_response.json index 8de8537..a257ecc 100644 --- a/test/placement-tests/policy_response.json +++ b/test/placement-tests/policy_response.json @@ -1,182 +1,694 @@ -[ { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"SubscriberPolicy\",\"policyName\":\"oofBeijing.SubscriberPolicy_v1\",\"description\":\"Subscriber Policy\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"1\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"subscriber\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"subscriberpolicy\",\"subscriber_x\",\"subscriber_y\"],\"properties\":{\"subscriberName\":[\"subscriber_x\",\"subscriber_y\"],\"subscriberRole\":[\"PVT Homing\"],\"provStatus\":[\"CAPPED\"]},\"policyType\":\"SubscriberPolicy\"}}", - "policyName": "oofBeijing.Config_MS_SubscriberPolicy_v1.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "SubscriberPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"hpaPolicy\",\"policyName\":\"oofBeijing.hpaPolicy_vGMuxInfra\",\"description\":\"HPA policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"1.0\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"resources\":\"vGMuxInfra\",\"identity\":\"hpaPolicy_vGMuxInfra\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"hpaPolicy\",\"flavorFeatures\":[{\"flavorLabel\":\"flavor_label_vm_01\",\"flavorProperties\":[{\"hpa-feature\":\"cpuTopology\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numCpusockets\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpusockets\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"4\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"8\",\"operator\":\"<=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"ovsDpdk\",\"mandatory\":\"False\",\"score\":\"3\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"dataProcessingAccelerationLibrary\",\"hpa-attribute-value\":\"ovsDpdk_version\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"cpuInstructionSetExtensions\",\"mandatory\":\"True\",\"architecture\":\"INTEL-64\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"instructionSetExtensions\",\"hpa-attribute-value\":[\"<CPUINST>\",\"<CPUINST>\"],\"operator\":\"ALL\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_02\",\"flavorProperties\":[{\"hpa-feature\":\"cpuPinningy\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"logicalCpuThreadPinningPolicy\",\"hpa-attribute-value\":\"<CPUTHREADPOLICY>\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"logicalCpuPinningPolicy\",\"hpa-attribute-value\":\"<CPUPOLICY>\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"localStorage\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"diskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"ephemeralDiskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"swapMemSize\",\"hpa-attribute-value\":\"16\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"pcie\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"pciCount\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciVendorId\",\"hpa-attribute-value\":\"8086\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciDeviceId\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"functionType\",\"hpa-attribute-value\":\"<PCITYPEVALUE>\",\"operator\":\"=\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_03\",\"flavorProperties\":[{\"hpa-feature\":\"numa\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numaNodes\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaCpu-0\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-0\",\"hpa-attribute-value\":\"2048\",\"operator\":\"=\",\"unit\":\"MB\"},{\"hpa-attribute-key\":\"numaCpu-1\",\"hpa-attribute-value\":\"4\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-1\",\"value\":\"4096\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"hugePages\",\"mandatory\":\"False\",\"score\":\"7\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"memoryPageSize\",\"hpa-attribute-value\":\"<MEMORYPAGESIZE>\",\"operator\":\"=\",\"unit\":\"\"}]}]}]}}", - "policyName": "oofBeijing.Config_MS_hpaPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "hpaPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vG\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vG\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"international\",\"ip\",\"vG\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vG\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", - "policyName": "oofBeijing.Config_MS_vnfPolicy_vG.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "VnfPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vGMuxInfra\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vGMuxInfra\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"international\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vGMuxInfra\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", - "policyName": "oofBeijing.Config_MS_vnfPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "VnfPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vG\",\"description\":\"Distance Policy for vG\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"1500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vG\",\"resources\":[\"vG\"],\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vG\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", - "policyName": "oofBeijing.Config_MS_distancePolicy_vG.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "distancePolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vG\", \"description\": \"Capacity policy for vG\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vG\", \"policyScope\": [\"vcpe\", \"PVT Homing\",\"us\", \"international\", \"ip\", \"vG\", \"vim_fit\"], \"resources\": [\"vG\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", - "policyName": "oofBeijing.Config_MS_capacityPolicy_vG.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "capacityPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\": \"optimizationQueryPolicy\", \"policyName\": \"oofBeijing.queryPolicy_vcpe\", \"description\": \"Optimization query policy for vcpe\", \"templateVersion\": \"0.0.1\", \"version\": \"oofBeijing\", \"priority\": \"3\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"queryProperties\": [{\"attribute\": \"locationId\", \"attribute_location\": \"customerLocation\", \"value\": \"\"}, {\"attribute\": \"id\", \"attribute_location\": \"vpnInfo.vpnId\", \"value\": \"\"}, {\"attribute\": \"upstreamBW\", \"attribute_location\": \"vpnInfo.upstreamBW\", \"value\": \"\"}, {\"attribute\": \"customerLatitude\", \"attribute_location\": \"customerLatitude\", \"value\": 32.89748}, {\"attribute\": \"customerLongitude\", \"attribute_location\": \"customerLongitude\", \"value\": -97.040443}], \"policyScope\": [\"vcpe\", \"PVT Homing\",\"us\", \"international\", \"ip\", \"vGMuxInfra\", \"vG\", \"optimizationQueryPolicy\"], \"policyType\": \"optimizationQueryPolicy\"}}", - "policyName": "oofBeijing.Config_MS_queryPolicy_vcpe.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "optimizationQueryPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"PlacementOptimizationPolicy\",\"policyName\":\"oofBeijing.PlacementOptimizationPolicy_vGMuxInfra\",\"description\":\"Placement Optimization Policy for vGMuxInfra\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"5\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"objectiveParameter\":{\"parameterAttributes\":[{\"resource\":[\"vGMuxInfra\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"},{\"resource\":[\"vG\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"}],\"operator\":\"sum\"},\"identity\":\"optimization\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vGMuxInfra\",\"vG\"],\"policyType\":\"placementOptimization\",\"objective\":\"minimize\"}}", - "policyName": "oofBeijing.Config_MS_PlacementOptimizationPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "PlacementOptimizationPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vGMuxInfra\",\"description\":\"Distance Policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vGMuxInfra\",\"resources\":[\"vGMuxInfra\"],\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", - "policyName": "oofBeijing.Config_MS_distancePolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "distancePolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vGMuxInfra\", \"description\": \"Capacity policy for vGMuxInfra\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vGMuxInfra\", \"policyScope\": [\"vcpe\", \"PVT Homing\",\"us\", \"international\", \"ip\", \"vGMuxInfra\", \"vim_fit\"], \"resources\": [\"vGMuxInfra\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", - "policyName": "oofBeijing.Config_MS_capacityPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "capacityPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null + "policies": { + "OSDF_FRANKFURT.SubscriberPolicy_v1": { + "type": "onap.policies.optimization.SubscriberPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.SubscriberPolicy_v1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "identity": "subscriber_vCPE", + "properties": { + "subscriberName": [ + "subscriber_x", + "subscriber_y" + ], + "subscriberRole": [ + "PVT Homing" + ], + "provStatus": [ + "CAPPED" + ] + } + } + }, + "OSDF_FRANKFURT.vnfPolicy_vG": { + "type": "onap.policies.optimization.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vG", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vnf_vG", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] + } + }, + "OSDF_FRANKFURT.vnfPolicy_vGMuxInfra": { + "type": "onap.policies.optimization.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vnf_vGMuxInfra", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "vGMuxInfra-xx", + "inventoryType": "service", + "customerId": "SDN-ETHERNET-INTERNET", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] + } + }, + "OSDF_FRANKFURT.Distance_vG_1": { + "type": "onap.policies.optimization.DistancePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Distance_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "distance-vG", + "applicableResources": "any", + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { + "value": "1500", + "operator": "<", + "unit": "km" + } + } + } + }, + "OSDF_FRANKFURT.Distance_vGMuxInfra": { + "type": "onap.policies.optimization.DistancePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Distance_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "distance-vGMuxInfra", + "applicableResources": "any", + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { + "value": "500", + "operator": "<", + "unit": "km" + } + } + } + }, + "OSDF_FRANKFURT.Placement_optimization_1": { + "type": "onap.policies.optimization.OptimizationPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Placement_optimization_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG", + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "optimization", + "objective": "minimize", + "objectiveParameter": { + "parameterAttributes": [ + { + "resources": [ + "vGMuxInfra" + ], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resources": [ + "vG" + ], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resources": [ + "vG" + ], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" + }, + { + "resources": [ + "vFW" + ], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "100", + "operator": "product" + }, + { + "resources": [ + "vFW" + ], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" + } + ], + "operator": "sum" + } + } + }, + "OSDF_FRANKFURT.queryPolicy_vCPE": { + "type": "onap.policies.optimization.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.queryPolicy_vCPE", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra", + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vCPE_Query_Policy", + "queryProperties": [ + { + "attribute": "customerLatitude", + "attribute_location": "customerLatitude" + }, + { + "attribute": "customerLongitude", + "attribute_location": "customerLongitude" + } + ] + } + }, + "OSDF_FRANKFURT.hpa_policy_vG_1": { + "type": "onap.policies.optimization.HpaPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.hpa_policy_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "hpa-vG", + "flavorFeatures": [ + { + "id": "vg_1", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_01", + "attribute_value": "" + } + ] + } + ], + "flavorProperties": [ + { + "hpa-feature": "cpuTopology", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numCpuSockets", + "hpa-attribute-value": "2", + "operator": ">=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuSockets", + "hpa-attribute-value": "4", + "operator": "<=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuCores", + "hpa-attribute-value": "2", + "operator": ">=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuCores", + "hpa-attribute-value": "4", + "operator": "<=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuThreads", + "hpa-attribute-value": "4", + "operator": ">=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuThreads", + "hpa-attribute-value": "8", + "operator": "<=", + "unit": "" + } + ] + }, + { + "hpa-feature": "basicCapabilities", + "mandatory": true, + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + } + ] + }, + { + "hpa-feature": "ovsDpdk", + "mandatory": false, + "score": 3, + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "dataProcessingAccelerationLibrary", + "hpa-attribute-value": "ovsDpdk_version", + "operator": [ + "=" + ], + "unit": "" + } + ] + }, + { + "hpa-feature": "cpuInstructionSetExtensions", + "mandatory": true, + "architecture": "INTEL-64", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "instructionSetExtensions", + "hpa-attribute-value": [ + "<CPUINST>", + "<CPUINST>" + ], + "operator": [ + "ALL" + ], + "unit": "" + } + ] + } + ] + }, + { + "id": "vg_2", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_02", + "attribute_value": "" + } + ] + } + ], + "flavorProperties": [ + { + "hpa-feature": "cpuPinningy", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "logicalCpuThreadPinningPolicy", + "hpa-attribute-value": "<CPUTHREADPOLICY>", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "logicalCpuPinningPolicy", + "hpa-attribute-value": "<CPUPOLICY>", + "operator": "=", + "unit": "" + } + ] + }, + { + "hpa-feature": "basicCapabilities", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "GB" + } + ] + }, + { + "hpa-feature": "localStorage", + "mandatory": "False", + "score": "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "diskSize", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "GB" + }, + { + "hpa-attribute-key": "ephemeralDiskSize", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "GB" + }, + { + "hpa-attribute-key": "swapMemSize", + "hpa-attribute-value": "16", + "operator": "=", + "unit": "MB" + } + ] + }, + { + "hpa-feature": "pciePassthrough", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "pciCount", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "pciVendorId", + "hpa-attribute-value": "8086", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "pciDeviceId", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + } + ] + } + ] + }, + { + "id": "vg_3", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_03", + "attribute_value": "" + } + ] + } + ], + "flavorProperties": [ + { + "hpa-feature": "numa", + "mandatory": "False", + "score": "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numaNodes", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "numaCpu-0", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "numaMem-0", + "hpa-attribute-value": "2048", + "operator": "=", + "unit": "MB" + }, + { + "hpa-attribute-key": "numaCpu-1", + "hpa-attribute-value": "4", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "numaMem-1", + "value": "4096", + "operator": "=", + "unit": "MB" + } + ] + }, + { + "hpa-feature": "basicCapabilities", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "GB" + } + ] + }, + { + "hpa-feature": "hugePages", + "mandatory": "False", + "score": "7", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "memoryPageSize", + "hpa-attribute-value": "<MEMORYPAGESIZE>", + "operator": "=", + "unit": "" + } + ] + } + ] + } + ] + } + }, + "OSDF_FRANKFURT.Capacity_vG_1": { + "type": "onap.policies.optimization.Vim_fit", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Capacity_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "capacity_vG", + "applicableResources": "any", + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + } + } + }, + "OSDF_FRANKFURT.Capacity_vGMuxInfra": { + "type": "onap.policies.optimization.Vim_fit", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Capacity_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "capacity_vGMuxInfra", + "applicableResources": "any", + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + } + } + } + } } -]
\ No newline at end of file diff --git a/test/placement-tests/policy_response2.json b/test/placement-tests/policy_response2.json index 2cd6dcb..15ddbe1 100644 --- a/test/placement-tests/policy_response2.json +++ b/test/placement-tests/policy_response2.json @@ -1,182 +1,705 @@ -[ - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"SubscriberPolicy\",\"policyName\":\"oofBeijing.SubscriberPolicy_v1\",\"description\":\"Subscriber Policy\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"1\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"subscriber\",\"policyScope\":[\"vCPE\",\"PVT Homing\",\"SubscriberPolicy\",\"subscriber_x\",\"subscriber_y\"],\"properties\":{\"subscriberName\":[\"subscriber_x\",\"subscriber_y\"],\"subscriberRole\":[\"PVT Homing\"],\"provStatus\":[\"CAPPED\"]},\"policyType\":\"SubscriberPolicy\"}}", - "policyName": "oofBeijing.Config_MS_SubscriberPolicy_v1.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "SubscriberPolicy", - "uuid": "", - "Location": "" +{ + "policies": { + "OSDF_FRANKFURT.SubscriberPolicy_v1": { + "type": "onap.policies.optimization.SubscriberPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.SubscriberPolicy_v1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "identity": "subscriber_vCPE", + "properties": { + "subscriberName": [ + "subscriber_x", + "subscriber_y" + ], + "subscriberRole": [ + "PVT Homing" + ], + "provStatus": [ + "CAPPED" + ] + } + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"hpaPolicy\",\"policyName\":\"oofBeijing.hpaPolicy_vGMuxInfra\",\"description\":\"HPA policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"1.0\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"resources\":\"vGMuxInfra\",\"identity\":\"hpaPolicy_vGMuxInfra\",\"policyScope\":[\"vCPE\",\"PVT Homing\",\"US\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"hpaPolicy\",\"flavorFeatures\":[{\"flavorLabel\":\"flavor_label_vm_01\",\"flavorProperties\":[{\"hpa-feature\":\"cpuTopology\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numCpuSockets\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuSockets\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"4\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"8\",\"operator\":\"<=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"ovsDpdk\",\"mandatory\":\"False\",\"score\":\"3\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"dataProcessingAccelerationLibrary\",\"hpa-attribute-value\":\"ovsDpdk_version\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"cpuInstructionSetExtensions\",\"mandatory\":\"True\",\"architecture\":\"INTEL-64\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"instructionSetExtensions\",\"hpa-attribute-value\":[\"<CPUINST>\",\"<CPUINST>\"],\"operator\":\"ALL\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_02\",\"flavorProperties\":[{\"hpa-feature\":\"cpuPinningy\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"logicalCpuThreadPinningPolicy\",\"hpa-attribute-value\":\"<CPUTHREADPOLICY>\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"logicalCpuPinningPolicy\",\"hpa-attribute-value\":\"<CPUPOLICY>\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"localStorage\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"diskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"ephemeralDiskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"swapMemSize\",\"hpa-attribute-value\":\"16\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"pcie\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"pciCount\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciVendorId\",\"hpa-attribute-value\":\"8086\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciDeviceId\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"functionType\",\"hpa-attribute-value\":\"<PCITYPEVALUE>\",\"operator\":\"=\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_03\",\"flavorProperties\":[{\"hpa-feature\":\"numa\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numaNodes\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaCpu-0\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-0\",\"hpa-attribute-value\":\"2048\",\"operator\":\"=\",\"unit\":\"MB\"},{\"hpa-attribute-key\":\"numaCpu-1\",\"hpa-attribute-value\":\"4\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-1\",\"value\":\"4096\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"hugePages\",\"mandatory\":\"False\",\"score\":\"7\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"memoryPageSize\",\"hpa-attribute-value\":\"<MEMORYPAGESIZE>\",\"operator\":\"=\",\"unit\":\"\"}]}]}]}}", - "policyName": "oofBeijing.Config_MS_hpaPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "hpaPolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.vnfPolicy_vG": { + "type": "onap.policies.optimization.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vG", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vnf_vG", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vG\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vG\",\"policyScope\":[\"vCPE\",\"INTERNATIONAL\",\"ip\",\"vG\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vG\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", - "policyName": "oofBeijing.Config_MS_vnfPolicy_vG.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "VnfPolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.vnfPolicy_vGMuxInfra": { + "type": "onap.policies.optimization.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vnf_vGMuxInfra", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "vGMuxInfra-xx", + "inventoryType": "service", + "customerId": "SDN-ETHERNET-INTERNET", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vGMuxInfra\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vGMuxInfra\",\"policyScope\":[\"vCPE\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vGMuxInfra\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", - "policyName": "oofBeijing.Config_MS_vnfPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "VnfPolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.Distance_vG_1": { + "type": "onap.policies.optimization.DistancePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Distance_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "distance-vG", + "applicableResources": "any", + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { + "value": "1500", + "operator": "<", + "unit": "km" + } + } + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vG\",\"description\":\"Distance Policy for vG\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"1500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vG\",\"resources\":[\"vG\"],\"policyScope\":[\"vCPE\",\"US\",\"INTERNATIONAL\",\"ip\",\"vG\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", - "policyName": "oofBeijing.Config_MS_distancePolicy_vG.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "distancePolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.Distance_vGMuxInfra": { + "type": "onap.policies.optimization.DistancePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Distance_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "distance-vGMuxInfra", + "applicableResources": "any", + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { + "value": "500", + "operator": "<", + "unit": "km" + } + } + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vG\", \"description\": \"Capacity policy for vG\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vG\", \"policyScope\": [\"VCPE\",\"US\", \"INTERNATIONAL\", \"ip\", \"vG\", \"vim_fit\"], \"resources\": [\"vG\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", - "policyName": "oofBeijing.Config_MS_capacityPolicy_vG.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "capacityPolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.Placement_optimization_1": { + "type": "onap.policies.optimization.OptimizationPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Placement_optimization_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG", + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "optimization", + "objective": "minimize", + "objectiveParameter": { + "parameterAttributes": [ + { + "resources": [ + "vGMuxInfra" + ], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resources": [ + "vG" + ], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resources": [ + "vG" + ], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" + }, + { + "resources": [ + "vFW" + ], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "100", + "operator": "product" + }, + { + "resources": [ + "vFW" + ], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" + } + ], + "operator": "sum" + } + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\": \"optimizationQueryPolicy\", \"policyName\": \"oofBeijing.queryPolicy_vCPE\", \"description\": \"Optimization query policy for vCPE\", \"templateVersion\": \"0.0.1\", \"version\": \"oofBeijing\", \"priority\": \"3\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"queryProperties\": [{\"attribute\": \"locationId\", \"attribute_location\": \"customerLocation\", \"value\": \"\"}, {\"attribute\": \"id\", \"attribute_location\": \"vpnInfo.vpnId\", \"value\": \"\"}, {\"attribute\": \"upstreamBW\", \"attribute_location\": \"vpnInfo.upstreamBW\", \"value\": \"\"}, {\"attribute\": \"customerLatitude\", \"attribute_location\": \"customerLatitude\", \"value\": 32.89748}, {\"attribute\": \"customerLongitude\", \"attribute_location\": \"customerLongitude\", \"value\": -97.040443}], \"policyScope\": [\"vCPE\",\"US\", \"INTERNATIONAL\", \"ip\", \"vGMuxInfra\", \"vG\", \"optimizationQueryPolicy\"], \"policyType\": \"optimizationQueryPolicy\"}}", - "policyName": "oofBeijing.Config_MS_queryPolicy_vCPE.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "optimizationQueryPolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.queryPolicy_vCPE": { + "type": "onap.policies.optimization.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.queryPolicy_vCPE", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra", + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vCPE_Query_Policy", + "queryProperties": [ + { + "attribute": "customerLatitude", + "attribute_location": "customerLatitude" + }, + { + "attribute": "customerLongitude", + "attribute_location": "customerLongitude" + } + ] + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"PlacementOptimizationPolicy\",\"policyName\":\"oofBeijing.PlacementOptimizationPolicy_vGMuxInfra\",\"description\":\"Placement Optimization Policy for vGMuxInfra\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"5\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"objectiveParameter\":{\"parameterAttributes\":[{\"resource\":[\"vGMuxInfra\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"},{\"resource\":[\"vG\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"}],\"operator\":\"sum\"},\"identity\":\"optimization\",\"policyScope\":[\"vCPE\",\"US\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\",\"vG\"],\"policyType\":\"placementOptimization\",\"objective\":\"minimize\"}}", - "policyName": "oofBeijing.Config_MS_PlacementOptimizationPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "PlacementOptimizationPolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.hpa_policy_vG_1": { + "type": "onap.policies.optimization.HpaPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.hpa_policy_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "hpa-vG", + "flavorFeatures": [ + { + "id": "vg_1", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_01", + "attribute_value": "" + } + ] + } + ], + "flavorProperties": [ + { + "hpa-feature": "cpuTopology", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numCpuSockets", + "hpa-attribute-value": "2", + "operator": ">=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuSockets", + "hpa-attribute-value": "4", + "operator": "<=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuCores", + "hpa-attribute-value": "2", + "operator": ">=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuCores", + "hpa-attribute-value": "4", + "operator": "<=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuThreads", + "hpa-attribute-value": "4", + "operator": ">=", + "unit": "" + }, + { + "hpa-attribute-key": "numCpuThreads", + "hpa-attribute-value": "8", + "operator": "<=", + "unit": "" + } + ] + }, + { + "hpa-feature": "basicCapabilities", + "mandatory": true, + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + } + ] + }, + { + "hpa-feature": "ovsDpdk", + "mandatory": false, + "score": 3, + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "dataProcessingAccelerationLibrary", + "hpa-attribute-value": "ovsDpdk_version", + "operator": [ + "=" + ], + "unit": "" + } + ] + }, + { + "hpa-feature": "cpuInstructionSetExtensions", + "mandatory": true, + "architecture": "INTEL-64", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "instructionSetExtensions", + "hpa-attribute-value": [ + "<CPUINST>", + "<CPUINST>" + ], + "operator": [ + "ALL" + ], + "unit": "" + } + ] + } + ] + }, + { + "id": "vg_2", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_02", + "attribute_value": "" + } + ] + } + ], + "flavorProperties": [ + { + "hpa-feature": "cpuPinningy", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "logicalCpuThreadPinningPolicy", + "hpa-attribute-value": "<CPUTHREADPOLICY>", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "logicalCpuPinningPolicy", + "hpa-attribute-value": "<CPUPOLICY>", + "operator": "=", + "unit": "" + } + ] + }, + { + "hpa-feature": "basicCapabilities", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "GB" + } + ] + }, + { + "hpa-feature": "localStorage", + "mandatory": "False", + "score": "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "diskSize", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "GB" + }, + { + "hpa-attribute-key": "ephemeralDiskSize", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "GB" + }, + { + "hpa-attribute-key": "swapMemSize", + "hpa-attribute-value": "16", + "operator": "=", + "unit": "MB" + } + ] + }, + { + "hpa-feature": "pciePassthrough", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "pciCount", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "pciVendorId", + "hpa-attribute-value": "8086", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "pciDeviceId", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + } + ] + } + ] + }, + { + "id": "vg_3", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_03", + "attribute_value": "" + } + ] + } + ], + "flavorProperties": [ + { + "hpa-feature": "numa", + "mandatory": "False", + "score": "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numaNodes", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "numaCpu-0", + "hpa-attribute-value": "2", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "numaMem-0", + "hpa-attribute-value": "2048", + "operator": "=", + "unit": "MB" + }, + { + "hpa-attribute-key": "numaCpu-1", + "hpa-attribute-value": "4", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "numaMem-1", + "value": "4096", + "operator": "=", + "unit": "MB" + } + ] + }, + { + "hpa-feature": "basicCapabilities", + "mandatory": "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": "6", + "operator": "=", + "unit": "GB" + } + ] + }, + { + "hpa-feature": "hugePages", + "mandatory": "False", + "score": "7", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + { + "hpa-attribute-key": "memoryPageSize", + "hpa-attribute-value": "<MEMORYPAGESIZE>", + "operator": "=", + "unit": "" + } + ] + } + ] + } + ] + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vGMuxInfra\",\"description\":\"Distance Policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vGMuxInfra\",\"resources\":[\"vGMuxInfra\"],\"policyScope\":[\"vCPE\",\"US\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", - "policyName": "oofBeijing.Config_MS_distancePolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "distancePolicy", - "uuid": "", - "Location": "" + "OSDF_FRANKFURT.Capacity_vG_1": { + "type": "onap.policies.optimization.Vim_fit", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Capacity_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "capacity_vG", + "applicableResources": "any", + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + } + } }, - "responseAttributes": { }, - "property": null - }, - { - "policyConfigMessage": "Config Retrieved! ", - "policyConfigStatus": "CONFIG_RETRIEVED", - "type": "JSON", - "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vGMuxInfra\", \"description\": \"Capacity policy for vGMuxInfra\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vGMuxInfra\", \"policyScope\": [\"VCPE\",\"US\", \"INTERNATIONAL\", \"ip\", \"vGMuxInfra\", \"vim_fit\"], \"resources\": [\"vGMuxInfra\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", - "policyName": "oofBeijing.Config_MS_capacityPolicy_vGMuxInfra.1.xml", - "policyVersion": "1", - "matchingConditions": { - "ECOMPName": "SNIRO-Placement", - "ONAPName": "SNIRO-Placement", - "ConfigName": "", - "service": "capacityPolicy", - "uuid": "", - "Location": "" - }, - "responseAttributes": { }, - "property": null + "OSDF_FRANKFURT.Capacity_vGMuxInfra": { + "type": "onap.policies.optimization.Vim_fit", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Capacity_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "PVT Homing" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "capacity_vGMuxInfra", + "applicableResources": "any", + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + } + } + } } -]
\ No newline at end of file +} + diff --git a/test/placement-tests/policy_response2_old_format.json b/test/placement-tests/policy_response2_old_format.json new file mode 100644 index 0000000..2cd6dcb --- /dev/null +++ b/test/placement-tests/policy_response2_old_format.json @@ -0,0 +1,182 @@ +[ + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"SubscriberPolicy\",\"policyName\":\"oofBeijing.SubscriberPolicy_v1\",\"description\":\"Subscriber Policy\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"1\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"subscriber\",\"policyScope\":[\"vCPE\",\"PVT Homing\",\"SubscriberPolicy\",\"subscriber_x\",\"subscriber_y\"],\"properties\":{\"subscriberName\":[\"subscriber_x\",\"subscriber_y\"],\"subscriberRole\":[\"PVT Homing\"],\"provStatus\":[\"CAPPED\"]},\"policyType\":\"SubscriberPolicy\"}}", + "policyName": "oofBeijing.Config_MS_SubscriberPolicy_v1.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "SubscriberPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"hpaPolicy\",\"policyName\":\"oofBeijing.hpaPolicy_vGMuxInfra\",\"description\":\"HPA policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"1.0\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"resources\":\"vGMuxInfra\",\"identity\":\"hpaPolicy_vGMuxInfra\",\"policyScope\":[\"vCPE\",\"PVT Homing\",\"US\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"hpaPolicy\",\"flavorFeatures\":[{\"flavorLabel\":\"flavor_label_vm_01\",\"flavorProperties\":[{\"hpa-feature\":\"cpuTopology\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numCpuSockets\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuSockets\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"4\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"8\",\"operator\":\"<=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"ovsDpdk\",\"mandatory\":\"False\",\"score\":\"3\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"dataProcessingAccelerationLibrary\",\"hpa-attribute-value\":\"ovsDpdk_version\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"cpuInstructionSetExtensions\",\"mandatory\":\"True\",\"architecture\":\"INTEL-64\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"instructionSetExtensions\",\"hpa-attribute-value\":[\"<CPUINST>\",\"<CPUINST>\"],\"operator\":\"ALL\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_02\",\"flavorProperties\":[{\"hpa-feature\":\"cpuPinningy\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"logicalCpuThreadPinningPolicy\",\"hpa-attribute-value\":\"<CPUTHREADPOLICY>\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"logicalCpuPinningPolicy\",\"hpa-attribute-value\":\"<CPUPOLICY>\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"localStorage\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"diskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"ephemeralDiskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"swapMemSize\",\"hpa-attribute-value\":\"16\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"pcie\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"pciCount\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciVendorId\",\"hpa-attribute-value\":\"8086\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciDeviceId\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"functionType\",\"hpa-attribute-value\":\"<PCITYPEVALUE>\",\"operator\":\"=\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_03\",\"flavorProperties\":[{\"hpa-feature\":\"numa\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numaNodes\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaCpu-0\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-0\",\"hpa-attribute-value\":\"2048\",\"operator\":\"=\",\"unit\":\"MB\"},{\"hpa-attribute-key\":\"numaCpu-1\",\"hpa-attribute-value\":\"4\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-1\",\"value\":\"4096\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"hugePages\",\"mandatory\":\"False\",\"score\":\"7\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"memoryPageSize\",\"hpa-attribute-value\":\"<MEMORYPAGESIZE>\",\"operator\":\"=\",\"unit\":\"\"}]}]}]}}", + "policyName": "oofBeijing.Config_MS_hpaPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "hpaPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vG\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vG\",\"policyScope\":[\"vCPE\",\"INTERNATIONAL\",\"ip\",\"vG\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vG\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", + "policyName": "oofBeijing.Config_MS_vnfPolicy_vG.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "VnfPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vGMuxInfra\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vGMuxInfra\",\"policyScope\":[\"vCPE\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vGMuxInfra\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", + "policyName": "oofBeijing.Config_MS_vnfPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "VnfPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vG\",\"description\":\"Distance Policy for vG\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"1500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vG\",\"resources\":[\"vG\"],\"policyScope\":[\"vCPE\",\"US\",\"INTERNATIONAL\",\"ip\",\"vG\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", + "policyName": "oofBeijing.Config_MS_distancePolicy_vG.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "distancePolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vG\", \"description\": \"Capacity policy for vG\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vG\", \"policyScope\": [\"VCPE\",\"US\", \"INTERNATIONAL\", \"ip\", \"vG\", \"vim_fit\"], \"resources\": [\"vG\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", + "policyName": "oofBeijing.Config_MS_capacityPolicy_vG.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "capacityPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\": \"optimizationQueryPolicy\", \"policyName\": \"oofBeijing.queryPolicy_vCPE\", \"description\": \"Optimization query policy for vCPE\", \"templateVersion\": \"0.0.1\", \"version\": \"oofBeijing\", \"priority\": \"3\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"queryProperties\": [{\"attribute\": \"locationId\", \"attribute_location\": \"customerLocation\", \"value\": \"\"}, {\"attribute\": \"id\", \"attribute_location\": \"vpnInfo.vpnId\", \"value\": \"\"}, {\"attribute\": \"upstreamBW\", \"attribute_location\": \"vpnInfo.upstreamBW\", \"value\": \"\"}, {\"attribute\": \"customerLatitude\", \"attribute_location\": \"customerLatitude\", \"value\": 32.89748}, {\"attribute\": \"customerLongitude\", \"attribute_location\": \"customerLongitude\", \"value\": -97.040443}], \"policyScope\": [\"vCPE\",\"US\", \"INTERNATIONAL\", \"ip\", \"vGMuxInfra\", \"vG\", \"optimizationQueryPolicy\"], \"policyType\": \"optimizationQueryPolicy\"}}", + "policyName": "oofBeijing.Config_MS_queryPolicy_vCPE.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "optimizationQueryPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"PlacementOptimizationPolicy\",\"policyName\":\"oofBeijing.PlacementOptimizationPolicy_vGMuxInfra\",\"description\":\"Placement Optimization Policy for vGMuxInfra\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"5\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"objectiveParameter\":{\"parameterAttributes\":[{\"resource\":[\"vGMuxInfra\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"},{\"resource\":[\"vG\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"}],\"operator\":\"sum\"},\"identity\":\"optimization\",\"policyScope\":[\"vCPE\",\"US\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\",\"vG\"],\"policyType\":\"placementOptimization\",\"objective\":\"minimize\"}}", + "policyName": "oofBeijing.Config_MS_PlacementOptimizationPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "PlacementOptimizationPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vGMuxInfra\",\"description\":\"Distance Policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vGMuxInfra\",\"resources\":[\"vGMuxInfra\"],\"policyScope\":[\"vCPE\",\"US\",\"INTERNATIONAL\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", + "policyName": "oofBeijing.Config_MS_distancePolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "distancePolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vGMuxInfra\", \"description\": \"Capacity policy for vGMuxInfra\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vGMuxInfra\", \"policyScope\": [\"VCPE\",\"US\", \"INTERNATIONAL\", \"ip\", \"vGMuxInfra\", \"vim_fit\"], \"resources\": [\"vGMuxInfra\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", + "policyName": "oofBeijing.Config_MS_capacityPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "capacityPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + } +]
\ No newline at end of file diff --git a/test/placement-tests/policy_response_old_format.json b/test/placement-tests/policy_response_old_format.json new file mode 100644 index 0000000..8de8537 --- /dev/null +++ b/test/placement-tests/policy_response_old_format.json @@ -0,0 +1,182 @@ +[ + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"SubscriberPolicy\",\"policyName\":\"oofBeijing.SubscriberPolicy_v1\",\"description\":\"Subscriber Policy\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"1\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"subscriber\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"subscriberpolicy\",\"subscriber_x\",\"subscriber_y\"],\"properties\":{\"subscriberName\":[\"subscriber_x\",\"subscriber_y\"],\"subscriberRole\":[\"PVT Homing\"],\"provStatus\":[\"CAPPED\"]},\"policyType\":\"SubscriberPolicy\"}}", + "policyName": "oofBeijing.Config_MS_SubscriberPolicy_v1.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "SubscriberPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"hpaPolicy\",\"policyName\":\"oofBeijing.hpaPolicy_vGMuxInfra\",\"description\":\"HPA policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"1.0\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"resources\":\"vGMuxInfra\",\"identity\":\"hpaPolicy_vGMuxInfra\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"hpaPolicy\",\"flavorFeatures\":[{\"flavorLabel\":\"flavor_label_vm_01\",\"flavorProperties\":[{\"hpa-feature\":\"cpuTopology\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numCpusockets\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpusockets\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"2\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuCores\",\"hpa-attribute-value\":\"4\",\"operator\":\"<=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"4\",\"operator\":\">=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numCpuThreads\",\"hpa-attribute-value\":\"8\",\"operator\":\"<=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"ovsDpdk\",\"mandatory\":\"False\",\"score\":\"3\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"dataProcessingAccelerationLibrary\",\"hpa-attribute-value\":\"ovsDpdk_version\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"cpuInstructionSetExtensions\",\"mandatory\":\"True\",\"architecture\":\"INTEL-64\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"instructionSetExtensions\",\"hpa-attribute-value\":[\"<CPUINST>\",\"<CPUINST>\"],\"operator\":\"ALL\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_02\",\"flavorProperties\":[{\"hpa-feature\":\"cpuPinningy\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"logicalCpuThreadPinningPolicy\",\"hpa-attribute-value\":\"<CPUTHREADPOLICY>\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"logicalCpuPinningPolicy\",\"hpa-attribute-value\":\"<CPUPOLICY>\",\"operator\":\"=\",\"unit\":\"\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"localStorage\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"diskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"ephemeralDiskSize\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"GB\"},{\"hpa-attribute-key\":\"swapMemSize\",\"hpa-attribute-value\":\"16\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"pcie\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"pciCount\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciVendorId\",\"hpa-attribute-value\":\"8086\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"pciDeviceId\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"functionType\",\"hpa-attribute-value\":\"<PCITYPEVALUE>\",\"operator\":\"=\",\"unit\":\"\"}]}]},{\"flavorLabel\":\"flavor_label_vm_03\",\"flavorProperties\":[{\"hpa-feature\":\"numa\",\"mandatory\":\"False\",\"score\":\"5\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numaNodes\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaCpu-0\",\"hpa-attribute-value\":\"2\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-0\",\"hpa-attribute-value\":\"2048\",\"operator\":\"=\",\"unit\":\"MB\"},{\"hpa-attribute-key\":\"numaCpu-1\",\"hpa-attribute-value\":\"4\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"numaMem-1\",\"value\":\"4096\",\"operator\":\"=\",\"unit\":\"MB\"}]},{\"hpa-feature\":\"basicCapabilities\",\"mandatory\":\"True\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"numVirtualCpu\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"\"},{\"hpa-attribute-key\":\"virtualMemSize\",\"hpa-attribute-value\":\"6\",\"operator\":\"=\",\"unit\":\"GB\"}]},{\"hpa-feature\":\"hugePages\",\"mandatory\":\"False\",\"score\":\"7\",\"architecture\":\"generic\",\"hpa-feature-attributes\":[{\"hpa-attribute-key\":\"memoryPageSize\",\"hpa-attribute-value\":\"<MEMORYPAGESIZE>\",\"operator\":\"=\",\"unit\":\"\"}]}]}]}}", + "policyName": "oofBeijing.Config_MS_hpaPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "hpaPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vG\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vG\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"international\",\"ip\",\"vG\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vG\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", + "policyName": "oofBeijing.Config_MS_vnfPolicy_vG.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "VnfPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"VnfPolicy\",\"policyName\":\"oofBeijing.vnfPolicy_vGMuxInfra\",\"description\":\"vnfPolicy\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"6\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"identity\":\"vnf_vGMuxInfra\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"international\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"vnf_policy\",\"resources\":[\"vGMuxInfra\"],\"applicableResources\":\"any\",\"vnfProperties\":[{\"inventoryProvider\":\"aai\",\"serviceType\":\"\",\"inventoryType\":\"cloud\",\"customerId\":\"\"},{\"inventoryProvider\":\"multicloud\",\"serviceType\":\"HNGATEWAY\",\"inventoryType\":\"service\",\"customerId\":\"21014aa2-526b-11e6-beb8-9e71128cae77\"}]}}", + "policyName": "oofBeijing.Config_MS_vnfPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "VnfPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vG\",\"description\":\"Distance Policy for vG\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"1500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vG\",\"resources\":[\"vG\"],\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vG\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", + "policyName": "oofBeijing.Config_MS_distancePolicy_vG.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "distancePolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vG\", \"description\": \"Capacity policy for vG\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vG\", \"policyScope\": [\"vcpe\", \"PVT Homing\",\"us\", \"international\", \"ip\", \"vG\", \"vim_fit\"], \"resources\": [\"vG\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", + "policyName": "oofBeijing.Config_MS_capacityPolicy_vG.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "capacityPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\": \"optimizationQueryPolicy\", \"policyName\": \"oofBeijing.queryPolicy_vcpe\", \"description\": \"Optimization query policy for vcpe\", \"templateVersion\": \"0.0.1\", \"version\": \"oofBeijing\", \"priority\": \"3\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"queryProperties\": [{\"attribute\": \"locationId\", \"attribute_location\": \"customerLocation\", \"value\": \"\"}, {\"attribute\": \"id\", \"attribute_location\": \"vpnInfo.vpnId\", \"value\": \"\"}, {\"attribute\": \"upstreamBW\", \"attribute_location\": \"vpnInfo.upstreamBW\", \"value\": \"\"}, {\"attribute\": \"customerLatitude\", \"attribute_location\": \"customerLatitude\", \"value\": 32.89748}, {\"attribute\": \"customerLongitude\", \"attribute_location\": \"customerLongitude\", \"value\": -97.040443}], \"policyScope\": [\"vcpe\", \"PVT Homing\",\"us\", \"international\", \"ip\", \"vGMuxInfra\", \"vG\", \"optimizationQueryPolicy\"], \"policyType\": \"optimizationQueryPolicy\"}}", + "policyName": "oofBeijing.Config_MS_queryPolicy_vcpe.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "optimizationQueryPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"PlacementOptimizationPolicy\",\"policyName\":\"oofBeijing.PlacementOptimizationPolicy_vGMuxInfra\",\"description\":\"Placement Optimization Policy for vGMuxInfra\",\"templateVersion\":\"1702.03\",\"version\":\"oofBeijing\",\"priority\":\"5\",\"riskType\":\"test\",\"riskLevel\":\"3\",\"guard\":\"False\",\"content\":{\"objectiveParameter\":{\"parameterAttributes\":[{\"resource\":[\"vGMuxInfra\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"},{\"resource\":[\"vG\"],\"customerLocationInfo\":\"customer_loc\",\"parameter\":\"distance\",\"weight\":\"1\",\"operator\":\"product\"}],\"operator\":\"sum\"},\"identity\":\"optimization\",\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vGMuxInfra\",\"vG\"],\"policyType\":\"placementOptimization\",\"objective\":\"minimize\"}}", + "policyName": "oofBeijing.Config_MS_PlacementOptimizationPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "PlacementOptimizationPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\":\"distancePolicy\",\"policyName\":\"oofBeijing.distancePolicy_vGMuxInfra\",\"description\":\"Distance Policy for vGMuxInfra\",\"templateVersion\":\"0.0.1\",\"version\":\"oofBeijing\",\"priority\":\"3\",\"riskType\":\"test\",\"riskLevel\":\"2\",\"guard\":\"False\",\"content\":{\"distanceProperties\":{\"locationInfo\":\"customer_location\",\"distance\":{\"value\":\"500\",\"operator\":\"<\",\"unit\":\"km\"}},\"identity\":\"distance-vGMuxInfra\",\"resources\":[\"vGMuxInfra\"],\"policyScope\":[\"vcpe\",\"PVT Homing\",\"us\",\"international\",\"ip\",\"vGMuxInfra\"],\"policyType\":\"distancePolicy\",\"applicableResources\":\"any\"}}", + "policyName": "oofBeijing.Config_MS_distancePolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "distancePolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + }, + { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + "config": "{\"service\": \"capacityPolicy\", \"policyName\": \"oofBeijing.capacityPolicy_vGMuxInfra\", \"description\": \"Capacity policy for vGMuxInfra\", \"templateVersion\": \"1702.03\", \"version\": \"oofBeijing\", \"priority\": \"5\", \"riskType\": \"test\", \"riskLevel\": \"2\", \"guard\": \"False\", \"content\": {\"identity\": \"capacity_vGMuxInfra\", \"policyScope\": [\"vcpe\", \"PVT Homing\",\"us\", \"international\", \"ip\", \"vGMuxInfra\", \"vim_fit\"], \"resources\": [\"vGMuxInfra\"], \"capacityProperty\": {\"controller\": \"multicloud\", \"request\": \"{\\\"vCPU\\\": 10, \\\"Memory\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_MEM\\\"}, \\\"unit\\\": \\\"GB\\\"}, \\\"Storage\\\": {\\\"quantity\\\": {\\\"get_param\\\": \\\"REQUIRED_DISK\\\", \\\"unit\\\": \\\"GB\\\"}}}\"}, \"policyType\": \"vim_fit\", \"applicableResources\": \"any\"}}", + "policyName": "oofBeijing.Config_MS_capacityPolicy_vGMuxInfra.1.xml", + "policyVersion": "1", + "matchingConditions": { + "ECOMPName": "SNIRO-Placement", + "ONAPName": "SNIRO-Placement", + "ConfigName": "", + "service": "capacityPolicy", + "uuid": "", + "Location": "" + }, + "responseAttributes": { }, + "property": null + } +]
\ No newline at end of file diff --git a/test/placement-tests/request_placement_vfmod.json b/test/placement-tests/request_placement_vfmod.json index e4186d6..4b2b852 100644 --- a/test/placement-tests/request_placement_vfmod.json +++ b/test/placement-tests/request_placement_vfmod.json @@ -1,92 +1,113 @@ { - "name": "de4f04e3-0a65-470b-9d07-8ea6c2fb3e10", - "template": { - "constraints": { - "affinity_vFW_TD": { - "demands": ["vFW-SINK", "vPGN"], - "properties": { - "category": "region", - "qualifier": "same" - }, - "type": "zone" - } - }, - "parameters": { - "service_name": "vFW_TD", - "chosen_region": "RegionOne", - "service_id": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c", - "customer_long": 2.2, - "REQUIRED_MEM": "", - "customer_lat": 1.1, - "REQUIRED_DISK": "" + "name": "de4f04e3-0a65-470b-9d07-8ea6c2fb3e10", + "files": {}, + "timeout": 1200, + "num_solution": "100", + "template": { + "homing_template_version": "2017-10-10", + "parameters": { + "REQUIRED_MEM": "", + "REQUIRED_DISK": "", + "customer_lat": 1.1, + "customer_long": 2.2, + "service_name": "vFW_TD", + "service_id": "3e8d118c-10ca-4b4b-b3db-089b5e9e6a1c", + "chosen_region": "RegionOne" + }, + "locations": { + "customer_loc": { + "latitude": { + "get_param": "customer_lat" }, - "locations": { - "customer_loc": { - "longitude": { - "get_param": "customer_long" - }, - "latitude": { - "get_param": "customer_lat" - } + "longitude": { + "get_param": "customer_long" + } + } + }, + "demands": { + "vFW-SINK": [ + { + "inventory_provider": "aai", + "inventory_type": "vfmodule", + "service_type": "vFW-SINK-XX", + "service_resource_id": "vFW-SINK-XX", + "filtering_attributes": { + "global-customer-id": { + "get_param": "chosen_customer_id" + }, + "model-invariant-id": "e7227847-dea6-4374-abca-4561b070fe7d", + "model-version-id": "763731df-84fd-494b-b824-01fc59a5ff2d", + "orchestration-status": [ + "active" + ], + "prov-status": "ACTIVE", + "cloud-region-id": { + "get_param": "chosen_region" + }, + "service_instance_id": { + "get_param": "service_id" } - }, - "demands": { - "vFW-SINK": [{ - "attributes": { - "global-customer-id": { - "get_param": "chosen_customer_id" - }, - "cloud-region-id": { - "get_param": "chosen_region" - }, - "model-version-id": "763731df-84fd-494b-b824-01fc59a5ff2d", - "orchestration-status": ["active"], - "model-invariant-id": "e7227847-dea6-4374-abca-4561b070fe7d", - "service_instance_id": { - "get_param": "service_id" - }, - "prov-status": "ACTIVE" - }, - "inventory_provider": "aai", - "service_resource_id": "vFW-SINK-XX", - "inventory_type": "vfmodule", - "service_type": "vFW-SINK-XX", - "excluded_candidates": [{ - "inventory_type": "vfmodule", - "candidate_id": "e765d576-8755-4145-8536-0bb6d9b1dc9a" - }] - }], - "vPGN": [{ - "attributes": { - "global-customer-id": { - "get_param": "chosen_customer_id" - }, - "cloud-region-id": { - "get_param": "chosen_region" - }, - "model-version-id": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b", - "orchestration-status": ["active"], - "model-invariant-id": "762472ef-5284-4daa-ab32-3e7bee2ec355", - "service_instance_id": { - "get_param": "service_id" - }, - "prov-status": "ACTIVE" - }, - "inventory_provider": "aai", - "service_resource_id": "vPGN-XX", - "unique": "false", - "inventory_type": "vfmodule", - "service_type": "vPGN-XX" - }] - }, - "optimization": { - "minimize": { - "sum": [] + }, + "passthrough_attributes": { + "td-role": "destination" + }, + "excluded_candidates": [ + { + "inventory_type": "vfmodule", + "candidate_id": [ + "e765d576-8755-4145-8536-0bb6d9b1dc9a" + ] } - }, - "homing_template_version": "2017-10-10" + ] + } + ], + "vPGN": [ + { + "inventory_provider": "aai", + "inventory_type": "vfmodule", + "service_type": "vPGN-XX", + "service_resource_id": "vPGN-XX", + "unique": "False", + "filtering_attributes": { + "global-customer-id": { + "get_param": "chosen_customer_id" + }, + "model-invariant-id": "762472ef-5284-4daa-ab32-3e7bee2ec355", + "model-version-id": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b", + "orchestration-status": [ + "active" + ], + "prov-status": "ACTIVE", + "cloud-region-id": { + "get_param": "chosen_region" + }, + "service_instance_id": { + "get_param": "service_id" + } + }, + "passthrough_attributes": { + "td-role": "anchor" + } + } + ] + }, + "constraints": { + "affinity_vFW_TD": { + "type": "zone", + "demands": [ + "vFW-SINK", + "vPGN" + ], + "properties": { + "category": "region", + "qualifier": "same" + } + } }, - "num_solution": "100", - "files": {}, - "timeout": 1200 -} + "optimization": { + "minimize": { + "sum": [] + } + } + } +}
\ No newline at end of file diff --git a/test/placement-tests/request_vfmod.json b/test/placement-tests/request_vfmod.json index 1e95e22..1a8a462 100644 --- a/test/placement-tests/request_vfmod.json +++ b/test/placement-tests/request_vfmod.json @@ -39,7 +39,6 @@ { "resourceModuleName": "vPGN", "serviceResourceId": "vPGN-XX", - "unique": "false", "resourceModelInfo": { "modelInvariantId": "762472ef-5284-4daa-ab32-3e7bee2ec355", "modelVersionId": "e02a7e5c-9d27-4360-ab7c-73bb83b07e3b" diff --git a/test/placement-tests/test_by_scope.yaml b/test/placement-tests/test_by_scope.yaml index 2cdd4e4..1968b89 100644 --- a/test/placement-tests/test_by_scope.yaml +++ b/test/placement-tests/test_by_scope.yaml @@ -2,23 +2,32 @@ references: service_name: source: request value: serviceInfo.serviceName + resource: + source: request + value: placementInfo.placementDemands.resourceModuleName subscriber_role: - source: SubscriberPolicy - value: content.properties.subscriberRole + source: onap.policies.optimization.SubscriberPolicy + value: properties.properties.subscriberRole policy_info: placement: policy_fetch: by_scope policy_scope: - default_scope: OSDF_R2 - vcpe_scope: OSDF_R2 - secondary_scopes: - - - - get_param: service_name - - SubscriberPolicy - - + - + scope: + - OSDF_FRANKFURT + - onap.policies.optimization.SubscriberPolicy + service: - get_param: service_name + - + scope: + - OSDF_FRANKFURT - get_param: subscriber_role + service: + - get_param: service_name + # - + # - get_param: service_name + # - get_param: subscriber_role default: # if no explicit service related information is needed policy_fetch: by_name - policy_scope: none + policy_scope: none
\ No newline at end of file diff --git a/test/placement-tests/test_by_scope_old_format.yaml b/test/placement-tests/test_by_scope_old_format.yaml new file mode 100644 index 0000000..2cdd4e4 --- /dev/null +++ b/test/placement-tests/test_by_scope_old_format.yaml @@ -0,0 +1,24 @@ +references: + service_name: + source: request + value: serviceInfo.serviceName + subscriber_role: + source: SubscriberPolicy + value: content.properties.subscriberRole + +policy_info: + placement: + policy_fetch: by_scope + policy_scope: + default_scope: OSDF_R2 + vcpe_scope: OSDF_R2 + secondary_scopes: + - + - get_param: service_name + - SubscriberPolicy + - + - get_param: service_name + - get_param: subscriber_role + default: # if no explicit service related information is needed + policy_fetch: by_name + policy_scope: none diff --git a/test/policy-local-files/Affinity_vCPE_1.json b/test/policy-local-files/Affinity_vCPE_1.json index 2953589..5364d5d 100644 --- a/test/policy-local-files/Affinity_vCPE_1.json +++ b/test/policy-local-files/Affinity_vCPE_1.json @@ -1,21 +1,33 @@ { - "service": "affinityPolicy", - "policyName": "OSDF_DUBLIN.Affinity_vCPE_1", - "description": "Zone policy for vCPE", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "identity": "affinity_vCPE", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], - "affinityProperty": { - "qualifier": "same", - "category": "complex" - }, - "policyType": "zone", - "resources": ["vGMuxInfra", "vG"] + "OSDF_FRANKFURT.Affinity_vCPE_1": { + "type": "onap.policies.optimization.resource.AffinityPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Affinity_vCPE_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG", + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "affinity_vCPE", + "applicableResources": "any", + "affinityProperties": { + "qualifier": "same", + "category": "complex" + } } + } } diff --git a/test/policy-local-files/Affinity_vFW_TD.json b/test/policy-local-files/Affinity_vFW_TD.json new file mode 100644 index 0000000..5df3bf4 --- /dev/null +++ b/test/policy-local-files/Affinity_vFW_TD.json @@ -0,0 +1,31 @@ +{ + "OSDF_FRANKFURT.Affinity_vFW_TD": { + "type": "onap.policies.optimization.resource.AffinityPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Affinity_vFW_TD", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "TD" + ], + "resources": [ + "vFW-SINK", + "vPGN" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "affinity_vFW_TD", + "applicableResources": "any", + "affinityProperties": { + "qualifier": "same", + "category": "region" + } + } + } +} diff --git a/test/policy-local-files/Attribute_vNS_1.json b/test/policy-local-files/Attribute_vNS_1.json new file mode 100644 index 0000000..97c967c --- /dev/null +++ b/test/policy-local-files/Attribute_vNS_1.json @@ -0,0 +1,51 @@ +{ + "OSDF_FRANKFURT.Attribute_vNS_1": { + "type": "onap.policies.optimization.resource.AttributePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Attribute_vNS_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vNS" + ], + "resources": [ + "vNS" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "attribute-vNS", + "attributeProperties": { + "cloudRegion": { + "serviceRequests": [ + "", + "" + ], + "cloudRequests": [ + "", + "" + ] + }, + "networkRoles": { + "all": [ + "", + "" + ] + }, + "complex": { + "any": [ + "", + "" + ] + } + } + } + } +}
\ No newline at end of file diff --git a/test/policy-local-files/Capacity_vGMuxInfra.json b/test/policy-local-files/Capacity_vGMuxInfra.json index 010cf3f..7ce02bf 100644 --- a/test/policy-local-files/Capacity_vGMuxInfra.json +++ b/test/policy-local-files/Capacity_vGMuxInfra.json @@ -1,22 +1,32 @@ { - "service": "vim_fit", - "policyName": "OSDF_DUBLIN.Capacity_vGMuxInfra", - "description": "Capacity policy for vGMuxInfra", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "5", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "identity": "capacity_vGMuxInfra", - "policyScope": ["VCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], - "resources": ["vGMuxInfra"], - "capacityProperty": { - "controller": "multicloud", - "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" - }, - "policyType": "vim_fit", - "applicableResources": "any" + "OSDF_FRANKFURT.Capacity_vGMuxInfra": { + "type": "onap.policies.optimization.resource.Vim_fit", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Capacity_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "capacity_vGMuxInfra", + "applicableResources": "any", + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + } } + } } diff --git a/test/policy-local-files/Capacity_vG_1.json b/test/policy-local-files/Capacity_vG_1.json index fedcc4f..c897a87 100644 --- a/test/policy-local-files/Capacity_vG_1.json +++ b/test/policy-local-files/Capacity_vG_1.json @@ -1,22 +1,32 @@ { - "service": "vim_fit", - "policyName": "OSDF_DUBLIN.Capacity_vG_1", - "description": "Capacity policy for vG", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "5", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "identity": "capacity_vG", - "policyScope": ["VCPE", "US", "INTERNATIONAL", "ip", "vG"], - "resources": ["vG"], - "capacityProperty": { - "controller": "multicloud", - "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" - }, - "policyType": "vim_fit", - "applicableResources": "any" + "OSDF_FRANKFURT.Capacity_vG_1": { + "type": "onap.policies.optimization.resource.Vim_fit", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Capacity_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "capacity_vG", + "applicableResources": "any", + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + } } + } } diff --git a/test/policy-local-files/Distance_vGMuxInfra_1.json b/test/policy-local-files/Distance_vGMuxInfra_1.json index e3ba83c..163d0df 100644 --- a/test/policy-local-files/Distance_vGMuxInfra_1.json +++ b/test/policy-local-files/Distance_vGMuxInfra_1.json @@ -1,22 +1,36 @@ { - "service": "distancePolicy", - "policyName": "OSDF_DUBLIN.Distance_vGMuxInfra", - "description": "Distance Policy for vGMuxInfra", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "distanceProperties": { - "locationInfo": "customer_loc", - "distance": { "value": "500", "operator": "<", "unit": "km" } + "OSDF_FRANKFURT.Distance_vGMuxInfra": { + "type": "onap.policies.optimization.resource.DistancePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Distance_vGMuxInfra", + "policy-version": 1 }, - "identity": "distance-vGMuxInfra", - "resources": ["vGMuxInfra"], - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], - "policyType": "distance_to_location", - "applicableResources": "any" + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "distance-vGMuxInfra", + "applicableResources": "any", + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { + "value": "500", + "operator": "<", + "unit": "km" + } + } + } } } diff --git a/test/policy-local-files/Distance_vG_1.json b/test/policy-local-files/Distance_vG_1.json index c498c7a..aa6badc 100644 --- a/test/policy-local-files/Distance_vG_1.json +++ b/test/policy-local-files/Distance_vG_1.json @@ -1,22 +1,36 @@ { - "service": "distancePolicy", - "policyName": "OSDF_DUBLIN.Distance_vG_1", - "description": "Distance Policy for vG", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "distanceProperties": { - "locationInfo": "customer_loc", - "distance": { "value": "1500", "operator": "<", "unit": "km" } + "OSDF_FRANKFURT.Distance_vG_1": { + "type": "onap.policies.optimization.resource.DistancePolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Distance_vG_1", + "policy-version": 1 }, - "identity": "distance-vG", - "resources": ["vG"], - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], - "policyType": "distance_to_location", - "applicableResources": "any" + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "distance-vG", + "applicableResources": "any", + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { + "value": "1500", + "operator": "<", + "unit": "km" + } + } + } } } diff --git a/test/policy-local-files/Placement_Optimization_1.json b/test/policy-local-files/Placement_Optimization_1.json index 9b062b0..62b024c 100644 --- a/test/policy-local-files/Placement_Optimization_1.json +++ b/test/policy-local-files/Placement_Optimization_1.json @@ -1,15 +1,30 @@ { - "service": "optimizationPolicy", - "policyName": "OSDF_DUBLIN.Placement_Optimization_1", - "description": "Placement Optimization Policy for vGMuxInfra", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "5", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "objectiveParameter": { + "OSDF_FRANKFURT.Placement_optimization_1": { + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.Placement_optimization_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG", + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "optimization", + "objective": "minimize", + "objectiveParameter": { "parameterAttributes": [ { "resources": ["vGMuxInfra"], @@ -46,10 +61,7 @@ } ], "operator": "sum" - }, - "identity": "optimization", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], - "policyType": "placement_optimization", - "objective": "minimize" + } } + } } diff --git a/test/policy-local-files/QueryPolicy_vCPE.json b/test/policy-local-files/QueryPolicy_vCPE.json index 5097964..3bcc67e 100644 --- a/test/policy-local-files/QueryPolicy_vCPE.json +++ b/test/policy-local-files/QueryPolicy_vCPE.json @@ -1,21 +1,38 @@ { - "service": "queryPolicy", - "policyName": "OSDF_DUBLIN.QueryPolicy_vCPE", - "description": "Query policy for vCPE", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "queryProperties": [ - {"attribute":"customerLatitude", "attribute_location": "customerLatitude"}, - {"attribute":"customerLongitude", "attribute_location": "customerLongitude"} - ], - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], - "policyType": "request_param_query", - "serviceName": "vCPE", - "identity": "vCPE_Query_Policy" + "OSDF_FRANKFURT.queryPolicy_vCPE": { + "type": "onap.policies.optimization.service.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.queryPolicy_vCPE", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra", + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vCPE_Query_Policy", + "queryProperties": [ + { + "attribute": "customerLatitude", + "attribute_location": "customerLatitude" + }, + { + "attribute": "customerLongitude", + "attribute_location": "customerLongitude" + } + ] + } } } diff --git a/test/policy-local-files/QueryPolicy_vCPE_2.json b/test/policy-local-files/QueryPolicy_vCPE_2.json index e398f39..3d9f4ca 100644 --- a/test/policy-local-files/QueryPolicy_vCPE_2.json +++ b/test/policy-local-files/QueryPolicy_vCPE_2.json @@ -1,24 +1,55 @@ { - "service": "queryPolicy", - "policyName": "OSDF_DUBLIN.queryPolicy_vCPE", - "description": "Query policy for vCPE", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "queryProperties": [ - {"attribute":"locationId", "attribute_location": "customerLocation", "value": ""}, - {"attribute":"id", "attribute_location": "vpnInfo.vpnId", "value": ""}, - {"attribute":"upstreamBW", "attribute_location": "vpnInfo.upstreamBW", "value": ""}, - {"attribute":"customerLatitude", "attribute_location": "customerLatitude", "value": 1.1}, - {"attribute":"customerLongitude", "attribute_location": "customerLongitude", "value": 2.2} - ], - "serviceName": "vCPE", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], - "policyType": "request_param_query", - "identity": "vCPE_Query_Policy" + "OSDF_FRANKFURT.queryPolicy_vCPE": { + "type": "onap.policies.optimization.service.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.queryPolicy_vCPE", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra", + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vCPE_Query_Policy", + "queryProperties": [ + { + "attribute": "locationId", + "attribute_location": "customerLocation", + "value": "" + }, + { + "attribute": "id", + "attribute_location": "vpnInfo.vpnId", + "value": "" + }, + { + "attribute": "upstreamBW", + "attribute_location": "vpnInfo.upstreamBW", + "value": "" + }, + { + "attribute": "customerLatitude", + "attribute_location": "customerLatitude", + "value": 1.1 + }, + { + "attribute": "customerLongitude", + "attribute_location": "customerLongitude", + "value": 2.2 + } + ] + } } } diff --git a/test/policy-local-files/QueryPolicy_vFW_TD.json b/test/policy-local-files/QueryPolicy_vFW_TD.json index 8866635..ab199ac 100644 --- a/test/policy-local-files/QueryPolicy_vFW_TD.json +++ b/test/policy-local-files/QueryPolicy_vFW_TD.json @@ -1,32 +1,47 @@ { - "service": "queryPolicy", - "policyName": "OSDF_DUBLIN.QueryPolicy_vFW_TD", - "description": "Query policy for vFW TD", - "templateVersion": "OpenSource.version.1", - "version": "oofDublin", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "queryProperties": [ - {"attribute":"customerLatitude", "attribute_location": "customerLatitude", "value": 1.1}, - {"attribute":"customerLongitude", "attribute_location": "customerLongitude", "value": 2.2}, - {"attribute":"chosen_region", "attribute_location": "chosenRegion"}, - {"attribute":"chosen_customer_id", "attribute_location": "chosenCustomerId"} - ], - "policyScope": [ - "td", - "us", - "vFW-SINK", - "vPGN" - ], - "policyType": "request_param_query", - "serviceName": "vFW_TD", - "identity": "vFW_TD_Query_Policy", - "resources": [ - "vFW-SINK", - "vPGN" - ] + "OSDF_FRANKFURT.queryPolicy_vFW_TD": { + "type": "onap.policies.optimization.service.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.queryPolicy_vFW_TD", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vFW_TD" + ], + "resources": [ + "vFW-SINK", + "vPGN" + ], + "geography": [ + "US" + ], + "identity": "vFW_TD_Query_Policy", + "queryProperties": [ + { + "attribute": "chosen_region", + "attribute_location": "chosenRegion" + }, + { + "attribute": "chosen_customer_id", + "attribute_location": "chosenCustomerId" + }, + { + "attribute": "customerLatitude", + "attribute_location": "customerLatitude", + "value": 1.1 + }, + { + "attribute": "customerLongitude", + "attribute_location": "customerLongitude", + "value": 2.2 + } + ] + } } } diff --git a/test/policy-local-files/aggregationPolicy_URLLC_1.json b/test/policy-local-files/aggregationPolicy_URLLC_1.json new file mode 100644 index 0000000..13014c1 --- /dev/null +++ b/test/policy-local-files/aggregationPolicy_URLLC_1.json @@ -0,0 +1,37 @@ +{ + "OSDF_FRANKFURT.Aggregation_URLLC_1":{ + "type":"onap.policies.optimization.resource.AggregationPolicy", + "version":"1.0.0", + "type_version":"1.0.0", + "metadata":{ + "policy-id":"OSDF_FRANKFURT.Aggregation_URLLC_1", + "policy-version":1 + }, + "properties":{ + "scope":[ + "OSDF_FRANKFURT", + "URLLC_1" + ], + "geography": [], + "resources":[ + "URLLC_Core_1" + ], + "services":[ + "URLLC_1" + ], + "identity":"Aggregation_URLLC_1", + "applicableResources":"any", + "aggregationProperties":[ + { + "attribute":"latency", + "function":"sum", + "operator":"lte", + "threshold":{ + "get_param": "latency" + }, + "unit":"ms" + } + ] + } + } +}
\ No newline at end of file diff --git a/test/policy-local-files/hpa_policy_vGMuxInfra_1.json b/test/policy-local-files/hpa_policy_vGMuxInfra_1.json index 690f5dc..b6317f8 100644 --- a/test/policy-local-files/hpa_policy_vGMuxInfra_1.json +++ b/test/policy-local-files/hpa_policy_vGMuxInfra_1.json @@ -1,18 +1,27 @@ { - "service": "hpaPolicy", - "policyName": "OSDF_DUBLIN.hpa_policy_vGMuxInfra_1", - "description": "HPA policy for vGMuxInfra", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "resources": ["vGMuxInfra"], + "OSDF_FRANKFURT.hpa_policy_vGMuxInfra_1": { + "type": "onap.policies.optimization.resource.HpaPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.hpa_policy_vGMuxInfra_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], "identity": "hpa-vGMuxInfra", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], - "policyType": "hpa", "flavorFeatures": [ { "id": "vgmux_1", @@ -28,8 +37,8 @@ ] } ], - "flavorProperties":[ - { + "flavorProperties": [ + { "hpa-feature" : "cpuTopology", "mandatory" : "True", "architecture": "generic", @@ -43,33 +52,64 @@ {"hpa-attribute-key":"numCpuThreads", "hpa-attribute-value": "8", "operator":"<=", "unit": ""} ] }, - { - "hpa-feature" : "basicCapabilities", - "mandatory" : "True", + { + "hpa-feature": "basicCapabilities", + "mandatory": true, "architecture": "generic", "directives": [], "hpa-feature-attributes": [ - {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, - {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + } ] }, { - "hpa-feature" : "ovsDpdk", - "mandatory" : "False", - "score" : "3", + "hpa-feature": "ovsDpdk", + "mandatory": false, + "score": 3, "architecture": "generic", "directives": [], "hpa-feature-attributes": [ - {"hpa-attribute-key":"dataProcessingAccelerationLibrary", "hpa-attribute-value":"ovsDpdk_version", "operator": "=", "unit":""} + { + "hpa-attribute-key": "dataProcessingAccelerationLibrary", + "hpa-attribute-value": "ovsDpdk_version", + "operator": [ + "=" + ], + "unit": "" + } ] }, { - "hpa-feature" : "cpuInstructionSetExtensions", - "mandatory" : "True", + "hpa-feature": "cpuInstructionSetExtensions", + "mandatory": true, "architecture": "INTEL-64", "directives": [], "hpa-feature-attributes": [ - {"hpa-attribute-key":"instructionSetExtensions", "hpa-attribute-value":["<CPUINST>", "<CPUINST>"], "operator": "ALL", "unit":""} + { + "hpa-attribute-key": "instructionSetExtensions", + "hpa-attribute-value": [ + "<CPUINST>", + "<CPUINST>" + ], + "operator": [ + "ALL" + ], + "unit": "" + } ] } ] @@ -99,7 +139,7 @@ {"hpa-attribute-key":"logicalCpuPinningPolicy", "hpa-attribute-value": "<CPUPOLICY>","operator": "=", "unit":""} ] }, - { + { "hpa-feature" : "basicCapabilities", "mandatory" : "True", "architecture": "generic", @@ -122,18 +162,17 @@ ] }, { - "hpa-feature" : "pcie", + "hpa-feature" : "pciePassthrough", "mandatory" : "True", "architecture": "generic", "directives": [], "hpa-feature-attributes": [ {"hpa-attribute-key": "pciCount", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, {"hpa-attribute-key": "pciVendorId", "hpa-attribute-value":"8086", "operator": "=", "unit": ""}, - {"hpa-attribute-key": "pciDeviceId", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, - {"hpa-attribute-key": "functionType", "hpa-attribute-value": "<PCITYPEVALUE>","operator": "=", "unit": ""} + {"hpa-attribute-key": "pciDeviceId", "hpa-attribute-value": "2", "operator": "=", "unit": ""} ] } - ] +] }, { "id": "vgmux_3", @@ -163,7 +202,7 @@ {"hpa-attribute-key": "numaCpu-1", "hpa-attribute-value":"4", "operator": "=", "unit": ""}, {"hpa-attribute-key": "numaMem-1", "value": "4096", "operator": "=", "unit": "MB"} ] - }, +}, { "hpa-feature" : "basicCapabilities", "mandatory" : "True", @@ -188,4 +227,5 @@ } ] } + } } diff --git a/test/policy-local-files/hpa_policy_vG_1.json b/test/policy-local-files/hpa_policy_vG_1.json index b29c67d..59ba4b1 100644 --- a/test/policy-local-files/hpa_policy_vG_1.json +++ b/test/policy-local-files/hpa_policy_vG_1.json @@ -1,18 +1,27 @@ { - "service": "hpaPolicy", - "policyName": "OSDF_DUBLIN.hpa_policy_vG_1", - "description": "HPA policy for vG", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "3", - "riskType": "test", - "riskLevel": "2", - "guard": "False", - "content": { - "resources": ["vG"], + "OSDF_FRANKFURT.hpa_policy_vG_1": { + "type": "onap.policies.optimization.resource.HpaPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.hpa_policy_vG_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], "identity": "hpa-vG", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], - "policyType": "hpa", "flavorFeatures": [ { "id": "vg_1", @@ -28,8 +37,8 @@ ] } ], - "flavorProperties":[ - { + "flavorProperties": [ + { "hpa-feature" : "cpuTopology", "mandatory" : "True", "architecture": "generic", @@ -43,33 +52,64 @@ {"hpa-attribute-key":"numCpuThreads", "hpa-attribute-value": "8", "operator":"<=", "unit": ""} ] }, - { - "hpa-feature" : "basicCapabilities", - "mandatory" : "True", + { + "hpa-feature": "basicCapabilities", + "mandatory": true, "architecture": "generic", "directives": [], "hpa-feature-attributes": [ - {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, - {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + { + "hpa-attribute-key": "numVirtualCpu", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + }, + { + "hpa-attribute-key": "virtualMemSize", + "hpa-attribute-value": 6, + "operator": [ + "=" + ], + "unit": "" + } ] }, { - "hpa-feature" : "ovsDpdk", - "mandatory" : "False", - "score" : "3", + "hpa-feature": "ovsDpdk", + "mandatory": false, + "score": 3, "architecture": "generic", "directives": [], "hpa-feature-attributes": [ - {"hpa-attribute-key":"dataProcessingAccelerationLibrary", "hpa-attribute-value":"ovsDpdk_version", "operator": "=", "unit":""} + { + "hpa-attribute-key": "dataProcessingAccelerationLibrary", + "hpa-attribute-value": "ovsDpdk_version", + "operator": [ + "=" + ], + "unit": "" + } ] }, { - "hpa-feature" : "cpuInstructionSetExtensions", - "mandatory" : "True", + "hpa-feature": "cpuInstructionSetExtensions", + "mandatory": true, "architecture": "INTEL-64", "directives": [], "hpa-feature-attributes": [ - {"hpa-attribute-key":"instructionSetExtensions", "hpa-attribute-value":["<CPUINST>", "<CPUINST>"], "operator": "ALL", "unit":""} + { + "hpa-attribute-key": "instructionSetExtensions", + "hpa-attribute-value": [ + "<CPUINST>", + "<CPUINST>" + ], + "operator": [ + "ALL" + ], + "unit": "" + } ] } ] @@ -99,7 +139,7 @@ {"hpa-attribute-key":"logicalCpuPinningPolicy", "hpa-attribute-value": "<CPUPOLICY>","operator": "=", "unit":""} ] }, - { + { "hpa-feature" : "basicCapabilities", "mandatory" : "True", "architecture": "generic", @@ -132,7 +172,7 @@ {"hpa-attribute-key": "pciDeviceId", "hpa-attribute-value": "2", "operator": "=", "unit": ""} ] } - ] +] }, { "id": "vg_3", @@ -162,7 +202,7 @@ {"hpa-attribute-key": "numaCpu-1", "hpa-attribute-value":"4", "operator": "=", "unit": ""}, {"hpa-attribute-key": "numaMem-1", "value": "4096", "operator": "=", "unit": "MB"} ] - }, +}, { "hpa-feature" : "basicCapabilities", "mandatory" : "True", @@ -187,4 +227,5 @@ } ] } + } } diff --git a/test/policy-local-files/meta-valid-policies-old.txt b/test/policy-local-files/meta-valid-policies-old.txt new file mode 100644 index 0000000..99e3e88 --- /dev/null +++ b/test/policy-local-files/meta-valid-policies-old.txt @@ -0,0 +1,16 @@ +Affinity_vCPE_1.json +Capacity_vGMuxInfra.json +Capacity_vG_1.json +Distance_vGMuxInfra_1.json +Distance_vG_1.json +Placement_Optimization_1.json +QueryPolicy_vCPE.json +QueryPolicy_vCPE_2.json +hpa_policy_vGMuxInfra_1.json +hpa_policy_vG_1.json +vnfPolicy_vG.json +vnfPolicy_vGMuxInfra.json +QueryPolicy_vFW_TD.json +vnfPolicy_vFW_TD.json +vnfPolicy_vPGN_TD.json +affinity_vFW_TD.json
\ No newline at end of file diff --git a/test/policy-local-files/meta-valid-policies.txt b/test/policy-local-files/meta-valid-policies.txt index 99e3e88..5f969bf 100644 --- a/test/policy-local-files/meta-valid-policies.txt +++ b/test/policy-local-files/meta-valid-policies.txt @@ -13,4 +13,5 @@ vnfPolicy_vGMuxInfra.json QueryPolicy_vFW_TD.json vnfPolicy_vFW_TD.json vnfPolicy_vPGN_TD.json -affinity_vFW_TD.json
\ No newline at end of file +Affinity_vFW_TD.json +Attribute_vNS_1.json
\ No newline at end of file diff --git a/test/policy-local-files/nst-selection-files/attribute_policy_nst.json b/test/policy-local-files/nst-selection-files/attribute_policy_nst.json new file mode 100644 index 0000000..0989927 --- /dev/null +++ b/test/policy-local-files/nst-selection-files/attribute_policy_nst.json @@ -0,0 +1,42 @@ +{ + "Threshold_nst": { + "metadata": { + "policy-id": "Threshold_nst", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "nst_Threshold", + "resources": [ + "nst" + ], + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "nst" + ], + "thresholdProperties": [ + { + "attribute": "latency", + "operator": "lte", + "threshold": { + "get_param": "latency" + }, + "unit": "ms" + }, + { + "attribute": "reliability", + "operator": "gte", + "threshold": { + "get_param": "reliability" + }, + "unit": "" + } + ] + }, + "type": "onap.policies.optimization.resource.ThresholdPolicy", + "type_version": "1.0.0", + "version": "1.0.0" + } +}
\ No newline at end of file diff --git a/test/policy-local-files/nst-selection-files/optimization_policy_nst.json b/test/policy-local-files/nst-selection-files/optimization_policy_nst.json new file mode 100644 index 0000000..27c1f7c --- /dev/null +++ b/test/policy-local-files/nst-selection-files/optimization_policy_nst.json @@ -0,0 +1,37 @@ +{ + "nst_minimize_latency": { + "metadata": { + "policy-id": "nst_minimize_latency", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "latency", + "demand": "nst" + } + } + ] + }, + "resources": [ + "nst" + ], + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "nst" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +}
\ No newline at end of file diff --git a/test/policy-local-files/nst-selection-files/query_policy_nst.json b/test/policy-local-files/nst-selection-files/query_policy_nst.json new file mode 100644 index 0000000..1955e7b --- /dev/null +++ b/test/policy-local-files/nst-selection-files/query_policy_nst.json @@ -0,0 +1,31 @@ +{ + "queryPolicy_nst": { + "type": "onap.policies.optimization.service.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "queryPolicy_nst", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "nst" + ], + "geography": [], + "identity": "queryPolicy_nst", + "queryProperties": [ + { + "attribute": "latency", + "attribute_location": "latency" + }, + { + "attribute": "reliability", + "attribute_location": "reliability" + } + ] + } + } +}
\ No newline at end of file diff --git a/test/policy-local-files/nst-selection-files/vnf_policy_nst.json b/test/policy-local-files/nst-selection-files/vnf_policy_nst.json new file mode 100644 index 0000000..14906df --- /dev/null +++ b/test/policy-local-files/nst-selection-files/vnf_policy_nst.json @@ -0,0 +1,34 @@ +{ + "vnfPolicy_nst": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "vnfPolicy_nst", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_GUILIN" + ], + "resources": [ + "nst" + ], + "services": [ + "nst" + ], + "identity": "vnf_nst", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "inventoryType": "nst", + "unique": "true", + "attributes": { + "model-role": "nst" + } + } + ] + } + } +}
\ No newline at end of file diff --git a/test/policy-local-files/old-policies/Affinity_vCPE_1.json b/test/policy-local-files/old-policies/Affinity_vCPE_1.json new file mode 100644 index 0000000..2953589 --- /dev/null +++ b/test/policy-local-files/old-policies/Affinity_vCPE_1.json @@ -0,0 +1,21 @@ +{ + "service": "affinityPolicy", + "policyName": "OSDF_DUBLIN.Affinity_vCPE_1", + "description": "Zone policy for vCPE", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "affinity_vCPE", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "affinityProperty": { + "qualifier": "same", + "category": "complex" + }, + "policyType": "zone", + "resources": ["vGMuxInfra", "vG"] + } +} diff --git a/test/policy-local-files/old-policies/Attribute_vNS_1.json b/test/policy-local-files/old-policies/Attribute_vNS_1.json new file mode 100644 index 0000000..e4766db --- /dev/null +++ b/test/policy-local-files/old-policies/Attribute_vNS_1.json @@ -0,0 +1,49 @@ +{ + "service": "attributePolicy", + "policyName": "OSDF_FRANKFURT.AttributePolicy_vNS_1", + "description": "Attribute Policy for Network Slicing (NS)", + "templateVersion": "OpenSource.version.1", + "version": "OpenSource.version.1", + "priority": "1", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "attribute-vNS", + "policyScope": [ + "vNS", + "us", + "international", + "ip" + ], + "policyType": "attribute", + "resources": [ + "vNS", + "" + ], + "attributeProperties": { + "cloudRegion": { + "serviceRequests": [ + "", + "" + ], + "cloudRequests": [ + "", + "" + ] + }, + "networkRoles": { + "all": [ + "", + "" + ] + }, + "complex": { + "any": [ + "", + "" + ] + } + } + } +}
\ No newline at end of file diff --git a/test/policy-local-files/Capacity_vFW_1.json b/test/policy-local-files/old-policies/Capacity_vFW_1.json index d5e80ab..d5e80ab 100644 --- a/test/policy-local-files/Capacity_vFW_1.json +++ b/test/policy-local-files/old-policies/Capacity_vFW_1.json diff --git a/test/policy-local-files/old-policies/Capacity_vGMuxInfra.json b/test/policy-local-files/old-policies/Capacity_vGMuxInfra.json new file mode 100644 index 0000000..010cf3f --- /dev/null +++ b/test/policy-local-files/old-policies/Capacity_vGMuxInfra.json @@ -0,0 +1,22 @@ +{ + "service": "vim_fit", + "policyName": "OSDF_DUBLIN.Capacity_vGMuxInfra", + "description": "Capacity policy for vGMuxInfra", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "capacity_vGMuxInfra", + "policyScope": ["VCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "resources": ["vGMuxInfra"], + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + }, + "policyType": "vim_fit", + "applicableResources": "any" + } +} diff --git a/test/policy-local-files/old-policies/Capacity_vG_1.json b/test/policy-local-files/old-policies/Capacity_vG_1.json new file mode 100644 index 0000000..fedcc4f --- /dev/null +++ b/test/policy-local-files/old-policies/Capacity_vG_1.json @@ -0,0 +1,22 @@ +{ + "service": "vim_fit", + "policyName": "OSDF_DUBLIN.Capacity_vG_1", + "description": "Capacity policy for vG", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "5", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "identity": "capacity_vG", + "policyScope": ["VCPE", "US", "INTERNATIONAL", "ip", "vG"], + "resources": ["vG"], + "capacityProperty": { + "controller": "multicloud", + "request": "{\"vCPU\": 10, \"Memory\": {\"quantity\": {\"get_param\": \"REQUIRED_MEM\"}, \"unit\": \"GB\"}, \"Storage\": {\"quantity\": {\"get_param\": \"REQUIRED_DISK\"}, \"unit\": \"GB\"}}" + }, + "policyType": "vim_fit", + "applicableResources": "any" + } +} diff --git a/test/policy-local-files/Distance_vFW_1.json b/test/policy-local-files/old-policies/Distance_vFW_1.json index 80e08d9..80e08d9 100644 --- a/test/policy-local-files/Distance_vFW_1.json +++ b/test/policy-local-files/old-policies/Distance_vFW_1.json diff --git a/test/policy-local-files/old-policies/Distance_vGMuxInfra_1.json b/test/policy-local-files/old-policies/Distance_vGMuxInfra_1.json new file mode 100644 index 0000000..e3ba83c --- /dev/null +++ b/test/policy-local-files/old-policies/Distance_vGMuxInfra_1.json @@ -0,0 +1,22 @@ +{ + "service": "distancePolicy", + "policyName": "OSDF_DUBLIN.Distance_vGMuxInfra", + "description": "Distance Policy for vGMuxInfra", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { "value": "500", "operator": "<", "unit": "km" } + }, + "identity": "distance-vGMuxInfra", + "resources": ["vGMuxInfra"], + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "policyType": "distance_to_location", + "applicableResources": "any" + } +} diff --git a/test/policy-local-files/old-policies/Distance_vG_1.json b/test/policy-local-files/old-policies/Distance_vG_1.json new file mode 100644 index 0000000..c498c7a --- /dev/null +++ b/test/policy-local-files/old-policies/Distance_vG_1.json @@ -0,0 +1,22 @@ +{ + "service": "distancePolicy", + "policyName": "OSDF_DUBLIN.Distance_vG_1", + "description": "Distance Policy for vG", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "distanceProperties": { + "locationInfo": "customer_loc", + "distance": { "value": "1500", "operator": "<", "unit": "km" } + }, + "identity": "distance-vG", + "resources": ["vG"], + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], + "policyType": "distance_to_location", + "applicableResources": "any" + } +} diff --git a/test/policy-local-files/old-policies/Placement_Optimization_1.json b/test/policy-local-files/old-policies/Placement_Optimization_1.json new file mode 100644 index 0000000..9b062b0 --- /dev/null +++ b/test/policy-local-files/old-policies/Placement_Optimization_1.json @@ -0,0 +1,55 @@ +{ + "service": "optimizationPolicy", + "policyName": "OSDF_DUBLIN.Placement_Optimization_1", + "description": "Placement Optimization Policy for vGMuxInfra", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "5", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "objectiveParameter": { + "parameterAttributes": [ + { + "resources": ["vGMuxInfra"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resources": ["vG"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "1", + "operator": "product" + }, + { + "resources": ["vG"], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" + }, + { + "resources": ["vFW"], + "customerLocationInfo": "customer_loc", + "parameter": "distance", + "weight": "100", + "operator": "product" + }, + { + "resources": ["vFW"], + "parameter": "hpa_score", + "weight": "200", + "operator": "product" + } + ], + "operator": "sum" + }, + "identity": "optimization", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "policyType": "placement_optimization", + "objective": "minimize" + } +} diff --git a/test/policy-local-files/old-policies/QueryPolicy_vCPE.json b/test/policy-local-files/old-policies/QueryPolicy_vCPE.json new file mode 100644 index 0000000..5097964 --- /dev/null +++ b/test/policy-local-files/old-policies/QueryPolicy_vCPE.json @@ -0,0 +1,21 @@ +{ + "service": "queryPolicy", + "policyName": "OSDF_DUBLIN.QueryPolicy_vCPE", + "description": "Query policy for vCPE", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "queryProperties": [ + {"attribute":"customerLatitude", "attribute_location": "customerLatitude"}, + {"attribute":"customerLongitude", "attribute_location": "customerLongitude"} + ], + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "policyType": "request_param_query", + "serviceName": "vCPE", + "identity": "vCPE_Query_Policy" + } +} diff --git a/test/policy-local-files/old-policies/QueryPolicy_vCPE_2.json b/test/policy-local-files/old-policies/QueryPolicy_vCPE_2.json new file mode 100644 index 0000000..e398f39 --- /dev/null +++ b/test/policy-local-files/old-policies/QueryPolicy_vCPE_2.json @@ -0,0 +1,24 @@ +{ + "service": "queryPolicy", + "policyName": "OSDF_DUBLIN.queryPolicy_vCPE", + "description": "Query policy for vCPE", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "queryProperties": [ + {"attribute":"locationId", "attribute_location": "customerLocation", "value": ""}, + {"attribute":"id", "attribute_location": "vpnInfo.vpnId", "value": ""}, + {"attribute":"upstreamBW", "attribute_location": "vpnInfo.upstreamBW", "value": ""}, + {"attribute":"customerLatitude", "attribute_location": "customerLatitude", "value": 1.1}, + {"attribute":"customerLongitude", "attribute_location": "customerLongitude", "value": 2.2} + ], + "serviceName": "vCPE", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra", "vG"], + "policyType": "request_param_query", + "identity": "vCPE_Query_Policy" + } +} diff --git a/test/policy-local-files/QueryPolicy_vFW.json b/test/policy-local-files/old-policies/QueryPolicy_vFW.json index 932074b..932074b 100644 --- a/test/policy-local-files/QueryPolicy_vFW.json +++ b/test/policy-local-files/old-policies/QueryPolicy_vFW.json diff --git a/test/policy-local-files/old-policies/QueryPolicy_vFW_TD.json b/test/policy-local-files/old-policies/QueryPolicy_vFW_TD.json new file mode 100644 index 0000000..8866635 --- /dev/null +++ b/test/policy-local-files/old-policies/QueryPolicy_vFW_TD.json @@ -0,0 +1,32 @@ +{ + "service": "queryPolicy", + "policyName": "OSDF_DUBLIN.QueryPolicy_vFW_TD", + "description": "Query policy for vFW TD", + "templateVersion": "OpenSource.version.1", + "version": "oofDublin", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "queryProperties": [ + {"attribute":"customerLatitude", "attribute_location": "customerLatitude", "value": 1.1}, + {"attribute":"customerLongitude", "attribute_location": "customerLongitude", "value": 2.2}, + {"attribute":"chosen_region", "attribute_location": "chosenRegion"}, + {"attribute":"chosen_customer_id", "attribute_location": "chosenCustomerId"} + ], + "policyScope": [ + "td", + "us", + "vFW-SINK", + "vPGN" + ], + "policyType": "request_param_query", + "serviceName": "vFW_TD", + "identity": "vFW_TD_Query_Policy", + "resources": [ + "vFW-SINK", + "vPGN" + ] + } +} diff --git a/test/policy-local-files/affinity_vFW_TD.json b/test/policy-local-files/old-policies/affinity_vFW_TD.json index 2df2d50..2df2d50 100644 --- a/test/policy-local-files/affinity_vFW_TD.json +++ b/test/policy-local-files/old-policies/affinity_vFW_TD.json diff --git a/test/policy-local-files/hpa_policy_vFW_1.json b/test/policy-local-files/old-policies/hpa_policy_vFW_1.json index a60ad47..a60ad47 100644 --- a/test/policy-local-files/hpa_policy_vFW_1.json +++ b/test/policy-local-files/old-policies/hpa_policy_vFW_1.json diff --git a/test/policy-local-files/old-policies/hpa_policy_vGMuxInfra_1.json b/test/policy-local-files/old-policies/hpa_policy_vGMuxInfra_1.json new file mode 100644 index 0000000..690f5dc --- /dev/null +++ b/test/policy-local-files/old-policies/hpa_policy_vGMuxInfra_1.json @@ -0,0 +1,191 @@ +{ + "service": "hpaPolicy", + "policyName": "OSDF_DUBLIN.hpa_policy_vGMuxInfra_1", + "description": "HPA policy for vGMuxInfra", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "resources": ["vGMuxInfra"], + "identity": "hpa-vGMuxInfra", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "policyType": "hpa", + "flavorFeatures": [ + { + "id": "vgmux_1", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_01", + "attribute_value": "" + } + ] + } + ], + "flavorProperties":[ + { + "hpa-feature" : "cpuTopology", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"numCpuSockets", "hpa-attribute-value": "2","operator": ">=", "unit": ""}, + {"hpa-attribute-key":"numCpuSockets", "hpa-attribute-value": "4","operator": "<=", "unit": ""}, + {"hpa-attribute-key":"numCpuCores", "hpa-attribute-value": "2", "operator":">=", "unit": ""}, + {"hpa-attribute-key":"numCpuCores", "hpa-attribute-value": "4", "operator":"<=", "unit": ""}, + {"hpa-attribute-key":"numCpuThreads", "hpa-attribute-value": "4", "operator":">=", "unit": ""}, + {"hpa-attribute-key":"numCpuThreads", "hpa-attribute-value": "8", "operator":"<=", "unit": ""} + ] + }, + { + "hpa-feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa-feature" : "ovsDpdk", + "mandatory" : "False", + "score" : "3", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"dataProcessingAccelerationLibrary", "hpa-attribute-value":"ovsDpdk_version", "operator": "=", "unit":""} + ] + }, + { + "hpa-feature" : "cpuInstructionSetExtensions", + "mandatory" : "True", + "architecture": "INTEL-64", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"instructionSetExtensions", "hpa-attribute-value":["<CPUINST>", "<CPUINST>"], "operator": "ALL", "unit":""} + ] + } + ] + }, + { + "id": "vgmux_2", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_02", + "attribute_value": "" + } + ] + } + ], + "flavorProperties":[ + { + "hpa-feature" : "cpuPinningy", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"logicalCpuThreadPinningPolicy", "hpa-attribute-value":"<CPUTHREADPOLICY>", "operator": "=", "unit":""}, + {"hpa-attribute-key":"logicalCpuPinningPolicy", "hpa-attribute-value": "<CPUPOLICY>","operator": "=", "unit":""} + ] + }, + { + "hpa-feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa-feature" : "localStorage", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "diskSize", "hpa-attribute-value": "2", "operator": "=", "unit": "GB"}, + {"hpa-attribute-key": "ephemeralDiskSize", "hpa-attribute-value": "2", "operator": "=", "unit": "GB"}, + {"hpa-attribute-key": "swapMemSize", "hpa-attribute-value":"16", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa-feature" : "pcie", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "pciCount", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "pciVendorId", "hpa-attribute-value":"8086", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "pciDeviceId", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "functionType", "hpa-attribute-value": "<PCITYPEVALUE>","operator": "=", "unit": ""} + ] + } + ] + }, + { + "id": "vgmux_3", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_03", + "attribute_value": "" + } + ] + } + ], + "flavorProperties":[ + { + "hpa-feature" : "numa", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numaNodes", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "numaCpu-0", "hpa-attribute-value":"2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "numaMem-0", "hpa-attribute-value": "2048", "operator": "=", "unit": "MB"}, + {"hpa-attribute-key": "numaCpu-1", "hpa-attribute-value":"4", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "numaMem-1", "value": "4096", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa-feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa-feature" : "hugePages", + "mandatory" : "False", + "score" : "7", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "memoryPageSize", "hpa-attribute-value": "<MEMORYPAGESIZE>", "operator": "=", "unit": ""} + ] + } + ] + } + ] + } +} diff --git a/test/policy-local-files/old-policies/hpa_policy_vG_1.json b/test/policy-local-files/old-policies/hpa_policy_vG_1.json new file mode 100644 index 0000000..b29c67d --- /dev/null +++ b/test/policy-local-files/old-policies/hpa_policy_vG_1.json @@ -0,0 +1,190 @@ +{ + "service": "hpaPolicy", + "policyName": "OSDF_DUBLIN.hpa_policy_vG_1", + "description": "HPA policy for vG", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "3", + "riskType": "test", + "riskLevel": "2", + "guard": "False", + "content": { + "resources": ["vG"], + "identity": "hpa-vG", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], + "policyType": "hpa", + "flavorFeatures": [ + { + "id": "vg_1", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_01", + "attribute_value": "" + } + ] + } + ], + "flavorProperties":[ + { + "hpa-feature" : "cpuTopology", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"numCpuSockets", "hpa-attribute-value": "2","operator": ">=", "unit": ""}, + {"hpa-attribute-key":"numCpuSockets", "hpa-attribute-value": "4","operator": "<=", "unit": ""}, + {"hpa-attribute-key":"numCpuCores", "hpa-attribute-value": "2", "operator":">=", "unit": ""}, + {"hpa-attribute-key":"numCpuCores", "hpa-attribute-value": "4", "operator":"<=", "unit": ""}, + {"hpa-attribute-key":"numCpuThreads", "hpa-attribute-value": "4", "operator":">=", "unit": ""}, + {"hpa-attribute-key":"numCpuThreads", "hpa-attribute-value": "8", "operator":"<=", "unit": ""} + ] + }, + { + "hpa-feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa-feature" : "ovsDpdk", + "mandatory" : "False", + "score" : "3", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"dataProcessingAccelerationLibrary", "hpa-attribute-value":"ovsDpdk_version", "operator": "=", "unit":""} + ] + }, + { + "hpa-feature" : "cpuInstructionSetExtensions", + "mandatory" : "True", + "architecture": "INTEL-64", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"instructionSetExtensions", "hpa-attribute-value":["<CPUINST>", "<CPUINST>"], "operator": "ALL", "unit":""} + ] + } + ] + }, + { + "id": "vg_2", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_02", + "attribute_value": "" + } + ] + } + ], + "flavorProperties":[ + { + "hpa-feature" : "cpuPinningy", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key":"logicalCpuThreadPinningPolicy", "hpa-attribute-value":"<CPUTHREADPOLICY>", "operator": "=", "unit":""}, + {"hpa-attribute-key":"logicalCpuPinningPolicy", "hpa-attribute-value": "<CPUPOLICY>","operator": "=", "unit":""} + ] + }, + { + "hpa-feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa-feature" : "localStorage", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "diskSize", "hpa-attribute-value": "2", "operator": "=", "unit": "GB"}, + {"hpa-attribute-key": "ephemeralDiskSize", "hpa-attribute-value": "2", "operator": "=", "unit": "GB"}, + {"hpa-attribute-key": "swapMemSize", "hpa-attribute-value":"16", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa-feature" : "pciePassthrough", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "pciCount", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "pciVendorId", "hpa-attribute-value":"8086", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "pciDeviceId", "hpa-attribute-value": "2", "operator": "=", "unit": ""} + ] + } + ] + }, + { + "id": "vg_3", + "type": "vnfc", + "directives": [ + { + "type": "flavor_directives", + "attributes": [ + { + "attribute_name": "flavor_label_vm_03", + "attribute_value": "" + } + ] + } + ], + "flavorProperties":[ + { + "hpa-feature" : "numa", + "mandatory" : "False", + "score" : "5", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numaNodes", "hpa-attribute-value": "2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "numaCpu-0", "hpa-attribute-value":"2", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "numaMem-0", "hpa-attribute-value": "2048", "operator": "=", "unit": "MB"}, + {"hpa-attribute-key": "numaCpu-1", "hpa-attribute-value":"4", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "numaMem-1", "value": "4096", "operator": "=", "unit": "MB"} + ] + }, + { + "hpa-feature" : "basicCapabilities", + "mandatory" : "True", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "numVirtualCpu", "hpa-attribute-value": "6", "operator": "=", "unit": ""}, + {"hpa-attribute-key": "virtualMemSize", "hpa-attribute-value":"6", "operator": "=", "unit": "GB"} + ] + }, + { + "hpa-feature" : "hugePages", + "mandatory" : "False", + "score" : "7", + "architecture": "generic", + "directives": [], + "hpa-feature-attributes": [ + {"hpa-attribute-key": "memoryPageSize", "hpa-attribute-value": "<MEMORYPAGESIZE>", "operator": "=", "unit": ""} + ] + } + ] + } + ] + } +} diff --git a/test/policy-local-files/old-policies/subscriber_policy_vCPE.json b/test/policy-local-files/old-policies/subscriber_policy_vCPE.json new file mode 100644 index 0000000..9359c20 --- /dev/null +++ b/test/policy-local-files/old-policies/subscriber_policy_vCPE.json @@ -0,0 +1,22 @@ +{ + "service": "subscriberPolicy", + "policyName": "OSDF_DUBLIN.SubscriberPolicy_v1", + "description": "Subscriber Policy for vCPE", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "1", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "subscriber_vCPE", + "policyScope": ["vCPE", "subscriber_x", "subscriber_y", "subscriberPolicy"], + "properties": { + "subscriberName": ["subscriber_x", "subscriber_y"], + "subscriberRole": ["PVT Homing"], + "provStatus": ["CAPPED"] + }, + "policyType": "subscriberPolicy", + "serviceName": "vCPE" + } +} diff --git a/test/policy-local-files/subscriber_policy_vFW.json b/test/policy-local-files/old-policies/subscriber_policy_vFW.json index 299da04..299da04 100644 --- a/test/policy-local-files/subscriber_policy_vFW.json +++ b/test/policy-local-files/old-policies/subscriber_policy_vFW.json diff --git a/test/policy-local-files/vnfPolicy_vFW.json b/test/policy-local-files/old-policies/vnfPolicy_vFW.json index 0b7d9a2..0b7d9a2 100644 --- a/test/policy-local-files/vnfPolicy_vFW.json +++ b/test/policy-local-files/old-policies/vnfPolicy_vFW.json diff --git a/test/policy-local-files/old-policies/vnfPolicy_vFW_TD.json b/test/policy-local-files/old-policies/vnfPolicy_vFW_TD.json new file mode 100644 index 0000000..a471a77 --- /dev/null +++ b/test/policy-local-files/old-policies/vnfPolicy_vFW_TD.json @@ -0,0 +1,44 @@ +{ + "service": "vnfPolicy", + "policyName": "OSDF_DUBLIN.vnfPolicy_vFW_TD", + "description": "vnfPolicy", + "templateVersion": "OpenSource.version.1", + "version": "oofDublin", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vnf_vFW_TD", + "policyScope": [ + "td", + "us", + "vFW-SINK" + ], + "policyType": "vnfPolicy", + "resources": ["vFW-SINK"], + "applicableResources": "any", + "vnfProperties": [{ + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "vfmodule", + "customerId": { + "get_param": "chosen_customer_id" + }, + "equipmentRole": "", + "attributes": { + "orchestrationStatus": ["active"], + "provStatus": "ACTIVE", + "cloudRegionId": { + "get_param": "chosen_region" + }, + "service_instance_id": { + "get_param": "service_id" + } + }, + "passthroughAttributes": { + "td-role": "destination" + } + }] + } +} diff --git a/test/policy-local-files/old-policies/vnfPolicy_vG.json b/test/policy-local-files/old-policies/vnfPolicy_vG.json new file mode 100644 index 0000000..b047686 --- /dev/null +++ b/test/policy-local-files/old-policies/vnfPolicy_vG.json @@ -0,0 +1,29 @@ +{ + "service": "vnfPolicy", + "policyName": "OSDF_DUBLIN.vnfPolicy_vG", + "description": "vnfPolicy", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vnf_vG", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], + "policyType": "vnfPolicy", + "resources": ["vG"], + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "", + "orchestrationStatus": "", + "equipmentRole": "" + + } + ] + } +} diff --git a/test/policy-local-files/old-policies/vnfPolicy_vGMuxInfra.json b/test/policy-local-files/old-policies/vnfPolicy_vGMuxInfra.json new file mode 100644 index 0000000..45d67f6 --- /dev/null +++ b/test/policy-local-files/old-policies/vnfPolicy_vGMuxInfra.json @@ -0,0 +1,28 @@ +{ + "service": "vnfPolicy", + "policyName": "OSDF_DUBLIN.vnfPolicy_vGMuxInfra", + "description": "vnfPolicy", + "templateVersion": "OpenSource.version.1", + "version": "test1", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vnf_vGMuxInfra", + "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], + "policyType": "vnfPolicy", + "resources": ["vGMuxInfra"], + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "vGMuxInfra-xx", + "inventoryType": "service", + "customerId": "SDN-ETHERNET-INTERNET", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] + } +} diff --git a/test/policy-local-files/old-policies/vnfPolicy_vPGN_TD.json b/test/policy-local-files/old-policies/vnfPolicy_vPGN_TD.json new file mode 100644 index 0000000..2e79f2f --- /dev/null +++ b/test/policy-local-files/old-policies/vnfPolicy_vPGN_TD.json @@ -0,0 +1,51 @@ +{ + "service": "vnfPolicy", + "policyName": "OSDF_DUBLIN.vnfPolicy_vPGN_TD", + "description": "vnfPolicy", + "templateVersion": "OpenSource.version.1", + "version": "oofDublin", + "priority": "6", + "riskType": "test", + "riskLevel": "3", + "guard": "False", + "content": { + "identity": "vnf_vPGN_TD", + "policyScope": [ + "td", + "us", + "vPGN" + ], + "policyType": "vnfPolicy", + "resources": [ + "vPGN" + ], + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "vfmodule", + "customerId": { + "get_param": "chosen_customer_id" + }, + "equipmentRole": "", + "unique": "False", + "attributes": { + "orchestrationStatus": [ + "active" + ], + "provStatus": "ACTIVE", + "cloudRegionId": { + "get_param": "chosen_region" + }, + "service_instance_id": { + "get_param": "service_id" + } + }, + "passthroughAttributes": { + "td-role": "anchor" + } + } + ] + } +}
\ No newline at end of file diff --git a/test/policy-local-files/queryPolicy_URLLC_.json b/test/policy-local-files/queryPolicy_URLLC_.json new file mode 100644 index 0000000..3114234 --- /dev/null +++ b/test/policy-local-files/queryPolicy_URLLC_.json @@ -0,0 +1,29 @@ +{ + "OSDF_FRANKFURT.queryPolicy_URLLC_1": { + "type": "onap.policies.optimization.service.QueryPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.queryPolicy_URLLC_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "URLLC_1" + ], + "geography": [ + "US" + ], + "identity": "queryPolicy_URLLC_1", + "queryProperties": [ + { + "attribute": "latency", + "attribute_location": "latency" + } + ] + } + } +} diff --git a/test/policy-local-files/slice-selection-files/opt_policy_nsi_reuse.json b/test/policy-local-files/slice-selection-files/opt_policy_nsi_reuse.json new file mode 100644 index 0000000..fa02d4e --- /dev/null +++ b/test/policy-local-files/slice-selection-files/opt_policy_nsi_reuse.json @@ -0,0 +1,37 @@ +{ + "OSDF_FRANKFURT.minimizeCost_URLLC": { + "metadata": { + "policy-id": "OSDF_FRANKFURT.minimizeCost_URLLC", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "creation_cost", + "demand": "embb-nst" + } + } + ] + }, + "resources": [ + "embb-nst" + ], + "scope": [ + "shared,reuse" + ], + "services": [ + "embb-nst" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} diff --git a/test/policy-local-files/slice-selection-files/opt_policy_nssi.json b/test/policy-local-files/slice-selection-files/opt_policy_nssi.json new file mode 100644 index 0000000..196add0 --- /dev/null +++ b/test/policy-local-files/slice-selection-files/opt_policy_nssi.json @@ -0,0 +1,37 @@ +{ + "OSDF_FRANKFURT.minimizeCost_URLLC": { + "metadata": { + "policy-id": "OSDF_FRANKFURT.minimizeCost_URLLC", + "policy-version": 1 + }, + "properties": { + "geography": [], + "identity": "optimization", + "goal": "minimize", + "operation_function": { + "operator": "sum", + "operands": [ + { + "function": "attribute", + "params": { + "attribute": "latency", + "demand": "embb-cn" + } + } + ] + }, + "resources": [ + "embb-cn" + ], + "scope": [ + "OSDF_GUILIN" + ], + "services": [ + "embb-cn" + ] + }, + "type": "onap.policies.optimization.resource.OptimizationPolicy", + "type_version": "2.0.0", + "version": "1.0.0" + } +} diff --git a/test/policy-local-files/slice-selection-files/query_policy_nsi.json b/test/policy-local-files/slice-selection-files/query_policy_nsi.json new file mode 100644 index 0000000..c370ccd --- /dev/null +++ b/test/policy-local-files/slice-selection-files/query_policy_nsi.json @@ -0,0 +1,54 @@ + + { + "OSDF_GUILIN.queryPolicy_URLLC":{ + "type":"onap.policies.optimization.service.QueryPolicy", + "version":"1.0.0", + "type_version":"1.0.0", + "metadata":{ + "policy-id":"OSDF_GUILIN.queryPolicy_URLLC", + "policy-version":1 + }, + "properties":{ + "scope":[ + "shared,reuse", + "shared,create_new", + "not-shared" + ], + "services":[ + "embb-nst" + ], + "geography":[], + "identity":"queryPolicy_URLLC", + "queryProperties":[ + { + "attribute":"latency", + "attribute_location":"latency" + }, + { + "attribute":"reliability", + "attribute_location":"reliability" + }, + { + "attribute":"an_latency", + "attribute_location":"an_latency" + }, + { + "attribute":"cn_latency", + "attribute_location":"cn_latency" + }, + { + "attribute":"tn_bh_latency", + "attribute_location":"tn_bh_latency" + }, + { + "attribute":"model_invariant_id", + "attribute_location":"invariantUUID" + }, + { + "attribute":"model_version_id", + "attribute_location":"UUID" + } + ] + } + } + } diff --git a/test/policy-local-files/slice-selection-files/query_policy_nssi.json b/test/policy-local-files/slice-selection-files/query_policy_nssi.json new file mode 100644 index 0000000..c1e941f --- /dev/null +++ b/test/policy-local-files/slice-selection-files/query_policy_nssi.json @@ -0,0 +1,40 @@ + + { + "OSDF_GUILIN.queryPolicy_URLLC":{ + "type":"onap.policies.optimization.service.QueryPolicy", + "version":"1.0.0", + "type_version":"1.0.0", + "metadata":{ + "policy-id":"OSDF_GUILIN.queryPolicy_URLLC", + "policy-version":1 + }, + "properties":{ + "scope":[ + "OSDF_GUILIN" + ], + "services":[ + "embb-cn" + ], + "geography":[], + "identity":"queryPolicy_URLLC", + "queryProperties":[ + { + "attribute":"latency", + "attribute_location":"latency" + }, + { + "attribute":"reliability", + "attribute_location":"reliability" + }, + { + "attribute":"model_invariant_id", + "attribute_location":"invariantUUID" + }, + { + "attribute":"model_version_id", + "attribute_location":"UUID" + } + ] + } + } + } diff --git a/test/policy-local-files/slice-selection-files/threshold_policy_nsi.json b/test/policy-local-files/slice-selection-files/threshold_policy_nsi.json new file mode 100644 index 0000000..8769641 --- /dev/null +++ b/test/policy-local-files/slice-selection-files/threshold_policy_nsi.json @@ -0,0 +1,48 @@ + + { + "OSDF_GUILIN.Threshold_URLLC":{ + "metadata":{ + "policy-id":"OSDF_GUILIN.Threshold_URLLC", + "policy-version":1 + }, + "properties":{ + "geography":[ + + ], + "identity":"Threshold_URLLC", + "resources":[ + "embb-nst" + ], + "scope":[ + "shared,reuse", + "shared,create_new", + "not-shared" + ], + "services":[ + "embb-nst" + ], + "thresholdProperties":[ + { + "attribute":"latency", + "operator":"lte", + "threshold":{ + "get_param":"latency" + }, + "unit":"ms" + }, + { + "attribute":"reliability", + "operator":"gte", + "threshold":{ + "get_param":"reliability" + }, + "unit":"" + } + ] + }, + "type":"onap.policies.optimization.resource.ThresholdPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } + } + diff --git a/test/policy-local-files/slice-selection-files/threshold_policy_nssi.json b/test/policy-local-files/slice-selection-files/threshold_policy_nssi.json new file mode 100644 index 0000000..bf8690f --- /dev/null +++ b/test/policy-local-files/slice-selection-files/threshold_policy_nssi.json @@ -0,0 +1,46 @@ + + { + "OSDF_GUILIN.Threshold_URLLC":{ + "metadata":{ + "policy-id":"OSDF_GUILIN.Threshold_URLLC", + "policy-version":1 + }, + "properties":{ + "geography":[ + + ], + "identity":"Threshold_URLLC", + "resources":[ + "embb-cn" + ], + "scope":[ + "OSDF_GUILIN" + ], + "services":[ + "embb-cn" + ], + "thresholdProperties":[ + { + "attribute":"latency", + "operator":"lte", + "threshold":{ + "get_param":"latency" + }, + "unit":"ms" + }, + { + "attribute":"reliability", + "operator":"gte", + "threshold":{ + "get_param":"reliability" + }, + "unit":"" + } + ] + }, + "type":"onap.policies.optimization.resource.ThresholdPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } + } + diff --git a/test/policy-local-files/slice-selection-files/vnf_policy_nsi_non_shared_case.json b/test/policy-local-files/slice-selection-files/vnf_policy_nsi_non_shared_case.json new file mode 100644 index 0000000..09aba6e --- /dev/null +++ b/test/policy-local-files/slice-selection-files/vnf_policy_nsi_non_shared_case.json @@ -0,0 +1,81 @@ + + { + "OSDF_GUILIN.vnfPolicy_URLLC":{ + "metadata":{ + "policy-id":"OSDF_GUILIN.vnfPolicy_URLLC", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_URLLC", + "resources":["embb-nst"], + "scope":[ + "not-shared" + ], + "services":[ + "embb-nst" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "service_profile": { + "latency": {"value": {"get_param": "latency"}, "operator": "lte"}, + "reliability": {"value": {"get_param": "reliability"}, "operator": "gte"} + }, + "subnets": { + "core":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"cn_latency"}, + "steps":1 + }, + "reliability":{ + "values":[ + 99.9, + 99.999 + ] + } + }, + "ran":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"an_latency"}, + "steps":1 + }, + "reliability":{ + "values":[ + 99.9, + 99.9 + ] + } + }, + "transport":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"tn_bh_latency"}, + "steps":1 + }, + "reliability":{ + "values":[ + 99.9, + 99.99 + ] + } + } + } + }, + "inventoryProvider":"generator", + "inventoryType":"slice_profiles", + "unique":"true", + "defaultAttributes":{ + "creation-cost" : 0.9 + } + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } + } + diff --git a/test/policy-local-files/slice-selection-files/vnf_policy_nsi_shared_case.json b/test/policy-local-files/slice-selection-files/vnf_policy_nsi_shared_case.json new file mode 100644 index 0000000..0446748 --- /dev/null +++ b/test/policy-local-files/slice-selection-files/vnf_policy_nsi_shared_case.json @@ -0,0 +1,96 @@ + + { + "OSDF_GUILIN.vnfPolicy_URLLC":{ + "metadata":{ + "policy-id":"OSDF_GUILIN.vnfPolicy_URLLC", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_URLLC", + "resources":["embb-nst"], + "scope":[ + "shared,reuse", + "shared,create_new" + ], + "services":[ + "embb-nst" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "modelInvariantId":{"get_param":"model_invariant_id"}, + "modelVersionId":{"get_param":"model_version_id"}, + "environment-context":"shared", + "service-role":"nsi" + }, + "inventoryProvider":"aai", + "inventoryType":"nsi", + "unique":"true", + "defaultAttributes":{ + "creation-cost" : 0.1 + } + }, + { + "attributes":{ + "service_profile": { + "latency": {"value": {"get_param": "latency"}, "operator": "lte"}, + "reliability": {"value": {"get_param": "reliability"}, "operator": "gte"} + }, + "subnets": { + "core":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"cn_latency"}, + "steps":1 + }, + "reliability":{ + "values":[ + 99.9, + 99.999 + ] + } + }, + "ran":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"an_latency"}, + "steps":1 + }, + "reliability":{ + "values":[ + 99.9, + 99.9 + ] + } + }, + "transport":{ + "latency":{ + "max":{"get_param":"latency"}, + "min":{"get_param":"tn_bh_latency"}, + "steps":1 + }, + "reliability":{ + "values":[ + 99.9, + 99.99 + ] + } + } + } + }, + "inventoryProvider":"generator", + "inventoryType":"slice_profiles", + "unique":"true", + "defaultAttributes":{ + "creation-cost" : 0.9 + } + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } + } + diff --git a/test/policy-local-files/slice-selection-files/vnf_policy_nssi_shared.json b/test/policy-local-files/slice-selection-files/vnf_policy_nssi_shared.json new file mode 100644 index 0000000..b5c81d1 --- /dev/null +++ b/test/policy-local-files/slice-selection-files/vnf_policy_nssi_shared.json @@ -0,0 +1,37 @@ + + { + "OSDF_GUILIN.vnfPolicy_URLLC":{ + "metadata":{ + "policy-id":"OSDF_GUILIN.vnfPolicy_URLLC", + "policy-version":1 + }, + "properties":{ + "identity":"vnf_URLLC", + "resources":["embb-cn"], + "scope":[ + "OSDF_GUILIN" + ], + "services":[ + "embb-cn" + ], + "geography":[], + "vnfProperties":[ + { + "attributes":{ + "modelInvariantId":{"get_param":"model_invariant_id"}, + "modelVersionId":{"get_param":"model_version_id"}, + "environment-context":"shared", + "service-role":"nssi" + }, + "inventoryProvider":"aai", + "inventoryType":"nssi", + "unique":"true" + } + ] + }, + "type":"onap.policies.optimization.resource.VnfPolicy", + "type_version":"1.0.0", + "version":"1.0.0" + } + } + diff --git a/test/policy-local-files/subscriber_policy_URLLC_1.json b/test/policy-local-files/subscriber_policy_URLLC_1.json new file mode 100644 index 0000000..d147273 --- /dev/null +++ b/test/policy-local-files/subscriber_policy_URLLC_1.json @@ -0,0 +1,26 @@ +{ + "OSDF_FRANKFURT.SubscriberPolicy_URLLC_1": { + "type": "onap.policies.optimization.service.SubscriberPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.SubscriberPolicy_URLLC_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "URLLC_1" + ], + "services": [ + "URLLC_1" + ], + "identity": "subscriber_URLLC_1", + "properties": { + "subscriberName": [ + "URLLC_Core_1" + ] + } + } +} +} diff --git a/test/policy-local-files/subscriber_policy_vCPE.json b/test/policy-local-files/subscriber_policy_vCPE.json index 9359c20..c02d8d6 100644 --- a/test/policy-local-files/subscriber_policy_vCPE.json +++ b/test/policy-local-files/subscriber_policy_vCPE.json @@ -1,22 +1,32 @@ { - "service": "subscriberPolicy", - "policyName": "OSDF_DUBLIN.SubscriberPolicy_v1", - "description": "Subscriber Policy for vCPE", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "1", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "subscriber_vCPE", - "policyScope": ["vCPE", "subscriber_x", "subscriber_y", "subscriberPolicy"], - "properties": { - "subscriberName": ["subscriber_x", "subscriber_y"], - "subscriberRole": ["PVT Homing"], - "provStatus": ["CAPPED"] - }, - "policyType": "subscriberPolicy", - "serviceName": "vCPE" + "OSDF_FRANKFURT.SubscriberPolicy_v1": { + "type": "onap.policies.optimization.service.SubscriberPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.SubscriberPolicy_v1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "identity": "subscriber_vCPE", + "properties": { + "subscriberName": [ + "subscriber_x", + "subscriber_y" + ], + "subscriberRole": [ + "PVT Homing" + ], + "provStatus": [ + "CAPPED" + ] + } } + } } diff --git a/test/policy-local-files/thresholdPolicy_URLLC_Core_1.json b/test/policy-local-files/thresholdPolicy_URLLC_Core_1.json new file mode 100644 index 0000000..6c602ee --- /dev/null +++ b/test/policy-local-files/thresholdPolicy_URLLC_Core_1.json @@ -0,0 +1,41 @@ +{ + "OSDF_FRANKFURT.Threshold_URLLC_Core_1":{ + "type":"onap.policies.optimization.resource.ThresholdPolicy", + "version":"1.0.0", + "type_version":"1.0.0", + "metadata":{ + "policy-id":"OSDF_FRANKFURT.Threshold_URLLC_Core_1", + "policy-version":1 + }, + "properties":{ + "scope":[ + "OSDF_FRANKFURT", + "URLLC_1", + "URLLC_Core_1" + ], + "resources":[ + "URLLC_Core_1" + ], + "services":[ + "URLLC_1" + ], + "geography": [], + "identity":"Threshold_URLLC_Core_1", + "thresholdProperties":[ + { + "attribute":"latency", + "operator":"lte", + "threshold":5, + "unit":"ms" + }, + { + "attribute":"reliability", + "operator":"gte", + "threshold":99.999, + "unit":"" + } + + ] + } + } +}
\ No newline at end of file diff --git a/test/policy-local-files/vnfPolicy_URLLC_Core_1.json b/test/policy-local-files/vnfPolicy_URLLC_Core_1.json new file mode 100644 index 0000000..0fa69fa --- /dev/null +++ b/test/policy-local-files/vnfPolicy_URLLC_Core_1.json @@ -0,0 +1,39 @@ +{ + "OSDF_FRANKFURT.vnfPolicy_URLLC_Core_1": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_URLLC_Core_1", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "URLLC_1", + "URLLC_Core_1" + ], + "resources": [ + "URLLC_Core_1" + ], + "services": [ + "URLLC_1" + ], + "identity": "vnf_URLLC_Core_1", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "inventoryType": "nssi", + "region": "RegionOne", + "attributes": { + "orchestrationStatus": "active", + "service-role": "nssi", + "modelInvariantId":"bfbg3636-e39c-iidd-0987-27c28f4oo3", + "modelVersionId":"bfbg3636-e39c-iidd-0987-27c28f4d33" + } + } + ] + } + } +} diff --git a/test/policy-local-files/vnfPolicy_vFW_TD.json b/test/policy-local-files/vnfPolicy_vFW_TD.json index 9a9cbe0..e63e2c2 100644 --- a/test/policy-local-files/vnfPolicy_vFW_TD.json +++ b/test/policy-local-files/vnfPolicy_vFW_TD.json @@ -1,41 +1,47 @@ { - "service": "vnfPolicy", - "policyName": "OSDF_DUBLIN.vnfPolicy_vFW_TD", - "description": "vnfPolicy", - "templateVersion": "OpenSource.version.1", - "version": "oofDublin", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vnf_vFW_TD", - "policyScope": [ - "td", - "us", - "vFW-SINK" - ], - "policyType": "vnfPolicy", - "resources": ["vFW-SINK"], - "applicableResources": "any", - "vnfProperties": [{ - "inventoryProvider": "aai", - "serviceType": "", - "inventoryType": "vfmodule", - "customerId": { - "get_param": "chosen_customer_id" - }, - "equipmentRole": "", - "attributes": { - "orchestrationStatus": ["active"], - "provStatus": "ACTIVE", - "cloudRegionId": { - "get_param": "chosen_region" - }, - "service_instance_id": { - "get_param": "service_id" - } - } - }] + "OSDF_FRANKFURT.vnfPolicy_vFW_TD": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vFW_TD", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "TD" + ], + "resources": [ + "vFW-SINK" + ], + "geography": [ + "US" + ], + "identity": "vnf_vFW_TD", + "applicableResources": "any", + "vnfProperties": [{ + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "vfmodule", + "customerId": { + "get_param": "chosen_customer_id" + }, + "equipmentRole": "", + "attributes": { + "orchestrationStatus": ["active"], + "provStatus": "ACTIVE", + "cloudRegionId": { + "get_param": "chosen_region" + }, + "service_instance_id": { + "get_param": "service_id" + } + }, + "passthroughAttributes": { + "td-role": "destination" + } + }] } -}
\ No newline at end of file + } +} diff --git a/test/policy-local-files/vnfPolicy_vG.json b/test/policy-local-files/vnfPolicy_vG.json index b047686..2dea670 100644 --- a/test/policy-local-files/vnfPolicy_vG.json +++ b/test/policy-local-files/vnfPolicy_vG.json @@ -1,29 +1,38 @@ { - "service": "vnfPolicy", - "policyName": "OSDF_DUBLIN.vnfPolicy_vG", - "description": "vnfPolicy", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vnf_vG", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vG"], - "policyType": "vnfPolicy", - "resources": ["vG"], - "applicableResources": "any", - "vnfProperties": [ - { - "inventoryProvider": "aai", - "serviceType": "", - "inventoryType": "cloud", - "customerId": "", - "orchestrationStatus": "", - "equipmentRole": "" - - } - ] + "OSDF_FRANKFURT.vnfPolicy_vG": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vG", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vG" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vnf_vG", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "cloud", + "customerId": "", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] } + } } diff --git a/test/policy-local-files/vnfPolicy_vGMuxInfra.json b/test/policy-local-files/vnfPolicy_vGMuxInfra.json index 45d67f6..65475a9 100644 --- a/test/policy-local-files/vnfPolicy_vGMuxInfra.json +++ b/test/policy-local-files/vnfPolicy_vGMuxInfra.json @@ -1,28 +1,38 @@ { - "service": "vnfPolicy", - "policyName": "OSDF_DUBLIN.vnfPolicy_vGMuxInfra", - "description": "vnfPolicy", - "templateVersion": "OpenSource.version.1", - "version": "test1", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vnf_vGMuxInfra", - "policyScope": ["vCPE", "US", "INTERNATIONAL", "ip", "vGMuxInfra"], - "policyType": "vnfPolicy", - "resources": ["vGMuxInfra"], - "applicableResources": "any", - "vnfProperties": [ - { - "inventoryProvider": "aai", - "serviceType": "vGMuxInfra-xx", - "inventoryType": "service", - "customerId": "SDN-ETHERNET-INTERNET", - "orchestrationStatus": "", - "equipmentRole": "" - } - ] + "OSDF_FRANKFURT.vnfPolicy_vGMuxInfra": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vGMuxInfra", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT" + ], + "services": [ + "vCPE" + ], + "resources": [ + "vGMuxInfra" + ], + "geography": [ + "US", + "INTERNATIONAL" + ], + "identity": "vnf_vGMuxInfra", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "vGMuxInfra-xx", + "inventoryType": "service", + "customerId": "SDN-ETHERNET-INTERNET", + "orchestrationStatus": "", + "equipmentRole": "" + } + ] } + } } diff --git a/test/policy-local-files/vnfPolicy_vPGN_TD.json b/test/policy-local-files/vnfPolicy_vPGN_TD.json index 7b374b4..077901a 100644 --- a/test/policy-local-files/vnfPolicy_vPGN_TD.json +++ b/test/policy-local-files/vnfPolicy_vPGN_TD.json @@ -1,41 +1,52 @@ { - "service": "vnfPolicy", - "policyName": "OSDF_DUBLIN.vnfPolicy_vPGN_TD", - "description": "vnfPolicy", - "templateVersion": "OpenSource.version.1", - "version": "oofDublin", - "priority": "6", - "riskType": "test", - "riskLevel": "3", - "guard": "False", - "content": { - "identity": "vnf_vPGN_TD", - "policyScope": [ - "td", - "us", - "vPGN" - ], - "policyType": "vnfPolicy", - "resources": ["vPGN"], - "applicableResources": "any", - "vnfProperties": [{ - "inventoryProvider": "aai", - "serviceType": "", - "inventoryType": "vfmodule", - "customerId": { - "get_param": "chosen_customer_id" + "OSDF_FRANKFURT.vnfPolicy_vPGN_TD": { + "type": "onap.policies.optimization.resource.VnfPolicy", + "version": "1.0.0", + "type_version": "1.0.0", + "metadata": { + "policy-id": "OSDF_FRANKFURT.vnfPolicy_vPGN_TD", + "policy-version": 1 + }, + "properties": { + "scope": [ + "OSDF_FRANKFURT", + "TD" + ], + "resources": [ + "vPGN" + ], + "geography": [ + "US" + ], + "identity": "vnf_vPGN_TD", + "applicableResources": "any", + "vnfProperties": [ + { + "inventoryProvider": "aai", + "serviceType": "", + "inventoryType": "vfmodule", + "customerId": { + "get_param": "chosen_customer_id" + }, + "equipmentRole": "", + "unique": "False", + "attributes": { + "orchestrationStatus": [ + "active" + ], + "provStatus": "ACTIVE", + "cloudRegionId": { + "get_param": "chosen_region" }, - "equipmentRole": "", - "attributes": { - "orchestrationStatus": ["active"], - "provStatus": "ACTIVE", - "cloudRegionId": { - "get_param": "chosen_region" - }, - "service_instance_id": { - "get_param": "service_id" - } + "service_instance_id": { + "get_param": "service_id" } - }] + }, + "passthroughAttributes": { + "td-role": "anchor" + } + } + ] } -}
\ No newline at end of file + } +} diff --git a/test/policy/test_policy_interface.py b/test/policy/test_policy_interface.py index 4f1efcf..082b7f9 100644 --- a/test/policy/test_policy_interface.py +++ b/test/policy/test_policy_interface.py @@ -15,16 +15,16 @@ # # ------------------------------------------------------------------------- # -import mock import os import unittest -from osdf.adapters.local_data import local_policies +import mock + import osdf.config.loader as config_loader +from osdf.adapters.local_data import local_policies +from osdf.adapters.policy import interface as pol from osdf.utils.interfaces import json_from_file from osdf.utils.programming_utils import DotDict -from osdf.optimizers.placementopt.conductor import translation as tr -from osdf.adapters.policy import interface as pol class TestPolicyInterface(unittest.TestCase): diff --git a/test/simple_route_opt/AAI.json b/test/simple_route_opt/AAI.json new file mode 100644 index 0000000..6ef264b --- /dev/null +++ b/test/simple_route_opt/AAI.json @@ -0,0 +1,164 @@ +{ + "logical-link": [ + { + "link-name": "link-id-1", + "in-maint": true, + "link-type": "example-link-type-val-16287", + "resource-version": "1585009311719", + "operational-status": "UP", + "relationship-list": { + "relationship": [ + { + "related-to": "p-interface", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v16/network/pnfs/pnf/20.20.20.20/p-interfaces/p-interface/p-interface-3", + "relationship-data": [ + { + "relationship-key": "pnf.pnf-name", + "relationship-value": "20.20.20.20" + }, + { + "relationship-key": "p-interface.interface-name", + "relationship-value": "p-interface-3" + } + ], + "related-to-property": [ + { + "property-key": "p-interface.prov-status" + } + ] + }, + { + "related-to": "p-interface", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v16/network/pnfs/pnf/10.10.10.10/p-interfaces/p-interface/p-interface-2", + "relationship-data": [ + { + "relationship-key": "pnf.pnf-name", + "relationship-value": "10.10.10.10" + }, + { + "relationship-key": "p-interface.interface-name", + "relationship-value": "p-interface-2" + } + ], + "related-to-property": [ + { + "property-key": "p-interface.prov-status" + } + ] + } + ] + } + }, + { + "link-name": "link-id-2", + "in-maint": true, + "link-type": "example-link-type-val-16287", + "resource-version": "1584943281792", + "operational-status": "UP", + "relationship-list": { + "relationship": [ + { + "related-to": "p-interface", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v16/network/pnfs/pnf/22.22.22.22/p-interfaces/p-interface/p-interface-7", + "relationship-data": [ + { + "relationship-key": "pnf.pnf-name", + "relationship-value": "22.22.22.22" + }, + { + "relationship-key": "p-interface.interface-name", + "relationship-value": "p-interface-7" + } + ], + "related-to-property": [ + { + "property-key": "p-interface.prov-status" + } + ] + }, + { + "related-to": "p-interface", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v16/network/pnfs/pnf/11.11.11.11/p-interfaces/p-interface/p-interface-6", + "relationship-data": [ + { + "relationship-key": "pnf.pnf-name", + "relationship-value": "11.11.11.11" + }, + { + "relationship-key": "p-interface.interface-name", + "relationship-value": "p-interface-6" + } + ], + "related-to-property": [ + { + "property-key": "p-interface.prov-status" + } + ] + } + ] + } + }, + { + "link-name": "link-id-3", + "in-maint": true, + "link-type": "example-link-type-val-16287", + "resource-version": "1584943345290", + "operational-status": "UP", + "relationship-list": { + "relationship": [ + { + "related-to": "p-interface", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v16/network/pnfs/pnf/11.11.11.11/p-interfaces/p-interface/p-interface-5", + "relationship-data": [ + { + "relationship-key": "pnf.pnf-name", + "relationship-value": "11.11.11.11" + }, + { + "relationship-key": "p-interface.interface-name", + "relationship-value": "p-interface-5" + } + ], + "related-to-property": [ + { + "property-key": "p-interface.prov-status" + } + ] + }, + { + "related-to": "p-interface", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v16/network/pnfs/pnf/20.20.20.20/p-interfaces/p-interface/p-interface-4", + "relationship-data": [ + { + "relationship-key": "pnf.pnf-name", + "relationship-value": "20.20.20.20" + }, + { + "relationship-key": "p-interface.interface-name", + "relationship-value": "p-interface-4" + } + ], + "related-to-property": [ + { + "property-key": "p-interface.prov-status" + } + ] + } + ] + } + }, + { + "link-name": "rahul", + "in-maint": true, + "link-type": "example-link-type-val-rahul", + "resource-version": "1585023629505", + "operational-status": "UP" + } + ] +}
\ No newline at end of file diff --git a/test/simple_route_opt/routeOpt.json b/test/simple_route_opt/routeOpt.json new file mode 100644 index 0000000..887b85b --- /dev/null +++ b/test/simple_route_opt/routeOpt.json @@ -0,0 +1,34 @@ +{ + "requestInfo": { + "transactionId": "xxx-xxx-xxxx", + "requestId": "yyy-yyy-yyyy", + "callbackUrl": "https://wiki.onap.org:5000/callbackUrl", + "sourceId": "", + "requestType": "create", + "numSolutions": 1, + "optimizers": [ + "route" + ], + "timeout": 600 + }, + "routeInfo": { + "routeRequests": [ + { + "srcPort": { + "accessTopologyId": "Topo113", + "accessClientId": "clientU12", + "accessProviderId": "VDF1234", + "accessNodeId": "22.22.22.22", + "accessLtpId": "1345" + }, + "dstPort": { + "accessTopologyId": "Topo3421", + "accessClientId": "clientD123", + "accessProviderId": "VDF3214", + "accessNodeId": "10.10.10.10", + "accessLtpId": "3452" + } + } + ] + } +} diff --git a/test/test-requirements.txt b/test/test-requirements.txt index 043d87a..2c801e2 100644 --- a/test/test-requirements.txt +++ b/test/test-requirements.txt @@ -3,3 +3,5 @@ moto pytest pytest-tap requests-mock +pylint +mock diff --git a/test/test_ConductorApiBuilder.py b/test/test_ConductorApiBuilder.py index 7e38f4d..34f6989 100644 --- a/test/test_ConductorApiBuilder.py +++ b/test/test_ConductorApiBuilder.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,8 +20,8 @@ import unittest import json import yaml +from osdf.adapters.conductor.api_builder import conductor_api_builder from osdf.adapters.local_data import local_policies -from osdf.optimizers.placementopt.conductor.api_builder import conductor_api_builder from osdf.utils.interfaces import json_from_file @@ -28,7 +29,7 @@ class TestConductorApiBuilder(unittest.TestCase): def setUp(self): self.main_dir = "" - self.conductor_api_template = self.main_dir + "osdf/templates/conductor_interface.json" + self.conductor_api_template = self.main_dir + "osdf/adapters/conductor/templates/conductor_interface.json" self.local_config_file = self.main_dir + "config/common_config.yaml" policy_data_path = self.main_dir + "test/policy-local-files" # "test/policy-local-files" @@ -42,21 +43,35 @@ class TestConductorApiBuilder(unittest.TestCase): parameter_data_file = self.main_dir + "test/placement-tests/request_placement_vfmod.json" self.request_placement_vfmod_json = json_from_file(parameter_data_file) self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + self.template_fields = { + 'location_enabled': True, + 'version': '2017-10-10' + } def test_conductor_api_call_builder(self): main_dir = self.main_dir request_json = self.request_json policies = self.policies - local_config = yaml.load(open(self.local_config_file)) - templ_string = conductor_api_builder(request_json, policies, local_config, self.conductor_api_template) + local_config = yaml.safe_load(open(self.local_config_file)) + req_info = request_json['requestInfo'] + demands = request_json['placementInfo']['placementDemands'] + request_parameters = request_json['placementInfo']['requestParameters'] + service_info = request_json['serviceInfo'] + templ_string = conductor_api_builder(req_info, demands, request_parameters, service_info, self.template_fields, + policies, local_config, self.conductor_api_template) templ_json = json.loads(templ_string) self.assertEqual(templ_json["name"], "yyy-yyy-yyyy") def test_conductor_api_call_builder_vfmod(self): request_json = self.request_vfmod_json policies = self.policies - local_config = yaml.load(open(self.local_config_file)) - templ_string = conductor_api_builder(request_json, policies, local_config, self.conductor_api_template) + local_config = yaml.safe_load(open(self.local_config_file)) + req_info = request_json['requestInfo'] + demands = request_json['placementInfo']['placementDemands'] + request_parameters = request_json['placementInfo']['requestParameters'] + service_info = request_json['serviceInfo'] + templ_string = conductor_api_builder(req_info, demands, request_parameters, service_info, self.template_fields, + policies, local_config, self.conductor_api_template) templ_json = json.loads(templ_string) self.assertEqual(templ_json, self.request_placement_vfmod_json) diff --git a/test/test_PolicyCalls.py b/test/test_PolicyCalls.py index 4c9366a..1ca14dc 100644 --- a/test/test_PolicyCalls.py +++ b/test/test_PolicyCalls.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,7 +25,7 @@ from osdf.adapters.policy import interface from osdf.utils.interfaces import RestClient, json_from_file import yaml from mock import patch -from osdf.optimizers.placementopt.conductor import translation +from osdf.adapters.conductor import translation from osdf.operation.exceptions import BusinessException @@ -63,7 +64,7 @@ class TestPolicyCalls(unittest.TestCase): "test/placement-tests/policy_response2.json") with patch('osdf.adapters.policy.interface.policy_api_call', return_value=policy_response): policy_list = interface.remote_api(req_json, osdf_config, service_type="placement") - policy_type = [policy['content']['policyType'] for policy in policy_list] + policy_type = [policy[list(policy.keys())[0]]['type'] for policy in policy_list] #self.assertEqual(set(policy_type), {'hpaPolicy', 'SubscriberPolicy'}) def failure_policy_call(self, req_json_file, resp_json_file): @@ -91,7 +92,7 @@ class TestPolicyCalls(unittest.TestCase): policy_config_file = yaml.load(yaml_file2) with patch('osdf.utils.interfaces.RestClient.request', return_value=req_json_obj2): policies_list = interface.get_by_scope(RestClient, req_json_obj, policy_config_file, 'placement') - self.assertTrue(policies_list, 'is null') + self.assertFalse(policies_list) self.assertRaises(Exception) def test_gen_demands(self): @@ -99,14 +100,16 @@ class TestPolicyCalls(unittest.TestCase): req_json = "./test/placement-tests/request.json" req_json = json.loads(open(req_json).read()) # need to run this only on vnf policies - vnf_policies = [x for x in self.policies if x["content"]["policyType"] == "vnfPolicy"] - gen_demands = translation.gen_demands(req_json, vnf_policies) + vnf_policies = [x for x in self.policies if x[list(x.keys())[0]]["type"] == + "onap.policies.optimization.resource.VnfPolicy"] + gen_demands = translation.gen_demands(req_json['placementInfo']['placementDemands'], vnf_policies) + for action in req_json['placementInfo']['placementDemands']: actions_list.append(action['resourceModuleName']) for key2,value in gen_demands.items(): gen_demands_list.append(key2) self.assertListEqual(gen_demands_list, actions_list, 'generated demands are not equal to the passed input' - '[placementDemand][resourceModuleName] list') + '[placementDemand][resourceModuleName] list') def test_local_policy_location(self): req_json = json_from_file("./test/placement-tests/request.json") diff --git a/test/test_api_validation.py b/test/test_api_validation.py index 389ff62..3a20262 100644 --- a/test/test_api_validation.py +++ b/test/test_api_validation.py @@ -18,9 +18,13 @@ import json import unittest -from osdf.models.api.placementRequest import PlacementAPI -from osdf.models.api.placementResponse import PlacementResponse -from schematics.exceptions import ModelValidationError +from schematics.exceptions import DataError + +from apps.placement.models.api.placementRequest import PlacementAPI +from apps.placement.models.api.placementResponse import PlacementResponse +from apps.slice_selection.models.api.nsi_selection_request import NSISelectionAPI +from apps.slice_selection.models.api.nssi_selection_request import NSSISelectionAPI +from apps.nxi_termination.models.api.nxi_termination_request import NxiTerminationApi class TestReqValidation(unittest.TestCase): @@ -35,9 +39,39 @@ class TestReqValidation(unittest.TestCase): req_json = json.loads(open(req_file).read()) self.assertEqual(PlacementAPI(req_json).validate(), None) + def test_req_nsi_validation(self): + req_file = "./test/apps/slice_selection/nsi_selection_request.json" + req_json = json.loads(open(req_file).read()) + self.assertEqual(NSISelectionAPI(req_json).validate(), None) + + def test_req_invalid_nsi(self): + req_file = "./test/apps/slice_selection/nsi_selection_invalid_request.json" + req_json = json.loads(open(req_file).read()) + self.assertRaises(DataError, lambda: NSISelectionAPI(req_json).validate()) + + def test_req_nssi_validation(self): + req_file = "./test/apps/slice_selection/nssi_selection_request.json" + req_json = json.loads(open(req_file).read()) + self.assertEqual(NSSISelectionAPI(req_json).validate(), None) + + def test_req_invalid_nssi(self): + req_file = "./test/apps/slice_selection/nssi_selection_invalid_request.json" + req_json = json.loads(open(req_file).read()) + self.assertRaises(DataError, lambda: NSSISelectionAPI(req_json).validate()) + def test_req_failure(self): req_json = {} - self.assertRaises(ModelValidationError, lambda: PlacementAPI(req_json).validate()) + self.assertRaises(DataError, lambda: PlacementAPI(req_json).validate()) + + def test_req_nxi_validation(self): + req_file = "./test/apps/nxi_termination/nxi_termination.json" + req_json = json.loads(open(req_file).read()) + self.assertEqual(NxiTerminationApi(req_json).validate(), None) + + def test_req_invalid_nxi(self): + req_file = "./test/apps/nxi_termination/invalid_request.json" + req_json = json.loads(open(req_file).read()) + self.assertRaises(DataError, lambda: NxiTerminationApi(req_json).validate()) class TestResponseValidation(unittest.TestCase): @@ -54,7 +88,7 @@ class TestResponseValidation(unittest.TestCase): def test_invalid_response(self): resp_json = {} - self.assertRaises(ModelValidationError, lambda: PlacementResponse(resp_json).validate()) + self.assertRaises(DataError, lambda: PlacementResponse(resp_json).validate()) if __name__ == "__main__": diff --git a/test/test_get_opt_query_data.py b/test/test_get_opt_query_data.py index 1e2db17..8e6c324 100644 --- a/test/test_get_opt_query_data.py +++ b/test/test_get_opt_query_data.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ # import unittest import json -from osdf.optimizers.placementopt.conductor.translation import get_opt_query_data +from osdf.adapters.conductor.translation import get_opt_query_data class TestGetOptQueryData(unittest.TestCase): @@ -30,7 +31,7 @@ class TestGetOptQueryData(unittest.TestCase): query_policy_data_file = ["QueryPolicy_vCPE.json"] request_json = json.loads(open(parameter_data_file).read()) policies = [json.loads(open(policy_data_path + file).read()) for file in query_policy_data_file] - req_param_dict = get_opt_query_data(request_json, policies) + req_param_dict = get_opt_query_data(request_json['placementInfo']['requestParameters'], policies) self.assertTrue(req_param_dict is not None) @@ -42,7 +43,7 @@ class TestGetOptQueryData(unittest.TestCase): query_policy_data_file = ["QueryPolicy_vFW_TD.json"] request_json = json.loads(open(parameter_data_file).read()) policies = [json.loads(open(policy_data_path + file).read()) for file in query_policy_data_file] - req_param_dict = get_opt_query_data(request_json, policies) + req_param_dict = get_opt_query_data(request_json['placementInfo']['requestParameters'], policies) self.assertTrue(req_param_dict is not None) diff --git a/test/test_inter_domain_route_opt.py b/test/test_inter_domain_route_opt.py new file mode 100644 index 0000000..3d18abc --- /dev/null +++ b/test/test_inter_domain_route_opt.py @@ -0,0 +1,151 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 Fujitsu Limited 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 unittest + +from unittest.mock import patch +from apps.route.optimizers.inter_domain_route_opt import InterDomainRouteOpt +import osdf.config.loader as config_loader +from osdf.utils.interfaces import json_from_file +from osdf.utils.programming_utils import DotDict + +count = 1 + +def mocked_requests_get(*args, **kwargs): + class MockResponse: + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + def json(self): + return self.json_data + + main_dir = "" + response_data_file = main_dir + "test/inter_domain_route_opt/get_links.json" + bandwidth_attributes = main_dir + "test/inter_domain_route_opt/bandwidth_attributes.json" + bandwidth_attribute_values = json_from_file(bandwidth_attributes) + + controllers_list = main_dir + "test/inter_domain_route_opt/controllers_list.json" + + if args[0] == 'https://api.url:30233/aai/v19/network/logical-links?link-type=inter-domain&operational-status=up': + return MockResponse(json_from_file(response_data_file), 200) + elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf1/p-interfaces/p-interface/int1?depth=all': + return MockResponse(bandwidth_attribute_values["int-1-bw"], 200) + elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int3?depth=all': + return MockResponse(bandwidth_attribute_values["int-3-bw"], 200) + elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int4?depth=all': + return MockResponse(bandwidth_attribute_values["int-4-bw"], 200) + elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int5?depth=all': + return MockResponse(bandwidth_attribute_values["int-5-bw"], 200) + elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int6?depth=all': + return MockResponse(bandwidth_attribute_values["int-6-bw"], 200) + elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf4/p-interfaces/p-interface/int7?depth=all': + return MockResponse(bandwidth_attribute_values["int-7-bw"], 200) + elif args[0] == 'https://api.url:30233/aai/v19/external-system/esr-thirdparty-sdnc-list': + return MockResponse(json_from_file(controllers_list), 200) + return MockResponse(None, 404) + + +def mocked_requests_put(*args, **kwargs): + class MockResponse: + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + def json(self): + return self.json_data + main_dir = "" + controllers_for_interfaces = main_dir + "test/inter_domain_route_opt/controllers_for_interfaces.json" + controllers_for_interfaces_values = json_from_file(controllers_for_interfaces) + + global count + + if count == 1: + count += 1 + return MockResponse(controllers_for_interfaces_values["int-1-cont"], 200) + elif count == 2: + count += 1 + return MockResponse(controllers_for_interfaces_values["int-3-cont"], 200) + elif count == 3: + count += 1 + return MockResponse(controllers_for_interfaces_values["int-4-cont"], 200) + elif count == 4: + count += 1 + return MockResponse(controllers_for_interfaces_values["int-5-cont"], 200) + elif count == 5: + count += 1 + return MockResponse(controllers_for_interfaces_values["int-6-cont"], 200) + elif count == 6: + count += 1 + return MockResponse(controllers_for_interfaces_values["int-7-cont"], 200) + + return MockResponse(None, 404) + + + +class TestInterDomainRouteOpt(unittest.TestCase): + @patch('apps.route.optimizers.inter_domain_route_opt.requests.get', side_effect=mocked_requests_get) + @patch('apps.route.optimizers.inter_domain_route_opt.requests.put', side_effect=mocked_requests_put) + @patch('apps.route.optimizers.simple_route_opt.pymzn.minizinc') + def test_process_get_route(self, mock_solve , mock_put, mock_get): + main_dir = "" + mock_solve.return_value = [{'x': [1, 1, 0, 0, 0, 0]}] + self.config_spec = { + "deployment": "test/functest/simulators/simulated-config/osdf_config.yaml", + "core": "test/functest/simulators/simulated-config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + parameter_data_file = main_dir + "test/inter_domain_route_opt/request.json" + request_json = json_from_file(parameter_data_file) + routopt = InterDomainRouteOpt() + actual_response = routopt.get_route(request_json,self.osdf_config) + mock_response = { + "requestId":"789456", + "transactionId":"123456", + "statusMessage":"SUCCESS", + "requestStatus":"accepted", + "solutions":{ + "routeInfo":{ + "serviceRoute":[ + { + "srcInterfaceId":"int19", + "dstInterfaceId":"int1", + "controllerId":"Controller1" + }, + { + "srcInterfaceId":"int3", + "dstInterfaceId":"int4", + "controllerId":"Controller2" + }, + { + "srcInterfaceId":"int5", + "dstInterfaceId":"int20", + "controllerId":"Controller3" + } + ], + "linkList":[ + "link1", + "link2" + ] + } + } + } + self.assertEqual(mock_response, actual_response) + + +if __name__ == '__main__': + unittest.main() +
\ No newline at end of file diff --git a/test/test_model_api.py b/test/test_model_api.py new file mode 100644 index 0000000..2a1cecf --- /dev/null +++ b/test/test_model_api.py @@ -0,0 +1,71 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 + +import pytest +from mock import patch +from schematics.exceptions import DataError + +from runtime.model_api import create_model_data, get_model_data, delete_model_data, retrieve_all_models +from runtime.models.api.model_request import OptimModelRequestAPI +from runtime.optim_engine import validate_request + +BASE_DIR = os.path.dirname(__file__) + +ret_val = {'modelId': 'test', 'description': 'desc', 'solver': 'mzn'} + + +class TestModelApi(): + + def test_valid_mapi_request(self): + req_json = json.loads(open("./test/optengine-tests/test_modelapi_valid.json").read()) + + assert OptimModelRequestAPI(req_json).validate() is None + + def test_invalid_mapi_request(self): + req_json = json.loads(open("./test/optengine-tests/test_modelapi_invalid.json").read()) + with pytest.raises(DataError): + validate_request(req_json) + + @patch('runtime.model_api.build_model_dict') + @patch('mysql.connector.connect') + @patch('runtime.model_api.osdf_config') + def test_create_model(self, config, conn, model_data): + model_data.return_value = ret_val + req_json = json.loads(open("./test/optengine-tests/test_modelapi_valid.json").read()) + + create_model_data(req_json) + + @patch('runtime.model_api.build_model_dict') + @patch('mysql.connector.connect') + @patch('runtime.model_api.osdf_config') + def test_retrieve_model(self, config, conn, model_data): + model_data.return_value = ret_val + get_model_data('test') + + @patch('mysql.connector.connect') + @patch('runtime.model_api.osdf_config') + def test_delete_model(self, config, conn): + delete_model_data('test') + + @patch('mysql.connector.connect') + @patch('runtime.model_api.osdf_config') + def test_retrieve_all_model(self, config, conn): + retrieve_all_models() diff --git a/test/test_optim_engine.py b/test/test_optim_engine.py new file mode 100644 index 0000000..e1756f8 --- /dev/null +++ b/test/test_optim_engine.py @@ -0,0 +1,78 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2020 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 + +import pytest +from mock import patch +from schematics.exceptions import DataError + +from osdf.operation.exceptions import BusinessException +from runtime.optim_engine import validate_request, process_request + +BASE_DIR = os.path.dirname(__file__) + + +class TestOptimEngine(): + + def test_valid_optim_request(self): + req_json = json.loads(open("./test/optengine-tests/test_optengine_valid.json").read()) + + assert validate_request(req_json) == True + + def test_invalid_optim_request(self): + req_json = json.loads(open("./test/optengine-tests/test_optengine_invalid.json").read()) + with pytest.raises(DataError): + validate_request(req_json) + + def test_invalid_optim_request_without_modelid(self): + req_json = json.loads(open("./test/optengine-tests/test_optengine_invalid2.json").read()) + with pytest.raises(BusinessException): + validate_request(req_json) + + def test_invalid_optim_request_no_optdata(self): + req_json = json.loads(open("./test/optengine-tests/test_optengine_no_optdata.json").read()) + with pytest.raises(BusinessException): + validate_request(req_json) + + def test_process_request(self): + req_json = json.loads(open("./test/optengine-tests/test_optengine_valid.json").read()) + + res = process_request(req_json) + assert res.status_code == 400 + + def test_py_process_request(self): + req_json = json.loads(open("./test/optengine-tests/test_py_optengine_valid.json").read()) + + res = process_request(req_json) + assert res.status_code == 200 + + def test_invalid_solver(self): + req_json = json.loads(open("./test/optengine-tests/test_optengine_invalid_solver.json").read()) + + with pytest.raises(BusinessException): + process_request(req_json) + + @patch('runtime.optim_engine.get_model_data') + def test_process_solverid_request(self, mocker): + req_json = json.loads(open("./test/optengine-tests/test_optengine_modelId.json").read()) + + data = 200, ('junk', '', '', 'py') + mocker.return_value = data + process_request(req_json) diff --git a/test/test_process_fixed_pci.py b/test/test_process_fixed_pci.py new file mode 100644 index 0000000..3d805c5 --- /dev/null +++ b/test/test_process_fixed_pci.py @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------- +# Copyright (c) 2018 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 mock +import unittest + +from flask import Response +from mock import patch +from osdf.adapters.local_data import local_policies +from apps.pci.optimizers.pci_opt_processor import process_pci_optimation +import osdf.config.loader as config_loader +from osdf.utils.interfaces import json_from_file +from osdf.utils.programming_utils import DotDict + + +class TestProcessPlacementOpt(unittest.TestCase): + + def setUp(self): + mock_req_accept_message = Response("Accepted Request", content_type='application/json; charset=utf-8') + self.patcher_req = patch('apps.pci.optimizers.config_request.request', + return_value={"solutionInfo": {"placementInfo": "dummy"}}) + self.patcher_req_accept = patch('osdf.operation.responses.osdf_response_for_request_accept', + return_value=mock_req_accept_message) + self.patcher_callback = patch( + 'apps.pci.optimizers.pci_opt_processor.process_pci_optimation', + return_value=mock_req_accept_message) + + mock_mzn_response = [{'pci': {0: 2, 1: 0, 2: 1, 3:3, 4:0} , 'used_ignorables': [0]}] + + self.patcher_minizinc_callback = patch( + 'apps.pci.optimizers.solver.optimizer.solve', + return_value=mock_mzn_response ) + self.patcher_RestClient = patch( + 'osdf.utils.interfaces.RestClient', return_value=mock.MagicMock()) + self.Mock_req = self.patcher_req.start() + self.Mock_req_accept = self.patcher_req_accept.start() + self.Mock_callback = self.patcher_callback.start() + self.Mock_RestClient = self.patcher_RestClient.start() + self.Mock_mzn_callback = self.patcher_minizinc_callback.start() + + def tearDown(self): + patch.stopall() + + def test_process_pci_opt_solutions(self): + main_dir = "" + parameter_data_file = main_dir + "test/pci-optimization-tests/fixed_pci.json" + policy_data_path = main_dir + "test/policy-local-files/" + self.config_spec = { + "deployment": "test/functest/simulators/simulated-config/osdf_config.yaml", + "core": "test/functest/simulators/simulated-config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + + valid_policies_list_file = policy_data_path + '/' + 'meta-valid-policies.txt' + valid_policies_files = local_policies.get_policy_names_from_file(valid_policies_list_file) + + request_json = json_from_file(parameter_data_file) + policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] + + templ_string = process_pci_optimation(request_json, self.osdf_config,policies) + + +if __name__ == "__main__": + unittest.main() + diff --git a/test/test_process_pci_anr_opt.py b/test/test_process_pci_anr_opt.py index 8c6a34c..17c3e6d 100644 --- a/test/test_process_pci_anr_opt.py +++ b/test/test_process_pci_anr_opt.py @@ -15,14 +15,15 @@ # # ------------------------------------------------------------------------- # -import mock import unittest +import mock from flask import Response from mock import patch -from osdf.adapters.local_data import local_policies -from osdf.optimizers.pciopt.pci_opt_processor import process_pci_optimation + import osdf.config.loader as config_loader +from apps.pci.optimizers.pci_opt_processor import process_pci_optimation +from osdf.adapters.local_data import local_policies from osdf.utils.interfaces import json_from_file from osdf.utils.programming_utils import DotDict @@ -31,19 +32,19 @@ class TestProcessPlacementOpt(unittest.TestCase): def setUp(self): mock_req_accept_message = Response("Accepted Request", content_type='application/json; charset=utf-8') - self.patcher_req = patch('osdf.optimizers.pciopt.configdb.request', + self.patcher_req = patch('apps.pci.optimizers.config_request.request', return_value={"solutionInfo": {"placementInfo": "dummy"}}) self.patcher_req_accept = patch('osdf.operation.responses.osdf_response_for_request_accept', return_value=mock_req_accept_message) self.patcher_callback = patch( - 'osdf.optimizers.pciopt.pci_opt_processor.process_pci_optimation', + 'apps.pci.optimizers.pci_opt_processor.process_pci_optimation', return_value=mock_req_accept_message) mock_mzn_response = [{'pci': {0: 0, 1: 1, 2: 2, 3: 3, 4: 0}, 'used_ignorables': [0]}] self.patcher_minizinc_callback = patch( - 'osdf.optimizers.pciopt.solver.optimizer.solve', - return_value=mock_mzn_response ) + 'apps.pci.optimizers.solver.optimizer.solve', + return_value=mock_mzn_response) self.patcher_RestClient = patch( 'osdf.utils.interfaces.RestClient', return_value=mock.MagicMock()) self.Mock_req = self.patcher_req.start() @@ -71,9 +72,8 @@ class TestProcessPlacementOpt(unittest.TestCase): request_json = json_from_file(parameter_data_file) policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files] - templ_string = process_pci_optimation(request_json, self.osdf_config,policies) + templ_string = process_pci_optimation(request_json, self.osdf_config, policies) if __name__ == "__main__": unittest.main() - diff --git a/test/test_process_pci_opt.py b/test/test_process_pci_opt.py index 31aa5fb..d52599d 100644 --- a/test/test_process_pci_opt.py +++ b/test/test_process_pci_opt.py @@ -21,7 +21,7 @@ import unittest from flask import Response from mock import patch from osdf.adapters.local_data import local_policies -from osdf.optimizers.pciopt.pci_opt_processor import process_pci_optimation +from apps.pci.optimizers.pci_opt_processor import process_pci_optimation import osdf.config.loader as config_loader from osdf.utils.interfaces import json_from_file from osdf.utils.programming_utils import DotDict @@ -31,18 +31,18 @@ class TestProcessPlacementOpt(unittest.TestCase): def setUp(self): mock_req_accept_message = Response("Accepted Request", content_type='application/json; charset=utf-8') - self.patcher_req = patch('osdf.optimizers.pciopt.configdb.request', + self.patcher_req = patch('apps.pci.optimizers.config_request.request', return_value={"solutionInfo": {"placementInfo": "dummy"}}) self.patcher_req_accept = patch('osdf.operation.responses.osdf_response_for_request_accept', return_value=mock_req_accept_message) self.patcher_callback = patch( - 'osdf.optimizers.pciopt.pci_opt_processor.process_pci_optimation', + 'apps.pci.optimizers.pci_opt_processor.process_pci_optimation', return_value=mock_req_accept_message) mock_mzn_response = [{'pci': {0: 0, 1: 1, 2: 2}}] self.patcher_minizinc_callback = patch( - 'osdf.optimizers.pciopt.solver.optimizer.solve', + 'apps.pci.optimizers.solver.optimizer.solve', return_value=mock_mzn_response ) self.patcher_RestClient = patch( 'osdf.utils.interfaces.RestClient', return_value=mock.MagicMock()) diff --git a/test/test_process_placement_opt.py b/test/test_process_placement_opt.py index 62a1ce6..8a29100 100644 --- a/test/test_process_placement_opt.py +++ b/test/test_process_placement_opt.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,8 +21,9 @@ import unittest from flask import Response from mock import patch + +from apps.placement.optimizers.conductor.remote_opt_processor import process_placement_opt from osdf.adapters.local_data import local_policies -from osdf.optimizers.placementopt.conductor.remote_opt_processor import process_placement_opt from osdf.utils.interfaces import json_from_file, yaml_from_file @@ -29,12 +31,14 @@ class TestProcessPlacementOpt(unittest.TestCase): def setUp(self): mock_req_accept_message = Response("Accepted Request", content_type='application/json; charset=utf-8') - self.patcher_req = patch('osdf.optimizers.placementopt.conductor.conductor.request', - return_value={"solutionInfo": {"placementInfo": "dummy"}}) + conductor_response_file = 'test/placement-tests/conductor_response.json' + conductor_response = json_from_file(conductor_response_file) + self.patcher_req = patch('osdf.adapters.conductor.conductor.request', + return_value=conductor_response) self.patcher_req_accept = patch('osdf.operation.responses.osdf_response_for_request_accept', return_value=mock_req_accept_message) self.patcher_callback = patch( - 'osdf.optimizers.placementopt.conductor.remote_opt_processor.process_placement_opt', + 'apps.placement.optimizers.conductor.remote_opt_processor.process_placement_opt', return_value=mock_req_accept_message) self.patcher_RestClient = patch( 'osdf.utils.interfaces.RestClient', return_value=mock.MagicMock()) diff --git a/test/test_simple_route_opt.py b/test/test_simple_route_opt.py new file mode 100644 index 0000000..3b4facc --- /dev/null +++ b/test/test_simple_route_opt.py @@ -0,0 +1,57 @@ +from apps.route.optimizers.simple_route_opt import RouteOpt +from osdf.utils.interfaces import json_from_file +from unittest.mock import patch +import osdf.config.loader as config_loader +from osdf.utils.programming_utils import DotDict +import unittest + + +class TestSimpleRouteOptimization(unittest.TestCase): + @patch('apps.route.optimizers.simple_route_opt.requests.get') + @patch('apps.route.optimizers.simple_route_opt.pymzn.minizinc') + def test_process_nst_selection_solutions( self, mock_solve, mock_get): + + main_dir = "" + response_data_file = main_dir + "test/simple_route_opt/AAI.json" + mock_get.return_value.json.return_value = json_from_file(response_data_file) + mock_get.return_value.status_code = 200 + mock_solve.return_value = [{'x': [1, 1, 1]}] + self.config_spec = { + "deployment": "test/functest/simulators/simulated-config/osdf_config.yaml", + "core": "test/functest/simulators/simulated-config/common_config.yaml" + } + self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec)) + parameter_data_file = main_dir + "test/simple_route_opt/routeOpt.json" + request_json = json_from_file(parameter_data_file) + mock_response = { + "requestId": "yyy-yyy-yyyy", + "requestStatus": "accepted", + "solutions": [ + { + "end_node": "10.10.10.10", + "link": "link-id-1", + "start_node": "20.20.20.20" + }, + { + "end_node": "11.11.11.11", + "link": "link-id-2", + "start_node": "22.22.22.22" + }, + { + "end_node": "20.20.20.20", + "link": "link-id-3", + "start_node": "11.11.11.11" + } + ], + "statusMessage": " ", + "transactionId": "xxx-xxx-xxxx" + } + routopt = RouteOpt() + actual_response = routopt.get_route(request_json,self.osdf_config) + self.assertEqual(mock_response, actual_response) + + + +if __name__ == '__main__': + unittest.main() + diff --git a/test/test_so_response_gen.py b/test/test_so_response_gen.py index ab73ef6..1e6079b 100644 --- a/test/test_so_response_gen.py +++ b/test/test_so_response_gen.py @@ -1,5 +1,6 @@ # ------------------------------------------------------------------------- # Copyright (c) 2017-2018 AT&T Intellectual Property +# Copyright (C) 2020 Wipro Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +17,9 @@ # ------------------------------------------------------------------------- # import unittest -import json -import yaml + +from apps.placement.optimizers.conductor.remote_opt_processor import conductor_response_processor from osdf.utils.interfaces import json_from_file -from osdf.optimizers.placementopt.conductor.conductor import conductor_response_processor from osdf.utils.interfaces import RestClient @@ -36,4 +36,4 @@ class TestSoResponseGen(unittest.TestCase): if __name__ == "__main__": - unittest.main()
\ No newline at end of file + unittest.main() @@ -1,25 +1,46 @@ [tox] skipsdist=True -envlist = py3 +envlist = py3, pylint, flake8diff [testenv] distribute = False +basepython=python3 setenv = OSDF_CONFIG_FILE={toxinidir}/test/config/osdf_config.yaml commands = - - cat /etc/hosts + /bin/cp config/slicing_config.yaml test/config/ /bin/bash test/functest/scripts/start-simulators.sh coverage run --module pytest --junitxml xunit-results.xml coverage xml --omit=".tox/py3/*","test/*" coverage report -m --omit=".tox/py3/*","test/*" /bin/bash test/functest/scripts/stop-simulators.sh + /bin/rm test/config/slicing_config.yaml # TODO: need to update the above "omit" when we package osdf as pip-installable deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test/test-requirements.txt + -r{toxinidir}/requirements-osdf.txt + -r{toxinidir}/requirements-opteng.txt [run] -source=./osdf/,osdfapp.py +source=./apps/,./osdf/,osdfapp.py,./runtime/,solverapp.py + +[testenv:pylint] +whitelist_externals=bash +commands = bash -c "pylint --reports=y osdf apps runtime| tee pylint.out" [testenv:py3] -basepython=python3.6 +basepython=python3 + +[testenv:flake8diff] +basepython=python3 +whitelist_externals=bash +deps = hacking>=2.0.0 +commands = + bash -c "files=$(git diff HEAD^ HEAD --diff-filter=d --name-only | grep -E '(^apps\/|osdf\/|runtime\/)'| grep -E '*\.py$'); if [[ -z $files ]]; then exit 0; else flake8 $files; fi" + +[flake8] +select = E,H,W,F +max-line-length = 119 +ignore = W503 #conflict with W504 +per-file-ignores= apps/pci/optimizers/__init__.py:F401 diff --git a/version.properties b/version.properties index 0428858..18c199b 100644 --- a/version.properties +++ b/version.properties @@ -17,9 +17,9 @@ # ------------------------------------------------------------------------- # -major=1 -minor=3 -patch=2 +major=3 +minor=0 +patch=8 base_version=${major}.${minor}.${patch} |