4b6d8d169be03770a45ac364b4dac7a18c9e909e
[optf/has.git] / conductor / conductor / data / plugins / inventory_provider / dcae.py
1 #
2 #
3 # -------------------------------------------------------------------------
4 #   Copyright (C) 2022 Wipro Limited.
5 #
6 #   Licensed under the Apache License, Version 2.0 (the "License");
7 #   you may not use this file except in compliance with the License.
8 #   You may obtain a copy of the License at
9 #
10 #       http://www.apache.org/licenses/LICENSE-2.0
11 #
12 #   Unless required by applicable law or agreed to in writing, software
13 #   distributed under the License is distributed on an "AS IS" BASIS,
14 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 #   See the License for the specific language governing permissions and
16 #   limitations under the License.
17 #
18 # -------------------------------------------------------------------------
19 #
20
21
22 from conductor.common import rest
23 from conductor.i18n import _LE
24 from http.client import HTTPConnection
25 import json
26 import logging
27 from oslo_config import cfg
28 from oslo_log import log
29 import time
30 import uuid
31
32 LOG = log.getLogger(__name__)
33
34 log = logging.getLogger('urllib3')
35 log.setLevel(logging.DEBUG)
36 ch = logging.StreamHandler()
37 ch.setLevel(logging.DEBUG)
38 log.addHandler(ch)
39 HTTPConnection.debuglevel = 1
40
41 CONF = cfg.CONF
42
43 DCAE_OPTS = [
44     cfg.StrOpt('table_prefix',
45                default='dcae',
46                help='Data Store table prefix.'),
47     cfg.StrOpt('server_url',
48                default='https://controller:8443/dcae',
49                help='Base URL for DCAE, up to and not including '
50                'the version, and without a trailing slash.'),
51     cfg.StrOpt('dcae_rest_timeout',
52                default='30',
53                help='Timeout for DCAE Rest Call'),
54     cfg.StrOpt('dcae_retries',
55                default='3',
56                help='Number of retry for DCAE Rest Call'),
57     cfg.StrOpt('server_url_version',
58                default='v1',
59                help='The version of DCAE in v# format.'),
60     cfg.StrOpt('certificate_file',
61                default='certificate.pem',
62                help='SSL/TLS certificate file in pem format.'
63                'This certificate must be registered with the A&AI '
64                'endpoint.'),
65     cfg.StrOpt('certificate_key_file',
66                default='certificate_key.pem',
67                help='Private Certificate Key file in pem format.'),
68     cfg.StrOpt('certificate_authority_bundle_file',
69                default='',
70                help='Certificate Authority Bundle file in pem format. '
71                'Must contain the appropriate trust chain for the '
72                'Certificate file.'),
73     cfg.StrOpt('username',
74                default='',
75                help='Username for DCAE'),
76     cfg.StrOpt('password',
77                default='',
78                help='Password for DCAE'),
79     cfg.StrOpt('get_slice_config_url',
80                default='',
81                help="url to get slice configuration from DCAE")
82 ]
83
84 CONF.register_opts(DCAE_OPTS, group='dcae')
85
86
87 class DCAE(object):
88
89     """DCAE Inventory Provider"""
90
91     def __init__(self):
92         """Initializer"""
93         self.conf = CONF
94         self.base = self.conf.dcae.server_url.rstrip('/')
95         self.version = self.conf.dcae.server_url_version.rstrip('/')
96         self.cert = self.conf.dcae.certificate_file
97         self.key = self.conf.dcae.certificate_key_file
98         self.verify = self.conf.dcae.certificate_authority_bundle_file
99         self.timeout = self.conf.dcae.dcae_rest_timeout
100         self.retries = self.conf.dcae.dcae_retries
101         self.username = self.conf.dcae.username
102         self.password = self.conf.dcae.password
103         self._init_python_request()
104
105     def initialize(self):
106
107         """Perform any late initialization."""
108         # Initialize the Python requests
109         # self._init_python_request()
110
111     def _init_python_request(self):
112
113         kwargs = {
114
115             "server_url": self.base,
116
117             "retries": self.retries,
118
119             "username": self.username,
120
121             "password": self.password,
122
123             "read_timeout": self.timeout,
124
125             "ca_bundle_file": self.verify,
126         }
127
128         self.rest = rest.REST(**kwargs)
129
130     def _dcae_versioned_path(self, path):
131
132         """Return a URL path with the DCAE version prepended"""
133         return '/{}/{}'.format(self.version, path.lstrip('/'))
134
135     def capacity_filter(self, candidates):
136         candidatesList = {}
137         updated_candidateList = []
138         LOG.debug("from AAI ", candidates)
139         for candidate in candidates:
140             inventory_type = candidate.get('inventory_type')
141             if inventory_type == 'nsi':
142                 candidate_id = candidate.get('instance_id')
143             elif inventory_type == 'nssi':
144                 candidate_id = candidate.get('instance_id')
145             else:
146                 LOG.debug("No candidate_id found")
147             domain = candidate.get('domain')
148             LOG.debug("domain from the candidate list is ", domain)
149             response = self.get_dcae_response(candidate_id)
150             LOG.debug(" DCAE response in capacity_filter() : ", response)
151             # max_no_of_connections = self.get_max_no_of_connections(response)
152             if response is not None:
153                 dLThpt = self.get_dLThpt(response, candidate_id)
154                 LOG.debug("dLThpt fetched from dcae response is", dLThpt)
155                 uLThpt = self.get_uLThpt(response, candidate_id)
156                 LOG.debug("uLThpt fetched from dcae response is", uLThpt)
157                 # max_no_of_pdu_sessions = self.get_max_no_of_pdu_sessions()
158                 if inventory_type == 'nsi':
159                     uLThpt_ServiceProfile = candidate.get('ul_thpt_per_slice')
160                     LOG.debug("uLThpt fetched from service profile is", uLThpt_ServiceProfile)
161                     dLThpt_ServiceProfile = candidate.get('dl_thpt_per_slice')
162                     LOG.debug("dLThpt fetched from service profile is", dLThpt_ServiceProfile)
163                     uLThpt_difference = self.get_difference(uLThpt_ServiceProfile, uLThpt)
164                     LOG.debug(" uLThpt_difference for nsi is ", uLThpt_difference)
165                     dLThpt_difference = self.get_difference(dLThpt_ServiceProfile, dLThpt)
166                     LOG.debug(" dLThpt_difference for nsi is ", dLThpt_difference)
167                     candidate['uLThpt_difference'] = uLThpt_difference
168                     candidate['dLThpt_difference'] = dLThpt_difference
169                 elif inventory_type == 'nssi' and (domain != 'TN_FH' and domain != 'TN_MH'):
170                     uLThpt_SliceProfile = candidate.get('exp_data_rate_ul')
171                     LOG.debug("uLThpt fetched from slice profile is", uLThpt_SliceProfile)
172                     dLThpt_SliceProfile = candidate.get('exp_data_rate_dl')
173                     LOG.debug("dLThpt fetched from slice profile is", dLThpt_SliceProfile)
174                     uLThpt_difference = self.get_difference(uLThpt_SliceProfile, uLThpt)
175                     LOG.debug(" uLThpt_difference for nssi is ", uLThpt_difference)
176                     dLThpt_difference = self.get_difference(dLThpt_SliceProfile, dLThpt)
177                     LOG.debug(" dLThpt_difference for nssi is ", dLThpt_difference)
178                     candidate['uLThpt_difference'] = uLThpt_difference
179                     candidate['dLThpt_difference'] = dLThpt_difference
180                     # connections_difference = self.get_difference(max_no_of_pdu_sessions, max_no_of_connections)
181                 elif inventory_type == 'nssi' and (domain == 'TN_FH' and domain == 'TN_MH'):
182                     uLThpt_difference = 10
183                     dLThpt_difference = 10
184                     candidate['uLThpt_difference'] = uLThpt_difference
185                     candidate['dLThpt_difference'] = dLThpt_difference
186                 else:
187                     LOG.debug("No difference attribute was added to the candidate")
188             else:
189                 candidate["ulthpt_difference"] = 2
190                 candidate["dlthpt_difference"] = 2
191                 LOG.debug("Returning original candidate list")
192             candidatesList.update(candidate)
193             LOG.debug("capacity filter ", candidatesList)
194             updated_candidateList.append(candidatesList)
195             LOG.debug("updated candidate list ", updated_candidateList)
196         return updated_candidateList
197         # def get_max_no_of_connections(self, response, candidate_id)
198         #   responseJson = json.loads(response)
199         #   maxNoConns = responseJson['sliceConfigDetails'][candidate_id]['aggregatedConfig']['maxNumberOfConns']
200         #   return maxNoConns
201
202     def get_uLThpt(self, response, candidate_id):
203         responseJson = json.loads(response)
204         configDetails = responseJson["sliceConfigDetails"]
205         for i in range(len(configDetails)):
206             if configDetails[i]["sliceIdentifiers"] == candidate_id:
207                 aggregatedConfig = configDetails[i]['aggregatedConfig']
208                 uLThpt = aggregatedConfig.get("ulthptPerSlice")
209                 LOG.debug(" uLthpt from DCAE is : ", uLThpt)
210         return uLThpt
211
212     def get_dLThpt(self, response, candidate_id):
213         responseJson = json.loads(response)
214         configDetails = responseJson["sliceConfigDetails"]
215         for i in range(len(configDetails)):
216             if configDetails[i]["sliceIdentifiers"] == candidate_id:
217                 aggregatedConfig = configDetails[i]['aggregatedConfig']
218                 dLThpt = aggregatedConfig.get("dlthptPerSlice")
219                 LOG.debug(" dLthpt from DCAE is : ", dLThpt)
220         return dLThpt
221
222     def get_difference(self, attribute1, attribute2):
223         LOG.debug("Computing the difference between two attributes")
224         difference = attribute1 - attribute2
225         return difference
226
227     def _request(self, method='get', path='/', data=None, context=None, value=None):
228
229         """Performs HTTP request"""
230
231         headers = {
232             'X-FromAppId': 'CONDUCTOR',
233             'X-TransactionId': str(uuid.uuid4()),
234         }
235
236         kwargs = {
237             "method": method,
238             "path": path,
239             "headers": headers,
240             "data": data,
241             "content_type": "application/json"
242         }
243         start_time = time.time()
244         response = self.rest.request(**kwargs)
245         elapsed = time.time() - start_time
246         LOG.debug("Total time for DCAE request ({0:}: {1:}): {2:.3f} sec".format(context, value, elapsed))
247         if response is None:
248             LOG.error(_LE("No response from DCAE ({}: {})").format(context, value))
249         elif response.status_code != 200:
250             LOG.error(_LE("DCAE request ({}: {}) returned HTTP status {} {},"
251                           "link: {}{}").format(context, value, response.status_code, response.reason, self.base, path))
252         return response
253
254     def get_dcae_response(self, candidate_id):
255         path = self.conf.dcae.get_slice_config_url
256         data = {"sliceIdentifiers": [candidate_id], "configParams": ["dLThptPerSlice",
257                 "uLThptPerSlice", "maxNumberOfConns"]}
258         dcae_response = self._request('get', path, data=data)
259         LOG.debug(self._request('get', path, data=data))
260         LOG.debug(" DCAE response : ", dcae_response)
261         if dcae_response is None or dcae_response.status_code != 200:
262             return None
263         if dcae_response:
264             LOG.debug("DCAE json response is : ", json.dumps(dcae_response.json()))
265             dcae_response2 = json.dumps(dcae_response.json())
266             LOG.debug(" processed DCAE response is ", dcae_response2)
267             responseJson = json.loads(dcae_response2)
268             LOG.debug("response json from DCAE is :", responseJson)
269             if 'sliceConfigDetails' not in responseJson or len(responseJson['sliceConfigDetails']) == 0:
270                 LOG.debug(" Returning None to capacity_filter()")
271                 return None
272             else:
273                 LOG.debug("returning DCAE response to capacity_filter() from get_dcae_response()")
274                 return dcae_response2