fixing flake8 tests
[osm/RO.git] / RO-VIM-opennebula / osm_rovim_opennebula / vimconn_opennebula.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2017 Telefonica Digital Spain S.L.U.
5 # This file is part of ETSI OSM
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
18 # License for the specific language governing permissions and limitations
19 # under the License.
20 #
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: patent-office@telefonica.com
23 ##
24
25 """
26 vimconnector implements all the methods to interact with OpenNebula using the XML-RPC API.
27 """
28 __author__ = "Jose Maria Carmona Perez,Juan Antonio Hernando Labajo, Emilio Abraham Garrido Garcia,Alberto Florez " \
29 "Pages, Andres Pozo Munoz, Santiago Perez Marin, Onlife Networks Telefonica I+D Product Innovation "
30 __date__ = "$13-dec-2017 11:09:29$"
31 from osm_ro_plugin import vimconn
32 import requests
33 # import logging
34 import oca
35 # import untangle
36 import math
37 import random
38 import pyone
39
40
41 class vimconnector(vimconn.VimConnector):
42 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
43 log_level="DEBUG", config={}, persistent_info={}):
44
45 """Constructor of VIM
46 Params:
47 'uuid': id asigned to this VIM
48 'name': name assigned to this VIM, can be used for logging
49 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used
50 'url_admin': (optional), url used for administrative tasks
51 'user', 'passwd': credentials of the VIM user
52 'log_level': provider if it should use a different log_level than the general one
53 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config
54 at creation and particular VIM config at teh attachment
55 'persistent_info': dict where the class can store information that will be available among class
56 destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
57 empty dict. Useful to store login/tokens information for speed up communication
58
59 Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity
60 check against the VIM
61 """
62
63 vimconn.VimConnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
64 config)
65
66 def _new_one_connection(self):
67 return pyone.OneServer(self.url, session=self.user + ':' + self.passwd)
68
69 def new_tenant(self, tenant_name, tenant_description):
70 # '''Adds a new tenant to VIM with this name and description, returns the tenant identifier'''
71 try:
72 client = oca.Client(self.user + ':' + self.passwd, self.url)
73 group_list = oca.GroupPool(client)
74 user_list = oca.UserPool(client)
75 group_list.info()
76 user_list.info()
77 create_primarygroup = 1
78 # create group-tenant
79 for group in group_list:
80 if str(group.name) == str(tenant_name):
81 create_primarygroup = 0
82 break
83 if create_primarygroup == 1:
84 oca.Group.allocate(client, tenant_name)
85 group_list.info()
86 # set to primary_group the tenant_group and oneadmin to secondary_group
87 for group in group_list:
88 if str(group.name) == str(tenant_name):
89 for user in user_list:
90 if str(user.name) == str(self.user):
91 if user.name == "oneadmin":
92 return str(0)
93 else:
94 self._add_secondarygroup(user.id, group.id)
95 user.chgrp(group.id)
96 return str(group.id)
97 except Exception as e:
98 self.logger.error("Create new tenant error: " + str(e))
99 raise vimconn.VimConnException(e)
100
101 def delete_tenant(self, tenant_id):
102 """Delete a tenant from VIM. Returns the old tenant identifier"""
103 try:
104 client = oca.Client(self.user + ':' + self.passwd, self.url)
105 group_list = oca.GroupPool(client)
106 user_list = oca.UserPool(client)
107 group_list.info()
108 user_list.info()
109 for group in group_list:
110 if str(group.id) == str(tenant_id):
111 for user in user_list:
112 if str(user.name) == str(self.user):
113 self._delete_secondarygroup(user.id, group.id)
114 group.delete(client)
115 return None
116 raise vimconn.VimConnNotFoundException("Group {} not found".format(tenant_id))
117 except Exception as e:
118 self.logger.error("Delete tenant " + str(tenant_id) + " error: " + str(e))
119 raise vimconn.VimConnException(e)
120
121 def _add_secondarygroup(self, id_user, id_group):
122 # change secondary_group to primary_group
123 params = '<?xml version="1.0"?> \
124 <methodCall>\
125 <methodName>one.user.addgroup</methodName>\
126 <params>\
127 <param>\
128 <value><string>{}:{}</string></value>\
129 </param>\
130 <param>\
131 <value><int>{}</int></value>\
132 </param>\
133 <param>\
134 <value><int>{}</int></value>\
135 </param>\
136 </params>\
137 </methodCall>'.format(self.user, self.passwd, (str(id_user)), (str(id_group)))
138 requests.post(self.url, params)
139
140 def _delete_secondarygroup(self, id_user, id_group):
141 params = '<?xml version="1.0"?> \
142 <methodCall>\
143 <methodName>one.user.delgroup</methodName>\
144 <params>\
145 <param>\
146 <value><string>{}:{}</string></value>\
147 </param>\
148 <param>\
149 <value><int>{}</int></value>\
150 </param>\
151 <param>\
152 <value><int>{}</int></value>\
153 </param>\
154 </params>\
155 </methodCall>'.format(self.user, self.passwd, (str(id_user)), (str(id_group)))
156 requests.post(self.url, params)
157
158 def new_network(self, net_name, net_type, ip_profile=None, shared=False, provider_network_profile=None):
159 """Adds a tenant network to VIM
160 Params:
161 'net_name': name of the network
162 'net_type': one of:
163 'bridge': overlay isolated network
164 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
165 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
166 'ip_profile': is a dict containing the IP parameters of the network
167 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
168 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
169 'gateway_address': (Optional) ip_schema, that is X.X.X.X
170 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
171 'dhcp_enabled': True or False
172 'dhcp_start_address': ip_schema, first IP to grant
173 'dhcp_count': number of IPs to grant.
174 'shared': if this network can be seen/use by other tenants/organization
175 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
176 Returns a tuple with the network identifier and created_items, or raises an exception on error
177 created_items can be None or a dictionary where this method can include key-values that will be passed to
178 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
179 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
180 as not present.
181 """
182
183 # oca library method cannot be used in this case (problem with cluster parameters)
184 try:
185 vlan = None
186 if provider_network_profile:
187 vlan = provider_network_profile.get("segmentation-id")
188 created_items = {}
189 one = self._new_one_connection()
190 size = "254"
191 if ip_profile is None:
192 subnet_rand = random.randint(0, 255)
193 ip_start = "192.168.{}.1".format(subnet_rand)
194 else:
195 index = ip_profile["subnet_address"].find("/")
196 ip_start = ip_profile["subnet_address"][:index]
197 if "dhcp_count" in ip_profile and ip_profile["dhcp_count"] is not None:
198 size = str(ip_profile["dhcp_count"])
199 elif "dhcp_count" not in ip_profile and ip_profile["ip_version"] == "IPv4":
200 prefix = ip_profile["subnet_address"][index + 1:]
201 size = int(math.pow(2, 32 - prefix))
202 if "dhcp_start_address" in ip_profile and ip_profile["dhcp_start_address"] is not None:
203 ip_start = str(ip_profile["dhcp_start_address"])
204 # if ip_profile["ip_version"] == "IPv6":
205 # ip_prefix_type = "GLOBAL_PREFIX"
206
207 if vlan is not None:
208 vlan_id = vlan
209 else:
210 vlan_id = str(random.randint(100, 4095))
211 # if "internal" in net_name:
212 # OpenNebula not support two networks with same name
213 random_net_name = str(random.randint(1, 1000000))
214 net_name = net_name + random_net_name
215 net_id = one.vn.allocate({
216 'NAME': net_name,
217 'VN_MAD': '802.1Q',
218 'PHYDEV': self.config["network"]["phydev"],
219 'VLAN_ID': vlan_id
220 }, self.config["cluster"]["id"])
221 arpool = {
222 'AR_POOL': {
223 'AR': {
224 'TYPE': 'IP4',
225 'IP': ip_start,
226 'SIZE': size
227 }
228 }
229 }
230 one.vn.add_ar(net_id, arpool)
231 return net_id, created_items
232 except Exception as e:
233 self.logger.error("Create new network error: " + str(e))
234 raise vimconn.VimConnException(e)
235
236 def get_network_list(self, filter_dict={}):
237 """Obtain tenant networks of VIM
238 :params filter_dict: (optional) contains entries to return only networks that matches ALL entries:
239 name: string => returns only networks with this name
240 id: string => returns networks with this VIM id, this imply returns one network at most
241 shared: boolean >= returns only networks that are (or are not) shared
242 tenant_id: sting => returns only networks that belong to this tenant/project
243 (not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
244 (not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
245 Returns the network list of dictionaries. each dictionary contains:
246 'id': (mandatory) VIM network id
247 'name': (mandatory) VIM network name
248 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
249 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat'
250 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id
251 'error_msg': (optional) text that explains the ERROR status
252 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
253 List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
254 authorization, or some other unspecific error
255 """
256
257 try:
258 one = self._new_one_connection()
259 net_pool = one.vnpool.info(-2, -1, -1).VNET
260 response = []
261 if "name" in filter_dict:
262 network_name_filter = filter_dict["name"]
263 else:
264 network_name_filter = None
265 if "id" in filter_dict:
266 network_id_filter = filter_dict["id"]
267 else:
268 network_id_filter = None
269 for network in net_pool:
270 if network.NAME == network_name_filter or str(network.ID) == str(network_id_filter):
271 net_dict = {"name": network.NAME, "id": str(network.ID), "status": "ACTIVE"}
272 response.append(net_dict)
273 return response
274 except Exception as e:
275 self.logger.error("Get network list error: " + str(e))
276 raise vimconn.VimConnException(e)
277
278 def get_network(self, net_id):
279 """Obtain network details from the 'net_id' VIM network
280 Return a dict that contains:
281 'id': (mandatory) VIM network id, that is, net_id
282 'name': (mandatory) VIM network name
283 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
284 'error_msg': (optional) text that explains the ERROR status
285 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
286 Raises an exception upon error or when network is not found
287 """
288 try:
289 one = self._new_one_connection()
290 net_pool = one.vnpool.info(-2, -1, -1).VNET
291 net = {}
292 for network in net_pool:
293 if str(network.ID) == str(net_id):
294 net['id'] = network.ID
295 net['name'] = network.NAME
296 net['status'] = "ACTIVE"
297 break
298 if net:
299 return net
300 else:
301 raise vimconn.VimConnNotFoundException("Network {} not found".format(net_id))
302 except Exception as e:
303 self.logger.error("Get network " + str(net_id) + " error): " + str(e))
304 raise vimconn.VimConnException(e)
305
306 def delete_network(self, net_id, created_items=None):
307 """
308 Removes a tenant network from VIM and its associated elements
309 :param net_id: VIM identifier of the network, provided by method new_network
310 :param created_items: dictionary with extra items to be deleted. provided by method new_network
311 Returns the network identifier or raises an exception upon error or when network is not found
312 """
313 try:
314
315 one = self._new_one_connection()
316 one.vn.delete(int(net_id))
317 return net_id
318 except Exception as e:
319 self.logger.error("Delete network " + str(net_id) + "error: network not found" + str(e))
320 raise vimconn.VimConnException(e)
321
322 def refresh_nets_status(self, net_list):
323 """Get the status of the networks
324 Params:
325 'net_list': a list with the VIM network id to be get the status
326 Returns a dictionary with:
327 'net_id': #VIM id of this network
328 status: #Mandatory. Text with one of:
329 # DELETED (not found at vim)
330 # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
331 # OTHER (Vim reported other status not understood)
332 # ERROR (VIM indicates an ERROR status)
333 # ACTIVE, INACTIVE, DOWN (admin down),
334 # BUILD (on building process)
335 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
336 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
337 'net_id2': ...
338 """
339 net_dict = {}
340 try:
341 for net_id in net_list:
342 net = {}
343 try:
344 net_vim = self.get_network(net_id)
345 net["status"] = net_vim["status"]
346 net["vim_info"] = None
347 except vimconn.VimConnNotFoundException as e:
348 self.logger.error("Exception getting net status: {}".format(str(e)))
349 net['status'] = "DELETED"
350 net['error_msg'] = str(e)
351 except vimconn.VimConnException as e:
352 self.logger.error(e)
353 net["status"] = "VIM_ERROR"
354 net["error_msg"] = str(e)
355 net_dict[net_id] = net
356 return net_dict
357 except vimconn.VimConnException as e:
358 self.logger.error(e)
359 for k in net_dict:
360 net_dict[k]["status"] = "VIM_ERROR"
361 net_dict[k]["error_msg"] = str(e)
362 return net_dict
363
364 def get_flavor(self, flavor_id): # Esta correcto
365 """Obtain flavor details from the VIM
366 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
367 Raises an exception upon error or if not found
368 """
369 try:
370
371 one = self._new_one_connection()
372 template = one.template.info(int(flavor_id))
373 if template is not None:
374 return {'id': template.ID, 'name': template.NAME}
375 raise vimconn.VimConnNotFoundException("Flavor {} not found".format(flavor_id))
376 except Exception as e:
377 self.logger.error("get flavor " + str(flavor_id) + " error: " + str(e))
378 raise vimconn.VimConnException(e)
379
380 def new_flavor(self, flavor_data):
381 """Adds a tenant flavor to VIM
382 flavor_data contains a dictionary with information, keys:
383 name: flavor name
384 ram: memory (cloud type) in MBytes
385 vpcus: cpus (cloud type)
386 extended: EPA parameters
387 - numas: #items requested in same NUMA
388 memory: number of 1G huge pages memory
389 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
390 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
391 - name: interface name
392 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
393 bandwidth: X Gbps; requested guarantee bandwidth
394 vpci: requested virtual PCI address
395 disk: disk size
396 is_public:
397 #TODO to concrete
398 Returns the flavor identifier"""
399
400 disk_size = str(int(flavor_data["disk"])*1024)
401
402 try:
403 one = self._new_one_connection()
404 template_id = one.template.allocate({
405 'TEMPLATE': {
406 'NAME': flavor_data["name"],
407 'CPU': flavor_data["vcpus"],
408 'VCPU': flavor_data["vcpus"],
409 'MEMORY': flavor_data["ram"],
410 'DISK': {
411 'SIZE': disk_size
412 },
413 'CONTEXT': {
414 'NETWORK': "YES",
415 'SSH_PUBLIC_KEY': '$USER[SSH_PUBLIC_KEY]'
416 },
417 'GRAPHICS': {
418 'LISTEN': '0.0.0.0',
419 'TYPE': 'VNC'
420 },
421 'CLUSTER_ID': self.config["cluster"]["id"]
422 }
423 })
424 return template_id
425
426 except Exception as e:
427 self.logger.error("Create new flavor error: " + str(e))
428 raise vimconn.VimConnException(e)
429
430 def delete_flavor(self, flavor_id):
431 """ Deletes a tenant flavor from VIM
432 Returns the old flavor_id
433 """
434 try:
435 one = self._new_one_connection()
436 one.template.delete(int(flavor_id), False)
437 return flavor_id
438 except Exception as e:
439 self.logger.error("Error deleting flavor " + str(flavor_id) + ". Flavor not found")
440 raise vimconn.VimConnException(e)
441
442 def get_image_list(self, filter_dict={}):
443 """Obtain tenant images from VIM
444 Filter_dict can be:
445 name: image name
446 id: image uuid
447 checksum: image checksum
448 location: image path
449 Returns the image list of dictionaries:
450 [{<the fields at Filter_dict plus some VIM specific>}, ...]
451 List can be empty
452 """
453 try:
454 one = self._new_one_connection()
455 image_pool = one.imagepool.info(-2, -1, -1).IMAGE
456 images = []
457 if "name" in filter_dict:
458 image_name_filter = filter_dict["name"]
459 else:
460 image_name_filter = None
461 if "id" in filter_dict:
462 image_id_filter = filter_dict["id"]
463 else:
464 image_id_filter = None
465 for image in image_pool:
466 if str(image_name_filter) == str(image.NAME) or str(image.ID) == str(image_id_filter):
467 images_dict = {"name": image.NAME, "id": str(image.ID)}
468 images.append(images_dict)
469 return images
470 except Exception as e:
471 self.logger.error("Get image list error: " + str(e))
472 raise vimconn.VimConnException(e)
473
474 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
475 availability_zone_index=None, availability_zone_list=None):
476 """
477 Adds a VM instance to VIM
478 :param name:
479 :param description:
480 :param start: (boolean) indicates if VM must start or created in pause mode.
481 :param image_id: image VIM id to use for the VM
482 :param flavor_id: flavor VIM id to use for the VM
483 :param net_list: list of interfaces, each one is a dictionary with:
484 'name': (optional) name for the interface.
485 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
486 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM
487 capabilities
488 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
489 'mac_address': (optional) mac address to assign to this interface
490 'ip_address': (optional) IP address to assign to this interface
491 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not
492 provided, the VLAN tag to be used. In case net_id is provided, the internal network vlan is
493 used for tagging VF
494 'type': (mandatory) can be one of:
495 'virtual', in this case always connected to a network of type 'net_type=bridge'
496 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to
497 a data/ptp network ot itcan created unconnected
498 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
499 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
500 are allocated on the same physical NIC
501 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
502 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
503 or True, it must apply the default VIM behaviour
504 After execution the method will add the key:
505 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
506 interface. 'net_list' is modified
507 :param cloud_config: (optional) dictionary with:
508 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
509 'users': (optional) list of users to be inserted, each item is a dict with:
510 'name': (mandatory) user name,
511 'key-pairs': (optional) list of strings with the public key to be inserted to the user
512 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
513 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
514 'config-files': (optional). List of files to be transferred. Each item is a dict with:
515 'dest': (mandatory) string with the destination absolute path
516 'encoding': (optional, by default text). Can be one of:
517 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
518 'content' (mandatory): string with the content of the file
519 'permissions': (optional) string with file permissions, typically octal notation '0644'
520 'owner': (optional) file owner, string with the format 'owner:group'
521 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
522 :param disk_list: (optional) list with additional disks to the VM. Each item is a dict with:
523 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
524 'size': (mandatory) string with the size of the disk in GB
525 :param availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV
526 required
527 :param availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
528 availability_zone_index is None
529 :return: a tuple with the instance identifier and created_items or raises an exception on error
530 created_items can be None or a dictionary where this method can include key-values that will be passed to
531 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
532 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
533 as not present.
534 """
535 self.logger.debug(
536 "new_vminstance input: image='{}' flavor='{}' nics='{}'".format(image_id, flavor_id, str(net_list)))
537 try:
538 one = self._new_one_connection()
539 template_vim = one.template.info(int(flavor_id), True)
540 disk_size = str(template_vim.TEMPLATE["DISK"]["SIZE"])
541
542 one = self._new_one_connection()
543 template_updated = ""
544 for net in net_list:
545 net_in_vim = one.vn.info(int(net["net_id"]))
546 net["vim_id"] = str(net_in_vim.ID)
547 network = 'NIC = [NETWORK = "{}",NETWORK_UNAME = "{}" ]'.format(
548 net_in_vim.NAME, net_in_vim.UNAME)
549 template_updated += network
550
551 template_updated += "DISK = [ IMAGE_ID = {},\n SIZE = {}]".format(image_id, disk_size)
552
553 if isinstance(cloud_config, dict):
554 if cloud_config.get("key-pairs"):
555 context = 'CONTEXT = [\n NETWORK = "YES",\n SSH_PUBLIC_KEY = "'
556 for key in cloud_config["key-pairs"]:
557 context += key + '\n'
558 # if False:
559 # context += '"\n USERNAME = '
560 context += '"]'
561 template_updated += context
562
563 vm_instance_id = one.template.instantiate(int(flavor_id), name, False, template_updated)
564 self.logger.info(
565 "Instanciating in OpenNebula a new VM name:{} id:{}".format(name, flavor_id))
566 return str(vm_instance_id), None
567 except pyone.OneNoExistsException as e:
568 self.logger.error("Network with id " + str(e) + " not found: " + str(e))
569 raise vimconn.VimConnNotFoundException(e)
570 except Exception as e:
571 self.logger.error("Create new vm instance error: " + str(e))
572 raise vimconn.VimConnException(e)
573
574 def get_vminstance(self, vm_id):
575 """Returns the VM instance information from VIM"""
576 try:
577 one = self._new_one_connection()
578 vm = one.vm.info(int(vm_id))
579 return vm
580 except Exception as e:
581 self.logger.error("Getting vm instance error: " + str(e) + ": VM Instance not found")
582 raise vimconn.VimConnException(e)
583
584 def delete_vminstance(self, vm_id, created_items=None):
585 """
586 Removes a VM instance from VIM and its associated elements
587 :param vm_id: VIM identifier of the VM, provided by method new_vminstance
588 :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
589 action_vminstance
590 :return: None or the same vm_id. Raises an exception on fail
591 """
592 try:
593 one = self._new_one_connection()
594 one.vm.recover(int(vm_id), 3)
595 vm = None
596 while True:
597 if vm is not None and vm.LCM_STATE == 0:
598 break
599 else:
600 vm = one.vm.info(int(vm_id))
601
602 except pyone.OneNoExistsException:
603 self.logger.info("The vm " + str(vm_id) + " does not exist or is already deleted")
604 raise vimconn.VimConnNotFoundException("The vm {} does not exist or is already deleted".format(vm_id))
605 except Exception as e:
606 self.logger.error("Delete vm instance " + str(vm_id) + " error: " + str(e))
607 raise vimconn.VimConnException(e)
608
609 def refresh_vms_status(self, vm_list):
610 """Get the status of the virtual machines and their interfaces/ports
611 Params: the list of VM identifiers
612 Returns a dictionary with:
613 vm_id: #VIM id of this Virtual Machine
614 status: #Mandatory. Text with one of:
615 # DELETED (not found at vim)
616 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
617 # OTHER (Vim reported other status not understood)
618 # ERROR (VIM indicates an ERROR status)
619 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
620 # BUILD (on building process), ERROR
621 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
622 #
623 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
624 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
625 interfaces: list with interface info. Each item a dictionary with:
626 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
627 mac_address: #Text format XX:XX:XX:XX:XX:XX
628 vim_net_id: #network id where this interface is connected, if provided at creation
629 vim_interface_id: #interface/port VIM id
630 ip_address: #null, or text with IPv4, IPv6 address
631 compute_node: #identification of compute node where PF,VF interface is allocated
632 pci: #PCI address of the NIC that hosts the PF,VF
633 vlan: #physical VLAN used for VF
634 """
635 vm_dict = {}
636 try:
637 for vm_id in vm_list:
638 vm = {}
639 if self.get_vminstance(vm_id) is not None:
640 vm_element = self.get_vminstance(vm_id)
641 else:
642 self.logger.info("The vm " + str(vm_id) + " does not exist.")
643 vm['status'] = "DELETED"
644 vm['error_msg'] = ("The vm " + str(vm_id) + " does not exist.")
645 continue
646 vm["vim_info"] = None
647 vm_status = vm_element.LCM_STATE
648 if vm_status == 3:
649 vm['status'] = "ACTIVE"
650 elif vm_status == 36:
651 vm['status'] = "ERROR"
652 vm['error_msg'] = "VM failure"
653 else:
654 vm['status'] = "BUILD"
655
656 if vm_element is not None:
657 interfaces = self._get_networks_vm(vm_element)
658 vm["interfaces"] = interfaces
659 vm_dict[vm_id] = vm
660 return vm_dict
661 except Exception as e:
662 self.logger.error(e)
663 for k in vm_dict:
664 vm_dict[k]["status"] = "VIM_ERROR"
665 vm_dict[k]["error_msg"] = str(e)
666 return vm_dict
667
668 def _get_networks_vm(self, vm_element):
669 interfaces = []
670 try:
671 if isinstance(vm_element.TEMPLATE["NIC"], list):
672 for net in vm_element.TEMPLATE["NIC"]:
673 interface = {'vim_info': None, "mac_address": str(net["MAC"]), "vim_net_id": str(net["NETWORK_ID"]),
674 "vim_interface_id": str(net["NETWORK_ID"])}
675 # maybe it should be 2 different keys for ip_address if an interface has ipv4 and ipv6
676 if 'IP' in net:
677 interface["ip_address"] = str(net["IP"])
678 if 'IP6_GLOBAL' in net:
679 interface["ip_address"] = str(net["IP6_GLOBAL"])
680 interfaces.append(interface)
681 else:
682 net = vm_element.TEMPLATE["NIC"]
683 interface = {'vim_info': None, "mac_address": str(net["MAC"]), "vim_net_id": str(net["NETWORK_ID"]),
684 "vim_interface_id": str(net["NETWORK_ID"])}
685 # maybe it should be 2 different keys for ip_address if an interface has ipv4 and ipv6
686 if 'IP' in net:
687 interface["ip_address"] = str(net["IP"])
688 if 'IP6_GLOBAL' in net:
689 interface["ip_address"] = str(net["IP6_GLOBAL"])
690 interfaces.append(interface)
691 return interfaces
692 except Exception:
693 self.logger.error("Error getting vm interface_information of vm_id: " + str(vm_element.ID))