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