1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 osconnector implements all the methods to interact with openstack using the python-client.
27 __author__
="Alfonso Tierno, Gerardo Garcia"
28 __date__
="$22-jun-2014 11:19:29$"
34 from novaclient
import client
as nClient
, exceptions
as nvExceptions
35 import keystoneclient
.v2_0
.client
as ksClient
36 import keystoneclient
.exceptions
as ksExceptions
37 import glanceclient
.v2
.client
as glClient
38 import glanceclient
.client
as gl1Client
39 import glanceclient
.exc
as gl1Exceptions
40 from httplib
import HTTPException
41 from neutronclient
.neutron
import client
as neClient
42 from neutronclient
.common
import exceptions
as neExceptions
43 from requests
.exceptions
import ConnectionError
45 '''contain the openstack virtual machine status to openmano status'''
46 vmStatus2manoFormat
={'ACTIVE':'ACTIVE',
48 'SUSPENDED': 'SUSPENDED',
51 'ERROR':'ERROR','DELETED':'DELETED'
53 netStatus2manoFormat
={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
56 class vimconnector(vimconn
.vimconnector
):
57 def __init__(self
, uuid
, name
, tenant
, url
, url_admin
=None, user
=None, passwd
=None, debug
=True, config
={}):
58 '''using common constructor parameters. In this case
59 'url' is the keystone authorization url,
60 'url_admin' is not use
62 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant
, url
, url_admin
, user
, passwd
, debug
, config
)
67 raise TypeError, 'url param can not be NoneType'
68 self
.k_creds
['auth_url'] = url
69 self
.n_creds
['auth_url'] = url
71 self
.k_creds
['tenant_name'] = tenant
72 self
.n_creds
['project_id'] = tenant
74 self
.k_creds
['username'] = user
75 self
.n_creds
['username'] = user
77 self
.k_creds
['password'] = passwd
78 self
.n_creds
['api_key'] = passwd
79 self
.reload_client
= True
81 def __setitem__(self
,index
, value
):
82 '''Set individuals parameters
83 Throw TypeError, KeyError
86 self
.reload_client
=True
89 self
.k_creds
['tenant_name'] = value
90 self
.n_creds
['project_id'] = value
92 del self
.k_creds
['tenant_name']
93 del self
.n_creds
['project_id']
95 self
.reload_client
=True
98 self
.k_creds
['username'] = value
99 self
.n_creds
['username'] = value
101 del self
.k_creds
['username']
102 del self
.n_creds
['username']
103 elif index
=='passwd':
104 self
.reload_client
=True
107 self
.k_creds
['password'] = value
108 self
.n_creds
['api_key'] = value
110 del self
.k_creds
['password']
111 del self
.n_creds
['api_key']
113 self
.reload_client
=True
116 self
.k_creds
['auth_url'] = value
117 self
.n_creds
['auth_url'] = value
119 raise TypeError, 'url param can not be NoneType'
121 vimconn
.vimconnector
.__setitem
__(self
,index
, value
)
123 def _reload_connection(self
):
124 '''Called before any operation, it check if credentials has changed
125 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
127 #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
128 if self
.reload_client
:
130 if len(self
.n_creds
) <4:
131 raise ksExceptions
.ClientException("Not enough parameters to connect to openstack")
132 self
.nova
= nClient
.Client(2, **self
.n_creds
)
133 self
.keystone
= ksClient
.Client(**self
.k_creds
)
134 self
.glance_endpoint
= self
.keystone
.service_catalog
.url_for(service_type
='image', endpoint_type
='publicURL')
135 self
.glance
= glClient
.Client(self
.glance_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
) #TODO check k_creds vs n_creds
136 self
.ne_endpoint
=self
.keystone
.service_catalog
.url_for(service_type
='network', endpoint_type
='publicURL')
137 self
.neutron
= neClient
.Client('2.0', endpoint_url
=self
.ne_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
)
138 self
.reload_client
= False
140 def new_external_port(self
, port_data
):
141 #TODO openstack if needed
142 '''Adds a external port to VIM'''
143 '''Returns the port identifier'''
144 return -vimconn
.HTTP_Internal_Server_Error
, "osconnector.new_external_port() not implemented"
146 def connect_port_network(self
, port_id
, network_id
, admin
=False):
147 #TODO openstack if needed
148 '''Connects a external port to a network'''
149 '''Returns status code of the VIM response'''
150 return -vimconn
.HTTP_Internal_Server_Error
, "osconnector.connect_port_network() not implemented"
152 def new_user(self
, user_name
, user_passwd
, tenant_id
=None):
153 '''Adds a new user to openstack VIM'''
154 '''Returns the user identifier'''
156 print "osconnector: Adding a new user to VIM"
158 self
._reload
_connection
()
159 user
=self
.keystone
.users
.create(user_name
, user_passwd
, tenant_id
=tenant_id
)
160 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
162 except ksExceptions
.ConnectionError
as e
:
163 error_value
=-vimconn
.HTTP_Bad_Request
164 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
165 except ksExceptions
.ClientException
as e
: #TODO remove
166 error_value
=-vimconn
.HTTP_Bad_Request
167 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
168 #TODO insert exception vimconn.HTTP_Unauthorized
169 #if reaching here is because an exception
171 print "new_tenant " + error_text
172 return error_value
, error_text
174 def delete_user(self
, user_id
):
175 '''Delete a user from openstack VIM'''
176 '''Returns the user identifier'''
178 print "osconnector: Deleting a user from VIM"
180 self
._reload
_connection
()
181 self
.keystone
.users
.delete(user_id
)
183 except ksExceptions
.ConnectionError
as e
:
184 error_value
=-vimconn
.HTTP_Bad_Request
185 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
186 except ksExceptions
.NotFound
as e
:
187 error_value
=-vimconn
.HTTP_Not_Found
188 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
189 except ksExceptions
.ClientException
as e
: #TODO remove
190 error_value
=-vimconn
.HTTP_Bad_Request
191 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
192 #TODO insert exception vimconn.HTTP_Unauthorized
193 #if reaching here is because an exception
195 print "delete_tenant " + error_text
196 return error_value
, error_text
198 def new_tenant(self
,tenant_name
,tenant_description
):
199 '''Adds a new tenant to openstack VIM'''
200 '''Returns the tenant identifier'''
202 print "osconnector: Adding a new tenant to VIM"
204 self
._reload
_connection
()
205 tenant
=self
.keystone
.tenants
.create(tenant_name
, tenant_description
)
206 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
208 except ksExceptions
.ConnectionError
as e
:
209 error_value
=-vimconn
.HTTP_Bad_Request
210 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
211 except ksExceptions
.ClientException
as e
: #TODO remove
212 error_value
=-vimconn
.HTTP_Bad_Request
213 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
214 #TODO insert exception vimconn.HTTP_Unauthorized
215 #if reaching here is because an exception
217 print "new_tenant " + error_text
218 return error_value
, error_text
220 def delete_tenant(self
,tenant_id
):
221 '''Delete a tenant from openstack VIM'''
222 '''Returns the tenant identifier'''
224 print "osconnector: Deleting a tenant from VIM"
226 self
._reload
_connection
()
227 self
.keystone
.tenants
.delete(tenant_id
)
228 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
230 except ksExceptions
.ConnectionError
as e
:
231 error_value
=-vimconn
.HTTP_Bad_Request
232 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
233 except ksExceptions
.ClientException
as e
: #TODO remove
234 error_value
=-vimconn
.HTTP_Bad_Request
235 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
236 #TODO insert exception vimconn.HTTP_Unauthorized
237 #if reaching here is because an exception
239 print "delete_tenant " + error_text
240 return error_value
, error_text
242 def __net_os2mano(self
, net_list_dict
):
243 '''Transform the net openstack format to mano format
244 net_list_dict can be a list of dict or a single dict'''
245 if type(net_list_dict
) is dict:
246 net_list_
=(net_list_dict
,)
247 elif type(net_list_dict
) is list:
248 net_list_
=net_list_dict
250 raise TypeError("param net_list_dict must be a list or a dictionary")
251 for net
in net_list_
:
252 if net
.get('provider:network_type') == "vlan":
257 def new_tenant_network(self
,net_name
,net_type
,public
=False,cidr
=None,vlan
=None):
258 '''Adds a tenant network to VIM'''
259 '''Returns the network identifier'''
261 print "osconnector: Adding a new tenant network to VIM (tenant: " + self
.tenant
+ ", type: " + net_type
+ "): "+ net_name
263 self
._reload
_connection
()
264 network_dict
= {'name': net_name
, 'admin_state_up': True}
265 if net_type
=="data" or net_type
=="ptp":
266 if self
.config
.get('dataplane_physical_net') == None:
267 return -vimconn
.HTTP_Bad_Request
, "You must provide a 'dataplane_physical_net' at config value before creating sriov network "
269 network_dict
["provider:physical_network"] = self
.config
['dataplane_physical_net'] #"physnet_sriov" #TODO physical
270 network_dict
["provider:network_type"] = "vlan"
272 network_dict
["provider:network_type"] = vlan
273 network_dict
["shared"]=public
274 new_net
=self
.neutron
.create_network({'network':network_dict
})
276 #create fake subnetwork
278 cidr
="192.168.111.0/24"
279 subnet
={"name":net_name
+"-subnet",
280 "network_id": new_net
["network"]["id"],
284 self
.neutron
.create_subnet({"subnet": subnet
} )
285 return 1, new_net
["network"]["id"]
286 except neExceptions
.ConnectionFailed
as e
:
287 error_value
=-vimconn
.HTTP_Bad_Request
288 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
289 except (ksExceptions
.ClientException
, neExceptions
.NeutronException
) as e
:
290 error_value
=-vimconn
.HTTP_Bad_Request
291 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
292 #TODO insert exception vimconn.HTTP_Unauthorized
293 #if reaching here is because an exception
295 print "new_tenant_network " + error_text
296 return error_value
, error_text
298 def get_network_list(self
, filter_dict
={}):
299 '''Obtain tenant networks of VIM
305 admin_state_up: boolean
307 Returns the network list of dictionaries
310 print "osconnector.get_network_list(): Getting network from VIM (filter: " + str(filter_dict
) + "): "
312 self
._reload
_connection
()
313 net_dict
=self
.neutron
.list_networks(**filter_dict
)
314 net_list
=net_dict
["networks"]
315 self
.__net
_os
2mano
(net_list
)
317 except neClient
.exceptions
.ConnectionFailed
as e
:
318 error_value
=-vimconn
.HTTP_Bad_Request
319 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
320 except (ksExceptions
.ClientException
, neExceptions
.NeutronException
) as e
:
321 error_value
=-vimconn
.HTTP_Bad_Request
322 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
323 #TODO insert exception vimconn.HTTP_Unauthorized
324 #if reaching here is because an exception
326 print "get_network_list " + error_text
327 return error_value
, error_text
329 def get_tenant_network(self
, net_id
, tenant_id
=None):
330 '''Obtain tenant networks of VIM'''
331 '''Returns the network information from a network id'''
333 print "osconnector.get_tenant_network(): Getting tenant network %s from VIM" % net_id
334 filter_dict
={"id": net_id
}
336 filter_dict
["tenant_id"] = tenant_id
337 r
, net_list
= self
.get_network_list(filter_dict
)
341 return -vimconn
.HTTP_Not_Found
, "Network '%s' not found" % net_id
342 elif len(net_list
)>1:
343 return -vimconn
.HTTP_Conflict
, "Found more than one network with this criteria"
346 for subnet_id
in net
.get("subnets", () ):
348 subnet
= self
.neutron
.show_subnet(subnet_id
)
349 except Exception as e
:
350 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
351 print "osconnector.get_tenant_network(): Error getting subnet %s %s" % (net_id
, error_text
)
352 subnet
= {"id": subnet_id
, "fault": error_text
}
353 subnets
.append(subnet
)
354 net
["subnets"] = subnets
358 def delete_tenant_network(self
, net_id
):
359 '''Deletes a tenant network from VIM'''
360 '''Returns the network identifier'''
362 print "osconnector: Deleting a new tenant network from VIM tenant: " + self
.tenant
+ ", id: " + net_id
364 self
._reload
_connection
()
365 #delete VM ports attached to this networks before the network
366 ports
= self
.neutron
.list_ports(network_id
=net_id
)
367 for p
in ports
['ports']:
369 self
.neutron
.delete_port(p
["id"])
370 except Exception as e
:
371 print "Error deleting port: " + type(e
).__name
__ + ": "+ str(e
)
372 self
.neutron
.delete_network(net_id
)
374 except neClient
.exceptions
.ConnectionFailed
as e
:
375 error_value
=-vimconn
.HTTP_Bad_Request
376 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
377 except neExceptions
.NetworkNotFoundClient
as e
:
378 error_value
=-vimconn
.HTTP_Not_Found
379 error_text
= type(e
).__name
__ + ": "+ str(e
.args
[0])
380 except (ksExceptions
.ClientException
, neExceptions
.NeutronException
) as e
:
381 error_value
=-vimconn
.HTTP_Bad_Request
382 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
383 #TODO insert exception vimconn.HTTP_Unauthorized
384 #if reaching here is because an exception
386 print "delete_tenant_network " + error_text
387 return error_value
, error_text
389 def get_tenant_flavor(self
, flavor_id
):
390 '''Obtain flavor details from the VIM
391 Returns the flavor dict details
393 print "VIMConnector: Getting flavor from VIM"
395 self
._reload
_connection
()
396 flavor
= self
.nova
.flavors
.find(id=flavor_id
)
397 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
398 return 1, {"flavor": flavor
.to_dict()}
399 except nvExceptions
.NotFound
as e
:
400 error_value
=-vimconn
.HTTP_Not_Found
401 error_text
= "flavor instance %s not found" % flavor_id
402 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
403 error_value
=-vimconn
.HTTP_Bad_Request
404 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
405 #TODO insert exception vimconn.HTTP_Unauthorized
406 #if reaching here is because an exception
408 print "get_tenant_flavor " + error_text
409 return error_value
, error_text
411 def new_tenant_flavor(self
, flavor_dict
, change_name_if_used
=True):
412 '''Adds a tenant flavor to openstack VIM
413 if change_name_if_used is True, it will change name in case of conflict
414 Returns the flavor identifier
418 name
=flavor_dict
['name']
422 self
._reload
_connection
()
423 if change_name_if_used
:
426 fl
=self
.nova
.flavors
.list()
428 fl_names
.append(f
.name
)
429 while name
in fl_names
:
431 name
= flavor_dict
['name']+"-" + str(name_suffix
)
433 ram
= flavor_dict
.get('ram',64)
434 vcpus
= flavor_dict
.get('vcpus',1)
437 extended
=flavor_dict
.get("extended")
439 numas
=extended
.get("numas")
441 numa_nodes
= len(numas
)
443 return -1, "Can not add flavor with more than one numa"
444 numa_properties
= {"hw:numa_nodes":str(numa_nodes
)}
445 numa_properties
["hw:mem_page_size"] = "large"
446 numa_properties
["hw:cpu_policy"] = "dedicated"
447 numa_properties
["hw:numa_mempolicy"] = "strict"
449 #overwrite ram and vcpus
450 ram
= numa
['memory']*1024
451 if 'paired-threads' in numa
:
452 vcpus
= numa
['paired-threads']*2
453 numa_properties
["hw:cpu_threads_policy"] = "prefer"
454 elif 'cores' in numa
:
455 vcpus
= numa
['cores']
456 #numa_properties["hw:cpu_threads_policy"] = "prefer"
457 elif 'threads' in numa
:
458 vcpus
= numa
['threads']
459 numa_properties
["hw:cpu_policy"] = "isolated"
460 for interface
in numa
.get("interfaces",() ):
461 if interface
["dedicated"]=="yes":
462 error_value
=-vimconn
.HTTP_Bad_Request
463 error_text
= "Passthrough interfaces are not supported for the openstack connector"
465 #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
468 new_flavor
=self
.nova
.flavors
.create(name
,
471 flavor_dict
.get('disk',1),
472 is_public
=flavor_dict
.get('is_public', True)
476 new_flavor
.set_keys(numa_properties
)
477 return 1, new_flavor
.id
478 except nvExceptions
.Conflict
as e
:
479 error_value
=-vimconn
.HTTP_Conflict
481 if change_name_if_used
:
484 #except nvExceptions.BadRequest as e:
485 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, ConnectionError
) as e
:
486 error_value
=-vimconn
.HTTP_Bad_Request
487 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
489 #TODO insert exception vimconn.HTTP_Unauthorized
490 #if reaching here is because an exception
492 print "new_tenant_flavor " + error_text
493 return error_value
, error_text
495 def delete_tenant_flavor(self
,flavor_id
):
496 '''Deletes a tenant flavor from openstack VIM
497 Returns >0,id if ok; or <0,error_text if error
503 self
._reload
_connection
()
504 self
.nova
.flavors
.delete(flavor_id
)
506 except nvExceptions
.NotFound
as e
:
507 error_value
= -vimconn
.HTTP_Not_Found
508 error_text
= "flavor '%s' not found" % flavor_id
510 #except nvExceptions.BadRequest as e:
511 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
512 error_value
=-vimconn
.HTTP_Bad_Request
513 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
516 print "delete_tenant_flavor " + error_text
517 #if reaching here is because an exception
518 return error_value
, error_text
520 def new_tenant_image(self
,image_dict
):
522 Adds a tenant image to VIM
523 if change_name_if_used is True, it will change name in case of conflict
525 >1, image-id if the image is created
526 <0, message if there is an error
529 #using version 1 of glance client
530 glancev1
= gl1Client
.Client('1',self
.glance_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
) #TODO check k_creds vs n_creds
534 self
._reload
_connection
()
535 #determine format http://docs.openstack.org/developer/glance/formats.html
536 if "disk_format" in image_dict
:
537 disk_format
=image_dict
["disk_format"]
538 else: #autodiscover base on extention
539 if image_dict
['location'][-6:]==".qcow2":
541 elif image_dict
['location'][-4:]==".vhd":
543 elif image_dict
['location'][-5:]==".vmdk":
545 elif image_dict
['location'][-4:]==".vdi":
547 elif image_dict
['location'][-4:]==".iso":
549 elif image_dict
['location'][-4:]==".aki":
551 elif image_dict
['location'][-4:]==".ari":
553 elif image_dict
['location'][-4:]==".ami":
557 print "new_tenant_image: '%s' loading from '%s'" % (image_dict
['name'], image_dict
['location'])
558 if image_dict
['location'][0:4]=="http":
559 new_image
= glancev1
.images
.create(name
=image_dict
['name'], is_public
=image_dict
.get('public',"yes")=="yes",
560 container_format
="bare", location
=image_dict
['location'], disk_format
=disk_format
)
562 with
open(image_dict
['location']) as fimage
:
563 new_image
= glancev1
.images
.create(name
=image_dict
['name'], is_public
=image_dict
.get('public',"yes")=="yes",
564 container_format
="bare", data
=fimage
, disk_format
=disk_format
)
565 #insert metadata. We cannot use 'new_image.properties.setdefault'
566 #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
567 new_image_nova
=self
.nova
.images
.find(id=new_image
.id)
568 new_image_nova
.metadata
.setdefault('location',image_dict
['location'])
569 metadata_to_load
= image_dict
.get('metadata')
571 for k
,v
in yaml
.load(metadata_to_load
).iteritems():
572 new_image_nova
.metadata
.setdefault(k
,v
)
573 return 1, new_image
.id
574 except nvExceptions
.Conflict
as e
:
575 error_value
=-vimconn
.HTTP_Conflict
576 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
578 except (HTTPException
, gl1Exceptions
.HTTPException
, gl1Exceptions
.CommunicationError
) as e
:
579 error_value
=-vimconn
.HTTP_Bad_Request
580 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
582 except IOError as e
: #can not open the file
583 error_value
=-vimconn
.HTTP_Bad_Request
584 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0])) + " for " + image_dict
['location']
586 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
587 error_value
=-vimconn
.HTTP_Bad_Request
588 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
590 #TODO insert exception vimconn.HTTP_Unauthorized
591 #if reaching here is because an exception
593 print "new_tenant_image " + error_text
594 return error_value
, error_text
596 def delete_tenant_image(self
, image_id
):
597 '''Deletes a tenant image from openstack VIM
598 Returns >0,id if ok; or <0,error_text if error
604 self
._reload
_connection
()
605 self
.nova
.images
.delete(image_id
)
607 except nvExceptions
.NotFound
as e
:
608 error_value
= -vimconn
.HTTP_Not_Found
609 error_text
= "flavor '%s' not found" % image_id
611 #except nvExceptions.BadRequest as e:
612 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
: #TODO remove
613 error_value
=-vimconn
.HTTP_Bad_Request
614 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
617 print "delete_tenant_image " + error_text
618 #if reaching here is because an exception
619 return error_value
, error_text
621 def new_tenant_vminstance(self
,name
,description
,start
,image_id
,flavor_id
,net_list
):
622 '''Adds a VM instance to VIM
624 start: indicates if VM must start or boot in pause mode. Ignored
625 image_id,flavor_id: iamge and flavor uuid
626 net_list: list of interfaces, each one is a dictionary with:
628 net_id: network uuid to connect
629 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
630 model: interface model, ignored #TODO
631 mac_address: used for SR-IOV ifaces #TODO for other types
632 use: 'data', 'bridge', 'mgmt'
633 type: 'virtual', 'PF', 'VF', 'VFnotShared'
634 vim_id: filled/added by this function
635 #TODO ip, security groups
636 Returns >=0, the instance identifier
640 print "osconnector: Creating VM into VIM"
641 print " image %s flavor %s nics=%s" %(image_id
, flavor_id
,net_list
)
645 self
._reload
_connection
()
648 if not net
.get("net_id"): #skip non connected iface
650 if net
["type"]=="virtual":
651 net_list_vim
.append({'net-id': net
["net_id"]})
653 metadata_vpci
[ net
["net_id"] ] = [[ net
["vpci"], "" ]]
654 elif net
["type"]=="PF":
655 print "new_tenant_vminstance: Warning, can not connect a passthrough interface "
656 #TODO insert this when openstack consider passthrough ports as openstack neutron ports
659 if "VF" not in metadata_vpci
:
660 metadata_vpci
["VF"]=[]
661 metadata_vpci
["VF"].append([ net
["vpci"], "" ])
663 "network_id": net
["net_id"],
664 "name": net
.get("name"),
665 "binding:vnic_type": "direct",
666 "admin_state_up": True
668 if not port_dict
["name"]:
669 port_dict
["name"] = name
670 if net
.get("mac_address"):
671 port_dict
["mac_address"]=net
["mac_address"]
672 #TODO: manage having SRIOV without vlan tag
673 #if net["type"] == "VFnotShared"
674 # port_dict["vlan"]=0
675 new_port
= self
.neutron
.create_port({"port": port_dict
})
676 net
["mac_adress"] = new_port
["port"]["mac_address"]
677 net
["vim_id"] = new_port
["port"]["id"]
678 net
["ip"] = new_port
["port"].get("fixed_ips",[{}])[0].get("ip_address")
679 net_list_vim
.append({"port-id": new_port
["port"]["id"]})
681 metadata
= {"pci_assignement": json
.dumps(metadata_vpci
)}
683 print "name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s"\
684 % (name
, image_id
, flavor_id
, str(net_list_vim
), description
, str(metadata
))
686 security_groups
= self
.config
.get('security_groups')
687 if type(security_groups
) is str:
688 security_groups
= ( security_groups
, )
689 server
= self
.nova
.servers
.create(name
, image_id
, flavor_id
, nics
=net_list_vim
, meta
=metadata
,
690 security_groups
= security_groups
,
691 availability_zone
= self
.config
.get('availability_zone'),
692 key_name
= self
.config
.get('keypair'),
693 ) #, description=description)
696 print "DONE :-)", server
698 # #TODO server.add_floating_ip("10.95.87.209")
699 # #To look for a free floating_ip
700 # free_floating_ip = None
701 # for floating_ip in self.neutron.list_floatingips().get("floatingips", () ):
702 # if not floating_ip["port_id"]:
703 # free_floating_ip = floating_ip["floating_ip_address"]
705 # if free_floating_ip:
706 # server.add_floating_ip(free_floating_ip)
710 # except nvExceptions.NotFound as e:
711 # error_value=-vimconn.HTTP_Not_Found
712 # error_text= "vm instance %s not found" % vm_id
713 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, ConnectionError
, TypeError) as e
:
714 error_value
=-vimconn
.HTTP_Bad_Request
715 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
716 except neClient
.exceptions
.ConnectionFailed
as e
:
717 error_value
=-vimconn
.HTTP_Bad_Request
718 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
719 #TODO insert exception vimconn.HTTP_Unauthorized
720 #if reaching here is because an exception
722 print "new_tenant_vminstance Exception",e
, error_text
723 return error_value
, error_text
725 def get_tenant_vminstance(self
,vm_id
):
726 '''Returns the VM instance information from VIM'''
728 print "osconnector: Getting VM from VIM"
730 self
._reload
_connection
()
731 server
= self
.nova
.servers
.find(id=vm_id
)
732 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
733 return 1, {"server": server
.to_dict()}
734 except nvExceptions
.NotFound
as e
:
735 error_value
=-vimconn
.HTTP_Not_Found
736 error_text
= "vm instance %s not found" % vm_id
737 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
738 error_value
=-vimconn
.HTTP_Bad_Request
739 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
740 #TODO insert exception vimconn.HTTP_Unauthorized
741 #if reaching here is because an exception
743 print "get_tenant_vminstance " + error_text
744 return error_value
, error_text
746 def get_tenant_vminstance_console(self
,vm_id
, console_type
="vnc"):
748 Get a console for the virtual machine
750 vm_id: uuid of the VM
751 console_type, can be:
752 "novnc" (by default), "xvpvnc" for VNC types,
753 "rdp-html5" for RDP types, "spice-html5" for SPICE types
754 Returns <0, text on error, for example not available
755 1, URL/text with the console parameters
758 print "osconnector: Getting VM CONSOLE from VIM"
760 self
._reload
_connection
()
761 server
= self
.nova
.servers
.find(id=vm_id
)
762 if console_type
== None or console_type
== "novnc":
763 console_dict
= server
.get_vnc_console("novnc")
764 elif console_type
== "xvpvnc":
765 console_dict
= server
.get_vnc_console(console_type
)
766 elif console_type
== "rdp-html5":
767 console_dict
= server
.get_rdp_console(console_type
)
768 elif console_type
== "spice-html5":
769 console_dict
= server
.get_spice_console(console_type
)
771 return -vimconn
.HTTP_Bad_Request
, "console type '%s' not allowed" % console_type
773 console_dict1
= console_dict
.get("console")
775 console_url
= console_dict1
.get("url")
778 protocol_index
= console_url
.find("//")
779 suffix_index
= console_url
[protocol_index
+2:].find("/") + protocol_index
+2
780 port_index
= console_url
[protocol_index
+2:suffix_index
].find(":") + protocol_index
+2
781 if protocol_index
< 0 or port_index
<0 or suffix_index
<0:
782 return -vimconn
.HTTP_Internal_Server_Error
, "Unexpected response from VIM"
783 console_dict
={"protocol": console_url
[0:protocol_index
],
784 "server": console_url
[protocol_index
+2:port_index
],
785 "port": console_url
[port_index
:suffix_index
],
786 "suffix": console_url
[suffix_index
+1:]
789 return 1, console_dict
790 return -vimconn
.HTTP_Internal_Server_Error
, "Unexpected response from VIM"
792 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
793 return 1, {"server": server
.to_dict()}
794 except nvExceptions
.NotFound
as e
:
795 error_value
=-vimconn
.HTTP_Not_Found
796 error_text
= "vm instance %s not found" % vm_id
797 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, nvExceptions
.BadRequest
) as e
:
798 error_value
=-vimconn
.HTTP_Bad_Request
799 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
800 #TODO insert exception vimconn.HTTP_Unauthorized
801 #if reaching here is because an exception
803 print "get_tenant_vminstance " + error_text
804 return error_value
, error_text
807 def delete_tenant_vminstance(self
, vm_id
):
808 '''Removes a VM instance from VIM
809 Returns >0, the instance identifier
813 print "osconnector: Getting VM from VIM"
815 self
._reload
_connection
()
816 #delete VM ports attached to this networks before the virtual machine
817 ports
= self
.neutron
.list_ports(device_id
=vm_id
)
818 for p
in ports
['ports']:
820 self
.neutron
.delete_port(p
["id"])
821 except Exception as e
:
822 print "Error deleting port: " + type(e
).__name
__ + ": "+ str(e
)
823 self
.nova
.servers
.delete(vm_id
)
825 except nvExceptions
.NotFound
as e
:
826 error_value
=-vimconn
.HTTP_Not_Found
827 error_text
= (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
828 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
829 error_value
=-vimconn
.HTTP_Bad_Request
830 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
831 #TODO insert exception vimconn.HTTP_Unauthorized
832 #if reaching here is because an exception
834 print "get_tenant_vminstance " + error_text
835 return error_value
, error_text
837 def refresh_tenant_vms_and_nets(self
, vmDict
, netDict
):
838 '''Refreshes the status of the dictionaries of VM instances and nets passed as arguments. It modifies the dictionaries
840 - result: 0 if all elements could be refreshed (even if its status didn't change)
841 n>0, the number of elements that couldn't be refreshed,
842 <0 if error (foreseen)
843 - error_msg: text with reference to possible errors
848 nets_unrefreshed
= []
850 print "osconnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM"
852 vmDict
[vm_id
] = {'error_msg':None, 'vim_info':None}
853 r
,c
= self
.get_tenant_vminstance(vm_id
)
855 print "osconnector refresh_tenant_vm. Error getting vm_id '%s' status: %s" % (vm_id
, c
)
856 if r
==-vimconn
.HTTP_Not_Found
:
857 vmDict
[vm_id
]['status'] = "DELETED"
859 vmDict
[vm_id
]['status'] = "VIM_ERROR"
860 vmDict
[vm_id
]['error_msg'] = c
861 vms_unrefreshed
.append(vm_id
)
864 vmDict
[vm_id
]['status'] = vmStatus2manoFormat
[ c
['server']['status'] ]
865 vmDict
[vm_id
]['vim_info'] = yaml
.safe_dump(c
['server'])
866 vmDict
[vm_id
]["interfaces"] = []
867 if c
['server'].get('fault'):
868 vmDict
[vm_id
]['error_msg'] = str(c
['server']['fault'])
871 self
._reload
_connection
()
872 port_dict
=self
.neutron
.list_ports(device_id
=vm_id
)
873 for port
in port_dict
["ports"]:
875 interface
['vim_info'] = yaml
.safe_dump(port
)
876 interface
["mac_address"] = port
.get("mac_address")
877 interface
["vim_net_id"] = port
["network_id"]
878 interface
["vim_interface_id"] = port
["id"]
880 #look for floating ip address
881 floating_ip_dict
= self
.neutron
.list_floatingips(port_id
=port
["id"])
882 if floating_ip_dict
.get("floatingips"):
883 ips
.append(floating_ip_dict
["floatingips"][0].get("floating_ip_address") )
885 for subnet
in port
["fixed_ips"]:
886 ips
.append(subnet
["ip_address"])
887 interface
["ip_address"] = ";".join(ips
)
888 vmDict
[vm_id
]["interfaces"].append(interface
)
889 except Exception as e
:
890 print type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
893 #error message at server.fault["message"]
894 except KeyError as e
:
895 print "osconnector refresh_tenant_elements KeyError %s getting vm_id '%s' status %s" % (str(e
), vm_id
, c
['server']['status'])
896 vmDict
[vm_id
]['status'] = "VIM_ERROR"
897 vmDict
[vm_id
]['error_msg'] = str(e
)
898 vms_unrefreshed
.append(vm_id
)
900 #print "VMs refreshed: %s" % str(vms_refreshed)
901 for net_id
in netDict
:
902 netDict
[net_id
]={'error_msg':None, 'vim_info':None}
903 r
,c
= self
.get_tenant_network(net_id
)
905 print "osconnector refresh_tenant_network. Error getting net_id '%s' status: %s" % (net_id
, c
)
906 if r
==-vimconn
.HTTP_Not_Found
:
907 netDict
[net_id
]['status'] = "DELETED" #TODO check exit status
909 netDict
[vm_id
]['status'] = "VIM_ERROR"
910 netDict
[vm_id
]['error_msg'] = c
911 nets_unrefreshed
.append(net_id
)
914 netDict
[net_id
]['status'] = netStatus2manoFormat
[ c
['status'] ]
915 if c
['status'] == "ACIVE" and not c
['admin_state_up']:
916 netDict
[net_id
]['status'] = 'DOWN'
917 netDict
[net_id
]['vim_info'] = yaml
.safe_dump(c
)
918 if c
.get('fault'): #TODO
919 netDict
[net_id
]['error_msg'] = str(c
['fault'])
920 except KeyError as e
:
921 print "osconnector refresh_tenant_elements KeyError %s getting vm_id '%s' status %s" % (str(e
), vm_id
, c
['network']['status'])
922 netDict
[net_id
]['status'] = "VIM_ERROR"
923 netDict
[net_id
]['error_msg'] = str(e
)
924 nets_unrefreshed
.append(net_id
)
926 #print "Nets refreshed: %s" % str(nets_refreshed)
929 if len(vms_unrefreshed
)+len(nets_unrefreshed
)>0:
930 error_msg
+= "VMs unrefreshed: " + str(vms_unrefreshed
) + "; nets unrefreshed: " + str(nets_unrefreshed
)
933 #return len(vms_unrefreshed)+len(nets_unrefreshed), error_msg, vms_refreshed, nets_refreshed
934 return len(vms_unrefreshed
)+len(nets_unrefreshed
), error_msg
936 def action_tenant_vminstance(self
, vm_id
, action_dict
):
937 '''Send and action over a VM instance from VIM
938 Returns the status'''
940 print "osconnector: Action over VM instance from VIM " + vm_id
942 self
._reload
_connection
()
943 server
= self
.nova
.servers
.find(id=vm_id
)
944 if "start" in action_dict
:
945 if action_dict
["start"]=="rebuild":
948 if server
.status
=="PAUSED":
950 elif server
.status
=="SUSPENDED":
952 elif server
.status
=="SHUTOFF":
954 elif "pause" in action_dict
:
956 elif "resume" in action_dict
:
958 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
960 elif "forceOff" in action_dict
:
962 elif "terminate" in action_dict
:
964 elif "createImage" in action_dict
:
965 server
.create_image()
967 #"description":description_schema,
969 #"metadata":metadata_schema,
970 #"imageRef": id_schema,
971 #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
972 elif "rebuild" in action_dict
:
973 server
.rebuild(server
.image
['id'])
974 elif "reboot" in action_dict
:
975 server
.reboot() #reboot_type='SOFT'
976 elif "console" in action_dict
:
977 console_type
= action_dict
["console"]
978 if console_type
== None or console_type
== "novnc":
979 console_dict
= server
.get_vnc_console("novnc")
980 elif console_type
== "xvpvnc":
981 console_dict
= server
.get_vnc_console(console_type
)
982 elif console_type
== "rdp-html5":
983 console_dict
= server
.get_rdp_console(console_type
)
984 elif console_type
== "spice-html5":
985 console_dict
= server
.get_spice_console(console_type
)
987 return -vimconn
.HTTP_Bad_Request
, "console type '%s' not allowed" % console_type
990 console_url
= console_dict
["console"]["url"]
992 protocol_index
= console_url
.find("//")
993 suffix_index
= console_url
[protocol_index
+2:].find("/") + protocol_index
+2
994 port_index
= console_url
[protocol_index
+2:suffix_index
].find(":") + protocol_index
+2
995 if protocol_index
< 0 or port_index
<0 or suffix_index
<0:
996 print "action_tenant_vminstance, console: response", str(console_dict
)
997 return -vimconn
.HTTP_Internal_Server_Error
, "Unexpected response from VIM"
998 console_dict2
={"protocol": console_url
[0:protocol_index
],
999 "server": console_url
[protocol_index
+2 : port_index
],
1000 "port": int(console_url
[port_index
+1 : suffix_index
]),
1001 "suffix": console_url
[suffix_index
+1:]
1004 return 1, console_dict2
1006 print "action_tenant_vminstance, console: response", str(console_dict
)
1007 return -vimconn
.HTTP_Internal_Server_Error
, "Unexpected response from VIM"
1010 except nvExceptions
.NotFound
as e
:
1011 error_value
=-vimconn
.HTTP_Not_Found
1012 error_text
= (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1013 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
1014 error_value
=-vimconn
.HTTP_Bad_Request
1015 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1016 #TODO insert exception vimconn.HTTP_Unauthorized
1017 #if reaching here is because an exception
1019 print "action_tenant_vminstance " + error_text
1020 return error_value
, error_text
1022 def get_hosts_info(self
):
1023 '''Get the information of deployed hosts
1024 Returns the hosts content'''
1026 print "osconnector: Getting Host info from VIM"
1029 self
._reload
_connection
()
1030 hypervisors
= self
.nova
.hypervisors
.list()
1031 for hype
in hypervisors
:
1032 h_list
.append( hype
.to_dict() )
1033 return 1, {"hosts":h_list
}
1034 except nvExceptions
.NotFound
as e
:
1035 error_value
=-vimconn
.HTTP_Not_Found
1036 error_text
= (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1037 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
1038 error_value
=-vimconn
.HTTP_Bad_Request
1039 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1040 #TODO insert exception vimconn.HTTP_Unauthorized
1041 #if reaching here is because an exception
1043 print "get_hosts_info " + error_text
1044 return error_value
, error_text
1046 def get_hosts(self
, vim_tenant
):
1047 '''Get the hosts and deployed instances
1048 Returns the hosts content'''
1049 r
, hype_dict
= self
.get_hosts_info()
1052 hypervisors
= hype_dict
["hosts"]
1054 servers
= self
.nova
.servers
.list()
1055 for hype
in hypervisors
:
1056 for server
in servers
:
1057 if server
.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype
['hypervisor_hostname']:
1059 hype
['vm'].append(server
.id)
1061 hype
['vm'] = [server
.id]
1063 except nvExceptions
.NotFound
as e
:
1064 error_value
=-vimconn
.HTTP_Not_Found
1065 error_text
= (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1066 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
1067 error_value
=-vimconn
.HTTP_Bad_Request
1068 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1069 #TODO insert exception vimconn.HTTP_Unauthorized
1070 #if reaching here is because an exception
1072 print "get_hosts " + error_text
1073 return error_value
, error_text
1075 def get_image_id_from_path(self
, path
):
1076 '''Get the image id from image path in the VIM database'''
1078 0,"Image not found" if there are no images with that path
1079 1,image-id if there is one image with that path
1080 <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.)
1083 self
._reload
_connection
()
1084 images
= self
.nova
.images
.list()
1085 for image
in images
:
1086 if image
.metadata
.get("location")==path
:
1088 return 0, "image with location '%s' not found" % path
1089 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
: #TODO remove
1090 error_value
=-vimconn
.HTTP_Bad_Request
1091 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1093 print "get_image_id_from_path " + error_text
1094 #if reaching here is because an exception
1095 return error_value
, error_text