1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
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
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 vimconn implement an Abstract class for the vim connector plugins
26 with the definition of the method to be implemented.
28 __author__
="Alfonso Tierno"
29 __date__
="$16-oct-2015 11:09:29$"
34 HTTP_Bad_Request
= 400
35 HTTP_Unauthorized
= 401
37 HTTP_Method_Not_Allowed
= 405
38 HTTP_Request_Timeout
= 408
40 HTTP_Not_Implemented
= 501
41 HTTP_Service_Unavailable
= 503
42 HTTP_Internal_Server_Error
= 500
44 class vimconnException(Exception):
45 """Common and base class Exception for all vimconnector exceptions"""
46 def __init__(self
, message
, http_code
=HTTP_Bad_Request
):
47 Exception.__init
__(self
, message
)
48 self
.http_code
= http_code
50 class vimconnConnectionException(vimconnException
):
51 """Connectivity error with the VIM"""
52 def __init__(self
, message
, http_code
=HTTP_Service_Unavailable
):
53 vimconnException
.__init
__(self
, message
, http_code
)
55 class vimconnUnexpectedResponse(vimconnException
):
56 """Get an wrong response from VIM"""
57 def __init__(self
, message
, http_code
=HTTP_Service_Unavailable
):
58 vimconnException
.__init
__(self
, message
, http_code
)
60 class vimconnAuthException(vimconnException
):
61 """Invalid credentials or authorization to perform this action over the VIM"""
62 def __init__(self
, message
, http_code
=HTTP_Unauthorized
):
63 vimconnException
.__init
__(self
, message
, http_code
)
65 class vimconnNotFoundException(vimconnException
):
66 """The item is not found at VIM"""
67 def __init__(self
, message
, http_code
=HTTP_Not_Found
):
68 vimconnException
.__init
__(self
, message
, http_code
)
70 class vimconnConflictException(vimconnException
):
71 """There is a conflict, e.g. more item found than one"""
72 def __init__(self
, message
, http_code
=HTTP_Conflict
):
73 vimconnException
.__init
__(self
, message
, http_code
)
75 class vimconnNotSupportedException(vimconnException
):
76 """The request is not supported by connector"""
77 def __init__(self
, message
, http_code
=HTTP_Service_Unavailable
):
78 vimconnException
.__init
__(self
, message
, http_code
)
80 class vimconnNotImplemented(vimconnException
):
81 """The method is not implemented by the connected"""
82 def __init__(self
, message
, http_code
=HTTP_Not_Implemented
):
83 vimconnException
.__init
__(self
, message
, http_code
)
87 """Abstract base class for all the VIM connector plugins
88 These plugins must implement a vimconnector class derived from this
89 and all these privated methods
91 def __init__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
=None, user
=None, passwd
=None, log_level
=None,
92 config
={}, persitent_info
={}):
95 'uuid': id asigned to this VIM
96 'name': name assigned to this VIM, can be used for logging
97 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used
98 'url_admin': (optional), url used for administrative tasks
99 'user', 'passwd': credentials of the VIM user
100 'log_level': provider if it should use a different log_level than the general one
101 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config
102 at creation and particular VIM config at teh attachment
103 'persistent_info': dict where the class can store information that will be available among class
104 destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
105 empty dict. Useful to store login/tokens information for speed up communication
107 Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity
108 check against the VIM
113 self
.url_admin
= url_admin
114 self
.tenant_id
= tenant_id
115 self
.tenant_name
= tenant_name
119 self
.availability_zone
= None
120 self
.logger
= logging
.getLogger('openmano.vim')
122 self
.logger
.setLevel( getattr(logging
, log_level
) )
123 if not self
.url_admin
: #try to use normal url
124 self
.url_admin
= self
.url
126 def __getitem__(self
,index
):
127 if index
=='tenant_id':
128 return self
.tenant_id
129 if index
=='tenant_name':
130 return self
.tenant_name
137 elif index
=='passwd':
141 elif index
=='url_admin':
142 return self
.url_admin
143 elif index
=="config":
146 raise KeyError("Invalid key '%s'" %str
(index
))
148 def __setitem__(self
,index
, value
):
149 if index
=='tenant_id':
150 self
.tenant_id
= value
151 if index
=='tenant_name':
152 self
.tenant_name
= value
159 elif index
=='passwd':
163 elif index
=='url_admin':
164 self
.url_admin
= value
166 raise KeyError("Invalid key '%s'" %str
(index
))
168 def check_vim_connectivity(self
):
169 """Checks VIM can be reached and user credentials are ok.
170 Returns None if success or raised vimconnConnectionException, vimconnAuthException, ...
172 raise vimconnNotImplemented( "Should have implemented this" )
174 def new_tenant(self
,tenant_name
,tenant_description
):
175 """Adds a new tenant to VIM with this name and description, this is done using admin_url if provided
176 "tenant_name": string max lenght 64
177 "tenant_description": string max length 256
178 returns the tenant identifier or raise exception
180 raise vimconnNotImplemented( "Should have implemented this" )
182 def delete_tenant(self
,tenant_id
,):
183 """Delete a tenant from VIM
184 tenant_id: returned VIM tenant_id on "new_tenant"
185 Returns None on success. Raises and exception of failure. If tenant is not found raises vimconnNotFoundException
187 raise vimconnNotImplemented( "Should have implemented this" )
189 def get_tenant_list(self
, filter_dict
={}):
190 """Obtain tenants of VIM
191 filter_dict dictionary that can contain the following keys:
192 name: filter by tenant name
193 id: filter by tenant uuid/id
195 Returns the tenant list of dictionaries, and empty list if no tenant match all the filers:
196 [{'name':'<name>, 'id':'<id>, ...}, ...]
198 raise vimconnNotImplemented( "Should have implemented this" )
200 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False, vlan
=None):
201 """Adds a tenant network to VIM
203 'net_name': name of the network
205 'bridge': overlay isolated network
206 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
207 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
208 'ip_profile': is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
209 'ip-version': can be one of ["IPv4","IPv6"]
210 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
211 'gateway-address': (Optional) ip_schema, that is X.X.X.X
212 'dns-address': (Optional) ip_schema,
213 'dhcp': (Optional) dict containing
214 'enabled': {"type": "boolean"},
215 'start-address': ip_schema, first IP to grant
216 'count': number of IPs to grant.
217 'shared': if this network can be seen/use by other tenants/organization
218 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
219 Returns the network identifier on success or raises and exception on failure
221 raise vimconnNotImplemented( "Should have implemented this" )
223 def get_network_list(self
, filter_dict
={}):
224 """Obtain tenant networks of VIM
226 'filter_dict' (optional) contains entries to return only networks that matches ALL entries:
227 name: string => returns only networks with this name
228 id: string => returns networks with this VIM id, this imply returns one network at most
229 shared: boolean >= returns only networks that are (or are not) shared
230 tenant_id: sting => returns only networks that belong to this tenant/project
231 ,#(not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
232 #(not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
233 Returns the network list of dictionaries. each dictionary contains:
234 'id': (mandatory) VIM network id
235 'name': (mandatory) VIM network name
236 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
237 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat'
238 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id
239 'error_msg': (optional) text that explains the ERROR status
240 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
241 List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
242 authorization, or some other unspecific error
244 raise vimconnNotImplemented( "Should have implemented this" )
246 def get_network(self
, net_id
):
247 """Obtain network details from the 'net_id' VIM network
248 Return a dict that contains:
249 'id': (mandatory) VIM network id, that is, net_id
250 'name': (mandatory) VIM network name
251 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
252 'error_msg': (optional) text that explains the ERROR status
253 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
254 Raises an exception upon error or when network is not found
256 raise vimconnNotImplemented( "Should have implemented this" )
258 def delete_network(self
, net_id
):
259 """Deletes a tenant network from VIM
260 Returns the network identifier or raises an exception upon error or when network is not found
262 raise vimconnNotImplemented( "Should have implemented this" )
264 def refresh_nets_status(self
, net_list
):
265 """Get the status of the networks
267 'net_list': a list with the VIM network id to be get the status
268 Returns a dictionary with:
269 'net_id': #VIM id of this network
270 status: #Mandatory. Text with one of:
271 # DELETED (not found at vim)
272 # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
273 # OTHER (Vim reported other status not understood)
274 # ERROR (VIM indicates an ERROR status)
275 # ACTIVE, INACTIVE, DOWN (admin down),
276 # BUILD (on building process)
277 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
278 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
281 raise vimconnNotImplemented( "Should have implemented this" )
283 def get_flavor(self
, flavor_id
):
284 """Obtain flavor details from the VIM
285 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
286 Raises an exception upon error or if not found
288 raise vimconnNotImplemented( "Should have implemented this" )
290 def get_flavor_id_from_data(self
, flavor_dict
):
291 """Obtain flavor id that match the flavor description
293 'flavor_dict': dictionary that contains:
294 'disk': main hard disk in GB
296 'vcpus': number of virtual cpus
297 #TODO: complete parameters for EPA
298 Returns the flavor_id or raises a vimconnNotFoundException
300 raise vimconnNotImplemented( "Should have implemented this" )
302 def new_flavor(self
, flavor_data
):
303 """Adds a tenant flavor to VIM
304 flavor_data contains a dictionary with information, keys:
306 ram: memory (cloud type) in MBytes
307 vpcus: cpus (cloud type)
308 extended: EPA parameters
309 - numas: #items requested in same NUMA
310 memory: number of 1G huge pages memory
311 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
312 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
313 - name: interface name
314 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
315 bandwidth: X Gbps; requested guarantee bandwidth
316 vpci: requested virtual PCI address
320 Returns the flavor identifier"""
321 raise vimconnNotImplemented( "Should have implemented this" )
323 def delete_flavor(self
, flavor_id
):
324 """Deletes a tenant flavor from VIM identify by its id
325 Returns the used id or raise an exception"""
326 raise vimconnNotImplemented( "Should have implemented this" )
328 def new_image(self
, image_dict
):
329 """ Adds a tenant image to VIM
330 Returns the image id or raises an exception if failed
332 raise vimconnNotImplemented( "Should have implemented this" )
334 def delete_image(self
, image_id
):
335 """Deletes a tenant image from VIM
336 Returns the image_id if image is deleted or raises an exception on error"""
337 raise vimconnNotImplemented( "Should have implemented this" )
339 def get_image_id_from_path(self
, path
):
340 """Get the image id from image path in the VIM database.
341 Returns the image_id or raises a vimconnNotFoundException
343 raise vimconnNotImplemented( "Should have implemented this" )
345 def get_image_list(self
, filter_dict
={}):
346 """Obtain tenant images from VIM
350 checksum: image checksum
352 Returns the image list of dictionaries:
353 [{<the fields at Filter_dict plus some VIM specific>}, ...]
356 raise vimconnNotImplemented( "Should have implemented this" )
358 def new_vminstance(self
, name
, description
, start
, image_id
, flavor_id
, net_list
, cloud_config
=None, disk_list
=None,
359 availavility_zone_index
=None, nfv_availability_zones
=None):
360 """Adds a VM instance to VIM
362 'start': (boolean) indicates if VM must start or created in pause mode.
363 'image_id','flavor_id': image and flavor VIM id to use for the VM
364 'net_list': list of interfaces, each one is a dictionary with:
365 'name': (optional) name for the interface.
366 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
367 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
368 'model': (optional and only have sense for type==virtual) interface model: virtio, e2000, ...
369 'mac_address': (optional) mac address to assign to this interface
370 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
371 the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
372 'type': (mandatory) can be one of:
373 'virtual', in this case always connected to a network of type 'net_type=bridge'
374 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
375 can created unconnected
376 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
377 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
378 are allocated on the same physical NIC
379 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
380 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
381 or True, it must apply the default VIM behaviour
382 After execution the method will add the key:
383 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
384 interface. 'net_list' is modified
385 'cloud_config': (optional) dictionary with:
386 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
387 'users': (optional) list of users to be inserted, each item is a dict with:
388 'name': (mandatory) user name,
389 'key-pairs': (optional) list of strings with the public key to be inserted to the user
390 'user-data': (optional) string is a text script to be passed directly to cloud-init
391 'config-files': (optional). List of files to be transferred. Each item is a dict with:
392 'dest': (mandatory) string with the destination absolute path
393 'encoding': (optional, by default text). Can be one of:
394 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
395 'content' (mandatory): string with the content of the file
396 'permissions': (optional) string with file permissions, typically octal notation '0644'
397 'owner': (optional) file owner, string with the format 'owner:group'
398 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
399 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
400 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
401 'size': (mandatory) string with the size of the disk in GB
402 availavility_zone_index: Index of nfv_availability_zones to use for this this VM
403 nfv_availability_zones: list of availability zones given by user in the VNFC descriptor. Ignore if
404 availability_zone_index is None
405 Returns the instance identifier or raises an exception on error
407 raise vimconnNotImplemented( "Should have implemented this" )
409 def get_vminstance(self
,vm_id
):
410 """Returns the VM instance information from VIM"""
411 raise vimconnNotImplemented( "Should have implemented this" )
413 def delete_vminstance(self
, vm_id
):
414 """Removes a VM instance from VIM
415 Returns the instance identifier"""
416 raise vimconnNotImplemented( "Should have implemented this" )
418 def refresh_vms_status(self
, vm_list
):
419 """Get the status of the virtual machines and their interfaces/ports
420 Params: the list of VM identifiers
421 Returns a dictionary with:
422 vm_id: #VIM id of this Virtual Machine
423 status: #Mandatory. Text with one of:
424 # DELETED (not found at vim)
425 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
426 # OTHER (Vim reported other status not understood)
427 # ERROR (VIM indicates an ERROR status)
428 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
429 # BUILD (on building process), ERROR
430 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
432 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
433 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
434 interfaces: list with interface info. Each item a dictionary with:
435 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
436 mac_address: #Text format XX:XX:XX:XX:XX:XX
437 vim_net_id: #network id where this interface is connected, if provided at creation
438 vim_interface_id: #interface/port VIM id
439 ip_address: #null, or text with IPv4, IPv6 address
440 compute_node: #identification of compute node where PF,VF interface is allocated
441 pci: #PCI address of the NIC that hosts the PF,VF
442 vlan: #physical VLAN used for VF
444 raise vimconnNotImplemented( "Should have implemented this" )
446 def action_vminstance(self
, vm_id
, action_dict
):
447 """Send and action over a VM instance from VIM
448 Returns the vm_id if the action was successfully sent to the VIM"""
449 raise vimconnNotImplemented( "Should have implemented this" )
451 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
453 Get a console for the virtual machine
455 vm_id: uuid of the VM
456 console_type, can be:
457 "novnc" (by default), "xvpvnc" for VNC types,
458 "rdp-html5" for RDP types, "spice-html5" for SPICE types
459 Returns dict with the console parameters:
460 protocol: ssh, ftp, http, https, ...
461 server: usually ip address
462 port: the http, ssh, ... port
463 suffix: extra text, e.g. the http path and query string
465 raise vimconnNotImplemented( "Should have implemented this" )
467 #NOT USED METHODS in current version
469 def host_vim2gui(self
, host
, server_dict
):
470 """Transform host dictionary from VIM format to GUI format,
471 and append to the server_dict
473 raise vimconnNotImplemented( "Should have implemented this" )
475 def get_hosts_info(self
):
476 """Get the information of deployed hosts
477 Returns the hosts content"""
478 raise vimconnNotImplemented( "Should have implemented this" )
480 def get_hosts(self
, vim_tenant
):
481 """Get the hosts and deployed instances
482 Returns the hosts content"""
483 raise vimconnNotImplemented( "Should have implemented this" )
485 def get_processor_rankings(self
):
486 """Get the processor rankings in the VIM database"""
487 raise vimconnNotImplemented( "Should have implemented this" )
489 def new_host(self
, host_data
):
490 """Adds a new host to VIM"""
491 """Returns status code of the VIM response"""
492 raise vimconnNotImplemented( "Should have implemented this" )
494 def new_external_port(self
, port_data
):
495 """Adds a external port to VIM"""
496 """Returns the port identifier"""
497 raise vimconnNotImplemented( "Should have implemented this" )
499 def new_external_network(self
,net_name
,net_type
):
500 """Adds a external network to VIM (shared)"""
501 """Returns the network identifier"""
502 raise vimconnNotImplemented( "Should have implemented this" )
504 def connect_port_network(self
, port_id
, network_id
, admin
=False):
505 """Connects a external port to a network"""
506 """Returns status code of the VIM response"""
507 raise vimconnNotImplemented( "Should have implemented this" )
509 def new_vminstancefromJSON(self
, vm_data
):
510 """Adds a VM instance to VIM"""
511 """Returns the instance identifier"""
512 raise vimconnNotImplemented( "Should have implemented this" )