3 # -------------------------------------------------------------------------
4 # Copyright (C) 2022 Wipro Limited.
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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.
18 # -------------------------------------------------------------------------
22 from conductor.common import rest
23 from conductor.i18n import _LE
24 from http.client import HTTPConnection
27 from oslo_config import cfg
28 from oslo_log import log
32 LOG = log.getLogger(__name__)
34 log = logging.getLogger('urllib3')
35 log.setLevel(logging.DEBUG)
36 ch = logging.StreamHandler()
37 ch.setLevel(logging.DEBUG)
39 HTTPConnection.debuglevel = 1
44 cfg.StrOpt('table_prefix',
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',
53 help='Timeout for DCAE Rest Call'),
54 cfg.StrOpt('dcae_retries',
56 help='Number of retry for DCAE Rest Call'),
57 cfg.StrOpt('server_url_version',
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 '
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',
70 help='Certificate Authority Bundle file in pem format. '
71 'Must contain the appropriate trust chain for the '
73 cfg.StrOpt('username',
75 help='Username for DCAE'),
76 cfg.StrOpt('password',
78 help='Password for DCAE'),
79 cfg.StrOpt('get_slice_config_url',
81 help="url to get slice configuration from DCAE")
84 CONF.register_opts(DCAE_OPTS, group='dcae')
89 """DCAE Inventory Provider"""
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()
105 def initialize(self):
107 """Perform any late initialization."""
108 # Initialize the Python requests
109 # self._init_python_request()
111 def _init_python_request(self):
115 "server_url": self.base,
117 "retries": self.retries,
119 "username": self.username,
121 "password": self.password,
123 "read_timeout": self.timeout,
125 "ca_bundle_file": self.verify,
128 self.rest = rest.REST(**kwargs)
130 def _dcae_versioned_path(self, path):
132 """Return a URL path with the DCAE version prepended"""
133 return '/{}/{}'.format(self.version, path.lstrip('/'))
135 def capacity_filter(self, candidates):
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')
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
187 LOG.debug("No difference attribute was added to the candidate")
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']
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)
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)
222 def get_difference(self, attribute1, attribute2):
223 LOG.debug("Computing the difference between two attributes")
224 difference = attribute1 - attribute2
227 def _request(self, method='get', path='/', data=None, context=None, value=None):
229 """Performs HTTP request"""
232 'X-FromAppId': 'CONDUCTOR',
233 'X-TransactionId': str(uuid.uuid4()),
241 "content_type": "application/json"
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))
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))
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:
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()")
273 LOG.debug("returning DCAE response to capacity_filter() from get_dcae_response()")
274 return dcae_response2