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