'''
osconnector implements all the methods to interact with openstack using the python-client.
'''
-__author__="Alfonso Tierno, Gerardo Garcia"
-__date__ ="$22-jun-2014 11:19:29$"
+__author__="Alfonso Tierno, Gerardo Garcia, xFlow Research"
+__date__ ="$24-nov-2016 09:10$"
import vimconn
import json
import yaml
import logging
+import netaddr
-from novaclient import client as nClient, exceptions as nvExceptions
-import keystoneclient.v2_0.client as ksClient
+from novaclient import client as nClient_v2, exceptions as nvExceptions, api_versions as APIVersion
+import keystoneclient.v2_0.client as ksClient_v2
+from novaclient.v2.client import Client as nClient
+import keystoneclient.v3.client as ksClient
import keystoneclient.exceptions as ksExceptions
import glanceclient.v2.client as glClient
import glanceclient.client as gl1Client
import glanceclient.exc as gl1Exceptions
from httplib import HTTPException
-from neutronclient.neutron import client as neClient
+from neutronclient.neutron import client as neClient_v2
+from neutronclient.v2_0 import client as neClient
from neutronclient.common import exceptions as neExceptions
from requests.exceptions import ConnectionError
}
class vimconnector(vimconn.vimconnector):
- def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="DEBUG", config={}):
+ def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, config={}):
'''using common constructor parameters. In this case
'url' is the keystone authorization url,
'url_admin' is not use
'''
+ self.osc_api_version = 'v2.0'
+ if config.get('APIversion') == 'v3.3':
+ self.osc_api_version = 'v3.3'
vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
self.k_creds={}
if passwd:
self.k_creds['password'] = passwd
self.n_creds['api_key'] = passwd
+ if self.osc_api_version == 'v3.3':
+ self.k_creds['project_name'] = tenant_name
+ self.k_creds['project_id'] = tenant_id
self.reload_client = True
- self.logger = logging.getLogger('mano.vim.openstack')
+ self.logger = logging.getLogger('openmano.vim.openstack')
+ if log_level:
+ self.logger.setLevel( getattr(logging, log_level) )
def __setitem__(self,index, value):
'''Set individuals parameters
if index=='tenant_id':
self.reload_client=True
self.tenant_id = value
- if value:
- self.k_creds['tenant_id'] = value
- self.n_creds['tenant_id'] = value
+ if self.osc_api_version == 'v3.3':
+ if value:
+ self.k_creds['project_id'] = value
+ self.n_creds['project_id'] = value
+ else:
+ del self.k_creds['project_id']
+ del self.n_creds['project_id']
else:
- del self.k_creds['tenant_name']
- del self.n_creds['project_id']
+ if value:
+ self.k_creds['tenant_id'] = value
+ self.n_creds['tenant_id'] = value
+ else:
+ del self.k_creds['tenant_id']
+ del self.n_creds['tenant_id']
elif index=='tenant_name':
self.reload_client=True
self.tenant_name = value
- if value:
- self.k_creds['tenant_name'] = value
- self.n_creds['project_id'] = value
+ if self.osc_api_version == 'v3.3':
+ if value:
+ self.k_creds['project_name'] = value
+ self.n_creds['project_name'] = value
+ else:
+ del self.k_creds['project_name']
+ del self.n_creds['project_name']
else:
- del self.k_creds['tenant_name']
- del self.n_creds['project_id']
+ if value:
+ self.k_creds['tenant_name'] = value
+ self.n_creds['project_id'] = value
+ else:
+ del self.k_creds['tenant_name']
+ del self.n_creds['project_id']
elif index=='user':
self.reload_client=True
self.user = value
#test valid params
if len(self.n_creds) <4:
raise ksExceptions.ClientException("Not enough parameters to connect to openstack")
- self.nova = nClient.Client(2, **self.n_creds)
- self.keystone = ksClient.Client(**self.k_creds)
+ if self.osc_api_version == 'v3.3':
+ self.nova = nClient(APIVersion(version_str='2'), **self.n_creds)
+ self.keystone = ksClient.Client(**self.k_creds)
+ self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
+ self.neutron = neClient.Client(APIVersion(version_str='2'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
+ else:
+ self.nova = nClient_v2.Client('2', **self.n_creds)
+ self.keystone = ksClient_v2.Client(**self.k_creds)
+ self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
+ self.neutron = neClient_v2.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL')
self.glance = glClient.Client(self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
- self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
- self.neutron = neClient.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
self.reload_client = False
-
+
def __net_os2mano(self, net_list_dict):
'''Transform the net openstack format to mano format
net_list_dict can be a list of dict or a single dict'''
def _format_exception(self, exception):
'''Transform a keystone, nova, neutron exception into a vimconn exception'''
if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
- ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed,
- neClient.exceptions.ConnectionFailed)):
+ ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed
+ )):
raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException,
neExceptions.NeutronException, nvExceptions.BadRequest)):
<other VIM specific>
Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
'''
- self.logger.debug("Getting tenant from VIM filter: '%s'", str(filter_dict))
+ self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
try:
self._reload_connection()
- tenant_class_list=self.keystone.tenants.findall(**filter_dict)
- tenant_list=[]
- for tenant in tenant_class_list:
- tenant_list.append(tenant.to_dict())
- return tenant_list
- except (ksExceptions.ConnectionError, ksExceptions.ClientException) as e:
+ if osc_api_version == 'v3.3':
+ project_class_list=self.keystone.projects.findall(**filter_dict)
+ else:
+ project_class_list=self.keystone.tenants.findall(**filter_dict)
+ project_list=[]
+ for project in project_class_list:
+ project_list.append(project.to_dict())
+ return project_list
+ except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
def new_tenant(self, tenant_name, tenant_description):
self.logger.debug("Adding a new tenant name: %s", tenant_name)
try:
self._reload_connection()
- tenant=self.keystone.tenants.create(tenant_name, tenant_description)
- return tenant.id
- except (ksExceptions.ConnectionError, ksExceptions.ClientException) as e:
+ if self.osc_api_version == 'v3.3':
+ project=self.keystone.projects.create(tenant_name, tenant_description)
+ else:
+ project=self.keystone.tenants.create(tenant_name, tenant_description)
+ return project.id
+ except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
def delete_tenant(self, tenant_id):
self.logger.debug("Deleting tenant %s from VIM", tenant_id)
try:
self._reload_connection()
- self.keystone.tenants.delete(tenant_id)
+ if osc_api_version == 'v3.3':
+ self.keystone.projects.delete(tenant_id)
+ else:
+ self.keystone.tenants.delete(tenant_id)
return tenant_id
- except (ksExceptions.ConnectionError, ksExceptions.ClientException) as e:
+ except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
-
- def new_network(self,net_name,net_type, shared=False, cidr=None, vlan=None):
+
+ def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
'''Adds a tenant network to VIM. Returns the network identifier'''
self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
+ #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
try:
+ new_net = None
self._reload_connection()
network_dict = {'name': net_name, 'admin_state_up': True}
if net_type=="data" or net_type=="ptp":
network_dict["shared"]=shared
new_net=self.neutron.create_network({'network':network_dict})
#print new_net
- #create fake subnetwork
- if not cidr:
- cidr="192.168.111.0/24"
+ #create subnetwork, even if there is no profile
+ if not ip_profile:
+ ip_profile = {}
+ if 'subnet_address' not in ip_profile:
+ #Fake subnet is required
+ ip_profile['subnet_address'] = "192.168.111.0/24"
+ if 'ip_version' not in ip_profile:
+ ip_profile['ip_version'] = "IPv4"
subnet={"name":net_name+"-subnet",
"network_id": new_net["network"]["id"],
- "ip_version": 4,
- "cidr": cidr
+ "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
+ "cidr": ip_profile['subnet_address']
}
+ if 'gateway_address' in ip_profile:
+ subnet['gateway_ip'] = ip_profile['gateway_address']
+ if ip_profile.get('dns_address'):
+ #TODO: manage dns_address as a list of addresses separated by commas
+ subnet['dns_nameservers'] = []
+ subnet['dns_nameservers'].append(ip_profile['dns_address'])
+ if 'dhcp_enabled' in ip_profile:
+ subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True
+ if 'dhcp_start_address' in ip_profile:
+ subnet['allocation_pools']=[]
+ subnet['allocation_pools'].append(dict())
+ subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
+ if 'dhcp_count' in ip_profile:
+ #parts = ip_profile['dhcp_start_address'].split('.')
+ #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
+ ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
+ ip_int += ip_profile['dhcp_count'] - 1
+ ip_str = str(netaddr.IPAddress(ip_int))
+ subnet['allocation_pools'][0]['end'] = ip_str
+ #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
self.neutron.create_subnet({"subnet": subnet} )
return new_net["network"]["id"]
- except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e:
+ except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
+ if new_net:
+ self.neutron.delete_network(new_net['network']['id'])
self._format_exception(e)
def get_network_list(self, filter_dict={}):
self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
try:
self._reload_connection()
+ if osc_api_version == 'v3.3' and "tenant_id" in filter_dict:
+ filter_dict['project_id'] = filter_dict.pop('tenant_id')
net_dict=self.neutron.list_networks(**filter_dict)
net_list=net_dict["networks"]
self.__net_os2mano(net_list)
return net_list
- except (neExceptions.ConnectionFailed, neClient.exceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e:
+ except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
self._format_exception(e)
def get_network(self, net_id):
self.neutron.delete_network(net_id)
return net_id
except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
- neClient.exceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e:
+ ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
self._format_exception(e)
def refresh_nets_status(self, net_list):
net["status"] = "OTHER"
net["error_msg"] = "VIM status reported " + net_vim['status']
- if net['status'] == "ACIVE" and not net_vim['admin_state_up']:
+ if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
net['status'] = 'DOWN'
- net['vim_info'] = yaml.safe_dump(net_vim)
+ 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)
if net_vim.get('fault'): #TODO
net['error_msg'] = str(net_vim['fault'])
except vimconn.vimconnNotFoundException as e:
flavor = self.nova.flavors.find(id=flavor_id)
#TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
return flavor.to_dict()
- except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException) as e:
+ except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
def new_flavor(self, flavor_data, change_name_if_used=True):
self.nova.flavors.delete(flavor_id)
return flavor_id
#except nvExceptions.BadRequest as e:
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException) as e:
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
def new_image(self,image_dict):
return new_image.id
except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
self._format_exception(e)
- except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError) as e:
+ except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
if retry==max_retries:
continue
self._format_exception(e)
self._reload_connection()
self.nova.images.delete(image_id)
return image_id
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e: #TODO remove
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove
self._format_exception(e)
def get_image_id_from_path(self, path):
- '''Get the image id from image path in the VIM database. Returns the image_id
- '''
+ '''Get the image id from image path in the VIM database. Returns the image_id'''
try:
self._reload_connection()
images = self.nova.images.list()
if image.metadata.get("location")==path:
return image.id
raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
- except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e:
+ except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
self._format_exception(e)
+ def get_image_list(self, filter_dict={}):
+ '''Obtain tenant images from VIM
+ Filter_dict can be:
+ id: image id
+ name: image name
+ checksum: image checksum
+ Returns the image list of dictionaries:
+ [{<the fields at Filter_dict plus some VIM specific>}, ...]
+ List can be empty
+ '''
+ self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
+ try:
+ self._reload_connection()
+ filter_dict_os=filter_dict.copy()
+ #First we filter by the available filter fields: name, id. The others are removed.
+ filter_dict_os.pop('checksum',None)
+ image_list=self.nova.images.findall(**filter_dict_os)
+ if len(image_list)==0:
+ return []
+ #Then we filter by the rest of filter fields: checksum
+ filtered_list = []
+ for image in image_list:
+ image_dict=self.glance.images.get(image.id)
+ if image_dict['checksum']==filter_dict.get('checksum'):
+ filtered_list.append(image)
+ return filtered_list
+ except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
+ self._format_exception(e)
+
def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None):
'''Adds a VM instance to VIM
Params:
# except nvExceptions.NotFound as e:
# error_value=-vimconn.HTTP_Not_Found
# error_text= "vm instance %s not found" % vm_id
- except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError,
- neClient.exceptions.ConnectionFailed) as e:
+ except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError
+ ) as e:
self._format_exception(e)
except TypeError as e:
raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
server = self.nova.servers.find(id=vm_id)
#TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
return server.to_dict()
- except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound) as e:
+ except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
self._format_exception(e)
def get_vminstance_console(self,vm_id, console_type="vnc"):
return console_dict
raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest) as e:
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
self._format_exception(e)
def delete_vminstance(self, vm_id):
self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e))
self.nova.servers.delete(vm_id)
return vm_id
- except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException) as e:
+ except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
self._format_exception(e)
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
else:
vm['status'] = "OTHER"
vm['error_msg'] = "VIM status reported " + vm_vim['status']
- vm['vim_info'] = yaml.safe_dump(vm_vim)
+ 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["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={}
- interface['vim_info'] = yaml.safe_dump(port)
+ try:
+ interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
+ except yaml.representer.RepresenterError:
+ interface['vim_info'] = str(port)
interface["mac_address"] = port.get("mac_address")
interface["vim_net_id"] = port["network_id"]
interface["vim_interface_id"] = port["id"]
raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
return vm_id
- except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound) as e:
+ except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
self._format_exception(e)
#TODO insert exception vimconn.HTTP_Unauthorized