feature 8029 change RO to python3. Using vim plugins
[osm/RO.git] / RO / osm_ro / vimconn.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
5 # This file is part of openmano
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 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
22 ##
23
24 """
25 vimconn implement an Abstract class for the vim connector plugins
26 with the definition of the method to be implemented.
27 """
28
29 import logging
30 import paramiko
31 import socket
32 from io import StringIO
33 import yaml
34 import sys
35 from email.mime.multipart import MIMEMultipart
36 from email.mime.text import MIMEText
37 from osm_ro.utils import deprecated
38
39 __author__ = "Alfonso Tierno, Igor D.C."
40 __date__ = "$14-aug-2017 23:59:59$"
41
42 #Error variables
43 HTTP_Bad_Request = 400
44 HTTP_Unauthorized = 401
45 HTTP_Not_Found = 404
46 HTTP_Method_Not_Allowed = 405
47 HTTP_Request_Timeout = 408
48 HTTP_Conflict = 409
49 HTTP_Not_Implemented = 501
50 HTTP_Service_Unavailable = 503
51 HTTP_Internal_Server_Error = 500
52
53
54 class vimconnException(Exception):
55 """Common and base class Exception for all vimconnector exceptions"""
56 def __init__(self, message, http_code=HTTP_Bad_Request):
57 Exception.__init__(self, message)
58 self.http_code = http_code
59
60
61 class vimconnConnectionException(vimconnException):
62 """Connectivity error with the VIM"""
63 def __init__(self, message, http_code=HTTP_Service_Unavailable):
64 vimconnException.__init__(self, message, http_code)
65
66
67 class vimconnUnexpectedResponse(vimconnException):
68 """Get an wrong response from VIM"""
69 def __init__(self, message, http_code=HTTP_Service_Unavailable):
70 vimconnException.__init__(self, message, http_code)
71
72
73 class vimconnAuthException(vimconnException):
74 """Invalid credentials or authorization to perform this action over the VIM"""
75 def __init__(self, message, http_code=HTTP_Unauthorized):
76 vimconnException.__init__(self, message, http_code)
77
78
79 class vimconnNotFoundException(vimconnException):
80 """The item is not found at VIM"""
81 def __init__(self, message, http_code=HTTP_Not_Found):
82 vimconnException.__init__(self, message, http_code)
83
84
85 class vimconnConflictException(vimconnException):
86 """There is a conflict, e.g. more item found than one"""
87 def __init__(self, message, http_code=HTTP_Conflict):
88 vimconnException.__init__(self, message, http_code)
89
90
91 class vimconnNotSupportedException(vimconnException):
92 """The request is not supported by connector"""
93 def __init__(self, message, http_code=HTTP_Service_Unavailable):
94 vimconnException.__init__(self, message, http_code)
95
96
97 class vimconnNotImplemented(vimconnException):
98 """The method is not implemented by the connected"""
99 def __init__(self, message, http_code=HTTP_Not_Implemented):
100 vimconnException.__init__(self, message, http_code)
101
102
103 class vimconnector():
104 """Abstract base class for all the VIM connector plugins
105 These plugins must implement a vimconnector class derived from this
106 and all these privated methods
107 """
108 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
109 config={}, persitent_info={}):
110 """
111 Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity
112 checking against the VIM
113 :param uuid: internal id of this VIM
114 :param name: name assigned to this VIM, can be used for logging
115 :param tenant_id: 'tenant_id': (only one of them is mandatory) VIM tenant to be used
116 :param tenant_name: 'tenant_name': (only one of them is mandatory) VIM tenant to be used
117 :param url: url used for normal operations
118 :param url_admin: (optional), url used for administrative tasks
119 :param user: user to access
120 :param passwd: password
121 :param log_level: provided if it should use a different log_level than the general one
122 :param config: dictionary with extra VIM information. This contains a consolidate version of VIM config
123 at VIM_ACCOUNT (attach)
124 :param persitent_info: dict where the class can store information that will be available among class
125 destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an
126 empty dict. Useful to store login/tokens information for speed up communication
127
128 """
129 self.id = uuid
130 self.name = name
131 self.url = url
132 self.url_admin = url_admin
133 self.tenant_id = tenant_id
134 self.tenant_name = tenant_name
135 self.user = user
136 self.passwd = passwd
137 self.config = config or {}
138 self.availability_zone = None
139 self.logger = logging.getLogger('openmano.vim')
140 if log_level:
141 self.logger.setLevel(getattr(logging, log_level))
142 if not self.url_admin: # try to use normal url
143 self.url_admin = self.url
144
145 def __getitem__(self, index):
146 if index == 'tenant_id':
147 return self.tenant_id
148 if index == 'tenant_name':
149 return self.tenant_name
150 elif index == 'id':
151 return self.id
152 elif index == 'name':
153 return self.name
154 elif index == 'user':
155 return self.user
156 elif index == 'passwd':
157 return self.passwd
158 elif index == 'url':
159 return self.url
160 elif index == 'url_admin':
161 return self.url_admin
162 elif index == "config":
163 return self.config
164 else:
165 raise KeyError("Invalid key '{}'".format(index))
166
167 def __setitem__(self, index, value):
168 if index == 'tenant_id':
169 self.tenant_id = value
170 if index == 'tenant_name':
171 self.tenant_name = value
172 elif index == 'id':
173 self.id = value
174 elif index == 'name':
175 self.name = value
176 elif index == 'user':
177 self.user = value
178 elif index == 'passwd':
179 self.passwd = value
180 elif index == 'url':
181 self.url = value
182 elif index == 'url_admin':
183 self.url_admin = value
184 else:
185 raise KeyError("Invalid key '{}'".format(index))
186
187 @staticmethod
188 def _create_mimemultipart(content_list):
189 """Creates a MIMEmultipart text combining the content_list
190 :param content_list: list of text scripts to be combined
191 :return: str of the created MIMEmultipart. If the list is empty returns None, if the list contains only one
192 element MIMEmultipart is not created and this content is returned
193 """
194 if not content_list:
195 return None
196 elif len(content_list) == 1:
197 return content_list[0]
198 combined_message = MIMEMultipart()
199 for content in content_list:
200 if content.startswith('#include'):
201 mime_format = 'text/x-include-url'
202 elif content.startswith('#include-once'):
203 mime_format = 'text/x-include-once-url'
204 elif content.startswith('#!'):
205 mime_format = 'text/x-shellscript'
206 elif content.startswith('#cloud-config'):
207 mime_format = 'text/cloud-config'
208 elif content.startswith('#cloud-config-archive'):
209 mime_format = 'text/cloud-config-archive'
210 elif content.startswith('#upstart-job'):
211 mime_format = 'text/upstart-job'
212 elif content.startswith('#part-handler'):
213 mime_format = 'text/part-handler'
214 elif content.startswith('#cloud-boothook'):
215 mime_format = 'text/cloud-boothook'
216 else: # by default
217 mime_format = 'text/x-shellscript'
218 sub_message = MIMEText(content, mime_format, sys.getdefaultencoding())
219 combined_message.attach(sub_message)
220 return combined_message.as_string()
221
222 def _create_user_data(self, cloud_config):
223 """
224 Creates a script user database on cloud_config info
225 :param cloud_config: dictionary with
226 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
227 'users': (optional) list of users to be inserted, each item is a dict with:
228 'name': (mandatory) user name,
229 'key-pairs': (optional) list of strings with the public key to be inserted to the user
230 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
231 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
232 'config-files': (optional). List of files to be transferred. Each item is a dict with:
233 'dest': (mandatory) string with the destination absolute path
234 'encoding': (optional, by default text). Can be one of:
235 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
236 'content' (mandatory): string with the content of the file
237 'permissions': (optional) string with file permissions, typically octal notation '0644'
238 'owner': (optional) file owner, string with the format 'owner:group'
239 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
240 :return: config_drive, userdata. The first is a boolean or None, the second a string or None
241 """
242 config_drive = None
243 userdata = None
244 userdata_list = []
245 if isinstance(cloud_config, dict):
246 if cloud_config.get("user-data"):
247 if isinstance(cloud_config["user-data"], str):
248 userdata_list.append(cloud_config["user-data"])
249 else:
250 for u in cloud_config["user-data"]:
251 userdata_list.append(u)
252 if cloud_config.get("boot-data-drive") is not None:
253 config_drive = cloud_config["boot-data-drive"]
254 if cloud_config.get("config-files") or cloud_config.get("users") or cloud_config.get("key-pairs"):
255 userdata_dict = {}
256 # default user
257 if cloud_config.get("key-pairs"):
258 userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
259 userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"]}]
260 if cloud_config.get("users"):
261 if "users" not in userdata_dict:
262 userdata_dict["users"] = ["default"]
263 for user in cloud_config["users"]:
264 user_info = {
265 "name": user["name"],
266 "sudo": "ALL = (ALL)NOPASSWD:ALL"
267 }
268 if "user-info" in user:
269 user_info["gecos"] = user["user-info"]
270 if user.get("key-pairs"):
271 user_info["ssh-authorized-keys"] = user["key-pairs"]
272 userdata_dict["users"].append(user_info)
273
274 if cloud_config.get("config-files"):
275 userdata_dict["write_files"] = []
276 for file in cloud_config["config-files"]:
277 file_info = {
278 "path": file["dest"],
279 "content": file["content"]
280 }
281 if file.get("encoding"):
282 file_info["encoding"] = file["encoding"]
283 if file.get("permissions"):
284 file_info["permissions"] = file["permissions"]
285 if file.get("owner"):
286 file_info["owner"] = file["owner"]
287 userdata_dict["write_files"].append(file_info)
288 userdata_list.append("#cloud-config\n" + yaml.safe_dump(userdata_dict, indent=4,
289 default_flow_style=False))
290 userdata = self._create_mimemultipart(userdata_list)
291 self.logger.debug("userdata: %s", userdata)
292 elif isinstance(cloud_config, str):
293 userdata = cloud_config
294 return config_drive, userdata
295
296 def check_vim_connectivity(self):
297 """Checks VIM can be reached and user credentials are ok.
298 Returns None if success or raises vimconnConnectionException, vimconnAuthException, ...
299 """
300 # by default no checking until each connector implements it
301 return None
302
303 def new_tenant(self, tenant_name, tenant_description):
304 """Adds a new tenant to VIM with this name and description, this is done using admin_url if provided
305 "tenant_name": string max lenght 64
306 "tenant_description": string max length 256
307 returns the tenant identifier or raise exception
308 """
309 raise vimconnNotImplemented("Should have implemented this")
310
311 def delete_tenant(self, tenant_id):
312 """Delete a tenant from VIM
313 tenant_id: returned VIM tenant_id on "new_tenant"
314 Returns None on success. Raises and exception of failure. If tenant is not found raises vimconnNotFoundException
315 """
316 raise vimconnNotImplemented("Should have implemented this")
317
318 def get_tenant_list(self, filter_dict={}):
319 """Obtain tenants of VIM
320 filter_dict dictionary that can contain the following keys:
321 name: filter by tenant name
322 id: filter by tenant uuid/id
323 <other VIM specific>
324 Returns the tenant list of dictionaries, and empty list if no tenant match all the filers:
325 [{'name':'<name>, 'id':'<id>, ...}, ...]
326 """
327 raise vimconnNotImplemented("Should have implemented this")
328
329 def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
330 """Adds a tenant network to VIM
331 Params:
332 'net_name': name of the network
333 'net_type': one of:
334 'bridge': overlay isolated network
335 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
336 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
337 'ip_profile': is a dict containing the IP parameters of the network
338 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
339 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
340 'gateway_address': (Optional) ip_schema, that is X.X.X.X
341 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
342 'dhcp_enabled': True or False
343 'dhcp_start_address': ip_schema, first IP to grant
344 'dhcp_count': number of IPs to grant.
345 'shared': if this network can be seen/use by other tenants/organization
346 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
347 Returns a tuple with the network identifier and created_items, or raises an exception on error
348 created_items can be None or a dictionary where this method can include key-values that will be passed to
349 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
350 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
351 as not present.
352 """
353 raise vimconnNotImplemented("Should have implemented this")
354
355 def get_network_list(self, filter_dict={}):
356 """Obtain tenant networks of VIM
357 Params:
358 'filter_dict' (optional) contains entries to return only networks that matches ALL entries:
359 name: string => returns only networks with this name
360 id: string => returns networks with this VIM id, this imply returns one network at most
361 shared: boolean >= returns only networks that are (or are not) shared
362 tenant_id: sting => returns only networks that belong to this tenant/project
363 ,#(not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active
364 #(not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status
365 Returns the network list of dictionaries. each dictionary contains:
366 'id': (mandatory) VIM network id
367 'name': (mandatory) VIM network name
368 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
369 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat'
370 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id
371 'error_msg': (optional) text that explains the ERROR status
372 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
373 List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity,
374 authorization, or some other unspecific error
375 """
376 raise vimconnNotImplemented("Should have implemented this")
377
378 def get_network(self, net_id):
379 """Obtain network details from the 'net_id' VIM network
380 Return a dict that contains:
381 'id': (mandatory) VIM network id, that is, net_id
382 'name': (mandatory) VIM network name
383 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
384 'error_msg': (optional) text that explains the ERROR status
385 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
386 Raises an exception upon error or when network is not found
387 """
388 raise vimconnNotImplemented("Should have implemented this")
389
390 def delete_network(self, net_id, created_items=None):
391 """
392 Removes a tenant network from VIM and its associated elements
393 :param net_id: VIM identifier of the network, provided by method new_network
394 :param created_items: dictionary with extra items to be deleted. provided by method new_network
395 Returns the network identifier or raises an exception upon error or when network is not found
396 """
397 raise vimconnNotImplemented("Should have implemented this")
398
399 def refresh_nets_status(self, net_list):
400 """Get the status of the networks
401 Params:
402 'net_list': a list with the VIM network id to be get the status
403 Returns a dictionary with:
404 'net_id': #VIM id of this network
405 status: #Mandatory. Text with one of:
406 # DELETED (not found at vim)
407 # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...)
408 # OTHER (Vim reported other status not understood)
409 # ERROR (VIM indicates an ERROR status)
410 # ACTIVE, INACTIVE, DOWN (admin down),
411 # BUILD (on building process)
412 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
413 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
414 'net_id2': ...
415 """
416 raise vimconnNotImplemented("Should have implemented this")
417
418 def get_flavor(self, flavor_id):
419 """Obtain flavor details from the VIM
420 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
421 Raises an exception upon error or if not found
422 """
423 raise vimconnNotImplemented("Should have implemented this")
424
425 def get_flavor_id_from_data(self, flavor_dict):
426 """Obtain flavor id that match the flavor description
427 Params:
428 'flavor_dict': dictionary that contains:
429 'disk': main hard disk in GB
430 'ram': meomry in MB
431 'vcpus': number of virtual cpus
432 #TODO: complete parameters for EPA
433 Returns the flavor_id or raises a vimconnNotFoundException
434 """
435 raise vimconnNotImplemented("Should have implemented this")
436
437 def new_flavor(self, flavor_data):
438 """Adds a tenant flavor to VIM
439 flavor_data contains a dictionary with information, keys:
440 name: flavor name
441 ram: memory (cloud type) in MBytes
442 vpcus: cpus (cloud type)
443 extended: EPA parameters
444 - numas: #items requested in same NUMA
445 memory: number of 1G huge pages memory
446 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
447 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
448 - name: interface name
449 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
450 bandwidth: X Gbps; requested guarantee bandwidth
451 vpci: requested virtual PCI address
452 disk: disk size
453 is_public:
454 #TODO to concrete
455 Returns the flavor identifier"""
456 raise vimconnNotImplemented("Should have implemented this")
457
458 def delete_flavor(self, flavor_id):
459 """Deletes a tenant flavor from VIM identify by its id
460 Returns the used id or raise an exception"""
461 raise vimconnNotImplemented("Should have implemented this")
462
463 def new_image(self, image_dict):
464 """ Adds a tenant image to VIM
465 Returns the image id or raises an exception if failed
466 """
467 raise vimconnNotImplemented("Should have implemented this")
468
469 def delete_image(self, image_id):
470 """Deletes a tenant image from VIM
471 Returns the image_id if image is deleted or raises an exception on error"""
472 raise vimconnNotImplemented("Should have implemented this")
473
474 def get_image_id_from_path(self, path):
475 """Get the image id from image path in the VIM database.
476 Returns the image_id or raises a vimconnNotFoundException
477 """
478 raise vimconnNotImplemented("Should have implemented this")
479
480 def get_image_list(self, filter_dict={}):
481 """Obtain tenant images from VIM
482 Filter_dict can be:
483 name: image name
484 id: image uuid
485 checksum: image checksum
486 location: image path
487 Returns the image list of dictionaries:
488 [{<the fields at Filter_dict plus some VIM specific>}, ...]
489 List can be empty
490 """
491 raise vimconnNotImplemented( "Should have implemented this" )
492
493 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
494 availability_zone_index=None, availability_zone_list=None):
495 """Adds a VM instance to VIM
496 Params:
497 'start': (boolean) indicates if VM must start or created in pause mode.
498 'image_id','flavor_id': image and flavor VIM id to use for the VM
499 'net_list': list of interfaces, each one is a dictionary with:
500 'name': (optional) name for the interface.
501 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
502 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
503 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
504 'mac_address': (optional) mac address to assign to this interface
505 'ip_address': (optional) IP address to assign to this interface
506 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
507 the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
508 'type': (mandatory) can be one of:
509 'virtual', in this case always connected to a network of type 'net_type=bridge'
510 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
511 can created unconnected
512 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
513 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
514 are allocated on the same physical NIC
515 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
516 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
517 or True, it must apply the default VIM behaviour
518 After execution the method will add the key:
519 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
520 interface. 'net_list' is modified
521 'cloud_config': (optional) dictionary with:
522 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
523 'users': (optional) list of users to be inserted, each item is a dict with:
524 'name': (mandatory) user name,
525 'key-pairs': (optional) list of strings with the public key to be inserted to the user
526 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
527 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
528 'config-files': (optional). List of files to be transferred. Each item is a dict with:
529 'dest': (mandatory) string with the destination absolute path
530 'encoding': (optional, by default text). Can be one of:
531 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
532 'content' (mandatory): string with the content of the file
533 'permissions': (optional) string with file permissions, typically octal notation '0644'
534 'owner': (optional) file owner, string with the format 'owner:group'
535 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
536 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
537 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
538 'size': (mandatory) string with the size of the disk in GB
539 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
540 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
541 availability_zone_index is None
542 Returns a tuple with the instance identifier and created_items or raises an exception on error
543 created_items can be None or a dictionary where this method can include key-values that will be passed to
544 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
545 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
546 as not present.
547 """
548 raise vimconnNotImplemented( "Should have implemented this" )
549
550 def get_vminstance(self,vm_id):
551 """Returns the VM instance information from VIM"""
552 raise vimconnNotImplemented( "Should have implemented this" )
553
554 def delete_vminstance(self, vm_id, created_items=None):
555 """
556 Removes a VM instance from VIM and its associated elements
557 :param vm_id: VIM identifier of the VM, provided by method new_vminstance
558 :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
559 action_vminstance
560 :return: None or the same vm_id. Raises an exception on fail
561 """
562 raise vimconnNotImplemented( "Should have implemented this" )
563
564 def refresh_vms_status(self, vm_list):
565 """Get the status of the virtual machines and their interfaces/ports
566 Params: the list of VM identifiers
567 Returns a dictionary with:
568 vm_id: #VIM id of this Virtual Machine
569 status: #Mandatory. Text with one of:
570 # DELETED (not found at vim)
571 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
572 # OTHER (Vim reported other status not understood)
573 # ERROR (VIM indicates an ERROR status)
574 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
575 # BUILD (on building process), ERROR
576 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
577 #
578 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
579 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
580 interfaces: list with interface info. Each item a dictionary with:
581 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
582 mac_address: #Text format XX:XX:XX:XX:XX:XX
583 vim_net_id: #network id where this interface is connected, if provided at creation
584 vim_interface_id: #interface/port VIM id
585 ip_address: #null, or text with IPv4, IPv6 address
586 compute_node: #identification of compute node where PF,VF interface is allocated
587 pci: #PCI address of the NIC that hosts the PF,VF
588 vlan: #physical VLAN used for VF
589 """
590 raise vimconnNotImplemented( "Should have implemented this" )
591
592 def action_vminstance(self, vm_id, action_dict, created_items={}):
593 """
594 Send and action over a VM instance. Returns created_items if the action was successfully sent to the VIM.
595 created_items is a dictionary with items that
596 :param vm_id: VIM identifier of the VM, provided by method new_vminstance
597 :param action_dict: dictionary with the action to perform
598 :param created_items: provided by method new_vminstance is a dictionary with key-values that will be passed to
599 the method delete_vminstance. Can be used to store created ports, volumes, etc. Format is vimconnector
600 dependent, but do not use nested dictionaries and a value of None should be the same as not present. This
601 method can modify this value
602 :return: None, or a console dict
603 """
604 raise vimconnNotImplemented( "Should have implemented this" )
605
606 def get_vminstance_console(self, vm_id, console_type="vnc"):
607 """
608 Get a console for the virtual machine
609 Params:
610 vm_id: uuid of the VM
611 console_type, can be:
612 "novnc" (by default), "xvpvnc" for VNC types,
613 "rdp-html5" for RDP types, "spice-html5" for SPICE types
614 Returns dict with the console parameters:
615 protocol: ssh, ftp, http, https, ...
616 server: usually ip address
617 port: the http, ssh, ... port
618 suffix: extra text, e.g. the http path and query string
619 """
620 raise vimconnNotImplemented( "Should have implemented this" )
621
622 def inject_user_key(self, ip_addr=None, user=None, key=None, ro_key=None, password=None):
623 """
624 Inject a ssh public key in a VM
625 Params:
626 ip_addr: ip address of the VM
627 user: username (default-user) to enter in the VM
628 key: public key to be injected in the VM
629 ro_key: private key of the RO, used to enter in the VM if the password is not provided
630 password: password of the user to enter in the VM
631 The function doesn't return a value:
632 """
633 if not ip_addr or not user:
634 raise vimconnNotSupportedException("All parameters should be different from 'None'")
635 elif not ro_key and not password:
636 raise vimconnNotSupportedException("All parameters should be different from 'None'")
637 else:
638 commands = {'mkdir -p ~/.ssh/', 'echo "{}" >> ~/.ssh/authorized_keys'.format(key),
639 'chmod 644 ~/.ssh/authorized_keys', 'chmod 700 ~/.ssh/'}
640 client = paramiko.SSHClient()
641 try:
642 if ro_key:
643 pkey = paramiko.RSAKey.from_private_key(StringIO(ro_key))
644 else:
645 pkey = None
646 client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
647 client.connect(ip_addr, username=user, password=password, pkey=pkey, timeout=10)
648 for command in commands:
649 (i, o, e) = client.exec_command(command, timeout=10)
650 returncode = o.channel.recv_exit_status()
651 output = o.read()
652 outerror = e.read()
653 if returncode != 0:
654 text = "run_command='{}' Error='{}'".format(command, outerror)
655 raise vimconnUnexpectedResponse("Cannot inject ssh key in VM: '{}'".format(text))
656 return
657 except (socket.error, paramiko.AuthenticationException, paramiko.SSHException) as message:
658 raise vimconnUnexpectedResponse(
659 "Cannot inject ssh key in VM: '{}' - {}".format(ip_addr, str(message)))
660 return
661
662 # Optional methods
663
664 def new_tenant(self,tenant_name,tenant_description):
665 """Adds a new tenant to VIM with this name and description, this is done using admin_url if provided
666 "tenant_name": string max lenght 64
667 "tenant_description": string max length 256
668 returns the tenant identifier or raise exception
669 """
670 raise vimconnNotImplemented( "Should have implemented this" )
671
672 def delete_tenant(self,tenant_id,):
673 """Delete a tenant from VIM
674 tenant_id: returned VIM tenant_id on "new_tenant"
675 Returns None on success. Raises and exception of failure. If tenant is not found raises vimconnNotFoundException
676 """
677 raise vimconnNotImplemented( "Should have implemented this" )
678
679 def get_tenant_list(self, filter_dict=None):
680 """Obtain tenants of VIM
681 filter_dict dictionary that can contain the following keys:
682 name: filter by tenant name
683 id: filter by tenant uuid/id
684 <other VIM specific>
685 Returns the tenant list of dictionaries, and empty list if no tenant match all the filers:
686 [{'name':'<name>, 'id':'<id>, ...}, ...]
687 """
688 raise vimconnNotImplemented( "Should have implemented this" )
689
690 def new_classification(self, name, ctype, definition):
691 """Creates a traffic classification in the VIM
692 Params:
693 'name': name of this classification
694 'ctype': type of this classification
695 'definition': definition of this classification (type-dependent free-form text)
696 Returns the VIM's classification ID on success or raises an exception on failure
697 """
698 raise vimconnNotImplemented( "SFC support not implemented" )
699
700 def get_classification(self, classification_id):
701 """Obtain classification details of the VIM's classification with ID='classification_id'
702 Return a dict that contains:
703 'id': VIM's classification ID (same as classification_id)
704 'name': VIM's classification name
705 'type': type of this classification
706 'definition': definition of the classification
707 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
708 'error_msg': (optional) text that explains the ERROR status
709 other VIM specific fields: (optional) whenever possible
710 Raises an exception upon error or when classification is not found
711 """
712 raise vimconnNotImplemented( "SFC support not implemented" )
713
714 def get_classification_list(self, filter_dict={}):
715 """Obtain classifications from the VIM
716 Params:
717 'filter_dict' (optional): contains the entries to filter the classifications on and only return those that match ALL:
718 id: string => returns classifications with this VIM's classification ID, which implies a return of one classification at most
719 name: string => returns only classifications with this name
720 type: string => returns classifications of this type
721 definition: string => returns classifications that have this definition
722 tenant_id: string => returns only classifications that belong to this tenant/project
723 Returns a list of classification dictionaries, each dictionary contains:
724 'id': (mandatory) VIM's classification ID
725 'name': (mandatory) VIM's classification name
726 'type': type of this classification
727 'definition': definition of the classification
728 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
729 List can be empty if no classification matches the filter_dict. Raise an exception only upon VIM connectivity,
730 authorization, or some other unspecific error
731 """
732 raise vimconnNotImplemented( "SFC support not implemented" )
733
734 def delete_classification(self, classification_id):
735 """Deletes a classification from the VIM
736 Returns the classification ID (classification_id) or raises an exception upon error or when classification is not found
737 """
738 raise vimconnNotImplemented( "SFC support not implemented" )
739
740 def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
741 """Creates a service function instance in the VIM
742 Params:
743 'name': name of this service function instance
744 'ingress_ports': set of ingress ports (VIM's port IDs)
745 'egress_ports': set of egress ports (VIM's port IDs)
746 'sfc_encap': boolean stating whether this specific instance supports IETF SFC Encapsulation
747 Returns the VIM's service function instance ID on success or raises an exception on failure
748 """
749 raise vimconnNotImplemented( "SFC support not implemented" )
750
751 def get_sfi(self, sfi_id):
752 """Obtain service function instance details of the VIM's service function instance with ID='sfi_id'
753 Return a dict that contains:
754 'id': VIM's sfi ID (same as sfi_id)
755 'name': VIM's sfi name
756 'ingress_ports': set of ingress ports (VIM's port IDs)
757 'egress_ports': set of egress ports (VIM's port IDs)
758 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
759 'error_msg': (optional) text that explains the ERROR status
760 other VIM specific fields: (optional) whenever possible
761 Raises an exception upon error or when service function instance is not found
762 """
763 raise vimconnNotImplemented( "SFC support not implemented" )
764
765 def get_sfi_list(self, filter_dict={}):
766 """Obtain service function instances from the VIM
767 Params:
768 'filter_dict' (optional): contains the entries to filter the sfis on and only return those that match ALL:
769 id: string => returns sfis with this VIM's sfi ID, which implies a return of one sfi at most
770 name: string => returns only service function instances with this name
771 tenant_id: string => returns only service function instances that belong to this tenant/project
772 Returns a list of service function instance dictionaries, each dictionary contains:
773 'id': (mandatory) VIM's sfi ID
774 'name': (mandatory) VIM's sfi name
775 'ingress_ports': set of ingress ports (VIM's port IDs)
776 'egress_ports': set of egress ports (VIM's port IDs)
777 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
778 List can be empty if no sfi matches the filter_dict. Raise an exception only upon VIM connectivity,
779 authorization, or some other unspecific error
780 """
781 raise vimconnNotImplemented( "SFC support not implemented" )
782
783 def delete_sfi(self, sfi_id):
784 """Deletes a service function instance from the VIM
785 Returns the service function instance ID (sfi_id) or raises an exception upon error or when sfi is not found
786 """
787 raise vimconnNotImplemented( "SFC support not implemented" )
788
789 def new_sf(self, name, sfis, sfc_encap=True):
790 """Creates (an abstract) service function in the VIM
791 Params:
792 'name': name of this service function
793 'sfis': set of service function instances of this (abstract) service function
794 'sfc_encap': boolean stating whether this service function supports IETF SFC Encapsulation
795 Returns the VIM's service function ID on success or raises an exception on failure
796 """
797 raise vimconnNotImplemented( "SFC support not implemented" )
798
799 def get_sf(self, sf_id):
800 """Obtain service function details of the VIM's service function with ID='sf_id'
801 Return a dict that contains:
802 'id': VIM's sf ID (same as sf_id)
803 'name': VIM's sf name
804 'sfis': VIM's sf's set of VIM's service function instance IDs
805 'sfc_encap': boolean stating whether this service function supports IETF SFC Encapsulation
806 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
807 'error_msg': (optional) text that explains the ERROR status
808 other VIM specific fields: (optional) whenever possible
809 Raises an exception upon error or when sf is not found
810 """
811
812 def get_sf_list(self, filter_dict={}):
813 """Obtain service functions from the VIM
814 Params:
815 'filter_dict' (optional): contains the entries to filter the sfs on and only return those that match ALL:
816 id: string => returns sfs with this VIM's sf ID, which implies a return of one sf at most
817 name: string => returns only service functions with this name
818 tenant_id: string => returns only service functions that belong to this tenant/project
819 Returns a list of service function dictionaries, each dictionary contains:
820 'id': (mandatory) VIM's sf ID
821 'name': (mandatory) VIM's sf name
822 'sfis': VIM's sf's set of VIM's service function instance IDs
823 'sfc_encap': boolean stating whether this service function supports IETF SFC Encapsulation
824 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
825 List can be empty if no sf matches the filter_dict. Raise an exception only upon VIM connectivity,
826 authorization, or some other unspecific error
827 """
828 raise vimconnNotImplemented( "SFC support not implemented" )
829
830 def delete_sf(self, sf_id):
831 """Deletes (an abstract) service function from the VIM
832 Returns the service function ID (sf_id) or raises an exception upon error or when sf is not found
833 """
834 raise vimconnNotImplemented( "SFC support not implemented" )
835
836 def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
837 """Creates a service function path
838 Params:
839 'name': name of this service function path
840 'classifications': set of traffic classifications that should be matched on to get into this sfp
841 'sfs': list of every service function that constitutes this path , from first to last
842 'sfc_encap': whether this is an SFC-Encapsulated chain (i.e using NSH), True by default
843 'spi': (optional) the Service Function Path identifier (SPI: Service Path Identifier) for this path
844 Returns the VIM's sfp ID on success or raises an exception on failure
845 """
846 raise vimconnNotImplemented( "SFC support not implemented" )
847
848 def get_sfp(self, sfp_id):
849 """Obtain service function path details of the VIM's sfp with ID='sfp_id'
850 Return a dict that contains:
851 'id': VIM's sfp ID (same as sfp_id)
852 'name': VIM's sfp name
853 'classifications': VIM's sfp's list of VIM's classification IDs
854 'sfs': VIM's sfp's list of VIM's service function IDs
855 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
856 'error_msg': (optional) text that explains the ERROR status
857 other VIM specific fields: (optional) whenever possible
858 Raises an exception upon error or when sfp is not found
859 """
860 raise vimconnNotImplemented( "SFC support not implemented" )
861
862 def get_sfp_list(self, filter_dict={}):
863 """Obtain service function paths from VIM
864 Params:
865 'filter_dict' (optional): contains the entries to filter the sfps on, and only return those that match ALL:
866 id: string => returns sfps with this VIM's sfp ID , which implies a return of one sfp at most
867 name: string => returns only sfps with this name
868 tenant_id: string => returns only sfps that belong to this tenant/project
869 Returns a list of service function path dictionaries, each dictionary contains:
870 'id': (mandatory) VIM's sfp ID
871 'name': (mandatory) VIM's sfp name
872 'classifications': VIM's sfp's list of VIM's classification IDs
873 'sfs': VIM's sfp's list of VIM's service function IDs
874 other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
875 List can be empty if no sfp matches the filter_dict. Raise an exception only upon VIM connectivity,
876 authorization, or some other unspecific error
877 """
878 raise vimconnNotImplemented( "SFC support not implemented" )
879
880 def delete_sfp(self, sfp_id):
881 """Deletes a service function path from the VIM
882 Returns the sfp ID (sfp_id) or raises an exception upon error or when sf is not found
883 """
884 raise vimconnNotImplemented( "SFC support not implemented" )
885
886 # NOT USED METHODS in current version. Deprecated
887
888 @deprecated
889 def host_vim2gui(self, host, server_dict):
890 """Transform host dictionary from VIM format to GUI format,
891 and append to the server_dict
892 """
893 raise vimconnNotImplemented( "Should have implemented this" )
894
895 @deprecated
896 def get_hosts_info(self):
897 """Get the information of deployed hosts
898 Returns the hosts content"""
899 raise vimconnNotImplemented( "Should have implemented this" )
900
901 @deprecated
902 def get_hosts(self, vim_tenant):
903 """Get the hosts and deployed instances
904 Returns the hosts content"""
905 raise vimconnNotImplemented( "Should have implemented this" )
906
907 @deprecated
908 def get_processor_rankings(self):
909 """Get the processor rankings in the VIM database"""
910 raise vimconnNotImplemented( "Should have implemented this" )
911
912 @deprecated
913 def new_host(self, host_data):
914 """Adds a new host to VIM"""
915 """Returns status code of the VIM response"""
916 raise vimconnNotImplemented( "Should have implemented this" )
917
918 @deprecated
919 def new_external_port(self, port_data):
920 """Adds a external port to VIM"""
921 """Returns the port identifier"""
922 raise vimconnNotImplemented( "Should have implemented this" )
923
924 @deprecated
925 def new_external_network(self,net_name,net_type):
926 """Adds a external network to VIM (shared)"""
927 """Returns the network identifier"""
928 raise vimconnNotImplemented( "Should have implemented this" )
929 @deprecated
930
931 @deprecated
932 def connect_port_network(self, port_id, network_id, admin=False):
933 """Connects a external port to a network"""
934 """Returns status code of the VIM response"""
935 raise vimconnNotImplemented( "Should have implemented this" )
936
937 @deprecated
938 def new_vminstancefromJSON(self, vm_data):
939 """Adds a VM instance to VIM"""
940 """Returns the instance identifier"""
941 raise vimconnNotImplemented( "Should have implemented this" )
942