eab0e75aa0c100fcd12e4a20d5d65e69752fdde5
[osm/RO.git] / RO-VIM-openvim / osm_rovim_openvim / vimconn_openvim.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
5 # This file is part of openmano
6 # All Rights Reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
22 ##
23
24 """
25 vimconnector implements all the methods to interact with openvim using the openvim API.
26 """
27 __author__ = "Alfonso Tierno, Gerardo Garcia"
28 __date__ = "$26-aug-2014 11:09:29$"
29
30 from osm_ro_plugin import vimconn
31 import requests
32 import json
33 import yaml
34 import logging
35 import math
36 from osm_ro.openmano_schemas import (
37 id_schema,
38 name_schema,
39 nameshort_schema,
40 description_schema,
41 vlan1000_schema,
42 integer0_schema,
43 )
44 from jsonschema import validate as js_v, exceptions as js_e
45 from urllib.parse import quote
46
47 """contain the openvim virtual machine status to openmano status"""
48 vmStatus2manoFormat = {
49 "ACTIVE": "ACTIVE",
50 "PAUSED": "PAUSED",
51 "SUSPENDED": "SUSPENDED",
52 "INACTIVE": "INACTIVE",
53 "CREATING": "BUILD",
54 "ERROR": "ERROR",
55 "DELETED": "DELETED",
56 }
57 netStatus2manoFormat = {
58 "ACTIVE": "ACTIVE",
59 "INACTIVE": "INACTIVE",
60 "BUILD": "BUILD",
61 "ERROR": "ERROR",
62 "DELETED": "DELETED",
63 "DOWN": "DOWN",
64 }
65
66
67 host_schema = {
68 "type": "object",
69 "properties": {
70 "id": id_schema,
71 "name": name_schema,
72 },
73 "required": ["id"],
74 }
75 image_schema = {
76 "type": "object",
77 "properties": {
78 "id": id_schema,
79 "name": name_schema,
80 },
81 "required": ["id", "name"],
82 }
83 server_schema = {
84 "type": "object",
85 "properties": {
86 "id": id_schema,
87 "name": name_schema,
88 },
89 "required": ["id", "name"],
90 }
91 new_host_response_schema = {
92 "title": "host response information schema",
93 "$schema": "http://json-schema.org/draft-04/schema#",
94 "type": "object",
95 "properties": {"host": host_schema},
96 "required": ["host"],
97 "additionalProperties": False,
98 }
99
100 get_images_response_schema = {
101 "title": "openvim images response information schema",
102 "$schema": "http://json-schema.org/draft-04/schema#",
103 "type": "object",
104 "properties": {
105 "images": {
106 "type": "array",
107 "items": image_schema,
108 }
109 },
110 "required": ["images"],
111 "additionalProperties": False,
112 }
113
114 get_hosts_response_schema = {
115 "title": "openvim hosts response information schema",
116 "$schema": "http://json-schema.org/draft-04/schema#",
117 "type": "object",
118 "properties": {
119 "hosts": {
120 "type": "array",
121 "items": host_schema,
122 }
123 },
124 "required": ["hosts"],
125 "additionalProperties": False,
126 }
127
128 get_host_detail_response_schema = (
129 new_host_response_schema # TODO: Content is not parsed yet
130 )
131
132 get_server_response_schema = {
133 "title": "openvim server response information schema",
134 "$schema": "http://json-schema.org/draft-04/schema#",
135 "type": "object",
136 "properties": {
137 "servers": {
138 "type": "array",
139 "items": server_schema,
140 }
141 },
142 "required": ["servers"],
143 "additionalProperties": False,
144 }
145
146 new_tenant_response_schema = {
147 "title": "tenant response information schema",
148 "$schema": "http://json-schema.org/draft-04/schema#",
149 "type": "object",
150 "properties": {
151 "tenant": {
152 "type": "object",
153 "properties": {
154 "id": id_schema,
155 "name": nameshort_schema,
156 "description": description_schema,
157 "enabled": {"type": "boolean"},
158 },
159 "required": ["id"],
160 }
161 },
162 "required": ["tenant"],
163 "additionalProperties": False,
164 }
165
166 new_network_response_schema = {
167 "title": "network response information schema",
168 "$schema": "http://json-schema.org/draft-04/schema#",
169 "type": "object",
170 "properties": {
171 "network": {
172 "type": "object",
173 "properties": {
174 "id": id_schema,
175 "name": name_schema,
176 "type": {
177 "type": "string",
178 "enum": ["bridge_man", "bridge_data", "data", "ptp"],
179 },
180 "shared": {"type": "boolean"},
181 "tenant_id": id_schema,
182 "admin_state_up": {"type": "boolean"},
183 "vlan": vlan1000_schema,
184 },
185 "required": ["id"],
186 }
187 },
188 "required": ["network"],
189 "additionalProperties": False,
190 }
191
192
193 # get_network_response_schema = {
194 # "title":"get network response information schema",
195 # "$schema": "http://json-schema.org/draft-04/schema#",
196 # "type":"object",
197 # "properties":{
198 # "network":{
199 # "type":"object",
200 # "properties":{
201 # "id":id_schema,
202 # "name":name_schema,
203 # "type":{"type":"string", "enum":["bridge_man","bridge_data","data", "ptp"]},
204 # "shared":{"type":"boolean"},
205 # "tenant_id":id_schema,
206 # "admin_state_up":{"type":"boolean"},
207 # "vlan":vlan1000_schema
208 # },
209 # "required": ["id"]
210 # }
211 # },
212 # "required": ["network"],
213 # "additionalProperties": False
214 # }
215
216
217 new_port_response_schema = {
218 "title": "port response information schema",
219 "$schema": "http://json-schema.org/draft-04/schema#",
220 "type": "object",
221 "properties": {
222 "port": {
223 "type": "object",
224 "properties": {
225 "id": id_schema,
226 },
227 "required": ["id"],
228 }
229 },
230 "required": ["port"],
231 "additionalProperties": False,
232 }
233
234 get_flavor_response_schema = {
235 "title": "openvim flavors response information schema",
236 "$schema": "http://json-schema.org/draft-04/schema#",
237 "type": "object",
238 "properties": {
239 "flavor": {
240 "type": "object",
241 "properties": {
242 "id": id_schema,
243 "name": name_schema,
244 "extended": {"type": "object"},
245 },
246 "required": ["id", "name"],
247 }
248 },
249 "required": ["flavor"],
250 "additionalProperties": False,
251 }
252
253 new_flavor_response_schema = {
254 "title": "flavor response information schema",
255 "$schema": "http://json-schema.org/draft-04/schema#",
256 "type": "object",
257 "properties": {
258 "flavor": {
259 "type": "object",
260 "properties": {
261 "id": id_schema,
262 },
263 "required": ["id"],
264 }
265 },
266 "required": ["flavor"],
267 "additionalProperties": False,
268 }
269
270 get_image_response_schema = {
271 "title": "openvim images response information schema",
272 "$schema": "http://json-schema.org/draft-04/schema#",
273 "type": "object",
274 "properties": {
275 "image": {
276 "type": "object",
277 "properties": {
278 "id": id_schema,
279 "name": name_schema,
280 },
281 "required": ["id", "name"],
282 }
283 },
284 "required": ["flavor"],
285 "additionalProperties": False,
286 }
287 new_image_response_schema = {
288 "title": "image response information schema",
289 "$schema": "http://json-schema.org/draft-04/schema#",
290 "type": "object",
291 "properties": {
292 "image": {
293 "type": "object",
294 "properties": {
295 "id": id_schema,
296 },
297 "required": ["id"],
298 }
299 },
300 "required": ["image"],
301 "additionalProperties": False,
302 }
303
304 new_vminstance_response_schema = {
305 "title": "server response information schema",
306 "$schema": "http://json-schema.org/draft-04/schema#",
307 "type": "object",
308 "properties": {
309 "server": {
310 "type": "object",
311 "properties": {
312 "id": id_schema,
313 },
314 "required": ["id"],
315 }
316 },
317 "required": ["server"],
318 "additionalProperties": False,
319 }
320
321 get_processor_rankings_response_schema = {
322 "title": "processor rankings information schema",
323 "$schema": "http://json-schema.org/draft-04/schema#",
324 "type": "object",
325 "properties": {
326 "rankings": {
327 "type": "array",
328 "items": {
329 "type": "object",
330 "properties": {"model": description_schema, "value": integer0_schema},
331 "additionalProperties": False,
332 "required": ["model", "value"],
333 },
334 },
335 "additionalProperties": False,
336 "required": ["rankings"],
337 },
338 }
339
340
341 class vimconnector(vimconn.VimConnector):
342 def __init__(
343 self,
344 uuid,
345 name,
346 tenant_id,
347 tenant_name,
348 url,
349 url_admin=None,
350 user=None,
351 passwd=None,
352 log_level="DEBUG",
353 config={},
354 persistent_info={},
355 ):
356 vimconn.VimConnector.__init__(
357 self,
358 uuid,
359 name,
360 tenant_id,
361 tenant_name,
362 url,
363 url_admin,
364 user,
365 passwd,
366 log_level,
367 config,
368 )
369 self.tenant = None
370 self.headers_req = {"content-type": "application/json"}
371 self.logger = logging.getLogger("ro.vim.openvim")
372 self.persistent_info = persistent_info
373 if tenant_id:
374 self.tenant = tenant_id
375
376 def __setitem__(self, index, value):
377 """Set individuals parameters
378 Throw TypeError, KeyError
379 """
380 if index == "tenant_id":
381 self.tenant = value
382 elif index == "tenant_name":
383 self.tenant = None
384 vimconn.VimConnector.__setitem__(self, index, value)
385
386 def _get_my_tenant(self):
387 """Obtain uuid of my tenant from name"""
388 if self.tenant:
389 return self.tenant
390
391 url = self.url + "/tenants?name=" + quote(self.tenant_name)
392 self.logger.info("Getting VIM tenant_id GET %s", url)
393 vim_response = requests.get(url, headers=self.headers_req)
394 self._check_http_request_response(vim_response)
395 try:
396 tenant_list = vim_response.json()["tenants"]
397 if len(tenant_list) == 0:
398 raise vimconn.VimConnNotFoundException(
399 "No tenant found for name '{}'".format(self.tenant_name)
400 )
401 elif len(tenant_list) > 1:
402 raise vimconn.VimConnConflictException(
403 "More that one tenant found for name '{}'".format(self.tenant_name)
404 )
405 self.tenant = tenant_list[0]["id"]
406 return self.tenant
407 except Exception as e:
408 raise vimconn.VimConnUnexpectedResponse(
409 "Get VIM tenant {} '{}'".format(type(e).__name__, str(e))
410 )
411
412 def _format_jsonerror(self, http_response):
413 # DEPRECATED, to delete in the future
414 try:
415 data = http_response.json()
416 return data["error"]["description"]
417 except Exception:
418 return http_response.text
419
420 def _format_in(self, http_response, schema):
421 # DEPRECATED, to delete in the future
422 try:
423 client_data = http_response.json()
424 js_v(client_data, schema)
425 # print "Input data: ", str(client_data)
426 return True, client_data
427 except js_e.ValidationError as exc:
428 print(
429 "validate_in error, jsonschema exception ", exc.message, "at", exc.path
430 )
431 return False, (
432 "validate_in error, jsonschema exception ",
433 exc.message,
434 "at",
435 exc.path,
436 )
437
438 def _remove_extra_items(self, data, schema):
439 deleted = []
440 if type(data) is tuple or type(data) is list:
441 for d in data:
442 a = self._remove_extra_items(d, schema["items"])
443 if a is not None:
444 deleted.append(a)
445 elif type(data) is dict:
446 to_delete = []
447 for k in data.keys():
448 if "properties" not in schema or k not in schema["properties"].keys():
449 to_delete.append(k)
450 deleted.append(k)
451 else:
452 a = self._remove_extra_items(data[k], schema["properties"][k])
453 if a is not None:
454 deleted.append({k: a})
455 for k in to_delete:
456 del data[k]
457 if len(deleted) == 0:
458 return None
459 elif len(deleted) == 1:
460 return deleted[0]
461 else:
462 return deleted
463
464 def _format_request_exception(self, request_exception):
465 """Transform a request exception into a vimconn exception"""
466 if isinstance(request_exception, js_e.ValidationError):
467 raise vimconn.VimConnUnexpectedResponse(
468 "jsonschema exception '{}' at '{}'".format(
469 request_exception.message, request_exception.path
470 )
471 )
472 elif isinstance(request_exception, requests.exceptions.HTTPError):
473 raise vimconn.VimConnUnexpectedResponse(
474 type(request_exception).__name__ + ": " + str(request_exception)
475 )
476 else:
477 raise vimconn.VimConnConnectionException(
478 type(request_exception).__name__ + ": " + str(request_exception)
479 )
480
481 def _check_http_request_response(self, request_response):
482 """Raise a vimconn exception if the response is not Ok"""
483 if request_response.status_code >= 200 and request_response.status_code < 300:
484 return
485 if request_response.status_code == vimconn.HTTP_Unauthorized:
486 raise vimconn.VimConnAuthException(request_response.text)
487 elif request_response.status_code == vimconn.HTTP_Not_Found:
488 raise vimconn.VimConnNotFoundException(request_response.text)
489 elif request_response.status_code == vimconn.HTTP_Conflict:
490 raise vimconn.VimConnConflictException(request_response.text)
491 else:
492 raise vimconn.VimConnUnexpectedResponse(
493 "VIM HTTP_response {}, {}".format(
494 request_response.status_code, str(request_response.text)
495 )
496 )
497
498 def new_tenant(self, tenant_name, tenant_description):
499 """Adds a new tenant to VIM with this name and description, returns the tenant identifier"""
500 # print "VIMConnector: Adding a new tenant to VIM"
501 payload_dict = {
502 "tenant": {
503 "name": tenant_name,
504 "description": tenant_description,
505 "enabled": True,
506 }
507 }
508 payload_req = json.dumps(payload_dict)
509 try:
510 url = self.url_admin + "/tenants"
511 self.logger.info("Adding a new tenant %s", url)
512 vim_response = requests.post(
513 url, headers=self.headers_req, data=payload_req
514 )
515 self._check_http_request_response(vim_response)
516 self.logger.debug(vim_response.text)
517 # print json.dumps(vim_response.json(), indent=4)
518 response = vim_response.json()
519 js_v(response, new_tenant_response_schema)
520 # r = self._remove_extra_items(response, new_tenant_response_schema)
521 # if r is not None:
522 # self.logger.warn("Warning: remove extra items %s", str(r))
523 tenant_id = response["tenant"]["id"]
524 return tenant_id
525 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
526 self._format_request_exception(e)
527
528 def delete_tenant(self, tenant_id):
529 """Delete a tenant from VIM. Returns the old tenant identifier"""
530 try:
531 url = self.url_admin + "/tenants/" + tenant_id
532 self.logger.info("Delete a tenant DELETE %s", url)
533 vim_response = requests.delete(url, headers=self.headers_req)
534 self._check_http_request_response(vim_response)
535 self.logger.debug(vim_response.text)
536 # print json.dumps(vim_response.json(), indent=4)
537 return tenant_id
538 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
539 self._format_request_exception(e)
540
541 def get_tenant_list(self, filter_dict={}):
542 """Obtain tenants of VIM
543 filter_dict can contain the following keys:
544 name: filter by tenant name
545 id: filter by tenant uuid/id
546 <other VIM specific>
547 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
548 """
549 filterquery = []
550 filterquery_text = ""
551 for k, v in filter_dict.items():
552 filterquery.append(str(k) + "=" + str(v))
553 if len(filterquery) > 0:
554 filterquery_text = "?" + "&".join(filterquery)
555 try:
556 url = self.url + "/tenants" + filterquery_text
557 self.logger.info("get_tenant_list GET %s", url)
558 vim_response = requests.get(url, headers=self.headers_req)
559 self._check_http_request_response(vim_response)
560 self.logger.debug(vim_response.text)
561 # print json.dumps(vim_response.json(), indent=4)
562 return vim_response.json()["tenants"]
563 except requests.exceptions.RequestException as e:
564 self._format_request_exception(e)
565
566 def new_network(
567 self,
568 net_name,
569 net_type,
570 ip_profile=None,
571 shared=False,
572 provider_network_profile=None,
573 ): # , **vim_specific):
574 """Adds a tenant network to VIM
575 Params:
576 'net_name': name of the network
577 'net_type': one of:
578 'bridge': overlay isolated network
579 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
580 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
581 'ip_profile': is a dict containing the IP parameters of the network
582 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
583 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
584 'gateway_address': (Optional) ip_schema, that is X.X.X.X
585 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
586 'dhcp_enabled': True or False
587 'dhcp_start_address': ip_schema, first IP to grant
588 'dhcp_count': number of IPs to grant.
589 'shared': if this network can be seen/use by other tenants/organization
590 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
591 Returns a tuple with the network identifier and created_items, or raises an exception on error
592 created_items can be None or a dictionary where this method can include key-values that will be passed to
593 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
594 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
595 as not present.
596 """
597 try:
598 vlan = None
599 if provider_network_profile:
600 vlan = provider_network_profile.get("segmentation-id")
601 created_items = {}
602 self._get_my_tenant()
603 if net_type == "bridge":
604 net_type = "bridge_data"
605 payload_req = {
606 "name": net_name,
607 "type": net_type,
608 "tenant_id": self.tenant,
609 "shared": shared,
610 }
611 if vlan:
612 payload_req["provider:vlan"] = vlan
613 # payload_req.update(vim_specific)
614 url = self.url + "/networks"
615 self.logger.info(
616 "Adding a new network POST: %s DATA: %s", url, str(payload_req)
617 )
618 vim_response = requests.post(
619 url, headers=self.headers_req, data=json.dumps({"network": payload_req})
620 )
621 self._check_http_request_response(vim_response)
622 self.logger.debug(vim_response.text)
623 # print json.dumps(vim_response.json(), indent=4)
624 response = vim_response.json()
625 js_v(response, new_network_response_schema)
626 # r = self._remove_extra_items(response, new_network_response_schema)
627 # if r is not None:
628 # self.logger.warn("Warning: remove extra items %s", str(r))
629 network_id = response["network"]["id"]
630 return network_id, created_items
631 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
632 self._format_request_exception(e)
633
634 def get_network_list(self, filter_dict={}):
635 """Obtain tenant networks of VIM
636 Filter_dict can be:
637 name: network name
638 id: network uuid
639 public: boolean
640 tenant_id: tenant
641 admin_state_up: boolean
642 status: 'ACTIVE'
643 Returns the network list of dictionaries
644 """
645 try:
646 if "tenant_id" not in filter_dict:
647 filter_dict["tenant_id"] = self._get_my_tenant()
648 elif not filter_dict["tenant_id"]:
649 del filter_dict["tenant_id"]
650 filterquery = []
651 filterquery_text = ""
652 for k, v in filter_dict.items():
653 filterquery.append(str(k) + "=" + str(v))
654 if len(filterquery) > 0:
655 filterquery_text = "?" + "&".join(filterquery)
656 url = self.url + "/networks" + filterquery_text
657 self.logger.info("Getting network list GET %s", url)
658 vim_response = requests.get(url, headers=self.headers_req)
659 self._check_http_request_response(vim_response)
660 self.logger.debug(vim_response.text)
661 # print json.dumps(vim_response.json(), indent=4)
662 response = vim_response.json()
663 return response["networks"]
664 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
665 self._format_request_exception(e)
666
667 def get_network(self, net_id):
668 """Obtain network details of network id"""
669 try:
670 url = self.url + "/networks/" + net_id
671 self.logger.info("Getting network GET %s", url)
672 vim_response = requests.get(url, headers=self.headers_req)
673 self._check_http_request_response(vim_response)
674 self.logger.debug(vim_response.text)
675 # print json.dumps(vim_response.json(), indent=4)
676 response = vim_response.json()
677 return response["network"]
678 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
679 self._format_request_exception(e)
680
681 def delete_network(self, net_id, created_items=None):
682 """
683 Removes a tenant network from VIM and its associated elements
684 :param net_id: VIM identifier of the network, provided by method new_network
685 :param created_items: dictionary with extra items to be deleted. provided by method new_network
686 Returns the network identifier or raises an exception upon error or when network is not found
687 """
688 try:
689 self._get_my_tenant()
690 url = self.url + "/networks/" + net_id
691 self.logger.info("Deleting VIM network DELETE %s", url)
692 vim_response = requests.delete(url, headers=self.headers_req)
693 self._check_http_request_response(vim_response)
694 # self.logger.debug(vim_response.text)
695 # print json.dumps(vim_response.json(), indent=4)
696 return net_id
697 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
698 self._format_request_exception(e)
699
700 def get_flavor(self, flavor_id):
701 """Obtain flavor details from the VIM"""
702 try:
703 self._get_my_tenant()
704 url = self.url + "/" + self.tenant + "/flavors/" + flavor_id
705 self.logger.info("Getting flavor GET %s", url)
706 vim_response = requests.get(url, headers=self.headers_req)
707 self._check_http_request_response(vim_response)
708 self.logger.debug(vim_response.text)
709 # print json.dumps(vim_response.json(), indent=4)
710 response = vim_response.json()
711 js_v(response, get_flavor_response_schema)
712 r = self._remove_extra_items(response, get_flavor_response_schema)
713 if r is not None:
714 self.logger.warn("Warning: remove extra items %s", str(r))
715 return response["flavor"]
716 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
717 self._format_request_exception(e)
718
719 def new_flavor(self, flavor_data):
720 """Adds a tenant flavor to VIM"""
721 """Returns the flavor identifier"""
722 try:
723 new_flavor_dict = flavor_data.copy()
724 for device in new_flavor_dict.get("extended", {}).get("devices", ()):
725 if "image name" in device:
726 del device["image name"]
727 if "name" in device:
728 del device["name"]
729 numas = new_flavor_dict.get("extended", {}).get("numas")
730 if numas:
731 numa = numas[0]
732 # translate memory, cpus to EPA
733 if (
734 "cores" not in numa
735 and "threads" not in numa
736 and "paired-threads" not in numa
737 ):
738 numa["paired-threads"] = new_flavor_dict["vcpus"]
739 if "memory" not in numa:
740 numa["memory"] = int(math.ceil(new_flavor_dict["ram"] / 1024.0))
741 for iface in numa.get("interfaces", ()):
742 if not iface.get("bandwidth"):
743 iface["bandwidth"] = "1 Mbps"
744
745 new_flavor_dict["name"] = flavor_data["name"][:64]
746 self._get_my_tenant()
747 payload_req = json.dumps({"flavor": new_flavor_dict})
748 url = self.url + "/" + self.tenant + "/flavors"
749 self.logger.info("Adding a new VIM flavor POST %s", url)
750 vim_response = requests.post(
751 url, headers=self.headers_req, data=payload_req
752 )
753 self._check_http_request_response(vim_response)
754 self.logger.debug(vim_response.text)
755 # print json.dumps(vim_response.json(), indent=4)
756 response = vim_response.json()
757 js_v(response, new_flavor_response_schema)
758 r = self._remove_extra_items(response, new_flavor_response_schema)
759 if r is not None:
760 self.logger.warn("Warning: remove extra items %s", str(r))
761 flavor_id = response["flavor"]["id"]
762 return flavor_id
763 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
764 self._format_request_exception(e)
765
766 def delete_flavor(self, flavor_id):
767 """Deletes a tenant flavor from VIM"""
768 """Returns the old flavor_id"""
769 try:
770 self._get_my_tenant()
771 url = self.url + "/" + self.tenant + "/flavors/" + flavor_id
772 self.logger.info("Deleting VIM flavor DELETE %s", url)
773 vim_response = requests.delete(url, headers=self.headers_req)
774 self._check_http_request_response(vim_response)
775 # self.logger.debug(vim_response.text)
776 # print json.dumps(vim_response.json(), indent=4)
777 return flavor_id
778 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
779 self._format_request_exception(e)
780
781 def get_image(self, image_id):
782 """Obtain image details from the VIM"""
783 try:
784 self._get_my_tenant()
785 url = self.url + "/" + self.tenant + "/images/" + image_id
786 self.logger.info("Getting image GET %s", url)
787 vim_response = requests.get(url, headers=self.headers_req)
788 self._check_http_request_response(vim_response)
789 self.logger.debug(vim_response.text)
790 # print json.dumps(vim_response.json(), indent=4)
791 response = vim_response.json()
792 js_v(response, get_image_response_schema)
793 r = self._remove_extra_items(response, get_image_response_schema)
794 if r is not None:
795 self.logger.warn("Warning: remove extra items %s", str(r))
796 return response["image"]
797 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
798 self._format_request_exception(e)
799
800 def new_image(self, image_dict):
801 """ Adds a tenant image to VIM, returns image_id"""
802 try:
803 self._get_my_tenant()
804 new_image_dict = {"name": image_dict["name"][:64]}
805 if image_dict.get("description"):
806 new_image_dict["description"] = image_dict["description"]
807 if image_dict.get("metadata"):
808 new_image_dict["metadata"] = yaml.load(
809 image_dict["metadata"], Loader=yaml.SafeLoader
810 )
811 if image_dict.get("location"):
812 new_image_dict["path"] = image_dict["location"]
813 payload_req = json.dumps({"image": new_image_dict})
814 url = self.url + "/" + self.tenant + "/images"
815 self.logger.info("Adding a new VIM image POST %s", url)
816 vim_response = requests.post(
817 url, headers=self.headers_req, data=payload_req
818 )
819 self._check_http_request_response(vim_response)
820 self.logger.debug(vim_response.text)
821 # print json.dumps(vim_response.json(), indent=4)
822 response = vim_response.json()
823 js_v(response, new_image_response_schema)
824 r = self._remove_extra_items(response, new_image_response_schema)
825 if r is not None:
826 self.logger.warn("Warning: remove extra items %s", str(r))
827 image_id = response["image"]["id"]
828 return image_id
829 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
830 self._format_request_exception(e)
831
832 def delete_image(self, image_id):
833 """Deletes a tenant image from VIM"""
834 """Returns the deleted image_id"""
835 try:
836 self._get_my_tenant()
837 url = self.url + "/" + self.tenant + "/images/" + image_id
838 self.logger.info("Deleting VIM image DELETE %s", url)
839 vim_response = requests.delete(url, headers=self.headers_req)
840 self._check_http_request_response(vim_response)
841 # self.logger.debug(vim_response.text)
842 # print json.dumps(vim_response.json(), indent=4)
843 return image_id
844 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
845 self._format_request_exception(e)
846
847 def get_image_id_from_path(self, path):
848 """Get the image id from image path in the VIM database. Returns the image_id"""
849 try:
850 self._get_my_tenant()
851 url = self.url + "/" + self.tenant + "/images?path=" + quote(path)
852 self.logger.info("Getting images GET %s", url)
853 vim_response = requests.get(url)
854 self._check_http_request_response(vim_response)
855 self.logger.debug(vim_response.text)
856 # print json.dumps(vim_response.json(), indent=4)
857 response = vim_response.json()
858 js_v(response, get_images_response_schema)
859 # r = self._remove_extra_items(response, get_images_response_schema)
860 # if r is not None:
861 # self.logger.warn("Warning: remove extra items %s", str(r))
862 if len(response["images"]) == 0:
863 raise vimconn.VimConnNotFoundException(
864 "Image not found at VIM with path '{}'".format(path)
865 )
866 elif len(response["images"]) > 1:
867 raise vimconn.VimConnConflictException(
868 "More than one image found at VIM with path '{}'".format(path)
869 )
870 return response["images"][0]["id"]
871 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
872 self._format_request_exception(e)
873
874 def get_image_list(self, filter_dict={}):
875 """Obtain tenant images from VIM
876 Filter_dict can be:
877 name: image name
878 id: image uuid
879 checksum: image checksum
880 location: image path
881 Returns the image list of dictionaries:
882 [{<the fields at Filter_dict plus some VIM specific>}, ...]
883 List can be empty
884 """
885 try:
886 self._get_my_tenant()
887 filterquery = []
888 filterquery_text = ""
889 for k, v in filter_dict.items():
890 filterquery.append(str(k) + "=" + str(v))
891 if len(filterquery) > 0:
892 filterquery_text = "?" + "&".join(filterquery)
893 url = self.url + "/" + self.tenant + "/images" + filterquery_text
894 self.logger.info("Getting image list GET %s", url)
895 vim_response = requests.get(url, headers=self.headers_req)
896 self._check_http_request_response(vim_response)
897 self.logger.debug(vim_response.text)
898 # print json.dumps(vim_response.json(), indent=4)
899 response = vim_response.json()
900 return response["images"]
901 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
902 self._format_request_exception(e)
903
904 def new_vminstancefromJSON(self, vm_data):
905 """Adds a VM instance to VIM"""
906 """Returns the instance identifier"""
907 try:
908 self._get_my_tenant()
909 except Exception as e:
910 return -vimconn.HTTP_Not_Found, str(e)
911 print("VIMConnector: Adding a new VM instance from JSON to VIM")
912 payload_req = vm_data
913 try:
914 vim_response = requests.post(
915 self.url + "/" + self.tenant + "/servers",
916 headers=self.headers_req,
917 data=payload_req,
918 )
919 except requests.exceptions.RequestException as e:
920 print("new_vminstancefromJSON Exception: ", e.args)
921 return -vimconn.HTTP_Not_Found, str(e.args[0])
922 # print vim_response
923 # print vim_response.status_code
924 if vim_response.status_code == 200:
925 # print vim_response.json()
926 # print json.dumps(vim_response.json(), indent=4)
927 res, http_content = self._format_in(vim_response, new_image_response_schema)
928 # print http_content
929 if res:
930 r = self._remove_extra_items(http_content, new_image_response_schema)
931 if r is not None:
932 print("Warning: remove extra items ", r)
933 # print http_content
934 vminstance_id = http_content["server"]["id"]
935 print("Tenant image id: ", vminstance_id)
936 return vim_response.status_code, vminstance_id
937 else:
938 return -vimconn.HTTP_Bad_Request, http_content
939 else:
940 # print vim_response.text
941 jsonerror = self._format_jsonerror(vim_response)
942 text = 'Error in VIM "{}": not possible to add new vm instance. HTTP Response: {}. Error: {}'.format(
943 self.url, vim_response.status_code, jsonerror
944 )
945 # print text
946 return -vim_response.status_code, text
947
948 def new_vminstance(
949 self,
950 name,
951 description,
952 start,
953 image_id,
954 flavor_id,
955 net_list,
956 cloud_config=None,
957 disk_list=None,
958 availability_zone_index=None,
959 availability_zone_list=None,
960 ):
961 """Adds a VM instance to VIM
962 Params:
963 start: indicates if VM must start or boot in pause mode. Ignored
964 image_id,flavor_id: image and flavor uuid
965 net_list: list of interfaces, each one is a dictionary with:
966 name:
967 net_id: network uuid to connect
968 vpci: virtual vcpi to assign
969 model: interface model, virtio, e1000, ...
970 mac_address:
971 use: 'data', 'bridge', 'mgmt'
972 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
973 vim_id: filled/added by this function
974 #TODO ip, security groups
975 Returns a tuple with the instance identifier and created_items or raises an exception on error
976 created_items can be None or a dictionary where this method can include key-values that will be passed to
977 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
978 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
979 as not present.
980 """
981 self.logger.debug(
982 "new_vminstance input: image='%s' flavor='%s' nics='%s'",
983 image_id,
984 flavor_id,
985 str(net_list),
986 )
987 try:
988 self._get_my_tenant()
989 # net_list = []
990 # for k,v in net_dict.items():
991 # print k,v
992 # net_list.append('{"name":"' + k + '", "uuid":"' + v + '"}')
993 # net_list_string = ', '.join(net_list)
994 virtio_net_list = []
995 for net in net_list:
996 if not net.get("net_id"):
997 continue
998 net_dict = {"uuid": net["net_id"]}
999 if net.get("type"):
1000 if net["type"] == "SR-IOV":
1001 net_dict["type"] = "VF"
1002 elif net["type"] == "PCI-PASSTHROUGH":
1003 net_dict["type"] = "PF"
1004 else:
1005 net_dict["type"] = net["type"]
1006 if net.get("name"):
1007 net_dict["name"] = net["name"]
1008 if net.get("vpci"):
1009 net_dict["vpci"] = net["vpci"]
1010 if net.get("model"):
1011 if net["model"] == "VIRTIO" or net["model"] == "paravirt":
1012 net_dict["model"] = "virtio"
1013 else:
1014 net_dict["model"] = net["model"]
1015 if net.get("mac_address"):
1016 net_dict["mac_address"] = net["mac_address"]
1017 if net.get("ip_address"):
1018 net_dict["ip_address"] = net["ip_address"]
1019 virtio_net_list.append(net_dict)
1020 payload_dict = {
1021 "name": name[:64],
1022 "description": description,
1023 "imageRef": image_id,
1024 "flavorRef": flavor_id,
1025 "networks": virtio_net_list,
1026 }
1027 if start is not None:
1028 payload_dict["start"] = start
1029 payload_req = json.dumps({"server": payload_dict})
1030 url = self.url + "/" + self.tenant + "/servers"
1031 self.logger.info("Adding a new vm POST %s DATA %s", url, payload_req)
1032 vim_response = requests.post(
1033 url, headers=self.headers_req, data=payload_req
1034 )
1035 self._check_http_request_response(vim_response)
1036 self.logger.debug(vim_response.text)
1037 # print json.dumps(vim_response.json(), indent=4)
1038 response = vim_response.json()
1039 js_v(response, new_vminstance_response_schema)
1040 # r = self._remove_extra_items(response, new_vminstance_response_schema)
1041 # if r is not None:
1042 # self.logger.warn("Warning: remove extra items %s", str(r))
1043 vminstance_id = response["server"]["id"]
1044
1045 # connect data plane interfaces to network
1046 for net in net_list:
1047 if net["type"] == "virtual":
1048 if not net.get("net_id"):
1049 continue
1050 for iface in response["server"]["networks"]:
1051 if "name" in net:
1052 if net["name"] == iface["name"]:
1053 net["vim_id"] = iface["iface_id"]
1054 break
1055 elif "net_id" in net:
1056 if net["net_id"] == iface["net_id"]:
1057 net["vim_id"] = iface["iface_id"]
1058 break
1059 else: # dataplane
1060 for numa in response["server"].get("extended", {}).get("numas", ()):
1061 for iface in numa.get("interfaces", ()):
1062 if net["name"] == iface["name"]:
1063 net["vim_id"] = iface["iface_id"]
1064 # Code bellow is not needed, current openvim connect dataplane interfaces
1065 # if net.get("net_id"):
1066 # connect dataplane interface
1067 # result, port_id = self.connect_port_network(iface['iface_id'], net["net_id"])
1068 # if result < 0:
1069 # error_text = "Error attaching port %s to network %s: %s." % (iface['iface_id']
1070 # , net["net_id"], port_id)
1071 # print "new_vminstance: " + error_text
1072 # self.delete_vminstance(vminstance_id)
1073 # return result, error_text
1074 break
1075
1076 return vminstance_id, None
1077 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1078 self._format_request_exception(e)
1079
1080 def get_vminstance(self, vm_id):
1081 """Returns the VM instance information from VIM"""
1082 try:
1083 self._get_my_tenant()
1084 url = self.url + "/" + self.tenant + "/servers/" + vm_id
1085 self.logger.info("Getting vm GET %s", url)
1086 vim_response = requests.get(url, headers=self.headers_req)
1087 vim_response = requests.get(url, headers=self.headers_req)
1088 self._check_http_request_response(vim_response)
1089 self.logger.debug(vim_response.text)
1090 # print json.dumps(vim_response.json(), indent=4)
1091 response = vim_response.json()
1092 js_v(response, new_vminstance_response_schema)
1093 # r = self._remove_extra_items(response, new_vminstance_response_schema)
1094 # if r is not None:
1095 # self.logger.warn("Warning: remove extra items %s", str(r))
1096 return response["server"]
1097 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1098 self._format_request_exception(e)
1099
1100 def delete_vminstance(self, vm_id, created_items=None):
1101 """Removes a VM instance from VIM, returns the deleted vm_id"""
1102 try:
1103 self._get_my_tenant()
1104 url = self.url + "/" + self.tenant + "/servers/" + vm_id
1105 self.logger.info("Deleting VIM vm DELETE %s", url)
1106 vim_response = requests.delete(url, headers=self.headers_req)
1107 self._check_http_request_response(vim_response)
1108 # self.logger.debug(vim_response.text)
1109 # print json.dumps(vim_response.json(), indent=4)
1110 return vm_id
1111 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1112 self._format_request_exception(e)
1113
1114 def refresh_vms_status(self, vm_list):
1115 """Refreshes the status of the virtual machines"""
1116 try:
1117 self._get_my_tenant()
1118 except requests.exceptions.RequestException as e:
1119 self._format_request_exception(e)
1120 vm_dict = {}
1121 for vm_id in vm_list:
1122 vm = {}
1123 # print "VIMConnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM"
1124 try:
1125 url = self.url + "/" + self.tenant + "/servers/" + vm_id
1126 self.logger.info("Getting vm GET %s", url)
1127 vim_response = requests.get(url, headers=self.headers_req)
1128 self._check_http_request_response(vim_response)
1129 response = vim_response.json()
1130 js_v(response, new_vminstance_response_schema)
1131 if response["server"]["status"] in vmStatus2manoFormat:
1132 vm["status"] = vmStatus2manoFormat[response["server"]["status"]]
1133 else:
1134 vm["status"] = "OTHER"
1135 vm["error_msg"] = (
1136 "VIM status reported " + response["server"]["status"]
1137 )
1138 if response["server"].get("last_error"):
1139 vm["error_msg"] = response["server"]["last_error"]
1140 vm["vim_info"] = yaml.safe_dump(response["server"])
1141 # get interfaces info
1142 try:
1143 management_ip = False
1144 url2 = self.url + "/ports?device_id=" + quote(vm_id)
1145 self.logger.info("Getting PORTS GET %s", url2)
1146 vim_response2 = requests.get(url2, headers=self.headers_req)
1147 self._check_http_request_response(vim_response2)
1148 client_data = vim_response2.json()
1149 if isinstance(client_data.get("ports"), list):
1150 vm["interfaces"] = []
1151 for port in client_data.get("ports"):
1152 interface = {}
1153 interface["vim_info"] = yaml.safe_dump(port)
1154 interface["mac_address"] = port.get("mac_address")
1155 interface["vim_net_id"] = port.get("network_id")
1156 interface["vim_interface_id"] = port["id"]
1157 interface["ip_address"] = port.get("ip_address")
1158 if interface["ip_address"]:
1159 management_ip = True
1160 if interface["ip_address"] == "0.0.0.0":
1161 interface["ip_address"] = None
1162 vm["interfaces"].append(interface)
1163
1164 except Exception as e:
1165 self.logger.error(
1166 "refresh_vms_and_nets. Port get %s: %s",
1167 type(e).__name__,
1168 str(e),
1169 )
1170
1171 if vm["status"] == "ACTIVE" and not management_ip:
1172 vm["status"] = "ACTIVE:NoMgmtIP"
1173
1174 except vimconn.VimConnNotFoundException as e:
1175 self.logger.error("Exception getting vm status: %s", str(e))
1176 vm["status"] = "DELETED"
1177 vm["error_msg"] = str(e)
1178 except (
1179 requests.exceptions.RequestException,
1180 js_e.ValidationError,
1181 vimconn.VimConnException,
1182 ) as e:
1183 self.logger.error("Exception getting vm status: %s", str(e))
1184 vm["status"] = "VIM_ERROR"
1185 vm["error_msg"] = str(e)
1186 vm_dict[vm_id] = vm
1187 return vm_dict
1188
1189 def refresh_nets_status(self, net_list):
1190 """Get the status of the networks
1191 Params: the list of network identifiers
1192 Returns a dictionary with:
1193 net_id: #VIM id of this network
1194 status: #Mandatory. Text with one of:
1195 # DELETED (not found at vim)
1196 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1197 # OTHER (Vim reported other status not understood)
1198 # ERROR (VIM indicates an ERROR status)
1199 # ACTIVE, INACTIVE, DOWN (admin down),
1200 # BUILD (on building process)
1201 #
1202 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1203 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1204
1205 """
1206 try:
1207 self._get_my_tenant()
1208 except requests.exceptions.RequestException as e:
1209 self._format_request_exception(e)
1210
1211 net_dict = {}
1212 for net_id in net_list:
1213 net = {}
1214 # print "VIMConnector refresh_tenant_vms_and_nets:
1215 # Getting tenant network from VIM (tenant: " + str(self.tenant) + "): "
1216 try:
1217 net_vim = self.get_network(net_id)
1218 if net_vim["status"] in netStatus2manoFormat:
1219 net["status"] = netStatus2manoFormat[net_vim["status"]]
1220 else:
1221 net["status"] = "OTHER"
1222 net["error_msg"] = "VIM status reported " + net_vim["status"]
1223
1224 if net["status"] == "ACTIVE" and not net_vim["admin_state_up"]:
1225 net["status"] = "DOWN"
1226 if net_vim.get("last_error"):
1227 net["error_msg"] = net_vim["last_error"]
1228 net["vim_info"] = yaml.safe_dump(net_vim)
1229 except vimconn.VimConnNotFoundException as e:
1230 self.logger.error("Exception getting net status: %s", str(e))
1231 net["status"] = "DELETED"
1232 net["error_msg"] = str(e)
1233 except (
1234 requests.exceptions.RequestException,
1235 js_e.ValidationError,
1236 vimconn.VimConnException,
1237 ) as e:
1238 self.logger.error("Exception getting net status: %s", str(e))
1239 net["status"] = "VIM_ERROR"
1240 net["error_msg"] = str(e)
1241 net_dict[net_id] = net
1242 return net_dict
1243
1244 def action_vminstance(self, vm_id, action_dict, created_items={}):
1245 """Send and action over a VM instance from VIM"""
1246 """Returns the status"""
1247 try:
1248 self._get_my_tenant()
1249 if "console" in action_dict:
1250 raise vimconn.VimConnException(
1251 "getting console is not available at openvim",
1252 http_code=vimconn.HTTP_Service_Unavailable,
1253 )
1254 url = self.url + "/" + self.tenant + "/servers/" + vm_id + "/action"
1255 self.logger.info("Action over VM instance POST %s", url)
1256 vim_response = requests.post(
1257 url, headers=self.headers_req, data=json.dumps(action_dict)
1258 )
1259 self._check_http_request_response(vim_response)
1260 return None
1261 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1262 self._format_request_exception(e)
1263
1264 # NOT USED METHODS in current version
1265
1266 def host_vim2gui(self, host, server_dict):
1267 """Transform host dictionary from VIM format to GUI format,
1268 and append to the server_dict
1269 """
1270 if type(server_dict) is not dict:
1271 print(
1272 "vimconnector.host_vim2gui() ERROR, param server_dict must be a dictionary"
1273 )
1274 return
1275 RAD = {}
1276 occupation = {}
1277 for numa in host["host"]["numas"]:
1278 RAD_item = {}
1279 occupation_item = {}
1280 # memory
1281 RAD_item["memory"] = {
1282 "size": str(numa["memory"]) + "GB",
1283 "eligible": str(numa["hugepages"]) + "GB",
1284 }
1285 occupation_item["memory"] = str(numa["hugepages_consumed"]) + "GB"
1286 # cpus
1287 RAD_item["cpus"] = {}
1288 RAD_item["cpus"]["cores"] = []
1289 RAD_item["cpus"]["eligible_cores"] = []
1290 occupation_item["cores"] = []
1291 for _ in range(0, len(numa["cores"]) // 2):
1292 RAD_item["cpus"]["cores"].append([])
1293 for core in numa["cores"]:
1294 RAD_item["cpus"]["cores"][core["core_id"]].append(core["thread_id"])
1295 if "status" not in core:
1296 RAD_item["cpus"]["eligible_cores"].append(core["thread_id"])
1297 if "instance_id" in core:
1298 occupation_item["cores"].append(core["thread_id"])
1299 # ports
1300 RAD_item["ports"] = {}
1301 occupation_item["ports"] = {}
1302 for iface in numa["interfaces"]:
1303 RAD_item["ports"][iface["pci"]] = "speed:" + str(iface["Mbps"]) + "M"
1304 occupation_item["ports"][iface["pci"]] = {
1305 "occupied": str(100 * iface["Mbps_consumed"] // iface["Mbps"]) + "%"
1306 }
1307
1308 RAD[numa["numa_socket"]] = RAD_item
1309 occupation[numa["numa_socket"]] = occupation_item
1310 server_dict[host["host"]["name"]] = {"RAD": RAD, "occupation": occupation}
1311
1312 def get_hosts_info(self):
1313 """Get the information of deployed hosts
1314 Returns the hosts content"""
1315 # obtain hosts list
1316 url = self.url + "/hosts"
1317 try:
1318 vim_response = requests.get(url)
1319 except requests.exceptions.RequestException as e:
1320 print("get_hosts_info Exception: ", e.args)
1321 return -vimconn.HTTP_Not_Found, str(e.args[0])
1322 print(
1323 "vim get", url, "response:", vim_response.status_code, vim_response.json()
1324 )
1325 # print vim_response.status_code
1326 # print json.dumps(vim_response.json(), indent=4)
1327 if vim_response.status_code != 200:
1328 # TODO: get error
1329 print(
1330 "vimconnector.get_hosts_info error getting host list {} {}".format(
1331 vim_response.status_code, vim_response.json()
1332 )
1333 )
1334 return -vim_response.status_code, "Error getting host list"
1335
1336 res, hosts = self._format_in(vim_response, get_hosts_response_schema)
1337
1338 if not res:
1339 print(
1340 "vimconnector.get_hosts_info error parsing GET HOSTS vim response",
1341 hosts,
1342 )
1343 return vimconn.HTTP_Internal_Server_Error, hosts
1344 # obtain hosts details
1345 hosts_dict = {}
1346 for host in hosts["hosts"]:
1347 url = self.url + "/hosts/" + host["id"]
1348 try:
1349 vim_response = requests.get(url)
1350 except requests.exceptions.RequestException as e:
1351 print("get_hosts_info Exception: ", e.args)
1352 return -vimconn.HTTP_Not_Found, str(e.args[0])
1353 print(
1354 "vim get",
1355 url,
1356 "response:",
1357 vim_response.status_code,
1358 vim_response.json(),
1359 )
1360 if vim_response.status_code != 200:
1361 print(
1362 "vimconnector.get_hosts_info error getting detailed host {} {}".format(
1363 vim_response.status_code, vim_response.json()
1364 )
1365 )
1366 continue
1367 res, host_detail = self._format_in(
1368 vim_response, get_host_detail_response_schema
1369 )
1370 if not res:
1371 print(
1372 "vimconnector.get_hosts_info error parsing GET HOSTS/{} vim response {}".format(
1373 host["id"], host_detail
1374 ),
1375 )
1376 continue
1377 # print 'host id '+host['id'], json.dumps(host_detail, indent=4)
1378 self.host_vim2gui(host_detail, hosts_dict)
1379 return 200, hosts_dict
1380
1381 def get_hosts(self, vim_tenant):
1382 """Get the hosts and deployed instances
1383 Returns the hosts content"""
1384 # obtain hosts list
1385 url = self.url + "/hosts"
1386 try:
1387 vim_response = requests.get(url)
1388 except requests.exceptions.RequestException as e:
1389 print("get_hosts Exception: ", e.args)
1390 return -vimconn.HTTP_Not_Found, str(e.args[0])
1391 print(
1392 "vim get", url, "response:", vim_response.status_code, vim_response.json()
1393 )
1394 # print vim_response.status_code
1395 # print json.dumps(vim_response.json(), indent=4)
1396 if vim_response.status_code != 200:
1397 # TODO: get error
1398 print(
1399 "vimconnector.get_hosts error getting host list {} {}".format(
1400 vim_response.status_code, vim_response.json()
1401 )
1402 )
1403 return -vim_response.status_code, "Error getting host list"
1404
1405 res, hosts = self._format_in(vim_response, get_hosts_response_schema)
1406
1407 if not res:
1408 print("vimconnector.get_host error parsing GET HOSTS vim response", hosts)
1409 return vimconn.HTTP_Internal_Server_Error, hosts
1410 # obtain instances from hosts
1411 for host in hosts["hosts"]:
1412 url = self.url + "/" + vim_tenant + "/servers?hostId=" + host["id"]
1413 try:
1414 vim_response = requests.get(url)
1415 except requests.exceptions.RequestException as e:
1416 print("get_hosts Exception: ", e.args)
1417 return -vimconn.HTTP_Not_Found, str(e.args[0])
1418 print(
1419 "vim get",
1420 url,
1421 "response:",
1422 vim_response.status_code,
1423 vim_response.json(),
1424 )
1425 if vim_response.status_code != 200:
1426 print(
1427 "vimconnector.get_hosts error getting instances at host {} {}".format(
1428 vim_response.status_code, vim_response.json()
1429 )
1430 )
1431 continue
1432 res, servers = self._format_in(vim_response, get_server_response_schema)
1433 if not res:
1434 print(
1435 "vimconnector.get_host error parsing GET SERVERS/{} vim response {}".format(
1436 host["id"], servers
1437 ),
1438 )
1439 continue
1440 # print 'host id '+host['id'], json.dumps(host_detail, indent=4)
1441 host["instances"] = servers["servers"]
1442 return 200, hosts["hosts"]
1443
1444 def get_processor_rankings(self):
1445 """Get the processor rankings in the VIM database"""
1446 url = self.url + "/processor_ranking"
1447 try:
1448 vim_response = requests.get(url)
1449 except requests.exceptions.RequestException as e:
1450 print("get_processor_rankings Exception: ", e.args)
1451 return -vimconn.HTTP_Not_Found, str(e.args[0])
1452 print(
1453 "vim get", url, "response:", vim_response.status_code, vim_response.json()
1454 )
1455 # print vim_response.status_code
1456 # print json.dumps(vim_response.json(), indent=4)
1457 if vim_response.status_code != 200:
1458 # TODO: get error
1459 print(
1460 "vimconnector.get_processor_rankings error getting processor rankings {} {}".format(
1461 vim_response.status_code, vim_response.json()
1462 )
1463 )
1464 return -vim_response.status_code, "Error getting processor rankings"
1465
1466 res, rankings = self._format_in(
1467 vim_response, get_processor_rankings_response_schema
1468 )
1469 return res, rankings["rankings"]
1470
1471 def new_host(self, host_data):
1472 """Adds a new host to VIM"""
1473 """Returns status code of the VIM response"""
1474 payload_req = host_data
1475 try:
1476 url = self.url_admin + "/hosts"
1477 self.logger.info("Adding a new host POST %s", url)
1478 vim_response = requests.post(
1479 url, headers=self.headers_req, data=payload_req
1480 )
1481 self._check_http_request_response(vim_response)
1482 self.logger.debug(vim_response.text)
1483 # print json.dumps(vim_response.json(), indent=4)
1484 response = vim_response.json()
1485 js_v(response, new_host_response_schema)
1486 r = self._remove_extra_items(response, new_host_response_schema)
1487 if r is not None:
1488 self.logger.warn("Warning: remove extra items %s", str(r))
1489 host_id = response["host"]["id"]
1490 return host_id
1491 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1492 self._format_request_exception(e)
1493
1494 def new_external_port(self, port_data):
1495 """Adds a external port to VIM"""
1496 """Returns the port identifier"""
1497 # TODO change to logging exception code policies
1498 print("VIMConnector: Adding a new external port")
1499 payload_req = port_data
1500 try:
1501 vim_response = requests.post(
1502 self.url_admin + "/ports", headers=self.headers_req, data=payload_req
1503 )
1504 except requests.exceptions.RequestException as e:
1505 self.logger.error("new_external_port Exception: ", str(e))
1506 return -vimconn.HTTP_Not_Found, str(e.args[0])
1507 print(vim_response)
1508 # print vim_response.status_code
1509 if vim_response.status_code == 200:
1510 # print vim_response.json()
1511 # print json.dumps(vim_response.json(), indent=4)
1512 res, http_content = self._format_in(vim_response, new_port_response_schema)
1513 # print http_content
1514 if res:
1515 r = self._remove_extra_items(http_content, new_port_response_schema)
1516 if r is not None:
1517 print("Warning: remove extra items ", r)
1518 # print http_content
1519 port_id = http_content["port"]["id"]
1520 print("Port id: ", port_id)
1521 return vim_response.status_code, port_id
1522 else:
1523 return -vimconn.HTTP_Bad_Request, http_content
1524 else:
1525 # print vim_response.text
1526 jsonerror = self._format_jsonerror(vim_response)
1527 text = 'Error in VIM "{}": not possible to add new external port. HTTP Response: {}. Error: {}'.format(
1528 self.url_admin, vim_response.status_code, jsonerror
1529 )
1530 # print text
1531 return -vim_response.status_code, text
1532
1533 def new_external_network(self, net_name, net_type):
1534 """Adds a external network to VIM (shared)"""
1535 """Returns the network identifier"""
1536 # TODO change to logging exception code policies
1537 print(
1538 "VIMConnector: Adding external shared network to VIM (type "
1539 + net_type
1540 + "): "
1541 + net_name
1542 )
1543
1544 payload_req = (
1545 '{"network":{"name": "'
1546 + net_name
1547 + '","shared":true,"type": "'
1548 + net_type
1549 + '"}}'
1550 )
1551 try:
1552 vim_response = requests.post(
1553 self.url + "/networks", headers=self.headers_req, data=payload_req
1554 )
1555 except requests.exceptions.RequestException as e:
1556 self.logger.error("new_external_network Exception: ", e.args)
1557 return -vimconn.HTTP_Not_Found, str(e.args[0])
1558 print(vim_response)
1559 # print vim_response.status_code
1560 if vim_response.status_code == 200:
1561 # print vim_response.json()
1562 # print json.dumps(vim_response.json(), indent=4)
1563 res, http_content = self._format_in(
1564 vim_response, new_network_response_schema
1565 )
1566 # print http_content
1567 if res:
1568 r = self._remove_extra_items(http_content, new_network_response_schema)
1569 if r is not None:
1570 print("Warning: remove extra items ", r)
1571 # print http_content
1572 network_id = http_content["network"]["id"]
1573 print("Network id: ", network_id)
1574 return vim_response.status_code, network_id
1575 else:
1576 return -vimconn.HTTP_Bad_Request, http_content
1577 else:
1578 # print vim_response.text
1579 jsonerror = self._format_jsonerror(vim_response)
1580 text = 'Error in VIM "{}": not possible to add new external network. HTTP Response: {}. Error: {}'.format(
1581 self.url, vim_response.status_code, jsonerror
1582 )
1583 # print text
1584 return -vim_response.status_code, text
1585
1586 def connect_port_network(self, port_id, network_id, admin=False):
1587 """Connects a external port to a network"""
1588 """Returns status code of the VIM response"""
1589 # TODO change to logging exception code policies
1590 print("VIMConnector: Connecting external port to network")
1591
1592 payload_req = '{"port":{"network_id":"' + network_id + '"}}'
1593 if admin:
1594 if self.url_admin is None:
1595 return (
1596 -vimconn.HTTP_Unauthorized,
1597 "datacenter cannot contain admin URL",
1598 )
1599 url = self.url_admin
1600 else:
1601 url = self.url
1602 try:
1603 vim_response = requests.put(
1604 url + "/ports/" + port_id, headers=self.headers_req, data=payload_req
1605 )
1606 except requests.exceptions.RequestException as e:
1607 print("connect_port_network Exception: ", e.args)
1608 return -vimconn.HTTP_Not_Found, str(e.args[0])
1609 print(vim_response)
1610 # print vim_response.status_code
1611 if vim_response.status_code == 200:
1612 # print vim_response.json()
1613 # print json.dumps(vim_response.json(), indent=4)
1614 res, http_content = self._format_in(vim_response, new_port_response_schema)
1615 # print http_content
1616 if res:
1617 r = self._remove_extra_items(http_content, new_port_response_schema)
1618 if r is not None:
1619 print("Warning: remove extra items ", r)
1620 # print http_content
1621 port_id = http_content["port"]["id"]
1622 print("Port id: ", port_id)
1623 return vim_response.status_code, port_id
1624 else:
1625 return -vimconn.HTTP_Bad_Request, http_content
1626 else:
1627 print(vim_response.text)
1628 jsonerror = self._format_jsonerror(vim_response)
1629 text = (
1630 'Error in VIM "{}": not possible to connect external port to network. HTTP Response: {}.'
1631 " Error: {}".format(self.url_admin, vim_response.status_code, jsonerror)
1632 )
1633 print(text)
1634 return -vim_response.status_code, text