import random
import re
import copy
+from pprint import pformat
+from types import StringTypes
from novaclient import client as nClient, exceptions as nvExceptions
from keystoneauth1.identity import v2, v3
volume_timeout = 600
server_timeout = 600
+
+class SafeDumper(yaml.SafeDumper):
+ def represent_data(self, data):
+ # Openstack APIs use custom subclasses of dict and YAML safe dumper
+ # is designed to not handle that (reference issue 142 of pyyaml)
+ if isinstance(data, dict) and data.__class__ != dict:
+ # A simple solution is to convert those items back to dicts
+ data = dict(data.items())
+
+ return super(SafeDumper, self).represent_data(data)
+
+
class vimconnector(vimconn.vimconnector):
def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
log_level=None, config={}, persistent_info={}):
self.persistent_info = persistent_info
self.availability_zone = persistent_info.get('availability_zone', None)
self.session = persistent_info.get('session', {'reload_client': True})
+ self.my_tenant_id = self.session.get('my_tenant_id')
self.nova = self.session.get('nova')
self.neutron = self.session.get('neutron')
self.cinder = self.session.get('cinder')
self.logger = logging.getLogger('openmano.vim.openstack')
+ # allow security_groups to be a list or a single string
+ if isinstance(self.config.get('security_groups'), str):
+ self.config['security_groups'] = [self.config['security_groups']]
+ self.security_groups_id = None
+
####### VIO Specific Changes #########
if self.vim_type == "VIO":
self.logger = logging.getLogger('openmano.vim.vio')
vimconn.vimconnector.__setitem__(self, index, value)
self.session['reload_client'] = True
+ def serialize(self, value):
+ """Serialization of python basic types.
+
+ In the case value is not serializable a message will be logged and a
+ simple representation of the data that cannot be converted back to
+ python is returned.
+ """
+ if isinstance(value, StringTypes):
+ return value
+
+ try:
+ return yaml.dump(value, Dumper=SafeDumper,
+ default_flow_style=True, width=256)
+ except yaml.representer.RepresenterError:
+ self.logger.debug(
+ 'The following entity cannot be serialized in YAML:'
+ '\n\n%s\n\n', pformat(value), exc_info=True)
+ return str(value)
+
def _reload_connection(self):
'''Called before any operation, it check if credentials has changed
Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
'''
- #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
+ #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
if self.session['reload_client']:
if self.config.get('APIversion'):
self.api_version3 = self.config['APIversion'] == 'v3.3' or self.config['APIversion'] == '3'
tenant_name=self.tenant_name,
tenant_id=self.tenant_id)
sess = session.Session(auth=auth, verify=self.verify)
+ # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River Titanium cloud and StarlingX
+ region_name = self.config.get('region_name')
if self.api_version3:
- self.keystone = ksClient_v3.Client(session=sess, endpoint_type=self.endpoint_type)
+ self.keystone = ksClient_v3.Client(session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
else:
self.keystone = ksClient_v2.Client(session=sess, endpoint_type=self.endpoint_type)
self.session['keystone'] = self.keystone
version = self.config.get("microversion")
if not version:
version = "2.1"
- 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)
+ # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River Titanium cloud and StarlingX
+ self.nova = self.session['nova'] = nClient.Client(str(version), session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
+ self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
+ self.cinder = self.session['cinder'] = cClient.Client(2, session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
+ try:
+ self.my_tenant_id = self.session['my_tenant_id'] = sess.get_project_id()
+ except Exception as e:
+ self.logger.error("Cannot get project_id from session", exc_info=True)
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()
+ # 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
# add availablity zone info inside self.persistent_info
self._set_availablity_zones()
self.persistent_info['availability_zone'] = self.availability_zone
+ self.security_groups_id = None # force to get again security_groups_ids next time they are needed
def __net_os2mano(self, net_list_dict):
'''Transform the net openstack format to mano format
self.logger.error("General Exception " + str(exception), exc_info=True)
raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
+ def _get_ids_from_name(self):
+ """
+ Obtain ids from name of tenant and security_groups. Store at self .security_groups_id"
+ :return: None
+ """
+ # get tenant_id if only tenant_name is supplied
+ self._reload_connection()
+ if not self.my_tenant_id:
+ raise vimconn.vimconnConnectionException("Error getting tenant information from name={} id={}".
+ format(self.tenant_name, self.tenant_id))
+ if self.config.get('security_groups') and not self.security_groups_id:
+ # convert from name to id
+ neutron_sg_list = self.neutron.list_security_groups(tenant_id=self.my_tenant_id)["security_groups"]
+
+ self.security_groups_id = []
+ for sg in self.config.get('security_groups'):
+ for neutron_sg in neutron_sg_list:
+ if sg in (neutron_sg["id"], neutron_sg["name"]):
+ self.security_groups_id.append(neutron_sg["id"])
+ break
+ else:
+ self.security_groups_id = None
+ raise vimconn.vimconnConnectionException("Not found security group {} for this tenant".format(sg))
+
def get_tenant_list(self, filter_dict={}):
'''Obtain tenants of VIM
filter_dict can contain the following keys:
subnets.append(subnet)
net["subnets"] = subnets
net["encapsulation"] = net.get('provider:network_type')
+ net["encapsulation_type"] = net.get('provider:network_type')
net["segmentation_id"] = net.get('provider:segmentation_id')
+ net["encapsulation_id"] = net.get('provider:segmentation_id')
return net
def delete_network(self, net_id):
net_id: #VIM id of this network
status: #Mandatory. Text with one of:
# DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
# OTHER (Vim reported other status not understood)
# ERROR (VIM indicates an ERROR status)
- # ACTIVE, INACTIVE, DOWN (admin down),
+ # ACTIVE, INACTIVE, DOWN (admin down),
# BUILD (on building process)
#
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ 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)
'''
if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
net['status'] = 'DOWN'
- try:
- net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256)
- except yaml.representer.RepresenterError:
- net['vim_info'] = str(net_vim)
+
+ net['vim_info'] = self.serialize(net_vim)
+
if net_vim.get('fault'): #TODO
net['error_msg'] = str(net_vim['fault'])
except vimconn.vimconnNotFoundException as e:
self._reload_connection()
# metadata_vpci = {} # For a specific neutron plugin
block_device_mapping = None
+
for net in net_list:
if not net.get("net_id"): # skip non connected iface
continue
- port_dict={
+ port_dict = {
"network_id": net["net_id"],
"name": net.get("name"),
"admin_state_up": True
}
+ if self.config.get("security_groups") and net.get("port_security") is not False and \
+ not self.config.get("no_port_security_extension"):
+ if not self.security_groups_id:
+ self._get_ids_from_name()
+ port_dict["security_groups"] = self.security_groups_id
+
if net["type"]=="virtual":
pass
# if "vpci" in net:
self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'",
name, image_id, flavor_id, str(net_list_vim), description)
- security_groups = self.config.get('security_groups')
- if type(security_groups) is str:
- security_groups = ( security_groups, )
# cloud config
config_drive, userdata = self._create_user_data(cloud_config)
self.logger.debug("nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
"availability_zone={}, key_name={}, userdata={}, config_drive={}, "
"block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim,
- security_groups, vm_av_zone, self.config.get('keypair'),
- userdata, config_drive, block_device_mapping))
+ self.config.get("security_groups"), vm_av_zone,
+ self.config.get('keypair'), userdata, config_drive,
+ block_device_mapping))
server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim,
- security_groups=security_groups,
+ security_groups=self.config.get("security_groups"),
+ # TODO remove security_groups in future versions. Already at neutron port
availability_zone=vm_av_zone,
key_name=self.config.get('keypair'),
userdata=userdata,
Params:
vm_id: uuid of the VM
console_type, can be:
- "novnc" (by default), "xvpvnc" for VNC types,
+ "novnc" (by default), "xvpvnc" for VNC types,
"rdp-html5" for RDP types, "spice-html5" for SPICE types
Returns dict with the console parameters:
protocol: ssh, ftp, http, https, ...
- server: usually ip address
- port: the http, ssh, ... port
- suffix: extra text, e.g. the http path and query string
+ server: usually ip address
+ port: the http, ssh, ... port
+ suffix: extra text, e.g. the http path and query string
'''
self.logger.debug("Getting VM CONSOLE from VIM")
try:
vm_id: #VIM id of this Virtual Machine
status: #Mandatory. Text with one of:
# DELETED (not found at vim)
- # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
# OTHER (Vim reported other status not understood)
# ERROR (VIM indicates an ERROR status)
- # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
+ # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
# CREATING (on building process), ERROR
# ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
#
- error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ 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)
interfaces:
- vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
else:
vm['status'] = "OTHER"
vm['error_msg'] = "VIM status reported " + vm_vim['status']
- try:
- vm['vim_info'] = yaml.safe_dump(vm_vim, default_flow_style=True, width=256)
- except yaml.representer.RepresenterError:
- vm['vim_info'] = str(vm_vim)
+
+ vm['vim_info'] = self.serialize(vm_vim)
+
vm["interfaces"] = []
if vm_vim.get('fault'):
vm['error_msg'] = str(vm_vim['fault'])
port_dict = self.neutron.list_ports(device_id=vm_id)
for port in port_dict["ports"]:
interface={}
- try:
- interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
- except yaml.representer.RepresenterError:
- interface['vim_info'] = str(port)
+ interface['vim_info'] = self.serialize(port)
interface["mac_address"] = port.get("mac_address")
interface["vim_net_id"] = port["network_id"]
interface["vim_interface_id"] = port["id"]
- # check if OS-EXT-SRV-ATTR:host is there,
+ # check if OS-EXT-SRV-ATTR:host is there,
# in case of non-admin credentials, it will be missing
if vm_vim.get('OS-EXT-SRV-ATTR:host'):
interface["compute_node"] = vm_vim['OS-EXT-SRV-ATTR:host']
interface["pci"] = None
- # check if binding:profile is there,
+ # check if binding:profile is there,
# in case of non-admin credentials, it will be missing
if port.get('binding:profile'):
if port['binding:profile'].get('pci_slot'):
self._reload_connection()
# In networking-sfc the MPLS encapsulation is legacy
# should be used when no full SFC Encapsulation is intended
- sfc_encap = 'mpls'
+ correlation = 'mpls'
if sfc_encap:
correlation = 'nsh'
sfp_dict = {'name': name,