import time
import yaml
import random
+import sys
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',
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.
else:
self.availability_zone = self._get_openstack_availablity_zones()
- def _get_vm_availavility_zone(self, availavility_zone_index, nfv_availability_zones):
+ def _get_vm_availability_zone(self, availability_zone_index, availability_zone_list):
"""
- Return a list with all availability zones create during datacenter attach.
- :return: List with availability zones
+ Return thge availability zone to be used by the created VM.
+ :return: The VIM availability zone to be used or None
"""
- openstack_avilability_zone = self.availability_zone
-
- # check if VIM offer enough availability zones describe in the VNFC
- if self.availability_zone and availavility_zone_index is not None \
- and 0 <= len(nfv_availability_zones) <= len(self.availability_zone):
-
- if nfv_availability_zones:
- vnf_azone = nfv_availability_zones[availavility_zone_index]
- zones_available = []
-
- for nfv_zone in nfv_availability_zones:
- for vim_zone in openstack_avilability_zone:
- if nfv_zone is vim_zone:
- zones_available.append(nfv_zone)
-
- if len(zones_available) == len(openstack_avilability_zone) and vnf_azone in openstack_avilability_zone:
- return vnf_azone
- else:
- return openstack_avilability_zone[availavility_zone_index]
+ if availability_zone_index is None:
+ if not self.config.get('availability_zone'):
+ return None
+ elif isinstance(self.config.get('availability_zone'), str):
+ return self.config['availability_zone']
+ else:
+ # TODO consider using a different parameter at config for default AV and AV list match
+ return self.config['availability_zone'][0]
+
+ vim_availability_zones = self.availability_zone
+ # check if VIM offer enough availability zones describe in the VNFD
+ if vim_availability_zones and len(availability_zone_list) <= len(vim_availability_zones):
+ # check if all the names of NFV AV match VIM AV names
+ match_by_index = False
+ for av in availability_zone_list:
+ if av not in vim_availability_zones:
+ match_by_index = True
+ break
+ if match_by_index:
+ return vim_availability_zones[availability_zone_index]
+ else:
+ return availability_zone_list[availability_zone_index]
else:
- raise vimconn.vimconnConflictException("No enough availablity zones for this deployment")
- return None
+ raise vimconn.vimconnConflictException("No enough availability zones at VIM for this deployment")
- def new_vminstance(self, name, description, start, image_id, flavor_id, net_list,cloud_config=None,disk_list=None,
- availavility_zone_index=None, nfv_availability_zones=None):
+ def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
+ availability_zone_index=None, availability_zone_list=None):
'''Adds a VM instance to VIM
Params:
start: indicates if VM must start or boot in pause mode. Ignored
'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
'size': (mandatory) string with the size of the disk in GB
- availavility_zone_index:counter for instance order in vim availability_zones availables
- nfv_availability_zones: Lost given by user in the VNFC descriptor.
+ availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
+ availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
+ availability_zone_index is None
#TODO ip, security groups
Returns the instance identifier
'''
#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
raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
http_code=vimconn.HTTP_Request_Timeout)
# get availability Zone
- vm_av_zone = self._get_vm_availavility_zone(availavility_zone_index, nfv_availability_zones)
+ vm_av_zone = self._get_vm_availability_zone(availability_zone_index, availability_zone_list)
self.logger.debug("nova.servers.create({}, {}, {}, nics={}, meta={}, security_groups={}, "
"availability_zone={}, key_name={}, userdata={}, config_drive={}, "