import time
import yaml
import random
+import sys
+import re
from novaclient import client as nClient, exceptions as nvExceptions
from keystoneauth1.identity import v2, v3
from neutronclient.neutron import client as neClient
from neutronclient.common import exceptions as neExceptions
from requests.exceptions import ConnectionError
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
-'''contain the openstack virtual machine status to openmano status'''
+
+"""contain the openstack virtual machine status to openmano status"""
vmStatus2manoFormat={'ACTIVE':'ACTIVE',
'PAUSED':'PAUSED',
'SUSPENDED': 'SUSPENDED',
if api_version and api_version not in ('v3.3', 'v2.0', '2', '3'):
raise vimconn.vimconnException("Invalid value '{}' for config:APIversion. "
"Allowed values are 'v3.3', 'v2.0', '2' or '3'".format(api_version))
+ vim_type = config.get('vim_type')
+ if vim_type and vim_type not in ('vio', 'VIO'):
+ raise vimconn.vimconnException("Invalid value '{}' for config:vim_type."
+ "Allowed values are 'vio' or 'VIO'".format(vim_type))
+
+ if config.get('dataplane_net_vlan_range') is not None:
+ #validate vlan ranges provided by user
+ self._validate_vlan_ranges(config.get('dataplane_net_vlan_range'))
+
vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
config)
self.glancev1 = self.session.get('glancev1')
self.keystone = self.session.get('keystone')
self.api_version3 = self.session.get('api_version3')
+ self.vim_type = self.config.get("vim_type")
+ if self.vim_type:
+ self.vim_type = self.vim_type.upper()
+ if self.config.get("use_internal_endpoint"):
+ self.endpoint_type = "internalURL"
+ else:
+ self.endpoint_type = None
self.logger = logging.getLogger('openmano.vim.openstack')
+
+ ####### VIO Specific Changes #########
+ if self.vim_type == "VIO":
+ self.logger = logging.getLogger('openmano.vim.vio')
+
if log_level:
- self.logger.setLevel( getattr(logging, log_level) )
+ self.logger.setLevel( getattr(logging, log_level))
def __getitem__(self, index):
"""Get individuals parameters.
tenant_id=self.tenant_id)
sess = session.Session(auth=auth, verify=not self.insecure)
if self.api_version3:
- self.keystone = ksClient_v3.Client(session=sess)
+ self.keystone = ksClient_v3.Client(session=sess, endpoint_type=self.endpoint_type)
else:
- self.keystone = ksClient_v2.Client(session=sess)
+ self.keystone = ksClient_v2.Client(session=sess, endpoint_type=self.endpoint_type)
self.session['keystone'] = self.keystone
# In order to enable microversion functionality an explicit microversion must be specified in 'config'.
# This implementation approach is due to the warning message in
version = self.config.get("microversion")
if not version:
version = "2.1"
- self.nova = self.session['nova'] = nClient.Client(str(version), session=sess)
- self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess)
- self.cinder = self.session['cinder'] = cClient.Client(2, session=sess)
- self.glance = self.session['glance'] = glClient.Client(2, session=sess)
- self.glancev1 = self.session['glancev1'] = glClient.Client('1', session=sess)
+ self.nova = self.session['nova'] = nClient.Client(str(version), session=sess, endpoint_type=self.endpoint_type)
+ self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess, endpoint_type=self.endpoint_type)
+ self.cinder = self.session['cinder'] = cClient.Client(2, session=sess, endpoint_type=self.endpoint_type)
+ if self.endpoint_type == "internalURL":
+ glance_service_id = self.keystone.services.list(name="glance")[0].id
+ glance_endpoint = self.keystone.endpoints.list(glance_service_id, interface="internal")[0].url
+ else:
+ glance_endpoint = None
+ self.glance = self.session['glance'] = glClient.Client(2, session=sess, endpoint=glance_endpoint)
+ #using version 1 of glance client in new_image()
+ self.glancev1 = self.session['glancev1'] = glClient.Client('1', session=sess,
+ endpoint=glance_endpoint)
self.session['reload_client'] = False
self.persistent_info['session'] = self.session
# add availablity zone info inside self.persistent_info
network_dict["provider:network_type"] = "vlan"
if vlan!=None:
network_dict["provider:network_type"] = vlan
+
+ ####### VIO Specific Changes #########
+ if self.vim_type == "VIO":
+ if vlan is not None:
+ network_dict["provider:segmentation_id"] = vlan
+ else:
+ if self.config.get('dataplane_net_vlan_range') is None:
+ raise vimconn.vimconnConflictException("You must provide "\
+ "'dataplane_net_vlan_range' in format [start_ID - end_ID]"\
+ "at config value before creating sriov network with vlan tag")
+
+ network_dict["provider:segmentation_id"] = self._genrate_vlanID()
+
network_dict["shared"]=shared
new_net=self.neutron.create_network({'network':network_dict})
#print new_net
while name in fl_names:
name_suffix += 1
name = flavor_data['name']+"-" + str(name_suffix)
-
+
ram = flavor_data.get('ram',64)
vcpus = flavor_data.get('vcpus',1)
numa_properties=None
numa_properties["hw:mem_page_size"] = "large"
numa_properties["hw:cpu_policy"] = "dedicated"
numa_properties["hw:numa_mempolicy"] = "strict"
+ if self.vim_type == "VIO":
+ numa_properties["vmware:extra_config"] = '{"numa.nodeAffinity":"0"}'
+ numa_properties["vmware:latency_sensitivity_level"] = "high"
for numa in numas:
#overwrite ram and vcpus
ram = numa['memory']*1024
vcpus,
flavor_data.get('disk',1),
is_public=flavor_data.get('is_public', True)
- )
+ )
#add metadata
if numa_properties:
new_flavor.set_keys(numa_properties)
except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
self._format_exception(e)
+ @staticmethod
+ def _create_mimemultipart(content_list):
+ """Creates a MIMEmultipart text combining the content_list
+ :param content_list: list of text scripts to be combined
+ :return: str of the created MIMEmultipart. If the list is empty returns None, if the list contains only one
+ element MIMEmultipart is not created and this content is returned
+ """
+ if not content_list:
+ return None
+ elif len(content_list) == 1:
+ return content_list[0]
+ combined_message = MIMEMultipart()
+ for content in content_list:
+ if content.startswith('#include'):
+ format = 'text/x-include-url'
+ elif content.startswith('#include-once'):
+ format = 'text/x-include-once-url'
+ elif content.startswith('#!'):
+ format = 'text/x-shellscript'
+ elif content.startswith('#cloud-config'):
+ format = 'text/cloud-config'
+ elif content.startswith('#cloud-config-archive'):
+ format = 'text/cloud-config-archive'
+ elif content.startswith('#upstart-job'):
+ format = 'text/upstart-job'
+ elif content.startswith('#part-handler'):
+ format = 'text/part-handler'
+ elif content.startswith('#cloud-boothook'):
+ format = 'text/cloud-boothook'
+ else: # by default
+ format = 'text/x-shellscript'
+ sub_message = MIMEText(content, format, sys.getdefaultencoding())
+ combined_message.attach(sub_message)
+ return combined_message.as_string()
+
def __wait_for_vm(self, vm_id, status):
"""wait until vm is in the desired status and return True.
If the VM gets in ERROR status, return false.
metadata_vpci["VF"]=[]
metadata_vpci["VF"].append([ net["vpci"], "" ])
port_dict["binding:vnic_type"]="direct"
- else: # For PT
+ ########## VIO specific Changes #######
+ if self.vim_type == "VIO":
+ #Need to create port with port_security_enabled = False and no-security-groups
+ port_dict["port_security_enabled"]=False
+ port_dict["provider_security_groups"]=[]
+ port_dict["security_groups"]=[]
+ else: #For PT
+ ########## VIO specific Changes #######
+ #Current VIO release does not support port with type 'direct-physical'
+ #So no need to create virtual port in case of PCI-device.
+ #Will update port_dict code when support gets added in next VIO release
+ if self.vim_type == "VIO":
+ raise vimconn.vimconnNotSupportedException("Current VIO release does not support full passthrough (PT)")
if "vpci" in net:
if "PF" not in metadata_vpci:
metadata_vpci["PF"]=[]
#cloud config
userdata=None
config_drive = None
+ userdata_list = []
if isinstance(cloud_config, dict):
if cloud_config.get("user-data"):
- userdata=cloud_config["user-data"]
+ if isinstance(cloud_config["user-data"], str):
+ userdata_list.append(cloud_config["user-data"])
+ else:
+ for u in cloud_config["user-data"]:
+ userdata_list.append(u)
if cloud_config.get("boot-data-drive") != None:
config_drive = cloud_config["boot-data-drive"]
if cloud_config.get("config-files") or cloud_config.get("users") or cloud_config.get("key-pairs"):
- if userdata:
- raise vimconn.vimconnConflictException("Cloud-config cannot contain both 'userdata' and 'config-files'/'users'/'key-pairs'")
userdata_dict={}
#default user
if cloud_config.get("key-pairs"):
if file.get("owner"):
file_info["owner"] = file["owner"]
userdata_dict["write_files"].append(file_info)
- userdata = "#cloud-config\n"
- userdata += yaml.safe_dump(userdata_dict, indent=4, default_flow_style=False)
+ userdata_list.append("#cloud-config\n" + yaml.safe_dump(userdata_dict, indent=4,
+ default_flow_style=False))
+ userdata = self._create_mimemultipart(userdata_list)
self.logger.debug("userdata: %s", userdata)
elif isinstance(cloud_config, str):
userdata = cloud_config
self._format_exception(e)
#TODO insert exception vimconn.HTTP_Unauthorized
+ ####### VIO Specific Changes #########
+ def _genrate_vlanID(self):
+ """
+ Method to get unused vlanID
+ Args:
+ None
+ Returns:
+ vlanID
+ """
+ #Get used VLAN IDs
+ usedVlanIDs = []
+ networks = self.get_network_list()
+ for net in networks:
+ if net.get('provider:segmentation_id'):
+ usedVlanIDs.append(net.get('provider:segmentation_id'))
+ used_vlanIDs = set(usedVlanIDs)
+
+ #find unused VLAN ID
+ for vlanID_range in self.config.get('dataplane_net_vlan_range'):
+ try:
+ start_vlanid , end_vlanid = map(int, vlanID_range.replace(" ", "").split("-"))
+ for vlanID in xrange(start_vlanid, end_vlanid + 1):
+ if vlanID not in used_vlanIDs:
+ return vlanID
+ except Exception as exp:
+ raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp))
+ else:
+ raise vimconn.vimconnConflictException("Unable to create the SRIOV VLAN network."\
+ " All given Vlan IDs {} are in use.".format(self.config.get('dataplane_net_vlan_range')))
+
+
+ def _validate_vlan_ranges(self, dataplane_net_vlan_range):
+ """
+ Method to validate user given vlanID ranges
+ Args: None
+ Returns: None
+ """
+ for vlanID_range in dataplane_net_vlan_range:
+ vlan_range = vlanID_range.replace(" ", "")
+ #validate format
+ vlanID_pattern = r'(\d)*-(\d)*$'
+ match_obj = re.match(vlanID_pattern, vlan_range)
+ if not match_obj:
+ raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}.You must provide "\
+ "'dataplane_net_vlan_range' in format [start_ID - end_ID].".format(vlanID_range))
+
+ start_vlanid , end_vlanid = map(int,vlan_range.split("-"))
+ if start_vlanid <= 0 :
+ raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\
+ "Start ID can not be zero. For VLAN "\
+ "networks valid IDs are 1 to 4094 ".format(vlanID_range))
+ if end_vlanid > 4094 :
+ raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\
+ "End VLAN ID can not be greater than 4094. For VLAN "\
+ "networks valid IDs are 1 to 4094 ".format(vlanID_range))
+
+ if start_vlanid > end_vlanid:
+ raise vimconn.vimconnConflictException("Invalid dataplane_net_vlan_range {}."\
+ "You must provide a 'dataplane_net_vlan_range' in format start_ID - end_ID and "\
+ "start_ID < end_ID ".format(vlanID_range))
+
#NOT USED FUNCTIONS
def new_external_port(self, port_data):
return error_value, error_text
+