summaryrefslogtreecommitdiffstats
path: root/conductor/conductor/data/plugins/inventory_provider
diff options
context:
space:
mode:
Diffstat (limited to 'conductor/conductor/data/plugins/inventory_provider')
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/aai.py45
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/candidates/nst_candidate.py29
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/sdc.py188
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py18
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/utils/csar.py98
5 files changed, 377 insertions, 1 deletions
diff --git a/conductor/conductor/data/plugins/inventory_provider/aai.py b/conductor/conductor/data/plugins/inventory_provider/aai.py
index a87cbb6..6ec15da 100644
--- a/conductor/conductor/data/plugins/inventory_provider/aai.py
+++ b/conductor/conductor/data/plugins/inventory_provider/aai.py
@@ -33,11 +33,13 @@ from conductor.data.plugins import constants
from conductor.data.plugins.inventory_provider import base
from conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
from conductor.data.plugins.inventory_provider.candidates.cloud_candidate import Cloud
+from conductor.data.plugins.inventory_provider.candidates.nst_candidate import NST
from conductor.data.plugins.inventory_provider.candidates.nxi_candidate import NxI
from conductor.data.plugins.inventory_provider.candidates.service_candidate import Service
from conductor.data.plugins.inventory_provider.candidates.transport_candidate import Transport
from conductor.data.plugins.inventory_provider.candidates.vfmodule_candidate import VfModule
from conductor.data.plugins.inventory_provider import hpa_utils
+from conductor.data.plugins.inventory_provider.sdc import SDC
from conductor.data.plugins.inventory_provider.utils import aai_utils
from conductor.data.plugins.triage_translator.triage_translator import TraigeTranslator
from conductor.i18n import _LE
@@ -1662,6 +1664,17 @@ class AAI(base.InventoryProviderBase):
default_attributes,
candidate_uniqueness, inventory_type))
+ elif inventory_type == 'nst':
+ if filtering_attributes:
+ second_level_match = aai_utils.get_first_level_and_second_level_filter(filtering_attributes,
+ "nst")
+ aai_response = self.get_nst_response(filtering_attributes)
+
+ sdc_candidates_list = self.get_nst_candidates(aai_response, second_level_match,
+ default_attributes, candidate_uniqueness,
+ inventory_type)
+ resolved_demands[name].extend(SDC().update_candidates(sdc_candidates_list))
+
else:
LOG.error("Unknown inventory_type "
" {}".format(inventory_type))
@@ -1901,3 +1914,35 @@ class AAI(base.InventoryProviderBase):
candidate = nxi_candidate.convert_nested_dict_to_dict()
candidates.append(candidate)
return candidates
+
+ def get_nst_response(self, filtering_attributes):
+ raw_path = 'service-design-and-creation/models' + aai_utils.add_query_params_and_depth(filtering_attributes,
+ "2")
+ path = self._aai_versioned_path(raw_path)
+ aai_response = self._request('get', path, data=None)
+
+ if aai_response is None or aai_response.status_code != 200:
+ return None
+ if aai_response.json():
+ return aai_response.json()
+
+ def get_nst_candidates(self, response_body, filtering_attributes, default_attributes, candidate_uniqueness,
+ type):
+ candidates = list()
+ if response_body is not None:
+ nst_metadatas = response_body.get("model", [])
+
+ for nst_metadata in nst_metadatas:
+ nst_info = aai_utils.get_nst_info(nst_metadata)
+ model_vers = nst_metadata.get('model-vers').get('model-ver')
+ for model_ver in model_vers:
+ model_version_id = model_ver.get('model-version-id')
+ cost = self.conf.data.nst_candidate_cost
+ info = Candidate.build_candidate_info('aai', type, cost, candidate_uniqueness, model_version_id)
+ model_version_obj = aai_utils.get_model_ver_info(model_ver)
+ model_ver_info = aai_utils.convert_hyphen_to_under_score(model_version_obj)
+ nst_candidate = NST(model_info=nst_info, model_ver=model_ver_info, info=info,
+ default_fields=aai_utils.convert_hyphen_to_under_score(default_attributes),
+ profile_info=None)
+ candidates.append(nst_candidate)
+ return candidates
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/nst_candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/nst_candidate.py
new file mode 100644
index 0000000..d5ea251
--- /dev/null
+++ b/conductor/conductor/data/plugins/inventory_provider/candidates/nst_candidate.py
@@ -0,0 +1,29 @@
+#
+# -------------------------------------------------------------------------
+# 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 conductor.data.plugins.inventory_provider.candidates.candidate import Candidate
+
+
+class NST(Candidate):
+ def __init__(self, **kwargs):
+ super().__init__(kwargs['info'])
+ self.nst_info = kwargs['model_info']
+ self.model_ver_info = kwargs['model_ver']
+ self.profile_info = kwargs['profile_info']
+ self.other = kwargs['default_fields']
diff --git a/conductor/conductor/data/plugins/inventory_provider/sdc.py b/conductor/conductor/data/plugins/inventory_provider/sdc.py
new file mode 100644
index 0000000..6f3cb4f
--- /dev/null
+++ b/conductor/conductor/data/plugins/inventory_provider/sdc.py
@@ -0,0 +1,188 @@
+#
+# -------------------------------------------------------------------------
+# 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 conductor.common import rest
+from conductor.data.plugins.inventory_provider.utils import csar
+from conductor.i18n import _LE
+import os
+from oslo_config import cfg
+from oslo_log import log
+import time
+import uuid
+
+
+LOG = log.getLogger(__name__)
+
+CONF = cfg.CONF
+
+SDC_OPTS = [
+ cfg.StrOpt('table_prefix',
+ default='sdc',
+ help='Data Store table prefix.'),
+ cfg.StrOpt('server_url',
+ default='https://controller:8443/sdc',
+ help='Base URL for SDC, up to and not including '
+ 'the version, and without a trailing slash.'),
+ cfg.StrOpt('sdc_rest_timeout',
+ default='30',
+ help='Timeout for SDC Rest Call'),
+ cfg.StrOpt('sdc_retries',
+ default='3',
+ help='Number of retry for SDC Rest Call'),
+ cfg.StrOpt('server_url_version',
+ default='v1',
+ help='The version of SDC in v# format.'),
+ # TODO(larry): follow-up with ONAP people on this (SDC basic auth username and password?)
+ cfg.StrOpt('certificate_file',
+ default='certificate.pem',
+ help='SSL/TLS certificate file in pem format. '
+ 'This certificate must be registered with the A&AI '
+ 'endpoint.'),
+ cfg.StrOpt('certificate_key_file',
+ default='certificate_key.pem',
+ help='Private Certificate Key file in pem format.'),
+ cfg.StrOpt('certificate_authority_bundle_file',
+ default='certificate_authority_bundle.pem',
+ help='Certificate Authority Bundle file in pem format. '
+ 'Must contain the appropriate trust chain for the '
+ 'Certificate file.'),
+ cfg.StrOpt('username',
+ default='',
+ help='Username for SDC.'),
+ cfg.StrOpt('password',
+ default='',
+ help='Password for SDC.'),
+ cfg.StrOpt('temp_path',
+ default=',',
+ help="path to store nst templates")
+]
+
+CONF.register_opts(SDC_OPTS, group='sdc')
+
+
+class SDC(object):
+ """SDC Inventory Provider"""
+
+ def __init__(self):
+ """Initializer"""
+
+ self.conf = CONF
+
+ self.base = self.conf.sdc.server_url.rstrip('/')
+ self.version = self.conf.sdc.server_url_version.rstrip('/')
+ self.cert = self.conf.sdc.certificate_file
+ self.key = self.conf.sdc.certificate_key_file
+ self.verify = self.conf.sdc.certificate_authority_bundle_file
+ self.timeout = self.conf.sdc.sdc_rest_timeout
+ self.retries = self.conf.sdc.sdc_retries
+ self.username = self.conf.sdc.username
+ self.password = self.conf.sdc.password
+ self._init_python_request()
+
+ def initialize(self):
+
+ """Perform any late initialization."""
+ # Initialize the Python requests
+ # self._init_python_request()
+
+ def _sdc_versioned_path(self, path):
+ """Return a URL path with the SDC version prepended"""
+ return '/{}/{}'.format(self.version, path.lstrip('/'))
+
+ def _request(self, method='get', path='/', data=None,
+ context=None, value=None):
+ """Performs HTTP request."""
+ headers = {
+ 'X-FromAppId': 'CONDUCTOR',
+ 'X-TransactionId': str(uuid.uuid4()),
+ }
+ kwargs = {
+ "method": method,
+ "path": path,
+ "headers": headers,
+ "data": data,
+ }
+
+ # TODO(jdandrea): Move timing/response logging into the rest helper?
+ start_time = time.time()
+ response = self.rest.request(**kwargs)
+ elapsed = time.time() - start_time
+ LOG.debug("Total time for SDC request "
+ "({0:}: {1:}): {2:.3f} sec".format(context, value, elapsed))
+
+ if response is None:
+ LOG.error(_LE("No response from SDC ({}: {})").
+ format(context, value))
+ elif response.status_code != 200:
+ LOG.error(_LE("SDC request ({}: {}) returned HTTP "
+ "status {} {}, link: {}{}").
+ format(context, value,
+ response.status_code, response.reason,
+ self.base, path))
+ return response
+
+ def _init_python_request(self):
+
+ kwargs = {
+ "server_url": self.base,
+ "retries": self.retries,
+ "username": self.username,
+ "password": self.password,
+ "read_timeout": self.timeout,
+ }
+ self.rest = rest.REST(**kwargs)
+
+ def update_candidates(self, candidates):
+ absfilepath = self.conf.sdc.temp_path
+ candidateslist = []
+ for candidate in candidates:
+ model_ver_obj = candidate.model_ver_info
+ model_name = model_ver_obj['model_name']
+ self.model_version_id = candidate.candidate_id
+ response = self.get_nst_template(self.model_version_id)
+ filepath = os.path.join(absfilepath, "{}.csar".format(self.model_version_id))
+ if not os.path.exists(absfilepath):
+ os.makedirs(absfilepath)
+ f = open(filepath, "wb")
+ file_res = response.content
+ f.write(file_res)
+ obj = csar.SDCCSAR(filepath, model_name)
+ nst_temp_prop = obj.validate()
+ nst_properties = self.get_nst_prop_dict(nst_temp_prop)
+ candidate.profile_info = nst_properties
+ finalcandidate = candidate.convert_nested_dict_to_dict()
+ candidateslist.append(finalcandidate)
+ return candidateslist
+
+ def get_nst_prop_dict(self, nst_properties):
+ properties_dict = dict()
+ for key in list(nst_properties):
+ temp_dict = nst_properties[key]
+ for temp_key in list(temp_dict):
+ if "default" in temp_key:
+ properties_dict[key] = temp_dict[temp_key]
+ return properties_dict
+
+ def get_nst_template(self, ver_id):
+ raw_path = "/catalog/services/{}/toscaModel".format(ver_id)
+ path = self._sdc_versioned_path(raw_path)
+ sdc_response = self._request('get', path, data=None)
+ if sdc_response is None or sdc_response.status_code != 200:
+ return None
+ if sdc_response:
+ return sdc_response
diff --git a/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py b/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py
index ee870d4..4c76645 100644
--- a/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py
+++ b/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py
@@ -19,7 +19,8 @@
QUERY_PARAMS = {'service_instance': ["service-instance-id", "service-instance-name", "environment-context",
"workload-context", "model-invariant-id", "model-version-id", "widget-model-id",
- "widget-model-version", "service-instance-location-id", "orchestration-status"]
+ "widget-model-version", "service-instance-location-id", "orchestration-status"],
+ 'nst': ["model-role"]
}
@@ -79,3 +80,18 @@ def get_instance_info(nxi_instance):
if nxi_instance.get('workload-context'):
nxi_dict['domain'] = nxi_instance.get('workload-context')
return nxi_dict
+
+
+def get_nst_info(nst_instance):
+ nst_dict = {}
+ nst_dict['model_invariant_id'] = nst_instance.get('model-invariant-id')
+ nst_dict['model_type'] = nst_instance.get('model-type')
+ nst_dict['model_role'] = nst_instance.get('model-role')
+ return nst_dict
+
+
+def get_model_ver_info(model_version):
+ for key in list(model_version):
+ if "model-elements" in key:
+ del model_version["model-elements"]
+ return model_version
diff --git a/conductor/conductor/data/plugins/inventory_provider/utils/csar.py b/conductor/conductor/data/plugins/inventory_provider/utils/csar.py
new file mode 100644
index 0000000..0caccac
--- /dev/null
+++ b/conductor/conductor/data/plugins/inventory_provider/utils/csar.py
@@ -0,0 +1,98 @@
+
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 os.path
+import requests
+from toscaparser.common.exception import ExceptionCollector
+from toscaparser.common.exception import ValidationError
+from toscaparser.prereq.csar import CSAR
+from toscaparser.utils.gettextutils import _
+from toscaparser.utils.urlutils import UrlUtils
+from toscaparser.utils import yamlparser
+import zipfile
+
+
+try: # Python 2.x
+ from BytesIO import BytesIO
+except ImportError: # Python 3.x
+ from io import BytesIO
+
+TOSCA_META = 'TOSCA-Metadata/TOSCA.meta'
+YAML_LOADER = yamlparser.load_yaml
+
+
+class SDCCSAR(CSAR):
+ def __init__(self, csar_file, model_name, a_file=True):
+ super(SDCCSAR, self).__init__(csar_file, a_file=True)
+ self.model_name = model_name
+
+ def validate(self):
+ """Validate the provided CSAR file."""
+ self.is_validated = True
+ # validate that the file or URL exists
+ missing_err_msg = (_('"%s" does not exist.') % self.path)
+ if self.a_file:
+ if not os.path.isfile(self.path):
+ ExceptionCollector.appendException(
+ ValidationError(message=missing_err_msg))
+ return False
+ else:
+ self.csar = self.path
+ else: # a URL
+ if not UrlUtils.validate_url(self.path):
+ ExceptionCollector.appendException(
+ ValidationError(message=missing_err_msg))
+ return False
+ else:
+ response = requests.get(self.path)
+ self.csar = BytesIO(response.content)
+
+ # validate that it is a valid zip file
+ if not zipfile.is_zipfile(self.csar):
+ err_msg = (_('"%s" is not a valid zip file.') % self.path)
+ ExceptionCollector.appendException(
+ ValidationError(message=err_msg))
+ return False
+
+ # validate that it contains the metadata file in the correct location
+ self.zfile = zipfile.ZipFile(self.csar, 'r')
+ filelist = self.zfile.namelist()
+ if TOSCA_META in filelist:
+ self.is_tosca_metadata = True
+ # validate that 'Entry-Definitions' property exists in TOSCA.meta
+ is_validated = self._validate_tosca_meta(filelist)
+ else:
+ self.is_tosca_metadata = False
+ is_validated = self._validate_root_level_yaml(filelist)
+
+ if is_validated:
+ main_tpl = self._read_template_yaml(self.main_template_file_name)
+ nst_properies_res = self.get_nst_properties(main_tpl)
+ print("nst properties", nst_properies_res)
+ return nst_properies_res
+
+ def get_nst_properties(self, main_tpl):
+ importsarr = main_tpl.get('imports')
+ for imports in importsarr:
+ for key in imports:
+ if "service-{}-interface".format(self.model_name) in key:
+ val = imports[key]
+ filename = val.get("file")
+ datanew = self._read_template_yaml("Definitions/" + filename)
+ node_types = datanew.get("node_types")
+ for key in list(node_types):
+ if "org.openecomp" in key:
+ nodedata = node_types[key]
+ nst_properties = nodedata.get("properties")
+ return nst_properties