1 # -*- coding: utf-8 -*-
4 # Copyright 2017 Telefonica Digital Spain S.L.U.
5 # This file is part of ETSI OSM
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
12 # http://www.apache.org/licenses/LICENSE-2.0
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
18 # License for the specific language governing permissions and limitations
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: patent-office@telefonica.com
26 vimconnector implements all the methods to interact with OpenNebula using the XML-RPC API.
29 "Jose Maria Carmona Perez,Juan Antonio Hernando Labajo, Emilio Abraham Garrido Garcia,Alberto Florez "
30 "Pages, Andres Pozo Munoz, Santiago Perez Marin, Onlife Networks Telefonica I+D Product Innovation "
32 __date__
= "$13-dec-2017 11:09:29$"
34 from osm_ro_plugin
import vimconn
47 class vimconnector(vimconn
.VimConnector
):
64 'uuid': id asigned to this VIM
65 'name': name assigned to this VIM, can be used for logging
66 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used
67 'url_admin': (optional), url used for administrative tasks
68 'user', 'passwd': credentials of the VIM user
69 'log_level': provider if it should use a different log_level than the general one
70 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config
71 at creation and particular VIM config at teh attachment
72 'persistent_info': dict where the class can store information that will be available among class
73 destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
74 empty dict. Useful to store login/tokens information for speed up communication
76 Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity
79 vimconn
.VimConnector
.__init
__(
93 self
.logger
= logging
.getLogger("ro.vim.openstack")
95 def _new_one_connection(self
):
96 return pyone
.OneServer(self
.url
, session
=self
.user
+ ":" + self
.passwd
)
98 def new_tenant(self
, tenant_name
, tenant_description
):
99 # """Adds a new tenant to VIM with this name and description, returns the tenant identifier"""
101 client
= oca
.Client(self
.user
+ ":" + self
.passwd
, self
.url
)
102 group_list
= oca
.GroupPool(client
)
103 user_list
= oca
.UserPool(client
)
106 create_primarygroup
= 1
108 # create group-tenant
109 for group
in group_list
:
110 if str(group
.name
) == str(tenant_name
):
111 create_primarygroup
= 0
114 if create_primarygroup
== 1:
115 oca
.Group
.allocate(client
, tenant_name
)
119 # set to primary_group the tenant_group and oneadmin to secondary_group
120 for group
in group_list
:
121 if str(group
.name
) == str(tenant_name
):
122 for user
in user_list
:
123 if str(user
.name
) == str(self
.user
):
124 if user
.name
== "oneadmin":
127 self
._add
_secondarygroup
(user
.id, group
.id)
131 except Exception as e
:
132 self
.logger
.error("Create new tenant error: " + str(e
))
134 raise vimconn
.VimConnException(e
)
136 def delete_tenant(self
, tenant_id
):
137 """Delete a tenant from VIM. Returns the old tenant identifier"""
139 client
= oca
.Client(self
.user
+ ":" + self
.passwd
, self
.url
)
140 group_list
= oca
.GroupPool(client
)
141 user_list
= oca
.UserPool(client
)
145 for group
in group_list
:
146 if str(group
.id) == str(tenant_id
):
147 for user
in user_list
:
148 if str(user
.name
) == str(self
.user
):
149 self
._delete
_secondarygroup
(user
.id, group
.id)
154 raise vimconn
.VimConnNotFoundException(
155 "Group {} not found".format(tenant_id
)
157 except Exception as e
:
158 self
.logger
.error("Delete tenant " + str(tenant_id
) + " error: " + str(e
))
159 raise vimconn
.VimConnException(e
)
161 def _add_secondarygroup(self
, id_user
, id_group
):
162 # change secondary_group to primary_group
163 params
= '<?xml version="1.0"?> \
165 <methodName>one.user.addgroup</methodName>\
168 <value><string>{}:{}</string></value>\
171 <value><int>{}</int></value>\
174 <value><int>{}</int></value>\
177 </methodCall>'.format(
178 self
.user
, self
.passwd
, (str(id_user
)), (str(id_group
))
180 requests
.post(self
.url
, params
)
182 def _delete_secondarygroup(self
, id_user
, id_group
):
183 params
= '<?xml version="1.0"?> \
185 <methodName>one.user.delgroup</methodName>\
188 <value><string>{}:{}</string></value>\
191 <value><int>{}</int></value>\
194 <value><int>{}</int></value>\
197 </methodCall>'.format(
198 self
.user
, self
.passwd
, (str(id_user
)), (str(id_group
))
200 requests
.post(self
.url
, params
)
208 provider_network_profile
=None,
210 """Adds a tenant network to VIM
212 'net_name': name of the network
214 'bridge': overlay isolated network
215 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
216 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
217 'ip_profile': is a dict containing the IP parameters of the network
218 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
219 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
220 'gateway_address': (Optional) ip_schema, that is X.X.X.X
221 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
222 'dhcp_enabled': True or False
223 'dhcp_start_address': ip_schema, first IP to grant
224 'dhcp_count': number of IPs to grant.
225 'shared': if this network can be seen/use by other tenants/organization
226 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
227 Returns a tuple with the network identifier and created_items, or raises an exception on error
228 created_items can be None or a dictionary where this method can include key-values that will be passed to
229 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
230 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
233 # oca library method cannot be used in this case (problem with cluster parameters)
237 if provider_network_profile
:
238 vlan
= provider_network_profile
.get("segmentation-id")
241 one
= self
._new
_one
_connection
()
244 if ip_profile
is None:
245 subnet_rand
= random
.randint(0, 255)
246 ip_start
= "192.168.{}.1".format(subnet_rand
)
248 index
= ip_profile
["subnet_address"].find("/")
249 ip_start
= ip_profile
["subnet_address"][:index
]
251 if "dhcp_count" in ip_profile
and ip_profile
["dhcp_count"] is not None:
252 size
= str(ip_profile
["dhcp_count"])
254 "dhcp_count" not in ip_profile
255 and ip_profile
["ip_version"] == "IPv4"
257 prefix
= ip_profile
["subnet_address"][index
+ 1 :]
258 size
= int(math
.pow(2, 32 - prefix
))
261 "dhcp_start_address" in ip_profile
262 and ip_profile
["dhcp_start_address"] is not None
264 ip_start
= str(ip_profile
["dhcp_start_address"])
265 # if ip_profile["ip_version"] == "IPv6":
266 # ip_prefix_type = "GLOBAL_PREFIX"
271 vlan_id
= str(random
.randint(100, 4095))
273 # if "internal" in net_name:
274 # OpenNebula not support two networks with same name
275 random_net_name
= str(random
.randint(1, 1000000))
276 net_name
= net_name
+ random_net_name
277 net_id
= one
.vn
.allocate(
281 "PHYDEV": self
.config
["network"]["phydev"],
284 self
.config
["cluster"]["id"],
286 arpool
= {"AR_POOL": {"AR": {"TYPE": "IP4", "IP": ip_start
, "SIZE": size
}}}
287 one
.vn
.add_ar(net_id
, arpool
)
289 return net_id
, created_items
290 except Exception as e
:
291 self
.logger
.error("Create new network error: " + str(e
))
293 raise vimconn
.VimConnException(e
)
295 def get_network_list(self
, filter_dict
={}):
296 """Obtain tenant networks of VIM
297 :params filter_dict: (optional) contains entries to return only networks that matches ALL entries:
298 name: string => returns only networks with this name
299 id: string => returns networks with this VIM id, this imply returns one network at most
300 shared: boolean >= returns only networks that are (or are not) shared
301 tenant_id: sting => returns only networks that belong to this tenant/project
302 (not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
303 (not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
304 Returns the network list of dictionaries. each dictionary contains:
305 'id': (mandatory) VIM network id
306 'name': (mandatory) VIM network name
307 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
308 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat'
309 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id
310 'error_msg': (optional) text that explains the ERROR status
311 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
312 List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
313 authorization, or some other unspecific error
316 one
= self
._new
_one
_connection
()
317 net_pool
= one
.vnpool
.info(-2, -1, -1).VNET
320 if "name" in filter_dict
:
321 network_name_filter
= filter_dict
["name"]
323 network_name_filter
= None
325 if "id" in filter_dict
:
326 network_id_filter
= filter_dict
["id"]
328 network_id_filter
= None
330 for network
in net_pool
:
331 if network
.NAME
== network_name_filter
or str(network
.ID
) == str(
335 "name": network
.NAME
,
336 "id": str(network
.ID
),
339 response
.append(net_dict
)
342 except Exception as e
:
343 self
.logger
.error("Get network list error: " + str(e
))
345 raise vimconn
.VimConnException(e
)
347 def get_network(self
, net_id
):
348 """Obtain network details from the 'net_id' VIM network
349 Return a dict that contains:
350 'id': (mandatory) VIM network id, that is, net_id
351 'name': (mandatory) VIM network name
352 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
353 'error_msg': (optional) text that explains the ERROR status
354 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
355 Raises an exception upon error or when network is not found
358 one
= self
._new
_one
_connection
()
359 net_pool
= one
.vnpool
.info(-2, -1, -1).VNET
362 for network
in net_pool
:
363 if str(network
.ID
) == str(net_id
):
364 net
["id"] = network
.ID
365 net
["name"] = network
.NAME
366 net
["status"] = "ACTIVE"
372 raise vimconn
.VimConnNotFoundException(
373 "Network {} not found".format(net_id
)
375 except Exception as e
:
376 self
.logger
.error("Get network " + str(net_id
) + " error): " + str(e
))
378 raise vimconn
.VimConnException(e
)
380 def delete_network(self
, net_id
, created_items
=None):
382 Removes a tenant network from VIM and its associated elements
383 :param net_id: VIM identifier of the network, provided by method new_network
384 :param created_items: dictionary with extra items to be deleted. provided by method new_network
385 Returns the network identifier or raises an exception upon error or when network is not found
388 one
= self
._new
_one
_connection
()
389 one
.vn
.delete(int(net_id
))
392 except Exception as e
:
394 "Delete network " + str(net_id
) + "error: network not found" + str(e
)
397 raise vimconn
.VimConnException(e
)
399 def refresh_nets_status(self
, net_list
):
400 """Get the status of the networks
402 'net_list': a list with the VIM network id to be get the status
403 Returns a dictionary with:
404 'net_id': #VIM id of this network
405 status: #Mandatory. Text with one of:
406 # DELETED (not found at vim)
407 # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
408 # OTHER (Vim reported other status not understood)
409 # ERROR (VIM indicates an ERROR status)
410 # ACTIVE, INACTIVE, DOWN (admin down),
411 # BUILD (on building process)
412 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
413 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
418 for net_id
in net_list
:
422 net_vim
= self
.get_network(net_id
)
423 net
["status"] = net_vim
["status"]
424 net
["vim_info"] = None
425 except vimconn
.VimConnNotFoundException
as e
:
426 self
.logger
.error("Exception getting net status: {}".format(str(e
)))
427 net
["status"] = "DELETED"
428 net
["error_msg"] = str(e
)
429 except vimconn
.VimConnException
as e
:
431 net
["status"] = "VIM_ERROR"
432 net
["error_msg"] = str(e
)
434 net_dict
[net_id
] = net
437 except vimconn
.VimConnException
as e
:
441 net_dict
[k
]["status"] = "VIM_ERROR"
442 net_dict
[k
]["error_msg"] = str(e
)
446 def get_flavor(self
, flavor_id
): # Esta correcto
447 """Obtain flavor details from the VIM
448 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
449 Raises an exception upon error or if not found
452 one
= self
._new
_one
_connection
()
453 template
= one
.template
.info(int(flavor_id
))
455 if template
is not None:
456 return {"id": template
.ID
, "name": template
.NAME
}
458 raise vimconn
.VimConnNotFoundException(
459 "Flavor {} not found".format(flavor_id
)
461 except Exception as e
:
462 self
.logger
.error("get flavor " + str(flavor_id
) + " error: " + str(e
))
464 raise vimconn
.VimConnException(e
)
466 def new_flavor(self
, flavor_data
):
467 """Adds a tenant flavor to VIM
468 flavor_data contains a dictionary with information, keys:
470 ram: memory (cloud type) in MBytes
471 vpcus: cpus (cloud type)
472 extended: EPA parameters
473 - numas: #items requested in same NUMA
474 memory: number of 1G huge pages memory
475 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
476 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
477 - name: interface name
478 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
479 bandwidth: X Gbps; requested guarantee bandwidth
480 vpci: requested virtual PCI address
484 Returns the flavor identifier"""
485 disk_size
= str(int(flavor_data
["disk"]) * 1024)
488 one
= self
._new
_one
_connection
()
489 template_id
= one
.template
.allocate(
492 "NAME": flavor_data
["name"],
493 "CPU": flavor_data
["vcpus"],
494 "VCPU": flavor_data
["vcpus"],
495 "MEMORY": flavor_data
["ram"],
496 "DISK": {"SIZE": disk_size
},
499 "SSH_PUBLIC_KEY": "$USER[SSH_PUBLIC_KEY]",
501 "GRAPHICS": {"LISTEN": "0.0.0.0", "TYPE": "VNC"},
502 "CLUSTER_ID": self
.config
["cluster"]["id"],
508 except Exception as e
:
509 self
.logger
.error("Create new flavor error: " + str(e
))
511 raise vimconn
.VimConnException(e
)
513 def delete_flavor(self
, flavor_id
):
514 """Deletes a tenant flavor from VIM
515 Returns the old flavor_id
518 one
= self
._new
_one
_connection
()
519 one
.template
.delete(int(flavor_id
), False)
522 except Exception as e
:
524 "Error deleting flavor " + str(flavor_id
) + ". Flavor not found"
527 raise vimconn
.VimConnException(e
)
529 def get_image_list(self
, filter_dict
={}):
530 """Obtain tenant images from VIM
534 checksum: image checksum
536 Returns the image list of dictionaries:
537 [{<the fields at Filter_dict plus some VIM specific>}, ...]
541 one
= self
._new
_one
_connection
()
542 image_pool
= one
.imagepool
.info(-2, -1, -1).IMAGE
545 if "name" in filter_dict
:
546 image_name_filter
= filter_dict
["name"]
548 image_name_filter
= None
550 if "id" in filter_dict
:
551 image_id_filter
= filter_dict
["id"]
553 image_id_filter
= None
555 for image
in image_pool
:
556 if str(image_name_filter
) == str(image
.NAME
) or str(image
.ID
) == str(
559 images_dict
= {"name": image
.NAME
, "id": str(image
.ID
)}
560 images
.append(images_dict
)
563 except Exception as e
:
564 self
.logger
.error("Get image list error: " + str(e
))
565 raise vimconn
.VimConnException(e
)
577 availability_zone_index
=None,
578 availability_zone_list
=None,
581 Adds a VM instance to VIM
584 :param start: (boolean) indicates if VM must start or created in pause mode.
585 :param image_id: image VIM id to use for the VM
586 :param flavor_id: flavor VIM id to use for the VM
587 :param net_list: list of interfaces, each one is a dictionary with:
588 'name': (optional) name for the interface.
589 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
590 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM
592 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
593 'mac_address': (optional) mac address to assign to this interface
594 'ip_address': (optional) IP address to assign to this interface
595 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not
596 provided, the VLAN tag to be used. In case net_id is provided, the internal network vlan is
598 'type': (mandatory) can be one of:
599 'virtual', in this case always connected to a network of type 'net_type=bridge'
600 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to
601 a data/ptp network ot itcan created unconnected
602 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
603 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
604 are allocated on the same physical NIC
605 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
606 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
607 or True, it must apply the default VIM behaviour
608 After execution the method will add the key:
609 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
610 interface. 'net_list' is modified
611 :param cloud_config: (optional) dictionary with:
612 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
613 'users': (optional) list of users to be inserted, each item is a dict with:
614 'name': (mandatory) user name,
615 'key-pairs': (optional) list of strings with the public key to be inserted to the user
616 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
617 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
618 'config-files': (optional). List of files to be transferred. Each item is a dict with:
619 'dest': (mandatory) string with the destination absolute path
620 'encoding': (optional, by default text). Can be one of:
621 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
622 'content' (mandatory): string with the content of the file
623 'permissions': (optional) string with file permissions, typically octal notation '0644'
624 'owner': (optional) file owner, string with the format 'owner:group'
625 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
626 :param disk_list: (optional) list with additional disks to the VM. Each item is a dict with:
627 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
628 'size': (mandatory) string with the size of the disk in GB
629 :param availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV
631 :param availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
632 availability_zone_index is None
633 :return: a tuple with the instance identifier and created_items or raises an exception on error
634 created_items can be None or a dictionary where this method can include key-values that will be passed to
635 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
636 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
640 "new_vminstance input: image='{}' flavor='{}' nics='{}'".format(
641 image_id
, flavor_id
, str(net_list
)
646 one
= self
._new
_one
_connection
()
647 template_vim
= one
.template
.info(int(flavor_id
), True)
648 disk_size
= str(template_vim
.TEMPLATE
["DISK"]["SIZE"])
650 one
= self
._new
_one
_connection
()
651 template_updated
= ""
654 net_in_vim
= one
.vn
.info(int(net
["net_id"]))
655 net
["vim_id"] = str(net_in_vim
.ID
)
656 network
= 'NIC = [NETWORK = "{}",NETWORK_UNAME = "{}" ]'.format(
657 net_in_vim
.NAME
, net_in_vim
.UNAME
659 template_updated
+= network
661 template_updated
+= "DISK = [ IMAGE_ID = {},\n SIZE = {}]".format(
665 if isinstance(cloud_config
, dict):
666 if cloud_config
.get("key-pairs"):
667 context
= 'CONTEXT = [\n NETWORK = "YES",\n SSH_PUBLIC_KEY = "'
669 for key
in cloud_config
["key-pairs"]:
670 context
+= key
+ "\n"
673 # context += '"\n USERNAME = '
675 template_updated
+= context
677 vm_instance_id
= one
.template
.instantiate(
678 int(flavor_id
), name
, False, template_updated
681 "Instanciating in OpenNebula a new VM name:{} id:{}".format(
686 return str(vm_instance_id
), None
687 except pyone
.OneNoExistsException
as e
:
688 self
.logger
.error("Network with id " + str(e
) + " not found: " + str(e
))
690 raise vimconn
.VimConnNotFoundException(e
)
691 except Exception as e
:
692 self
.logger
.error("Create new vm instance error: " + str(e
))
694 raise vimconn
.VimConnException(e
)
696 def get_vminstance(self
, vm_id
):
697 """Returns the VM instance information from VIM"""
699 one
= self
._new
_one
_connection
()
700 vm
= one
.vm
.info(int(vm_id
))
703 except Exception as e
:
705 "Getting vm instance error: " + str(e
) + ": VM Instance not found"
708 raise vimconn
.VimConnException(e
)
710 def delete_vminstance(self
, vm_id
, created_items
=None):
712 Removes a VM instance from VIM and its associated elements
713 :param vm_id: VIM identifier of the VM, provided by method new_vminstance
714 :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
716 :return: None or the same vm_id. Raises an exception on fail
719 one
= self
._new
_one
_connection
()
720 one
.vm
.recover(int(vm_id
), 3)
724 if vm
is not None and vm
.LCM_STATE
== 0:
727 vm
= one
.vm
.info(int(vm_id
))
728 except pyone
.OneNoExistsException
:
730 "The vm " + str(vm_id
) + " does not exist or is already deleted"
733 raise vimconn
.VimConnNotFoundException(
734 "The vm {} does not exist or is already deleted".format(vm_id
)
736 except Exception as e
:
737 self
.logger
.error("Delete vm instance " + str(vm_id
) + " error: " + str(e
))
738 raise vimconn
.VimConnException(e
)
740 def refresh_vms_status(self
, vm_list
):
741 """Get the status of the virtual machines and their interfaces/ports
742 Params: the list of VM identifiers
743 Returns a dictionary with:
744 vm_id: #VIM id of this Virtual Machine
745 status: #Mandatory. Text with one of:
746 # DELETED (not found at vim)
747 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
748 # OTHER (Vim reported other status not understood)
749 # ERROR (VIM indicates an ERROR status)
750 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
751 # BUILD (on building process), ERROR
752 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
754 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
755 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
756 interfaces: list with interface info. Each item a dictionary with:
757 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
758 mac_address: #Text format XX:XX:XX:XX:XX:XX
759 vim_net_id: #network id where this interface is connected, if provided at creation
760 vim_interface_id: #interface/port VIM id
761 ip_address: #null, or text with IPv4, IPv6 address
762 compute_node: #identification of compute node where PF,VF interface is allocated
763 pci: #PCI address of the NIC that hosts the PF,VF
764 vlan: #physical VLAN used for VF
768 for vm_id
in vm_list
:
771 if self
.get_vminstance(vm_id
) is not None:
772 vm_element
= self
.get_vminstance(vm_id
)
774 self
.logger
.info("The vm " + str(vm_id
) + " does not exist.")
775 vm
["status"] = "DELETED"
776 vm
["error_msg"] = "The vm " + str(vm_id
) + " does not exist."
779 vm
["vim_info"] = None
780 vm_status
= vm_element
.LCM_STATE
783 vm
["status"] = "ACTIVE"
784 elif vm_status
== 36:
785 vm
["status"] = "ERROR"
786 vm
["error_msg"] = "VM failure"
788 vm
["status"] = "BUILD"
790 if vm_element
is not None:
791 interfaces
= self
._get
_networks
_vm
(vm_element
)
792 vm
["interfaces"] = interfaces
797 except Exception as e
:
800 vm_dict
[k
]["status"] = "VIM_ERROR"
801 vm_dict
[k
]["error_msg"] = str(e
)
805 def _get_networks_vm(self
, vm_element
):
808 if isinstance(vm_element
.TEMPLATE
["NIC"], list):
809 for net
in vm_element
.TEMPLATE
["NIC"]:
812 "mac_address": str(net
["MAC"]),
813 "vim_net_id": str(net
["NETWORK_ID"]),
814 "vim_interface_id": str(net
["NETWORK_ID"]),
817 # maybe it should be 2 different keys for ip_address if an interface has ipv4 and ipv6
819 interface
["ip_address"] = str(net
["IP"])
821 if "IP6_GLOBAL" in net
:
822 interface
["ip_address"] = str(net
["IP6_GLOBAL"])
824 interfaces
.append(interface
)
826 net
= vm_element
.TEMPLATE
["NIC"]
829 "mac_address": str(net
["MAC"]),
830 "vim_net_id": str(net
["NETWORK_ID"]),
831 "vim_interface_id": str(net
["NETWORK_ID"]),
834 # maybe it should be 2 different keys for ip_address if an interface has ipv4 and ipv6
836 interface
["ip_address"] = str(net
["IP"])
838 if "IP6_GLOBAL" in net
:
839 interface
["ip_address"] = str(net
["IP6_GLOBAL"])
841 interfaces
.append(interface
)
845 "Error getting vm interface_information of vm_id: " + str(vm_element
.ID
)