blob: 3a323a83300a26b67382f203be6b119e5824f9ca [file] [log] [blame]
# -*- coding: utf-8 -*-
##
# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
# This file is part of openmano
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact with: nfvlabs@tid.es
##
'''
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$"
import vimconn
import json
import yaml
from novaclient import client as nClient, exceptions as nvExceptions
import keystoneclient.v2_0.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.common import exceptions as neExceptions
from requests.exceptions import ConnectionError
'''contain the openstack virtual machine status to openmano status'''
vmStatus2manoFormat={'ACTIVE':'ACTIVE',
'PAUSED':'PAUSED',
'SUSPENDED': 'SUSPENDED',
'SHUTOFF':'INACTIVE',
'BUILD':'BUILD',
'ERROR':'ERROR','DELETED':'DELETED'
}
netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
}
class vimconnector(vimconn.vimconnector):
def __init__(self, uuid, name, tenant, url, url_admin=None, user=None, passwd=None, debug=True, config={}):
'''using common constructor parameters. In this case
'url' is the keystone authorization url,
'url_admin' is not use
'''
vimconn.vimconnector.__init__(self, uuid, name, tenant, url, url_admin, user, passwd, debug, config)
self.k_creds={}
self.n_creds={}
if not url:
raise TypeError, 'url param can not be NoneType'
self.k_creds['auth_url'] = url
self.n_creds['auth_url'] = url
if tenant:
self.k_creds['tenant_name'] = tenant
self.n_creds['project_id'] = tenant
if user:
self.k_creds['username'] = user
self.n_creds['username'] = user
if passwd:
self.k_creds['password'] = passwd
self.n_creds['api_key'] = passwd
self.reload_client = True
def __setitem__(self,index, value):
'''Set individuals parameters
Throw TypeError, KeyError
'''
if index=='tenant':
self.reload_client=True
self.tenant = value
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
if value:
self.k_creds['username'] = value
self.n_creds['username'] = value
else:
del self.k_creds['username']
del self.n_creds['username']
elif index=='passwd':
self.reload_client=True
self.passwd = value
if value:
self.k_creds['password'] = value
self.n_creds['api_key'] = value
else:
del self.k_creds['password']
del self.n_creds['api_key']
elif index=='url':
self.reload_client=True
self.url = value
if value:
self.k_creds['auth_url'] = value
self.n_creds['auth_url'] = value
else:
raise TypeError, 'url param can not be NoneType'
else:
vimconn.vimconnector.__setitem__(self,index, 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 :-)
if self.reload_client:
#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)
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 new_external_port(self, port_data):
#TODO openstack if needed
'''Adds a external port to VIM'''
'''Returns the port identifier'''
return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
def connect_port_network(self, port_id, network_id, admin=False):
#TODO openstack if needed
'''Connects a external port to a network'''
'''Returns status code of the VIM response'''
return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
def new_user(self, user_name, user_passwd, tenant_id=None):
'''Adds a new user to openstack VIM'''
'''Returns the user identifier'''
if self.debug:
print "osconnector: Adding a new user to VIM"
try:
self._reload_connection()
user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id)
#self.keystone.tenants.add_user(self.k_creds["username"], #role)
return 1, user.id
except ksExceptions.ConnectionError as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except ksExceptions.ClientException as e: #TODO remove
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "new_tenant " + error_text
return error_value, error_text
def delete_user(self, user_id):
'''Delete a user from openstack VIM'''
'''Returns the user identifier'''
if self.debug:
print "osconnector: Deleting a user from VIM"
try:
self._reload_connection()
self.keystone.users.delete(user_id)
return 1, user_id
except ksExceptions.ConnectionError as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except ksExceptions.NotFound as e:
error_value=-vimconn.HTTP_Not_Found
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except ksExceptions.ClientException as e: #TODO remove
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "delete_tenant " + error_text
return error_value, error_text
def new_tenant(self,tenant_name,tenant_description):
'''Adds a new tenant to openstack VIM'''
'''Returns the tenant identifier'''
if self.debug:
print "osconnector: Adding a new tenant to VIM"
try:
self._reload_connection()
tenant=self.keystone.tenants.create(tenant_name, tenant_description)
#self.keystone.tenants.add_user(self.k_creds["username"], #role)
return 1, tenant.id
except ksExceptions.ConnectionError as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except ksExceptions.ClientException as e: #TODO remove
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "new_tenant " + error_text
return error_value, error_text
def delete_tenant(self,tenant_id):
'''Delete a tenant from openstack VIM'''
'''Returns the tenant identifier'''
if self.debug:
print "osconnector: Deleting a tenant from VIM"
try:
self._reload_connection()
self.keystone.tenants.delete(tenant_id)
#self.keystone.tenants.add_user(self.k_creds["username"], #role)
return 1, tenant_id
except ksExceptions.ConnectionError as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except ksExceptions.ClientException as e: #TODO remove
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "delete_tenant " + error_text
return error_value, error_text
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'''
if type(net_list_dict) is dict:
net_list_=(net_list_dict,)
elif type(net_list_dict) is list:
net_list_=net_list_dict
else:
raise TypeError("param net_list_dict must be a list or a dictionary")
for net in net_list_:
if net.get('provider:network_type') == "vlan":
net['type']='data'
else:
net['type']='bridge'
def new_tenant_network(self,net_name,net_type,public=False,cidr=None,vlan=None):
'''Adds a tenant network to VIM'''
'''Returns the network identifier'''
if self.debug:
print "osconnector: Adding a new tenant network to VIM (tenant: " + self.tenant + ", type: " + net_type + "): "+ net_name
try:
self._reload_connection()
network_dict = {'name': net_name, 'admin_state_up': True}
if net_type=="data" or net_type=="ptp":
if self.config.get('dataplane_physical_net') == None:
return -vimconn.HTTP_Bad_Request, "You must provide a 'dataplane_physical_net' at config value before creating sriov network "
network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical
network_dict["provider:network_type"] = "vlan"
if vlan!=None:
network_dict["provider:network_type"] = vlan
network_dict["shared"]=public
new_net=self.neutron.create_network({'network':network_dict})
#print new_net
#create fake subnetwork
if not cidr:
cidr="192.168.111.0/24"
subnet={"name":net_name+"-subnet",
"network_id": new_net["network"]["id"],
"ip_version": 4,
"cidr": cidr
}
self.neutron.create_subnet({"subnet": subnet} )
return 1, new_net["network"]["id"]
except neExceptions.ConnectionFailed as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except (ksExceptions.ClientException, neExceptions.NeutronException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "new_tenant_network " + error_text
return error_value, error_text
def get_network_list(self, filter_dict={}):
'''Obtain tenant networks of VIM
Filter_dict can be:
name: network name
id: network uuid
shared: boolean
tenant_id: tenant
admin_state_up: boolean
status: 'ACTIVE'
Returns the network list of dictionaries
'''
if self.debug:
print "osconnector.get_network_list(): Getting network from VIM (filter: " + str(filter_dict) + "): "
try:
self._reload_connection()
net_dict=self.neutron.list_networks(**filter_dict)
net_list=net_dict["networks"]
self.__net_os2mano(net_list)
return 1, net_list
except neClient.exceptions.ConnectionFailed as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except (ksExceptions.ClientException, neExceptions.NeutronException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_network_list " + error_text
return error_value, error_text
def get_tenant_network(self, net_id, tenant_id=None):
'''Obtain tenant networks of VIM'''
'''Returns the network information from a network id'''
if self.debug:
print "osconnector.get_tenant_network(): Getting tenant network %s from VIM" % net_id
filter_dict={"id": net_id}
if tenant_id:
filter_dict["tenant_id"] = tenant_id
r, net_list = self.get_network_list(filter_dict)
if r<0:
return r, net_list
if len(net_list)==0:
return -vimconn.HTTP_Not_Found, "Network '%s' not found" % net_id
elif len(net_list)>1:
return -vimconn.HTTP_Conflict, "Found more than one network with this criteria"
net = net_list[0]
subnets=[]
for subnet_id in net.get("subnets", () ):
try:
subnet = self.neutron.show_subnet(subnet_id)
except Exception as e:
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
print "osconnector.get_tenant_network(): Error getting subnet %s %s" % (net_id, error_text)
subnet = {"id": subnet_id, "fault": error_text}
subnets.append(subnet)
net["subnets"] = subnets
return 1, net
def delete_tenant_network(self, net_id):
'''Deletes a tenant network from VIM'''
'''Returns the network identifier'''
if self.debug:
print "osconnector: Deleting a new tenant network from VIM tenant: " + self.tenant + ", id: " + net_id
try:
self._reload_connection()
#delete VM ports attached to this networks before the network
ports = self.neutron.list_ports(network_id=net_id)
for p in ports['ports']:
try:
self.neutron.delete_port(p["id"])
except Exception as e:
print "Error deleting port: " + type(e).__name__ + ": "+ str(e)
self.neutron.delete_network(net_id)
return 1, net_id
except neClient.exceptions.ConnectionFailed as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except neExceptions.NetworkNotFoundClient as e:
error_value=-vimconn.HTTP_Not_Found
error_text= type(e).__name__ + ": "+ str(e.args[0])
except (ksExceptions.ClientException, neExceptions.NeutronException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "delete_tenant_network " + error_text
return error_value, error_text
def get_tenant_flavor(self, flavor_id):
'''Obtain flavor details from the VIM
Returns the flavor dict details
'''
print "VIMConnector: Getting flavor from VIM"
try:
self._reload_connection()
flavor = self.nova.flavors.find(id=flavor_id)
#TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
return 1, {"flavor": flavor.to_dict()}
except nvExceptions.NotFound as e:
error_value=-vimconn.HTTP_Not_Found
error_text= "flavor instance %s not found" % flavor_id
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_tenant_flavor " + error_text
return error_value, error_text
def new_tenant_flavor(self, flavor_dict, change_name_if_used=True):
'''Adds a tenant flavor to openstack VIM
if change_name_if_used is True, it will change name in case of conflict
Returns the flavor identifier
'''
retry=0
name_suffix = 0
name=flavor_dict['name']
while retry<2:
retry+=1
try:
self._reload_connection()
if change_name_if_used:
#get used names
fl_names=[]
fl=self.nova.flavors.list()
for f in fl:
fl_names.append(f.name)
while name in fl_names:
name_suffix += 1
name = flavor_dict['name']+"-" + str(name_suffix)
ram = flavor_dict.get('ram',64)
vcpus = flavor_dict.get('vcpus',1)
numa_properties=None
extended=flavor_dict.get("extended")
if extended:
numas=extended.get("numas")
if numas:
numa_nodes = len(numas)
if numa_nodes > 1:
return -1, "Can not add flavor with more than one numa"
numa_properties = {"hw:numa_nodes":str(numa_nodes)}
numa_properties["hw:mem_page_size"] = "large"
numa_properties["hw:cpu_policy"] = "dedicated"
numa_properties["hw:numa_mempolicy"] = "strict"
for numa in numas:
#overwrite ram and vcpus
ram = numa['memory']*1024
if 'paired-threads' in numa:
vcpus = numa['paired-threads']*2
numa_properties["hw:cpu_threads_policy"] = "prefer"
elif 'cores' in numa:
vcpus = numa['cores']
#numa_properties["hw:cpu_threads_policy"] = "prefer"
elif 'threads' in numa:
vcpus = numa['threads']
numa_properties["hw:cpu_policy"] = "isolated"
for interface in numa.get("interfaces",() ):
if interface["dedicated"]=="yes":
error_value=-vimconn.HTTP_Bad_Request
error_text= "Passthrough interfaces are not supported for the openstack connector"
break
#TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
#create flavor
new_flavor=self.nova.flavors.create(name,
ram,
vcpus,
flavor_dict.get('disk',1),
is_public=flavor_dict.get('is_public', True)
)
#add metadata
if numa_properties:
new_flavor.set_keys(numa_properties)
return 1, new_flavor.id
except nvExceptions.Conflict as e:
error_value=-vimconn.HTTP_Conflict
error_text= str(e)
if change_name_if_used:
continue
break
#except nvExceptions.BadRequest as e:
except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
break
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "new_tenant_flavor " + error_text
return error_value, error_text
def delete_tenant_flavor(self,flavor_id):
'''Deletes a tenant flavor from openstack VIM
Returns >0,id if ok; or <0,error_text if error
'''
retry=0
while retry<2:
retry+=1
try:
self._reload_connection()
self.nova.flavors.delete(flavor_id)
return 1, flavor_id
except nvExceptions.NotFound as e:
error_value = -vimconn.HTTP_Not_Found
error_text = "flavor '%s' not found" % flavor_id
break
#except nvExceptions.BadRequest as e:
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
break
if self.debug:
print "delete_tenant_flavor " + error_text
#if reaching here is because an exception
return error_value, error_text
def new_tenant_image(self,image_dict):
'''
Adds a tenant image to VIM
if change_name_if_used is True, it will change name in case of conflict
Returns:
>1, image-id if the image is created
<0, message if there is an error
'''
retry=0
#using version 1 of glance client
glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
while retry<2:
retry+=1
try:
self._reload_connection()
#determine format http://docs.openstack.org/developer/glance/formats.html
if "disk_format" in image_dict:
disk_format=image_dict["disk_format"]
else: #autodiscover base on extention
if image_dict['location'][-6:]==".qcow2":
disk_format="qcow2"
elif image_dict['location'][-4:]==".vhd":
disk_format="vhd"
elif image_dict['location'][-5:]==".vmdk":
disk_format="vmdk"
elif image_dict['location'][-4:]==".vdi":
disk_format="vdi"
elif image_dict['location'][-4:]==".iso":
disk_format="iso"
elif image_dict['location'][-4:]==".aki":
disk_format="aki"
elif image_dict['location'][-4:]==".ari":
disk_format="ari"
elif image_dict['location'][-4:]==".ami":
disk_format="ami"
else:
disk_format="raw"
print "new_tenant_image: '%s' loading from '%s'" % (image_dict['name'], image_dict['location'])
if image_dict['location'][0:4]=="http":
new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
container_format="bare", location=image_dict['location'], disk_format=disk_format)
else: #local path
with open(image_dict['location']) as fimage:
new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
container_format="bare", data=fimage, disk_format=disk_format)
#insert metadata. We cannot use 'new_image.properties.setdefault'
#because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
new_image_nova=self.nova.images.find(id=new_image.id)
new_image_nova.metadata.setdefault('location',image_dict['location'])
metadata_to_load = image_dict.get('metadata')
if metadata_to_load:
for k,v in yaml.load(metadata_to_load).iteritems():
new_image_nova.metadata.setdefault(k,v)
return 1, new_image.id
except nvExceptions.Conflict as e:
error_value=-vimconn.HTTP_Conflict
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
break
except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
continue
except IOError as e: #can not open the file
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0])) + " for " + image_dict['location']
break
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
break
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "new_tenant_image " + error_text
return error_value, error_text
def delete_tenant_image(self, image_id):
'''Deletes a tenant image from openstack VIM
Returns >0,id if ok; or <0,error_text if error
'''
retry=0
while retry<2:
retry+=1
try:
self._reload_connection()
self.nova.images.delete(image_id)
return 1, image_id
except nvExceptions.NotFound as e:
error_value = -vimconn.HTTP_Not_Found
error_text = "flavor '%s' not found" % image_id
break
#except nvExceptions.BadRequest as e:
except (ksExceptions.ClientException, nvExceptions.ClientException) as e: #TODO remove
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
break
if self.debug:
print "delete_tenant_image " + error_text
#if reaching here is because an exception
return error_value, error_text
def new_tenant_vminstance(self,name,description,start,image_id,flavor_id,net_list):
'''Adds a VM instance to VIM
Params:
start: indicates if VM must start or boot in pause mode. Ignored
image_id,flavor_id: iamge and flavor uuid
net_list: list of interfaces, each one is a dictionary with:
name:
net_id: network uuid to connect
vpci: virtual vcpi to assign, ignored because openstack lack #TODO
model: interface model, ignored #TODO
mac_address: used for SR-IOV ifaces #TODO for other types
use: 'data', 'bridge', 'mgmt'
type: 'virtual', 'PF', 'VF', 'VFnotShared'
vim_id: filled/added by this function
#TODO ip, security groups
Returns >=0, the instance identifier
<0, error_text
'''
if self.debug:
print "osconnector: Creating VM into VIM"
print " image %s flavor %s nics=%s" %(image_id, flavor_id,net_list)
try:
metadata=[]
net_list_vim=[]
self._reload_connection()
metadata_vpci={}
for net in net_list:
if not net.get("net_id"): #skip non connected iface
continue
if net["type"]=="virtual":
net_list_vim.append({'net-id': net["net_id"]})
if "vpci" in net:
metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
elif net["type"]=="PF":
print "new_tenant_vminstance: Warning, can not connect a passthrough interface "
#TODO insert this when openstack consider passthrough ports as openstack neutron ports
else: #VF
if "vpci" in net:
if "VF" not in metadata_vpci:
metadata_vpci["VF"]=[]
metadata_vpci["VF"].append([ net["vpci"], "" ])
port_dict={
"network_id": net["net_id"],
"name": net.get("name"),
"binding:vnic_type": "direct",
"admin_state_up": True
}
if not port_dict["name"]:
port_dict["name"] = name
if net.get("mac_address"):
port_dict["mac_address"]=net["mac_address"]
#TODO: manage having SRIOV without vlan tag
#if net["type"] == "VFnotShared"
# port_dict["vlan"]=0
new_port = self.neutron.create_port({"port": port_dict })
net["mac_adress"] = new_port["port"]["mac_address"]
net["vim_id"] = new_port["port"]["id"]
net["ip"] = new_port["port"].get("fixed_ips",[{}])[0].get("ip_address")
net_list_vim.append({"port-id": new_port["port"]["id"]})
if metadata_vpci:
metadata = {"pci_assignement": json.dumps(metadata_vpci)}
print "name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s"\
% (name, image_id, flavor_id, str(net_list_vim), description, str(metadata))
security_groups = self.config.get('security_groups')
if type(security_groups) is str:
security_groups = ( security_groups, )
server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, meta=metadata,
security_groups = security_groups,
availability_zone = self.config.get('availability_zone'),
key_name = self.config.get('keypair'),
) #, description=description)
print "DONE :-)", server
# #TODO server.add_floating_ip("10.95.87.209")
# #To look for a free floating_ip
# free_floating_ip = None
# for floating_ip in self.neutron.list_floatingips().get("floatingips", () ):
# if not floating_ip["port_id"]:
# free_floating_ip = floating_ip["floating_ip_address"]
# break
# if free_floating_ip:
# server.add_floating_ip(free_floating_ip)
return 1, server.id
# 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, TypeError) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
except neClient.exceptions.ConnectionFailed as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "new_tenant_vminstance Exception",e, error_text
return error_value, error_text
def get_tenant_vminstance(self,vm_id):
'''Returns the VM instance information from VIM'''
if self.debug:
print "osconnector: Getting VM from VIM"
try:
self._reload_connection()
server = self.nova.servers.find(id=vm_id)
#TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
return 1, {"server": server.to_dict()}
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) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_tenant_vminstance " + error_text
return error_value, error_text
def get_tenant_vminstance_console(self,vm_id, console_type="vnc"):
'''
Get a console for the virtual machine
Params:
vm_id: uuid of the VM
console_type, can be:
"novnc" (by default), "xvpvnc" for VNC types,
"rdp-html5" for RDP types, "spice-html5" for SPICE types
Returns <0, text on error, for example not available
1, URL/text with the console parameters
'''
if self.debug:
print "osconnector: Getting VM CONSOLE from VIM"
try:
self._reload_connection()
server = self.nova.servers.find(id=vm_id)
if console_type == None or console_type == "novnc":
console_dict = server.get_vnc_console("novnc")
elif console_type == "xvpvnc":
console_dict = server.get_vnc_console(console_type)
elif console_type == "rdp-html5":
console_dict = server.get_rdp_console(console_type)
elif console_type == "spice-html5":
console_dict = server.get_spice_console(console_type)
else:
return -vimconn.HTTP_Bad_Request, "console type '%s' not allowed" % console_type
console_dict1 = console_dict.get("console")
if console_dict1:
console_url = console_dict1.get("url")
if console_url:
#parse console_url
protocol_index = console_url.find("//")
suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
if protocol_index < 0 or port_index<0 or suffix_index<0:
return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
console_dict={"protocol": console_url[0:protocol_index],
"server": console_url[protocol_index+2:port_index],
"port": console_url[port_index:suffix_index],
"suffix": console_url[suffix_index+1:]
}
protocol_index += 2
return 1, console_dict
return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
#TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
return 1, {"server": server.to_dict()}
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, nvExceptions.BadRequest) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_tenant_vminstance " + error_text
return error_value, error_text
def delete_tenant_vminstance(self, vm_id):
'''Removes a VM instance from VIM
Returns >0, the instance identifier
<0, error_text
'''
if self.debug:
print "osconnector: Getting VM from VIM"
try:
self._reload_connection()
#delete VM ports attached to this networks before the virtual machine
ports = self.neutron.list_ports(device_id=vm_id)
for p in ports['ports']:
try:
self.neutron.delete_port(p["id"])
except Exception as e:
print "Error deleting port: " + type(e).__name__ + ": "+ str(e)
self.nova.servers.delete(vm_id)
return 1, vm_id
except nvExceptions.NotFound as e:
error_value=-vimconn.HTTP_Not_Found
error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_tenant_vminstance " + error_text
return error_value, error_text
def refresh_tenant_vms_and_nets(self, vmDict, netDict):
'''Refreshes the status of the dictionaries of VM instances and nets passed as arguments. It modifies the dictionaries
Returns:
- result: 0 if all elements could be refreshed (even if its status didn't change)
n>0, the number of elements that couldn't be refreshed,
<0 if error (foreseen)
- error_msg: text with reference to possible errors
'''
#vms_refreshed = []
#nets_refreshed = []
vms_unrefreshed = []
nets_unrefreshed = []
if self.debug:
print "osconnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM"
for vm_id in vmDict:
vmDict[vm_id] = {'error_msg':None, 'vim_info':None}
r,c = self.get_tenant_vminstance(vm_id)
if r<0:
print "osconnector refresh_tenant_vm. Error getting vm_id '%s' status: %s" % (vm_id, c)
if r==-vimconn.HTTP_Not_Found:
vmDict[vm_id]['status'] = "DELETED"
else:
vmDict[vm_id]['status'] = "VIM_ERROR"
vmDict[vm_id]['error_msg'] = c
vms_unrefreshed.append(vm_id)
else:
try:
vmDict[vm_id]['status'] = vmStatus2manoFormat[ c['server']['status'] ]
vmDict[vm_id]['vim_info'] = yaml.safe_dump(c['server'])
vmDict[vm_id]["interfaces"] = []
if c['server'].get('fault'):
vmDict[vm_id]['error_msg'] = str(c['server']['fault'])
#get interfaces
try:
self._reload_connection()
port_dict=self.neutron.list_ports(device_id=vm_id)
for port in port_dict["ports"]:
interface={}
interface['vim_info'] = yaml.safe_dump(port)
interface["mac_address"] = port.get("mac_address")
interface["vim_net_id"] = port["network_id"]
interface["vim_interface_id"] = port["id"]
ips=[]
#look for floating ip address
floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
if floating_ip_dict.get("floatingips"):
ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
for subnet in port["fixed_ips"]:
ips.append(subnet["ip_address"])
interface["ip_address"] = ";".join(ips)
vmDict[vm_id]["interfaces"].append(interface)
except Exception as e:
print type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#error message at server.fault["message"]
except KeyError as e:
print "osconnector refresh_tenant_elements KeyError %s getting vm_id '%s' status %s" % (str(e), vm_id, c['server']['status'])
vmDict[vm_id]['status'] = "VIM_ERROR"
vmDict[vm_id]['error_msg'] = str(e)
vms_unrefreshed.append(vm_id)
#print "VMs refreshed: %s" % str(vms_refreshed)
for net_id in netDict:
netDict[net_id]={'error_msg':None, 'vim_info':None}
r,c = self.get_tenant_network(net_id)
if r<0:
print "osconnector refresh_tenant_network. Error getting net_id '%s' status: %s" % (net_id, c)
if r==-vimconn.HTTP_Not_Found:
netDict[net_id]['status'] = "DELETED" #TODO check exit status
else:
netDict[vm_id]['status'] = "VIM_ERROR"
netDict[vm_id]['error_msg'] = c
nets_unrefreshed.append(net_id)
else:
try:
netDict[net_id]['status'] = netStatus2manoFormat[ c['status'] ]
if c['status'] == "ACIVE" and not c['admin_state_up']:
netDict[net_id]['status'] = 'DOWN'
netDict[net_id]['vim_info'] = yaml.safe_dump(c)
if c.get('fault'): #TODO
netDict[net_id]['error_msg'] = str(c['fault'])
except KeyError as e:
print "osconnector refresh_tenant_elements KeyError %s getting vm_id '%s' status %s" % (str(e), vm_id, c['network']['status'])
netDict[net_id]['status'] = "VIM_ERROR"
netDict[net_id]['error_msg'] = str(e)
nets_unrefreshed.append(net_id)
#print "Nets refreshed: %s" % str(nets_refreshed)
error_msg=""
if len(vms_unrefreshed)+len(nets_unrefreshed)>0:
error_msg += "VMs unrefreshed: " + str(vms_unrefreshed) + "; nets unrefreshed: " + str(nets_unrefreshed)
print error_msg
#return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg, vms_refreshed, nets_refreshed
return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg
def action_tenant_vminstance(self, vm_id, action_dict):
'''Send and action over a VM instance from VIM
Returns the status'''
if self.debug:
print "osconnector: Action over VM instance from VIM " + vm_id
try:
self._reload_connection()
server = self.nova.servers.find(id=vm_id)
if "start" in action_dict:
if action_dict["start"]=="rebuild":
server.rebuild()
else:
if server.status=="PAUSED":
server.unpause()
elif server.status=="SUSPENDED":
server.resume()
elif server.status=="SHUTOFF":
server.start()
elif "pause" in action_dict:
server.pause()
elif "resume" in action_dict:
server.resume()
elif "shutoff" in action_dict or "shutdown" in action_dict:
server.stop()
elif "forceOff" in action_dict:
server.stop() #TODO
elif "terminate" in action_dict:
server.delete()
elif "createImage" in action_dict:
server.create_image()
#"path":path_schema,
#"description":description_schema,
#"name":name_schema,
#"metadata":metadata_schema,
#"imageRef": id_schema,
#"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
elif "rebuild" in action_dict:
server.rebuild(server.image['id'])
elif "reboot" in action_dict:
server.reboot() #reboot_type='SOFT'
elif "console" in action_dict:
console_type = action_dict["console"]
if console_type == None or console_type == "novnc":
console_dict = server.get_vnc_console("novnc")
elif console_type == "xvpvnc":
console_dict = server.get_vnc_console(console_type)
elif console_type == "rdp-html5":
console_dict = server.get_rdp_console(console_type)
elif console_type == "spice-html5":
console_dict = server.get_spice_console(console_type)
else:
return -vimconn.HTTP_Bad_Request, "console type '%s' not allowed" % console_type
try:
console_url = console_dict["console"]["url"]
#parse console_url
protocol_index = console_url.find("//")
suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
if protocol_index < 0 or port_index<0 or suffix_index<0:
print "action_tenant_vminstance, console: response", str(console_dict)
return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
console_dict2={"protocol": console_url[0:protocol_index],
"server": console_url[protocol_index+2 : port_index],
"port": int(console_url[port_index+1 : suffix_index]),
"suffix": console_url[suffix_index+1:]
}
protocol_index += 2
return 1, console_dict2
except:
print "action_tenant_vminstance, console: response", str(console_dict)
return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
return 1, vm_id
except nvExceptions.NotFound as e:
error_value=-vimconn.HTTP_Not_Found
error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "action_tenant_vminstance " + error_text
return error_value, error_text
def get_hosts_info(self):
'''Get the information of deployed hosts
Returns the hosts content'''
if self.debug:
print "osconnector: Getting Host info from VIM"
try:
h_list=[]
self._reload_connection()
hypervisors = self.nova.hypervisors.list()
for hype in hypervisors:
h_list.append( hype.to_dict() )
return 1, {"hosts":h_list}
except nvExceptions.NotFound as e:
error_value=-vimconn.HTTP_Not_Found
error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_hosts_info " + error_text
return error_value, error_text
def get_hosts(self, vim_tenant):
'''Get the hosts and deployed instances
Returns the hosts content'''
r, hype_dict = self.get_hosts_info()
if r<0:
return r, hype_dict
hypervisors = hype_dict["hosts"]
try:
servers = self.nova.servers.list()
for hype in hypervisors:
for server in servers:
if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
if 'vm' in hype:
hype['vm'].append(server.id)
else:
hype['vm'] = [server.id]
return 1, hype_dict
except nvExceptions.NotFound as e:
error_value=-vimconn.HTTP_Not_Found
error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
#TODO insert exception vimconn.HTTP_Unauthorized
#if reaching here is because an exception
if self.debug:
print "get_hosts " + error_text
return error_value, error_text
def get_image_id_from_path(self, path):
'''Get the image id from image path in the VIM database'''
'''Returns:
0,"Image not found" if there are no images with that path
1,image-id if there is one image with that path
<0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.)
'''
try:
self._reload_connection()
images = self.nova.images.list()
for image in images:
if image.metadata.get("location")==path:
return 1, image.id
return 0, "image with location '%s' not found" % path
except (ksExceptions.ClientException, nvExceptions.ClientException) as e: #TODO remove
error_value=-vimconn.HTTP_Bad_Request
error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
if self.debug:
print "get_image_id_from_path " + error_text
#if reaching here is because an exception
return error_value, error_text