with the definition of the method to be implemented.
"""
-import logging
-import paramiko
-import socket
-from io import StringIO
-import yaml
-import sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from http import HTTPStatus
+from io import StringIO
+import logging
+import socket
+import sys
+import traceback
import warnings
+import paramiko
+import yaml
+
__author__ = "Alfonso Tierno, Igor D.C."
__date__ = "$14-aug-2017 23:59:59$"
class VimConnNotImplemented(VimConnException):
- """The method is not implemented by the connected"""
+ """The method is not implemented by the connector"""
def __init__(self, message, http_code=HTTP_Not_Implemented):
VimConnException.__init__(self, message, http_code)
+class VimConnInsufficientCredentials(VimConnException):
+ """The VIM account does not have efficient permissions to perform the requested operation."""
+
+ def __init__(self, message, http_code=HTTP_Unauthorized):
+ VimConnException.__init__(self, message, http_code)
+
+
class VimConnector:
"""Abstract base class for all the VIM connector plugins
These plugins must implement a VimConnector class derived from this
userdata = None
userdata_list = []
+ # For more information, check https://cloudinit.readthedocs.io/en/latest/reference/merging.html
+ # Basically, with this, we don't override the provider's cloud config
+ merge_how = yaml.safe_dump(
+ {
+ "merge_how": [
+ {
+ "name": "list",
+ "settings": ["append", "recurse_dict", "recurse_list"],
+ },
+ {
+ "name": "dict",
+ "settings": ["no_replace", "recurse_list", "recurse_dict"],
+ },
+ ]
+ },
+ indent=4,
+ default_flow_style=False,
+ )
+
if isinstance(cloud_config, dict):
if cloud_config.get("user-data"):
if isinstance(cloud_config["user-data"], str):
- userdata_list.append(cloud_config["user-data"])
+ userdata_list.append(cloud_config["user-data"] + f"\n{merge_how}")
else:
for u in cloud_config["user-data"]:
- userdata_list.append(u)
+ userdata_list.append(u + f"\n{merge_how}")
if cloud_config.get("boot-data-drive") is not None:
config_drive = cloud_config["boot-data-drive"]
# default user
if cloud_config.get("key-pairs"):
userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
- userdata_dict["users"] = [
- {
- "default": None,
- "ssh-authorized-keys": cloud_config["key-pairs"],
+ userdata_dict["system_info"] = {
+ "default_user": {
+ "ssh_authorized_keys": cloud_config["key-pairs"],
}
- ]
+ }
+ userdata_dict["users"] = ["default"]
if cloud_config.get("users"):
if "users" not in userdata_dict:
userdata_list.append(
"#cloud-config\n"
+ yaml.safe_dump(userdata_dict, indent=4, default_flow_style=False)
+ + f"\n{merge_how}"
)
userdata = self._create_mimemultipart(userdata_list)
self.logger.debug("userdata: %s", userdata)
"""
raise VimConnNotImplemented("Should have implemented this")
+ def get_affinity_group(self, affinity_group_id):
+ """Obtain affinity or anti affinity group details from the VIM
+ Returns the flavor dict details {'id':<>, 'name':<>, other vim specific }
+ Raises an exception upon error or if not found
+ """
+ raise VimConnNotImplemented("Should have implemented this")
+
+ def new_affinity_group(self, affinity_group_data):
+ """Adds an affinity or anti affinity group to VIM
+ affinity_group_data contains a dictionary with information, keys:
+ name: name in VIM for the affinity or anti-affinity group
+ type: affinity or anti-affinity
+ scope: Only nfvi-node allowed
+ Returns the affinity or anti affinity group identifier
+ """
+ raise VimConnNotImplemented("Should have implemented this")
+
+ def delete_affinity_group(self, affinity_group_id):
+ """Deletes an affinity or anti affinity group from the VIM identified by its id
+ Returns the used id or raise an exception
+ """
+ raise VimConnNotImplemented("Should have implemented this")
+
def new_image(self, image_dict):
"""Adds a tenant image to VIM
Returns the image id or raises an exception if failed
start,
image_id,
flavor_id,
+ affinity_group_list,
net_list,
cloud_config=None,
disk_list=None,
Params:
'start': (boolean) indicates if VM must start or created in pause mode.
'image_id','flavor_id': image and flavor VIM id to use for the VM
+ affinity_group_list: list of affinity groups, each one is a dictionary.
+ Ignore if empty.
'net_list': list of interfaces, each one is a dictionary with:
'name': (optional) name for the interface.
'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
"""Returns the VM instance information from VIM"""
raise VimConnNotImplemented("Should have implemented this")
- def delete_vminstance(self, vm_id, created_items=None):
+ def delete_vminstance(self, vm_id, created_items=None, volumes_to_hold=None):
"""
Removes a VM instance from VIM and its associated elements
:param vm_id: VIM identifier of the VM, provided by method new_vminstance
"chmod 644 ~/.ssh/authorized_keys",
"chmod 700 ~/.ssh/",
}
+
+ logging.basicConfig(
+ format="%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
+ )
+ logging.getLogger("paramiko").setLevel(logging.DEBUG)
client = paramiko.SSHClient()
try:
pkey = None
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+
client.connect(
- ip_addr, username=user, password=password, pkey=pkey, timeout=10
+ ip_addr,
+ username=user,
+ password=password,
+ pkey=pkey,
+ timeout=30,
+ auth_timeout=60,
)
for command in commands:
- (i, o, e) = client.exec_command(command, timeout=10)
+ (i, o, e) = client.exec_command(command, timeout=30)
returncode = o.channel.recv_exit_status()
outerror = e.read()
if returncode != 0:
text = "run_command='{}' Error='{}'".format(command, outerror)
+ self.logger.debug(traceback.format_tb(e.__traceback__))
raise VimConnUnexpectedResponse(
"Cannot inject ssh key in VM: '{}'".format(text)
)
-
return
except (
socket.error,
paramiko.AuthenticationException,
paramiko.SSHException,
) as message:
+ self.logger.debug(traceback.format_exc())
raise VimConnUnexpectedResponse(
"Cannot inject ssh key in VM: '{}' - {}".format(
ip_addr, str(message)
)
)
-
return
# Optional methods
"""
raise VimConnNotImplemented("Should have implemented this")
- def new_classification(self, name, ctype, definition):
- """Creates a traffic classification in the VIM
- Params:
- 'name': name of this classification
- 'ctype': type of this classification
- 'definition': definition of this classification (type-dependent free-form text)
- Returns the VIM's classification ID on success or raises an exception on failure
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_classification(self, classification_id):
- """Obtain classification details of the VIM's classification with ID='classification_id'
- Return a dict that contains:
- 'id': VIM's classification ID (same as classification_id)
- 'name': VIM's classification name
- 'type': type of this classification
- 'definition': definition of the classification
- 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
- 'error_msg': (optional) text that explains the ERROR status
- other VIM specific fields: (optional) whenever possible
- Raises an exception upon error or when classification is not found
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_classification_list(self, filter_dict={}):
- """Obtain classifications from the VIM
- Params:
- 'filter_dict' (optional): contains the entries to filter the classifications on and only return those that
- match ALL:
- id: string => returns classifications with this VIM's classification ID, which implies a return of one
- classification at most
- name: string => returns only classifications with this name
- type: string => returns classifications of this type
- definition: string => returns classifications that have this definition
- tenant_id: string => returns only classifications that belong to this tenant/project
- Returns a list of classification dictionaries, each dictionary contains:
- 'id': (mandatory) VIM's classification ID
- 'name': (mandatory) VIM's classification name
- 'type': type of this classification
- 'definition': definition of the classification
- other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
- List can be empty if no classification matches the filter_dict. Raise an exception only upon VIM connectivity,
- authorization, or some other unspecific error
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def refresh_classifications_status(self, classification_list):
- """Get the status of the classifications
- Params: the list of classification identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this classifier
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE,
- # CREATING (on building process)
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- """
- raise VimConnNotImplemented("Should have implemented this")
-
- def delete_classification(self, classification_id):
- """Deletes a classification from the VIM
- Returns the classification ID (classification_id) or raises an exception upon error or when classification is
- not found
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
- """Creates a service function instance in the VIM
- Params:
- 'name': name of this service function instance
- 'ingress_ports': set of ingress ports (VIM's port IDs)
- 'egress_ports': set of egress ports (VIM's port IDs)
- 'sfc_encap': boolean stating whether this specific instance supports IETF SFC Encapsulation
- Returns the VIM's service function instance ID on success or raises an exception on failure
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_sfi(self, sfi_id):
- """Obtain service function instance details of the VIM's service function instance with ID='sfi_id'
- Return a dict that contains:
- 'id': VIM's sfi ID (same as sfi_id)
- 'name': VIM's sfi name
- 'ingress_ports': set of ingress ports (VIM's port IDs)
- 'egress_ports': set of egress ports (VIM's port IDs)
- 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
- 'error_msg': (optional) text that explains the ERROR status
- other VIM specific fields: (optional) whenever possible
- Raises an exception upon error or when service function instance is not found
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_sfi_list(self, filter_dict={}):
- """Obtain service function instances from the VIM
- Params:
- 'filter_dict' (optional): contains the entries to filter the sfis on and only return those that match ALL:
- id: string => returns sfis with this VIM's sfi ID, which implies a return of one sfi at most
- name: string => returns only service function instances with this name
- tenant_id: string => returns only service function instances that belong to this tenant/project
- Returns a list of service function instance dictionaries, each dictionary contains:
- 'id': (mandatory) VIM's sfi ID
- 'name': (mandatory) VIM's sfi name
- 'ingress_ports': set of ingress ports (VIM's port IDs)
- 'egress_ports': set of egress ports (VIM's port IDs)
- other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
- List can be empty if no sfi matches the filter_dict. Raise an exception only upon VIM connectivity,
- authorization, or some other unspecific error
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def delete_sfi(self, sfi_id):
- """Deletes a service function instance from the VIM
- Returns the service function instance ID (sfi_id) or raises an exception upon error or when sfi is not found
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def refresh_sfis_status(self, sfi_list):
- """Get the status of the service function instances
- Params: the list of sfi identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this service function instance
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE,
- # CREATING (on building process)
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
- """
- raise VimConnNotImplemented("Should have implemented this")
-
- def new_sf(self, name, sfis, sfc_encap=True):
- """Creates (an abstract) service function in the VIM
+ def migrate_instance(self, vm_id, compute_host=None):
+ """Migrate a vdu
Params:
- 'name': name of this service function
- 'sfis': set of service function instances of this (abstract) service function
- 'sfc_encap': boolean stating whether this service function supports IETF SFC Encapsulation
- Returns the VIM's service function ID on success or raises an exception on failure
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_sf(self, sf_id):
- """Obtain service function details of the VIM's service function with ID='sf_id'
- Return a dict that contains:
- 'id': VIM's sf ID (same as sf_id)
- 'name': VIM's sf name
- 'sfis': VIM's sf's set of VIM's service function instance IDs
- 'sfc_encap': boolean stating whether this service function supports IETF SFC Encapsulation
- 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
- 'error_msg': (optional) text that explains the ERROR status
- other VIM specific fields: (optional) whenever possible
- Raises an exception upon error or when sf is not found
- """
-
- def get_sf_list(self, filter_dict={}):
- """Obtain service functions from the VIM
- Params:
- 'filter_dict' (optional): contains the entries to filter the sfs on and only return those that match ALL:
- id: string => returns sfs with this VIM's sf ID, which implies a return of one sf at most
- name: string => returns only service functions with this name
- tenant_id: string => returns only service functions that belong to this tenant/project
- Returns a list of service function dictionaries, each dictionary contains:
- 'id': (mandatory) VIM's sf ID
- 'name': (mandatory) VIM's sf name
- 'sfis': VIM's sf's set of VIM's service function instance IDs
- 'sfc_encap': boolean stating whether this service function supports IETF SFC Encapsulation
- other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
- List can be empty if no sf matches the filter_dict. Raise an exception only upon VIM connectivity,
- authorization, or some other unspecific error
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def delete_sf(self, sf_id):
- """Deletes (an abstract) service function from the VIM
- Returns the service function ID (sf_id) or raises an exception upon error or when sf is not found
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def refresh_sfs_status(self, sf_list):
- """Get the status of the service functions
- Params: the list of sf identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this service function
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE,
- # CREATING (on building process)
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ vm_id: ID of an instance
+ compute_host: Host to migrate the vdu to
+ Returns the vm state or raises an exception upon error
"""
raise VimConnNotImplemented("Should have implemented this")
- def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
- """Creates a service function path
- Params:
- 'name': name of this service function path
- 'classifications': set of traffic classifications that should be matched on to get into this sfp
- 'sfs': list of every service function that constitutes this path , from first to last
- 'sfc_encap': whether this is an SFC-Encapsulated chain (i.e using NSH), True by default
- 'spi': (optional) the Service Function Path identifier (SPI: Service Path Identifier) for this path
- Returns the VIM's sfp ID on success or raises an exception on failure
+ def resize_instance(self, vm_id, flavor_id=None):
"""
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_sfp(self, sfp_id):
- """Obtain service function path details of the VIM's sfp with ID='sfp_id'
- Return a dict that contains:
- 'id': VIM's sfp ID (same as sfp_id)
- 'name': VIM's sfp name
- 'classifications': VIM's sfp's list of VIM's classification IDs
- 'sfs': VIM's sfp's list of VIM's service function IDs
- 'status': 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER'
- 'error_msg': (optional) text that explains the ERROR status
- other VIM specific fields: (optional) whenever possible
- Raises an exception upon error or when sfp is not found
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def get_sfp_list(self, filter_dict={}):
- """Obtain service function paths from VIM
- Params:
- 'filter_dict' (optional): contains the entries to filter the sfps on, and only return those that match ALL:
- id: string => returns sfps with this VIM's sfp ID , which implies a return of one sfp at most
- name: string => returns only sfps with this name
- tenant_id: string => returns only sfps that belong to this tenant/project
- Returns a list of service function path dictionaries, each dictionary contains:
- 'id': (mandatory) VIM's sfp ID
- 'name': (mandatory) VIM's sfp name
- 'classifications': VIM's sfp's list of VIM's classification IDs
- 'sfs': VIM's sfp's list of VIM's service function IDs
- other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param
- List can be empty if no sfp matches the filter_dict. Raise an exception only upon VIM connectivity,
- authorization, or some other unspecific error
- """
- raise VimConnNotImplemented("SFC support not implemented")
-
- def refresh_sfps_status(self, sfp_list):
- """Get the status of the service function path
- Params: the list of sfp identifiers
- Returns a dictionary with:
- vm_id: #VIM id of this service function path
- status: #Mandatory. Text with one of:
- # DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
- # OTHER (Vim reported other status not understood)
- # ERROR (VIM indicates an ERROR status)
- # ACTIVE,
- # CREATING (on building process)
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
- """
- raise VimConnNotImplemented("Should have implemented this")
-
- def delete_sfp(self, sfp_id):
- """Deletes a service function path from the VIM
- Returns the sfp ID (sfp_id) or raises an exception upon error or when sf is not found
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor_id to resize the vdu to
"""
- raise VimConnNotImplemented("SFC support not implemented")
-
- # NOT USED METHODS in current version. Deprecated
- @deprecated
- def host_vim2gui(self, host, server_dict):
- """Transform host dictionary from VIM format to GUI format,
- and append to the server_dict
- """
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def get_hosts_info(self):
- """Get the information of deployed hosts
- Returns the hosts content"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def get_hosts(self, vim_tenant):
- """Get the hosts and deployed instances
- Returns the hosts content"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def get_processor_rankings(self):
- """Get the processor rankings in the VIM database"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def new_host(self, host_data):
- """Adds a new host to VIM"""
- """Returns status code of the VIM response"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def new_external_port(self, port_data):
- """Adds a external port to VIM"""
- """Returns the port identifier"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def new_external_network(self, net_name, net_type):
- """Adds a external network to VIM (shared)"""
- """Returns the network identifier"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def connect_port_network(self, port_id, network_id, admin=False):
- """Connects a external port to a network"""
- """Returns status code of the VIM response"""
- raise VimConnNotImplemented("Should have implemented this")
-
- @deprecated
- def new_vminstancefromJSON(self, vm_data):
- """Adds a VM instance to VIM"""
- """Returns the instance identifier"""
raise VimConnNotImplemented("Should have implemented this")