1 # Copyright (c) 2018 Amdocs
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 from multicloud_azure.pub.config.config import AAI_SCHEMA_VERSION
22 from multicloud_azure.pub.config.config import AAI_SERVICE_URL
23 from multicloud_azure.pub.config.config import AAI_USERNAME
24 from multicloud_azure.pub.config.config import AAI_PASSWORD
25 from multicloud_azure.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT
26 from multicloud_azure.pub.config.config import ARIA_SERVER_URL
28 from multicloud_azure.pub.exceptions import VimDriverAzureException
30 rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2
31 HTTP_200_OK, HTTP_201_CREATED = '200', '201'
32 HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED = '204', '202'
33 status_ok_list = [HTTP_200_OK, HTTP_201_CREATED,
34 HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED]
35 HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN = '404', '403'
36 HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '401', '400'
38 logger = logging.getLogger(__name__)
41 def call_req(base_url, user, passwd, auth_type, resource, method, content='',
43 callid = str(uuid.uuid1())
44 # logger.debug("[%s]call_req('%s','%s','%s',%s,'%s','%s','%s')" % (
45 # callid, base_url, user, passwd, auth_type, resource, method, content))
52 full_url = combine_url(base_url, resource)
55 headers['content-type'] = 'application/json'
58 headers['Authorization'] = 'Basic ' + \
59 ('%s:%s' % (user, passwd)).encode("base64")
61 for retry_times in range(3):
64 disable_ssl_certificate_validation=(
65 auth_type == rest_no_auth))
66 http.follow_all_redirects = True
68 logger.debug("request=%s" % full_url)
69 resp, resp_content = http.request(
70 full_url, method=method.upper(), body=content,
72 resp_status = resp['status']
73 resp_body = resp_content.decode('UTF-8')
75 if resp_status in status_ok_list:
76 ret = [0, resp_body, resp_status, resp]
78 ret = [1, resp_body, resp_status, resp]
80 except Exception as ex:
81 if 'httplib.ResponseNotReady' in str(sys.exc_info()):
82 logger.error(traceback.format_exc())
83 ret = [1, "Unable to connect to %s" % full_url,
87 except urllib2.URLError as err:
88 ret = [2, str(err), resp_status, resp]
89 except Exception as ex:
90 logger.error(traceback.format_exc())
91 logger.error("[%s]ret=%s" % (callid, str(sys.exc_info())))
92 res_info = str(sys.exc_info())
93 if 'httplib.ResponseNotReady' in res_info:
94 res_info = ("The URL[%s] request failed or is not responding." %
96 ret = [3, res_info, resp_status, resp]
97 # logger.debug("[%s]ret=%s" % (callid, str(ret)))
101 def req_by_msb(resource, method, content=''):
102 base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT)
103 return call_req(base_url, "", "", rest_no_auth, resource, method, content)
106 def combine_url(base_url, resource):
108 if base_url.endswith('/') and resource.startswith('/'):
109 full_url = base_url[:-1] + resource
110 elif base_url.endswith('/') and not resource.startswith('/'):
111 full_url = base_url + resource
112 elif not base_url.endswith('/') and resource.startswith('/'):
113 full_url = base_url + resource
115 full_url = base_url + '/' + resource
119 def get_res_from_aai(resource, content=''):
121 'X-FromAppId': 'MultiCloud',
122 'X-TransactionId': '9001',
123 'content-type': 'application/json',
124 'accept': 'application/json'
126 base_url = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
127 return call_req(base_url, AAI_USERNAME, AAI_PASSWORD, rest_no_auth,
128 resource, "GET", content, headers)
131 class AAIClient(object):
132 def __init__(self, cloud_owner, cloud_region):
133 self.base_url = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
134 self.username = AAI_USERNAME
135 self.password = AAI_PASSWORD
136 self.default_headers = {
137 'X-FromAppId': 'multicloud-azure',
138 'X-TransactionId': '9004',
139 'content-type': 'application/json',
140 'accept': 'application/json'
142 self.cloud_owner = cloud_owner
143 self.cloud_region = cloud_region
144 self._vim_info = None
146 def get_vim(self, get_all=False):
147 resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
148 "/%s/%s" % (self.cloud_owner, self.cloud_region))
150 resource = "%s?depth=all" % resource
151 resp = call_req(self.base_url, self.username, self.password,
152 rest_no_auth, resource, "GET",
153 headers=self.default_headers)
155 raise VimDriverAzureException(
157 content="Failed to query VIM with id (%s_%s) from extsys." % (
158 self.cloud_owner, self.cloud_region))
159 return json.loads(resp[1])
161 def delete_vim(self):
162 resp = self.get_vim(get_all=True)
163 logger.debug('Delete cloud region')
164 resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
165 "/%s/%s?resource-version=%s" %
166 (self.cloud_owner, self.cloud_region,
167 resp['resource-version']))
168 resp = call_req(self.base_url, self.username, self.password,
169 rest_no_auth, resource, "DELETE",
170 headers=self.default_headers)
172 raise VimDriverAzureException(
174 content="Failed to delete cloud %s_%s: %s." % (
175 self.cloud_owner, self.cloud_region, resp[1]))
177 def update_vim(self, content):
178 self.add_flavors(content)
180 def update_identity_url(self):
182 vim['identity-url'] = ("http://%s/api/multicloud/v0/%s_%s/identity/"
183 "v3" % (MSB_SERVICE_IP, self.cloud_owner,
185 resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
186 "/%s/%s" % (self.cloud_owner, self.cloud_region))
187 logger.debug("Updating identity url %s" % vim)
188 call_req(self.base_url, self.username, self.password,
189 rest_no_auth, resource, "PUT",
190 content=json.dumps(vim),
191 headers=self.default_headers)
193 def add_flavors(self, content):
194 for flavor in content['flavors']:
195 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
196 "%s/%s/flavors/flavor/%s" % (
197 self.cloud_owner, self.cloud_region,
200 'flavor-name': flavor['name'],
201 'flavor-vcpus': flavor['vcpus'],
202 'flavor-ram': flavor['ram'],
203 'flavor-disk': flavor['disk'],
204 'flavor-selflink': ""
207 if flavor['name'].startswith("onap."):
208 hpa_capabilities = self._get_hpa_capabilities(
210 body['hpa-capabilities'] = {
211 'hpa-capability': hpa_capabilities}
213 logger.debug("Adding flavors to cloud region")
214 call_req(self.base_url, self.username, self.password,
215 rest_no_auth, resource, "PUT",
216 content=json.dumps(body),
217 headers=self.default_headers)
219 def _get_hpa_capabilities(self, flavor):
223 caps_dict = self._get_hpa_basic_capabilities(flavor)
224 if len(caps_dict) > 0:
225 logger.debug("basic_capabilities_info: %s" % caps_dict)
226 hpa_caps.append(caps_dict)
228 # storage capabilities
229 caps_dict = self._get_storage_capabilities(flavor)
230 if len(caps_dict) > 0:
231 logger.debug("storage_capabilities_info: %s" % caps_dict)
232 hpa_caps.append(caps_dict)
234 # CPU instruction set extension capabilities
235 caps_dict = self._get_instruction_set_capabilities(
236 flavor['extra_specs'])
237 if len(caps_dict) > 0:
238 logger.debug("instruction_set_capabilities_info: %s" % caps_dict)
239 hpa_caps.append(caps_dict)
241 # ovsdpdk capabilities
242 caps_dict = self._get_ovsdpdk_capabilities()
243 if len(caps_dict) > 0:
244 logger.debug("ovsdpdk_capabilities_info: %s" % caps_dict)
245 hpa_caps.append(caps_dict)
249 def _get_hpa_basic_capabilities(self, flavor):
250 basic_capability = {}
251 feature_uuid = uuid.uuid4()
253 basic_capability['hpa-capability-id'] = str(feature_uuid)
254 basic_capability['hpa-feature'] = 'basicCapabilities'
255 basic_capability['architecture'] = 'generic'
256 basic_capability['hpa-version'] = 'v1'
258 basic_capability['hpa-feature-attributes'] = []
259 basic_capability['hpa-feature-attributes'].append({
260 'hpa-attribute-key': 'numVirtualCpu',
261 'hpa-attribute-value': json.dumps(
262 {'value': str(flavor['vcpus'])})})
263 basic_capability['hpa-feature-attributes'].append({
264 'hpa-attribute-key': 'virtualMemSize',
265 'hpa-attribute-value': json.dumps({'value': str(
266 flavor['ram']), 'unit': 'GB'})})
268 return basic_capability
270 def _get_storage_capabilities(self, flavor):
271 storage_capability = {}
272 feature_uuid = uuid.uuid4()
274 storage_capability['hpa-capability-id'] = str(feature_uuid)
275 storage_capability['hpa-feature'] = 'localStorage'
276 storage_capability['architecture'] = 'generic'
277 storage_capability['hpa-version'] = 'v1'
279 storage_capability['hpa-feature-attributes'] = []
280 storage_capability['hpa-feature-attributes'].append({
281 'hpa-attribute-key': 'diskSize',
282 'hpa-attribute-value': json.dumps({'value': str(
283 flavor['disk']), 'unit': 'MB'})
285 storage_capability['hpa-feature-attributes'].append({
286 'hpa-attribute-key': 'swapMemSize',
287 'hpa-attribute-value': json.dumps({'value': str(
288 flavor.get('swap', 0)), 'unit': 'MB'})
290 storage_capability['hpa-feature-attributes'].append({
291 'hpa-attribute-key': 'ephemeralDiskSize',
292 'hpa-attribute-value': json.dumps({'value': str(
293 flavor.get('OS-FLV-EXT-DATA:ephemeral', 0)), 'unit': 'GB'})
295 return storage_capability
297 def _get_instruction_set_capabilities(self, extra_specs):
298 instruction_capability = {}
299 feature_uuid = uuid.uuid4()
301 if extra_specs.get('hw:capabilities:cpu_info:features'):
302 instruction_capability['hpa-capability-id'] = str(feature_uuid)
303 instruction_capability['hpa-feature'] = 'instructionSetExtensions'
304 instruction_capability['architecture'] = 'Intel64'
305 instruction_capability['hpa-version'] = 'v1'
307 instruction_capability['hpa-feature-attributes'] = []
308 instruction_capability['hpa-feature-attributes'].append({
309 'hpa-attribute-key': 'instructionSetExtensions',
310 'hpa-attribute-value': json.dumps(
311 {'value': extra_specs[
312 'hw:capabilities:cpu_info:features']})
314 return instruction_capability
316 def _get_ovsdpdk_capabilities(self):
317 ovsdpdk_capability = {}
318 feature_uuid = uuid.uuid4()
320 if not self._vim_info:
321 self._vim_info = self.get_vim(get_all=True)
322 cloud_extra_info_str = self._vim_info.get('cloud-extra-info')
323 if not isinstance(cloud_extra_info_str, dict):
325 cloud_extra_info_str = json.loads(cloud_extra_info_str)
326 except Exception as ex:
327 logger.error("Can not convert cloud extra info %s %s" % (
328 str(ex), cloud_extra_info_str))
330 if cloud_extra_info_str:
331 cloud_dpdk_info = cloud_extra_info_str.get("ovsDpdk")
333 ovsdpdk_capability['hpa-capability-id'] = str(feature_uuid)
334 ovsdpdk_capability['hpa-feature'] = 'ovsDpdk'
335 ovsdpdk_capability['architecture'] = 'Intel64'
336 ovsdpdk_capability['hpa-version'] = 'v1'
338 ovsdpdk_capability['hpa-feature-attributes'] = []
339 ovsdpdk_capability['hpa-feature-attributes'].append({
340 'hpa-attribute-key': str(cloud_dpdk_info.get("libname")),
341 'hpa-attribute-value': json.dumps(
342 {'value': cloud_dpdk_info.get("libversion")})
344 return ovsdpdk_capability
347 def call_aria_rest(service_id, workflow_name):
348 base_url = "%s" % (ARIA_SERVER_URL)
349 resource = ("/services/%s/executions/%s" % (service_id, workflow_name))
351 headers['content-type'] = 'text/plain'
352 return call_req(base_url, "", "", rest_no_auth, resource, "POST",