Update candidate list with capacity attributes and version update
[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 import json
25 from oslo_config import cfg
26 from oslo_log import log
27 import time
28 import uuid
29
30 LOG = log.getLogger(__name__)
31
32 CONF = cfg.CONF
33
34 DCAE_OPTS = [
35     cfg.StrOpt('table_prefix',
36                default='dcae',
37                help='Data Store table prefix.'),
38     cfg.StrOpt('server_url',
39                default='https://controller:8443/dcae',
40                help='Base URL for DCAE, up to and not including '
41                'the version, and without a trailing slash.'),
42     cfg.StrOpt('dcae_rest_timeout',
43                default='30',
44                help='Timeout for DCAE Rest Call'),
45     cfg.StrOpt('dcae_retries',
46                default='3',
47                help='Number of retry for DCAE Rest Call'),
48     cfg.StrOpt('server_url_version',
49                default='v1',
50                help='The version of DCAE in v# format.'),
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 DCAE'),
67     cfg.StrOpt('password',
68                default='',
69                help='Password for DCAE'),
70     cfg.StrOpt('get_slice_config_url',
71                default='',
72                help="url to get slice configuration from DCAE")
73 ]
74
75 CONF.register_opts(DCAE_OPTS, group='dcae')
76
77
78 class DCAE(object):
79
80     """DCAE Inventory Provider"""
81
82     def __init__(self):
83         """Initializer"""
84         self.conf = CONF
85         self.base = self.conf.dcae.server_url.rstrip('/')
86         self.version = self.conf.dcae.server_url_version.rstrip('/')
87         self.cert = self.conf.dcae.certificate_file
88         self.key = self.conf.dcae.certificate_key_file
89         self.verify = self.conf.dcae.certificate_authority_bundle_file
90         self.timeout = self.conf.dcae.dcae_rest_timeout
91         self.retries = self.conf.dcae.dcae_retries
92         self.username = self.conf.dcae.username
93         self.password = self.conf.dcae.password
94         self._init_python_request()
95
96     def initialize(self):
97
98         """Perform any late initialization."""
99         # Initialize the Python requests
100         # self._init_python_request()
101
102     def _init_python_request(self):
103
104         kwargs = {
105
106             "server_url": self.base,
107
108             "retries": self.retries,
109
110             "username": self.username,
111
112             "password": self.password,
113
114             "read_timeout": self.timeout,
115
116             "ca_bundle_file": self.verify,
117         }
118
119         self.rest = rest.REST(**kwargs)
120
121     def _dcae_versioned_path(self, path):
122
123         """Return a URL path with the DCAE version prepended"""
124         return '/{}/{}'.format(self.version, path.lstrip('/'))
125
126     def capacity_filter(self, candidates):
127         candidatesList = {}
128         updated_candidateList = []
129         LOG.debug("from AAI ", candidates)
130         for candidate in candidates:
131             inventory_type = candidate.get('inventory_type')
132             candidate_id = candidate.get('candidate_id')
133             domain = candidate.get('domain')
134             response = self.get_dcae_response()
135             # max_no_of_connections = self.get_max_no_of_connections(response)
136             dLThpt = self.get_dLThpt(response, candidate_id)
137             uLThpt = self.get_uLThpt(response, candidate_id)
138             # max_no_of_pdu_sessions = self.get_max_no_of_pdu_sessions()
139             if inventory_type == 'nsi':
140                 uLThpt_ServiceProfile = candidate.get('uLThptPerSlice')
141                 dLThpt_ServiceProfile = candidate.get('dLThptPerSlice')
142                 uLThpt_difference = self.get_difference(uLThpt_ServiceProfile, uLThpt)
143                 dLThpt_difference = self.get_difference(dLThpt_ServiceProfile, dLThpt)
144                 candidate['uLThpt_difference'] = uLThpt_difference
145                 candidate['dLThpt_difference'] = dLThpt_difference
146             elif inventory_type == 'nssi' and (domain != 'tn_fh' and domain != 'tn_mh'):
147                 uLThpt_SliceProfile = candidate.get('exp_data_rate_ul')
148                 dLThpt_SliceProfile = candidate.get('exp_data_rate_dl')
149                 uLThpt_difference = self.get_difference(uLThpt_SliceProfile, uLThpt)
150                 dLThpt_difference = self.get_difference(dLThpt_SliceProfile, dLThpt)
151                 candidate['uLThpt_difference'] = uLThpt_difference
152                 candidate['dLThpt_difference'] = dLThpt_difference
153                 # connections_difference = self.get_difference(max_no_of_pdu_sessions, max_no_of_connections)
154             elif inventory_type == 'nssi' and (domain == 'tn_fh' and domain == 'tn_mh'):
155                 uLThpt_difference = 10
156                 dLThpt_difference = 10
157                 candidate['uLThpt_difference'] = uLThpt_difference
158                 candidate['dLThpt_difference'] = dLThpt_difference
159             else:
160                 LOG.debug("No difference attribute was added to the candidate")
161             candidatesList.update(candidate)
162             LOG.debug("capacity filter ", candidatesList)
163             updated_candidateList.append(candidatesList)
164             LOG.debug("updated candidate list ", updated_candidateList)
165         return updated_candidateList
166         # def get_max_no_of_connections(self, response, candidate_id)
167         #   responseJson = json.loads(response)
168         #   maxNoConns = responseJson['sliceConfigDetails'][candidate_id]['aggregatedConfig']['maxNumberOfConns']
169         #   return maxNoConns
170
171     def get_uLThpt(self, response, candidate_id):
172         responseJson = json.loads(response)
173         configDetails = responseJson["sliceConfigDetails"]
174         for i in range(len(configDetails)):
175             if configDetails[i]["sliceIdentifier"] == candidate_id:
176                 aggregatedConfig = configDetails[i]['aggregatedConfig']
177                 uLThpt = aggregatedConfig.get("uLThptPerSlice")
178         return uLThpt
179
180     def get_dLThpt(self, response, candidate_id):
181         responseJson = json.loads(response)
182         configDetails = responseJson["sliceConfigDetails"]
183         for i in range(len(configDetails)):
184             if configDetails[i]["sliceIdentifier"] == candidate_id:
185                 aggregatedConfig = configDetails[i]['aggregatedConfig']
186                 dLThpt = aggregatedConfig.get("dLThptPerSlice")
187         return dLThpt
188
189     def get_difference(self, attribute1, attribute2):
190         LOG.debug("Computing the difference between two attributes")
191         difference = attribute1 - attribute2
192         return difference
193
194     def _request(self, method='get', path='/', data=None, context=None, value=None):
195
196         """Performs HTTP request"""
197
198         headers = {
199             'X-FromAppId': 'CONDUCTOR',
200             'X-TransactionId': str(uuid.uuid4()),
201         }
202
203         kwargs = {
204             "method": method,
205             "path": path,
206             "headers": headers,
207             "data": data,
208             "content_type": "application/json"
209         }
210         start_time = time.time()
211         response = self.rest.request(**kwargs)
212         elapsed = time.time() - start_time
213         LOG.debug("Total time for DCAE request ({0:}: {1:}): {2:.3f} sec".format(context, value, elapsed))
214         if response is None:
215             LOG.error(_LE("No response from DCAE ({}: {})").format(context, value))
216         elif response.status_code != 200:
217             LOG.error(_LE("DCAE request ({}: {}) returned HTTP status {} {},"
218                           "link: {}{}").format(context, value, response.status_code, response.reason, self.base, path))
219         return response
220
221     def get_dcae_response(self):
222         path = self.conf.dcae.get_slice_config_url
223         dcae_response = self._request('get', path, data=None)
224         if dcae_response is None or dcae_response.status_code != 200:
225             return None
226         if dcae_response:
227             return dcae_response