1 # -*- coding: utf-8 -*-
4 # Copyright 2020 ADLINK Technology Inc.
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
17 # License for the specific language governing permissions and limitations
23 Eclipse fog05 connector, implements methods to interact with Eclipse fog05
25 Manages LXD containers on x86_64 by default, currently missing EPA and VF/PF
27 - arch : cpu architecture for the VIM
28 - hypervisor: virtualization technology supported by the VIM, can
29 can be one of: LXD, KVM, BARE, XEN, DOCKER, MCU
30 the selected VIM need to have at least a node with support
31 for the selected hypervisor
39 from osm_ro_plugin
import vimconn
41 from functools
import partial
42 from fog05
import FIMAPI
43 from fog05
import fimapi
44 from fog05_sdk
.interfaces
.FDU
import FDU
46 __author__
= "Gabriele Baldoni"
47 __date__
= "$2-june-2020 10:35:12$"
50 class vimconnector(vimconn
.VimConnector
):
51 def __init__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
=None, user
=None, passwd
=None, log_level
=None,
52 config
={}, persistent_info
={}):
55 'uuid': id asigned to this VIM
56 'name': name assigned to this VIM, can be used for logging
57 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used
58 'url_admin': (optional), url used for administrative tasks
59 'user', 'passwd': credentials of the VIM user
60 'log_level': provider if it should use a different log_level than the general one
61 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config
62 at creation and particular VIM config at teh attachment
63 'persistent_info': dict where the class can store information that will be available among class
64 destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
65 empty dict. Useful to store login/tokens information for speed up communication
67 Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity
71 vimconn
.VimConnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
, user
, passwd
, log_level
,
72 config
, persistent_info
)
74 self
.logger
= logging
.getLogger('ro.vim.fos')
75 self
.logger
.debug('vimconn_fos init with config: {}'.format(config
))
76 self
.arch
= config
.get('arch', 'x86_64')
77 self
.hv
= config
.get('hypervisor', 'LXD')
78 self
.nodes
= config
.get('nodes', [])
79 self
.fdu_node_map
= {}
80 self
.fos_api
= FIMAPI(locator
=self
.url
)
82 def __get_ip_range(self
, first
, count
):
83 int_first
= struct
.unpack('!L', socket
.inet_aton(first
))[0]
84 int_last
= int_first
+ count
85 last
= socket
.inet_ntoa(struct
.pack('!L', int_last
))
88 def __name_filter(self
, desc
, filter_name
=None):
89 if filter_name
is None:
91 return desc
.get('name') == filter_name
93 def __id_filter(self
, desc
, filter_id
=None):
96 return desc
.get('uuid') == filter_id
98 def __checksum_filter(self
, desc
, filter_checksum
=None):
99 if filter_checksum
is None:
101 return desc
.get('checksum') == filter_checksum
103 def check_vim_connectivity(self
):
104 """Checks VIM can be reached and user credentials are ok.
105 Returns None if success or raised VimConnConnectionException, VimConnAuthException, ...
108 self
.fos_api
.node
.list()
110 except fimapi
.FIMAuthExcetpion
as fae
:
111 raise vimconn
.VimConnAuthException("Unable to authenticate to the VIM. Error {}".format(fae
))
112 except Exception as e
:
113 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
115 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False, provider_network_profile
=None):
116 """Adds a tenant network to VIM
118 'net_name': name of the network
120 'bridge': overlay isolated network
121 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
122 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
123 'ip_profile': is a dict containing the IP parameters of the network
124 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
125 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
126 'gateway_address': (Optional) ip_schema, that is X.X.X.X
127 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
128 'dhcp_enabled': True or False
129 'dhcp_start_address': ip_schema, first IP to grant
130 'dhcp_count': number of IPs to grant.
131 'shared': if this network can be seen/use by other tenants/organization
132 Returns the network identifier on success or raises and exception on failure
134 self
.logger
.debug('new_network: {}'.format(locals()))
135 if net_type
in ['data', 'ptp']:
136 raise vimconn
.VimConnNotImplemented('{} type of network not supported'.format(net_type
))
138 net_uuid
= '{}'.format(uuid
.uuid4())
146 if ip_profile
is not None:
148 if ip_profile
.get('ip_version') == 'IPv4':
150 ip_range
= self
.__get
_ip
_range
(ip_profile
.get('dhcp_start_address'), ip_profile
.get('dhcp_count'))
151 dhcp_range
= '{},{}'.format(ip_range
[0], ip_range
[1])
152 ip
['subnet'] = ip_profile
.get('subnet_address')
153 ip
['dns'] = ip_profile
.get('dns', None)
154 ip
['dhcp_enable'] = ip_profile
.get('dhcp_enabled', False)
155 ip
['dhcp_range'] = dhcp_range
156 ip
['gateway'] = ip_profile
.get('gateway_address', None)
157 desc
['ip_configuration'] = ip_info
159 raise vimconn
.VimConnNotImplemented('IPV6 network is not implemented at VIM')
160 desc
['ip_configuration'] = ip
161 self
.logger
.debug('VIM new_network args: {} - Generated Eclipse fog05 Descriptor {}'.format(locals(), desc
))
163 self
.fos_api
.network
.add_network(desc
)
164 except fimapi
.FIMAResouceExistingException
as free
:
165 raise vimconn
.VimConnConflictException("Network already exists at VIM. Error {}".format(free
))
166 except Exception as e
:
167 raise vimconn
.VimConnException("Unable to create network {}. Error {}".format(net_name
, e
))
168 # No way from the current rest service to get the actual error, most likely it will be an already
172 def get_network_list(self
, filter_dict
={}):
173 """Obtain tenant networks of VIM
174 :param filter_dict: (optional) contains entries to return only networks that matches ALL entries:
175 name: string => returns only networks with this name
176 id: string => returns networks with this VIM id, this imply returns one network at most
177 shared: boolean >= returns only networks that are (or are not) shared
178 tenant_id: sting => returns only networks that belong to this tenant/project
179 (not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
180 (not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
181 Returns the network list of dictionaries. each dictionary contains:
182 'id': (mandatory) VIM network id
183 'name': (mandatory) VIM network name
184 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
185 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat'
186 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id
187 'error_msg': (optional) text that explains the ERROR status
188 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
189 List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
190 authorization, or some other unspecific error
192 self
.logger
.debug('get_network_list: {}'.format(filter_dict
))
195 nets
= self
.fos_api
.network
.list()
196 except Exception as e
:
197 raise vimconn
.VimConnConnectionException(
198 "Cannot get network list from VIM, connection error. Error {}".format(e
))
201 partial(self
.__name
_filter
, filter_name
=filter_dict
.get('name')),
202 partial(self
.__id
_filter
, filter_id
=filter_dict
.get('id'))
210 match
= match
and f(n
)
217 'name': n
.get('name'),
223 def get_network(self
, net_id
):
224 """Obtain network details from the 'net_id' VIM network
225 Return a dict that contains:
226 'id': (mandatory) VIM network id, that is, net_id
227 'name': (mandatory) VIM network name
228 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
229 'error_msg': (optional) text that explains the ERROR status
230 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
231 Raises an exception upon error or when network is not found
233 self
.logger
.debug('get_network: {}'.format(net_id
))
234 res
= self
.get_network_list(filter_dict
={'id': net_id
})
236 raise vimconn
.VimConnNotFoundException("Network {} not found at VIM".format(net_id
))
239 def delete_network(self
, net_id
, created_items
=None):
240 """Deletes a tenant network from VIM
241 Returns the network identifier or raises an exception upon error or when network is not found
243 self
.logger
.debug('delete_network: {}'.format(net_id
))
245 self
.fos_api
.network
.remove_network(net_id
)
246 except fimapi
.FIMNotFoundException
as fnfe
:
247 raise vimconn
.VimConnNotFoundException(
248 "Network {} not found at VIM (already deleted?). Error {}".format(net_id
, fnfe
))
249 except Exception as e
:
250 raise vimconn
.VimConnException("Cannot delete network {} from VIM. Error {}".format(net_id
, e
))
253 def refresh_nets_status(self
, net_list
):
254 """Get the status of the networks
256 'net_list': a list with the VIM network id to be get the status
257 Returns a dictionary with:
258 'net_id': #VIM id of this network
259 status: #Mandatory. Text with one of:
260 # DELETED (not found at vim)
261 # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
262 # OTHER (Vim reported other status not understood)
263 # ERROR (VIM indicates an ERROR status)
264 # ACTIVE, INACTIVE, DOWN (admin down),
265 # BUILD (on building process)
266 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
267 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
270 self
.logger
.debug('Refeshing network status with args: {}'.format(locals()))
274 osm_n
= self
.get_network(n
)
275 r
[osm_n
.get('id')] = {'status': osm_n
.get('status')}
276 except vimconn
.VimConnNotFoundException
:
277 r
[n
] = {'status': 'VIM_ERROR'}
280 def get_flavor(self
, flavor_id
):
281 """Obtain flavor details from the VIM
282 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
283 Raises an exception upon error or if not found
285 self
.logger
.debug('VIM get_flavor with args: {}'.format(locals()))
287 r
= self
.fos_api
.flavor
.get(flavor_id
)
288 except Exception as e
:
289 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
291 raise vimconn
.VimConnNotFoundException("Flavor not found at VIM")
292 return {'id': r
.get('uuid'), 'name': r
.get('name'), 'fos': r
}
294 def get_flavor_id_from_data(self
, flavor_dict
):
295 """Obtain flavor id that match the flavor description
297 'flavor_dict': dictionary that contains:
298 'disk': main hard disk in GB
300 'vcpus': number of virtual cpus
301 #TODO: complete parameters for EPA
302 Returns the flavor_id or raises a vimconnNotFoundException
304 self
.logger
.debug('VIM get_flavor_id_from_data with args : {}'.format(locals()))
307 flvs
= self
.fos_api
.flavor
.list()
308 except Exception as e
:
309 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
310 r
= [x
.get('uuid') for x
in flvs
if (x
.get('cpu_min_count') == flavor_dict
.get('vcpus') and
311 x
.get('ram_size_mb') == flavor_dict
.get('ram') and
312 x
.get('storage_size_gb') == flavor_dict
.get('disk'))]
314 raise vimconn
.VimConnNotFoundException("No flavor found")
317 def new_flavor(self
, flavor_data
):
318 """Adds a tenant flavor to VIM
319 flavor_data contains a dictionary with information, keys:
321 ram: memory (cloud type) in MBytes
322 vpcus: cpus (cloud type)
323 extended: EPA parameters
324 - numas: #items requested in same NUMA
325 memory: number of 1G huge pages memory
326 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
327 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
328 - name: interface name
329 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
330 bandwidth: X Gbps; requested guarantee bandwidth
331 vpci: requested virtual PCI address
335 Returns the flavor identifier"""
336 self
.logger
.debug('VIM new_flavor with args: {}'.format(locals()))
337 flv_id
= '{}'.format(uuid
.uuid4())
340 'name': flavor_data
.get('name'),
341 'cpu_arch': self
.arch
,
342 'cpu_min_count': flavor_data
.get('vcpus'),
344 'ram_size_mb': float(flavor_data
.get('ram')),
345 'storage_size_gb': float(flavor_data
.get('disk'))
348 self
.fos_api
.flavor
.add(desc
)
349 except fimapi
.FIMAResouceExistingException
as free
:
350 raise vimconn
.VimConnConflictException("Flavor {} already exist at VIM. Error {}".format(flv_id
, free
))
351 except Exception as e
:
352 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
355 def delete_flavor(self
, flavor_id
):
356 """Deletes a tenant flavor from VIM identify by its id
357 Returns the used id or raise an exception"""
359 self
.fos_api
.flavor
.remove(flavor_id
)
360 except fimapi
.FIMNotFoundException
as fnfe
:
361 raise vimconn
.VimConnNotFoundException(
362 "Flavor {} not found at VIM (already deleted?). Error {}".format(flavor_id
, fnfe
))
363 except Exception as e
:
364 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
367 def new_image(self
, image_dict
):
368 """ Adds a tenant image to VIM. imge_dict is a dictionary with:
370 disk_format: qcow2, vhd, vmdk, raw (by default), ...
371 location: path or URI
372 public: "yes" or "no"
373 metadata: metadata of the image
374 Returns the image id or raises an exception if failed
376 self
.logger
.debug('VIM new_image with args: {}'.format(locals()))
377 img_id
= '{}'.format(uuid
.uuid4())
379 'name': image_dict
.get('name'),
381 'uri': image_dict
.get('location'),
382 'format': image_dict
.get('disk_format')
385 self
.fos_api
.image
.add(desc
)
386 except fimapi
.FIMAResouceExistingException
as free
:
387 raise vimconn
.VimConnConflictException("Image {} already exist at VIM. Error {}".format(img_id
, free
))
388 except Exception as e
:
389 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
392 def get_image_id_from_path(self
, path
):
394 """Get the image id from image path in the VIM database.
395 Returns the image_id or raises a vimconnNotFoundException
397 self
.logger
.debug('VIM get_image_id_from_path with args: {}'.format(locals()))
399 imgs
= self
.fos_api
.image
.list()
400 except Exception as e
:
401 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
402 res
= [x
.get('uuid') for x
in imgs
if x
.get('uri') == path
]
404 raise vimconn
.VimConnNotFoundException("Image with this path was not found")
407 def get_image_list(self
, filter_dict
={}):
408 """Obtain tenant images from VIM
412 checksum: image checksum
414 Returns the image list of dictionaries:
415 [{<the fields at Filter_dict plus some VIM specific>}, ...]
418 self
.logger
.debug('VIM get_image_list args: {}'.format(locals()))
421 fimgs
= self
.fos_api
.image
.list()
422 except Exception as e
:
423 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
426 partial(self
.__name
_filter
, filter_name
=filter_dict
.get('name')),
427 partial(self
.__id
_filter
, filter_id
=filter_dict
.get('id')),
428 partial(self
.__checksum
_filter
, filter_checksum
=filter_dict
.get('checksum'))
436 match
= match
and f(i
)
442 'name': i
.get('name'),
444 'checksum': i
.get('checksum'),
445 'location': i
.get('uri'),
450 # raise VimConnNotImplemented( "Should have implemented this" )
452 def new_vminstance(self
, name
, description
, start
, image_id
, flavor_id
, net_list
, cloud_config
=None, disk_list
=None,
453 availability_zone_index
=None, availability_zone_list
=None):
454 """Adds a VM instance to VIM
455 :param start: (boolean) indicates if VM must start or created in pause mode.
456 :param image_id: :param flavor_id: image and flavor VIM id to use for the VM
457 :param net_list: list of interfaces, each one is a dictionary with:
458 'name': (optional) name for the interface.
459 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
460 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
461 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
462 'mac_address': (optional) mac address to assign to this interface
463 'ip_address': (optional) IP address to assign to this interface
464 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
465 the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
466 'type': (mandatory) can be one of:
467 'virtual', in this case always connected to a network of type 'net_type=bridge'
468 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a
469 data/ptp network ot it can created unconnected
470 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
471 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
472 are allocated on the same physical NIC
473 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
474 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
475 or True, it must apply the default VIM behaviour
476 After execution the method will add the key:
477 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
478 interface. 'net_list' is modified
479 :param cloud_config: (optional) dictionary with:
480 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
481 'users': (optional) list of users to be inserted, each item is a dict with:
482 'name': (mandatory) user name,
483 'key-pairs': (optional) list of strings with the public key to be inserted to the user
484 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
485 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
486 'config-files': (optional). List of files to be transferred. Each item is a dict with:
487 'dest': (mandatory) string with the destination absolute path
488 'encoding': (optional, by default text). Can be one of:
489 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
490 'content' (mandatory): string with the content of the file
491 'permissions': (optional) string with file permissions, typically octal notation '0644'
492 'owner': (optional) file owner, string with the format 'owner:group'
493 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
494 :param disk_list: (optional) list with additional disks to the VM. Each item is a dict with:
495 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
496 'size': (mandatory) string with the size of the disk in GB
497 :param availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
498 :param availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
499 availability_zone_index is None
500 Returns a tuple with the instance identifier and created_items or raises an exception on error
501 created_items can be None or a dictionary where this method can include key-values that will be passed to
502 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
503 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
506 self
.logger
.debug('new_vminstance with args: {}'.format(locals()))
507 fdu_uuid
= '{}'.format(uuid
.uuid4())
509 flv
= self
.fos_api
.flavor
.get(flavor_id
)
510 img
= self
.fos_api
.image
.get(image_id
)
513 raise vimconn
.VimConnNotFoundException("Flavor {} not found at VIM".format(flavor_id
))
515 raise vimconn
.VimConnNotFoundException("Image {} not found at VIM".format(image_id
))
520 'connection_points': []
527 'computation_requirements': flv
,
529 'hypervisor': self
.hv
,
530 'migration_kind': 'LIVE',
533 'connection_points': [],
542 cp_id
= '{}'.format(uuid
.uuid4())
544 pair_id
= n
.get('net_id')
552 'name': n
.get('name', 'eth{}'.format(intf_id
)),
554 'if_type': 'INTERNAL',
555 'virtual_interface': {
556 'intf_type': n
.get('model', 'VIRTIO'),
557 'vpci': n
.get('vpci', '0:0:0'),
558 'bandwidth': int(n
.get('bw', 100))
562 if n
.get('mac_address', None) is not None:
563 intf_d
['mac_address'] = n
['mac_address']
565 created_items
['connection_points'].append(cp_id
)
566 fdu_desc
['connection_points'].append(cp_d
)
567 fdu_desc
['interfaces'].append(intf_d
)
569 intf_id
= intf_id
+ 1
571 if cloud_config
is not None:
572 configuration
= {'conf_type': 'CLOUD_INIT'}
573 if cloud_config
.get('user-data') is not None:
574 configuration
['script'] = cloud_config
.get('user-data')
575 if cloud_config
.get('key-pairs') is not None:
576 configuration
['ssh_keys'] = cloud_config
.get('key-pairs')
578 if 'script' in configuration
:
579 fdu_desc
['configuration'] = configuration
581 self
.logger
.debug('Eclipse fog05 FDU Descriptor: {}'.format(fdu_desc
))
586 self
.fos_api
.fdu
.onboard(fdu
)
587 instance
= self
.fos_api
.fdu
.define(fdu_uuid
)
588 instance_list
= self
.fos_api
.fdu
.instance_list(fdu_uuid
)
590 for n
in instance_list
:
591 instances
= instance_list
[n
]
592 if instance
.uuid
in instances
:
594 if selected_node
== '':
595 raise ValueError("Unable to find node for network creation")
597 self
.logger
.debug('Selected node by VIM: {}'.format(selected_node
))
598 created_items
['fdu_id'] = fdu_uuid
599 created_items
['node_id'] = selected_node
601 for cp
in fdu_desc
['connection_points']:
602 nets
= self
.fos_api
.network
.list()
604 if net
.get('uuid') == cp
['vld_ref']:
605 self
.fos_api
.network
.add_network_to_node(net
, selected_node
)
607 self
.fos_api
.fdu
.configure(instance
.uuid
)
608 self
.fos_api
.fdu
.start(instance
.uuid
)
610 self
.logger
.debug('Eclipse fog05 FDU Started {}'.format(instance
.uuid
))
612 created_items
['instance_id'] = str(instance
.uuid
)
614 self
.fdu_node_map
[instance
.uuid
] = selected_node
615 self
.logger
.debug('new_vminstance returns: {} {}'.format(instance
.uuid
, created_items
))
616 return str(instance
.uuid
), created_items
617 except fimapi
.FIMAResouceExistingException
as free
:
618 raise vimconn
.VimConnConflictException("VM already exists at VIM. Error {}".format(free
))
619 except Exception as e
:
620 raise vimconn
.VimConnException("Error while instantiating VM {}. Error {}".format(name
, e
))
622 def get_vminstance(self
, vm_id
):
623 """Returns the VM instance information from VIM"""
624 self
.logger
.debug('VIM get_vminstance with args: {}'.format(locals()))
627 instance
= self
.fos_api
.fdu
.instance_info(vm_id
)
628 except Exception as e
:
629 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))
631 raise vimconn
.VimConnNotFoundException('VM with id {} not found!'.format(vm_id
))
632 return instance
.to_json()
634 def delete_vminstance(self
, vm_id
, created_items
=None):
636 Removes a VM instance from VIM and each associate elements
637 :param vm_id: VIM identifier of the VM, provided by method new_vminstance
638 :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
640 :return: None or the same vm_id. Raises an exception on fail
642 self
.logger
.debug('FOS delete_vminstance with args: {}'.format(locals()))
643 fduid
= created_items
.get('fdu_id')
645 instance
= self
.fos_api
.fdu
.instance_info(vm_id
)
646 instance_list
= self
.fos_api
.fdu
.instance_list(instance
.fdu_id
)
648 for n
in instance_list
:
649 instances
= instance_list
[n
]
650 if instance
.uuid
in instances
:
652 if selected_node
== '':
653 raise ValueError("Unable to find node for the given Instance")
655 self
.fos_api
.fdu
.stop(vm_id
)
657 for cp
in instance
.to_json()['connection_points']:
658 nets
= self
.fos_api
.network
.list()
660 if net
.get('uuid') == cp
['vld_ref']:
661 self
.fos_api
.network
.remove_network_from_node(net
.get('uuid'), selected_node
)
663 self
.fos_api
.fdu
.clean(vm_id
)
664 self
.fos_api
.fdu
.undefine(vm_id
)
666 self
.fos_api
.fdu
.offload(fduid
)
667 except Exception as e
:
668 raise vimconn
.VimConnException("Error on deleting VM with id {}. Error {}".format(vm_id
, e
))
671 # raise VimConnNotImplemented( "Should have implemented this" )
673 def refresh_vms_status(self
, vm_list
):
674 """Get the status of the virtual machines and their interfaces/ports
675 Params: the list of VM identifiers
676 Returns a dictionary with:
677 vm_id: #VIM id of this Virtual Machine
678 status: #Mandatory. Text with one of:
679 # DELETED (not found at vim)
680 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
681 # OTHER (Vim reported other status not understood)
682 # ERROR (VIM indicates an ERROR status)
683 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
684 # BUILD (on building process), ERROR
685 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
687 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
688 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
689 interfaces: list with interface info. Each item a dictionary with:
690 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
691 mac_address: #Text format XX:XX:XX:XX:XX:XX
692 vim_net_id: #network id where this interface is connected, if provided at creation
693 vim_interface_id: #interface/port VIM id
694 ip_address: #null, or text with IPv4, IPv6 address
695 compute_node: #identification of compute node where PF,VF interface is allocated
696 pci: #PCI address of the NIC that hosts the PF,VF
697 vlan: #physical VLAN used for VF
699 self
.logger
.debug('FOS refresh_vms_status with args: {}'.format(locals()))
702 'CONFIGURE': 'INACTIVE',
711 self
.logger
.debug('FOS refresh_vms_status for {}'.format(vm
))
714 nid
= self
.fdu_node_map
.get(vm
)
717 'status': 'VIM_ERROR',
718 'error_msg': 'Not compute node associated for VM'
723 vm_info
= self
.fos_api
.fdu
.instance_info(vm
)
726 'status': 'VIM_ERROR',
727 'error_msg': 'unable to connect to VIM'
732 r
[vm
:] = {'status': 'DELETED'}
735 desc
= self
.fos_api
.fdu
.info(str(vm_info
.fdu_id
))
737 vm_info
= vm_info
.to_json()
738 desc
= desc
.to_json()
740 osm_status
= fos2osm_status
.get(vm_info
.get('status'))
742 self
.logger
.debug('FOS status info {}'.format(vm_info
))
743 self
.logger
.debug('FOS status is {} <-> OSM Status {}'.format(vm_info
.get('status'), osm_status
))
744 info
['status'] = osm_status
745 if vm_info
.get('status') == 'ERROR':
746 info
['error_msg'] = vm_info
.get('error_code')
747 # yaml.safe_dump(json.loads(json.dumps(vm_info)))
748 # info['vim_info'] = ''
751 for intf_name
in vm_info
.get('hypervisor_info').get('network', []):
752 intf_info
= vm_info
.get('hypervisor_info').get('network').get(intf_name
)
754 face
['compute_node'] = nid
755 # face['vim_info'] = '' #yaml.safe_dump(json.loads(json.dumps(intf_info)))
756 face
['mac_address'] = intf_info
.get('hwaddr')
758 for a
in intf_info
.get('addresses'):
759 addrs
.append(a
.get('address'))
761 face
['ip_address'] = ','.join(addrs
)
763 face
['ip_address'] = ''
764 face
['pci'] = '0:0:0.0'
767 cp_info
= vm_info
.get('connection_points')[i
]
770 if cp_info
is not None:
771 cp_id
= cp_info
['cp_id']
772 cps_d
= desc
['connection_points']
773 matches
= [x
for x
in cps_d
if x
['id'] == cp_id
]
776 face
['vim_net_id'] = cpd
.get('vld_ref', '')
778 face
['vim_net_id'] = ''
779 face
['vim_interface_id'] = cp_id
780 # cp_info.get('uuid')
782 face
['vim_net_id'] = ''
783 face
['vim_interface_id'] = intf_name
787 info
['interfaces'] = faces
789 self
.logger
.debug('FOS refresh_vms_status res for {} is {}'.format(vm
, info
))
790 self
.logger
.debug('FOS refresh_vms_status res is {}'.format(r
))
793 def action_vminstance(self
, vm_id
, action_dict
, created_items
={}):
795 Send and action over a VM instance. Returns created_items if the action was successfully sent to the VIM.
796 created_items is a dictionary with items that
797 :param vm_id: VIM identifier of the VM, provided by method new_vminstance
798 :param action_dict: dictionary with the action to perform
799 :param created_items: provided by method new_vminstance is a dictionary with key-values that will be passed to
800 the method delete_vminstance. Can be used to store created ports, volumes, etc. Format is vimconnector
801 dependent, but do not use nested dictionaries and a value of None should be the same as not present. This
802 method can modify this value
803 :return: None, or a console dict
805 self
.logger
.debug('VIM action_vminstance with args: {}'.format(locals()))
806 nid
= self
.fdu_node_map
.get(vm_id
)
808 raise vimconn
.VimConnNotFoundException('No node for this VM')
810 instance
= self
.fos_api
.fdu
.instance_info(vm_id
)
811 if "start" in action_dict
:
812 if instance
.get('status') == 'CONFIGURE':
813 self
.fos_api
.fdu
.start(vm_id
)
814 elif instance
.get('status') == 'PAUSE':
815 self
.fos_api
.fdu
.resume(vm_id
)
817 raise vimconn
.VimConnConflictException('Cannot start from current state: {}'.format(
818 instance
.get('status')))
819 elif "pause" in action_dict
:
820 if instance
.get('status') == 'RUN':
821 self
.fos_api
.fdu
.pause(vm_id
)
823 raise vimconn
.VimConnConflictException('Cannot pause from current state: {}'.format(
824 instance
.get('status')))
825 elif "resume" in action_dict
:
826 if instance
.get('status') == 'PAUSE':
827 self
.fos_api
.fdu
.resume(vm_id
)
829 raise vimconn
.VimConnConflictException('Cannot resume from current state: {}'.format(
830 instance
.get('status')))
831 elif "shutoff" in action_dict
or "shutdown" or "forceOff" in action_dict
:
832 if instance
.get('status') == 'RUN':
833 self
.fos_api
.fdu
.stop(vm_id
)
835 raise vimconn
.VimConnConflictException('Cannot shutoff from current state: {}'.format(
836 instance
.get('status')))
837 elif "terminate" in action_dict
:
838 if instance
.get('status') == 'RUN':
839 self
.fos_api
.fdu
.stop(vm_id
)
840 self
.fos_api
.fdu
.clean(vm_id
)
841 self
.fos_api
.fdu
.undefine(vm_id
)
842 # self.fos_api.fdu.offload(vm_id)
843 elif instance
.get('status') == 'CONFIGURE':
844 self
.fos_api
.fdu
.clean(vm_id
)
845 self
.fos_api
.fdu
.undefine(vm_id
)
846 # self.fos_api.fdu.offload(vm_id)
847 elif instance
.get('status') == 'PAUSE':
848 self
.fos_api
.fdu
.resume(vm_id
)
849 self
.fos_api
.fdu
.stop(vm_id
)
850 self
.fos_api
.fdu
.clean(vm_id
)
851 self
.fos_api
.fdu
.undefine(vm_id
)
852 # self.fos_api.fdu.offload(vm_id)
854 raise vimconn
.VimConnConflictException('Cannot terminate from current state: {}'.format(
855 instance
.get('status')))
856 elif "rebuild" in action_dict
:
857 raise vimconn
.VimConnNotImplemented("Rebuild not implemented")
858 elif "reboot" in action_dict
:
859 if instance
.get('status') == 'RUN':
860 self
.fos_api
.fdu
.stop(vm_id
)
861 self
.fos_api
.fdu
.start(vm_id
)
863 raise vimconn
.VimConnConflictException('Cannot reboot from current state: {}'.format(
864 instance
.get('status')))
865 except Exception as e
:
866 raise vimconn
.VimConnConnectionException("VIM not reachable. Error {}".format(e
))