Fix for has to communicate with SDC for Nst-selection enhancements
[optf/has.git] / conductor / conductor / data / plugins / inventory_provider / sdc.py
1 #
2 # -------------------------------------------------------------------------
3 #   Copyright (C) 2020 Wipro Limited.
4 #
5 #   Licensed under the Apache License, Version 2.0 (the "License");
6 #   you may not use this file except in compliance with the License.
7 #   You may obtain a copy of the License at
8 #
9 #       http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #   Unless required by applicable law or agreed to in writing, software
12 #   distributed under the License is distributed on an "AS IS" BASIS,
13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 #   See the License for the specific language governing permissions and
15 #   limitations under the License.
16 #
17 # -------------------------------------------------------------------------
18 #
19 from conductor.common import rest
20 from conductor.data.plugins.inventory_provider.utils import csar
21 from conductor.i18n import _LE
22 import os
23 from oslo_config import cfg
24 from oslo_log import log
25 import time
26 import uuid
27
28
29 LOG = log.getLogger(__name__)
30
31 CONF = cfg.CONF
32
33 SDC_OPTS = [
34     cfg.StrOpt('table_prefix',
35                default='sdc',
36                help='Data Store table prefix.'),
37     cfg.StrOpt('server_url',
38                default='https://controller:8443/sdc',
39                help='Base URL for SDC, up to and not including '
40                     'the version, and without a trailing slash.'),
41     cfg.StrOpt('sdc_rest_timeout',
42                default='30',
43                help='Timeout for SDC Rest Call'),
44     cfg.StrOpt('sdc_retries',
45                default='3',
46                help='Number of retry for SDC Rest Call'),
47     cfg.StrOpt('server_url_version',
48                default='v1',
49                help='The version of SDC in v# format.'),
50     # TODO(larry): follow-up with ONAP people on this (SDC basic auth username and password?)
51     cfg.StrOpt('certificate_file',
52                default='certificate.pem',
53                help='SSL/TLS certificate file in pem format. '
54                     'This certificate must be registered with the A&AI '
55                     'endpoint.'),
56     cfg.StrOpt('certificate_key_file',
57                default='certificate_key.pem',
58                help='Private Certificate Key file in pem format.'),
59     cfg.StrOpt('certificate_authority_bundle_file',
60                default='',
61                help='Certificate Authority Bundle file in pem format. '
62                     'Must contain the appropriate trust chain for the '
63                     'Certificate file.'),
64     cfg.StrOpt('username',
65                default='',
66                help='Username for SDC.'),
67     cfg.StrOpt('password',
68                default='',
69                help='Password for SDC.'),
70     cfg.StrOpt('temp_path',
71                default=',',
72                help="path to store nst templates")
73 ]
74
75 CONF.register_opts(SDC_OPTS, group='sdc')
76
77
78 class SDC(object):
79     """SDC Inventory Provider"""
80
81     def __init__(self):
82         """Initializer"""
83
84         self.conf = CONF
85
86         self.base = self.conf.sdc.server_url.rstrip('/')
87         self.version = self.conf.sdc.server_url_version.rstrip('/')
88         self.cert = self.conf.sdc.certificate_file
89         self.key = self.conf.sdc.certificate_key_file
90         self.verify = self.conf.sdc.certificate_authority_bundle_file
91         self.timeout = self.conf.sdc.sdc_rest_timeout
92         self.retries = self.conf.sdc.sdc_retries
93         self.username = self.conf.sdc.username
94         self.password = self.conf.sdc.password
95         self._init_python_request()
96
97     def initialize(self):
98
99         """Perform any late initialization."""
100         # Initialize the Python requests
101         # self._init_python_request()
102
103     def _sdc_versioned_path(self, path):
104         """Return a URL path with the SDC version prepended"""
105         return '/{}/{}'.format(self.version, path.lstrip('/'))
106
107     def _request(self, method='get', path='/', data=None,
108                  context=None, value=None):
109         """Performs HTTP request."""
110         headers = {
111             'X-FromAppId': 'AAI',
112             'X-TransactionId': str(uuid.uuid4()),
113             'X-ECOMP-InstanceID': 'AAI',
114         }
115         kwargs = {
116             "method": method,
117             "path": path,
118             "headers": headers,
119             "data": data,
120             "content_type": "application/octet-stream"
121         }
122
123         # TODO(jdandrea): Move timing/response logging into the rest helper?
124         start_time = time.time()
125         response = self.rest.request(**kwargs)
126         elapsed = time.time() - start_time
127         LOG.debug("Total time for SDC request "
128                   "({0:}: {1:}): {2:.3f} sec".format(context, value, elapsed))
129
130         if response is None:
131             LOG.error(_LE("No response from SDC ({}: {})").
132                       format(context, value))
133         elif response.status_code != 200:
134             LOG.error(_LE("SDC request ({}: {}) returned HTTP "
135                           "status {} {}, link: {}{}").
136                       format(context, value,
137                              response.status_code, response.reason,
138                              self.base, path))
139         return response
140
141     def _init_python_request(self):
142
143         kwargs = {
144             "server_url": self.base,
145             "retries": self.retries,
146             "username": self.username,
147             "password": self.password,
148             "read_timeout": self.timeout,
149             "ca_bundle_file": self.verify,
150         }
151         self.rest = rest.REST(**kwargs)
152
153     def update_candidates(self, candidates):
154         absfilepath = self.conf.sdc.temp_path
155         candidateslist = []
156         for candidate in candidates:
157             model_ver_obj = candidate.model_ver_info
158             model_name = model_ver_obj['model_name']
159             self.model_version_id = candidate.candidate_id
160             response = self.get_nst_template(self.model_version_id)
161             filepath = os.path.join(absfilepath, "{}.csar".format(self.model_version_id))
162             if not os.path.exists(absfilepath):
163                 os.makedirs(absfilepath)
164             f = open(filepath, "wb")
165             file_res = response.content
166             f.write(file_res)
167             obj = csar.SDCCSAR(filepath, model_name)
168             nst_temp_prop = obj.validate()
169             nst_properties = self.get_nst_prop_dict(nst_temp_prop)
170             candidate.profile_info = nst_properties
171             finalcandidate = candidate.convert_nested_dict_to_dict()
172             candidateslist.append(finalcandidate)
173         return candidateslist
174
175     def get_nst_prop_dict(self, nst_properties):
176         properties_dict = dict()
177         for key in list(nst_properties):
178             temp_dict = nst_properties[key]
179             for temp_key in list(temp_dict):
180                 if "default" in temp_key:
181                     properties_dict[key] = temp_dict[temp_key]
182         return properties_dict
183
184     def get_nst_template(self, ver_id):
185         raw_path = "/catalog/services/{}/toscaModel".format(ver_id)
186         path = self._sdc_versioned_path(raw_path)
187         sdc_response = self._request('get', path, data=None)
188         if sdc_response is None or sdc_response.status_code != 200:
189             return None
190         if sdc_response:
191             return sdc_response