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, Pablo Montes, xFlow Research"
28 __date__
="$22-jun-2014 11:19:29$"
37 from novaclient
import client
as nClient_v2
, exceptions
as nvExceptions
, api_versions
as APIVersion
38 import keystoneclient
.v2_0
.client
as ksClient_v2
39 from novaclient
.v2
.client
import Client
as nClient
40 import keystoneclient
.v3
.client
as ksClient
41 import keystoneclient
.exceptions
as ksExceptions
42 import glanceclient
.v2
.client
as glClient
43 import glanceclient
.client
as gl1Client
44 import glanceclient
.exc
as gl1Exceptions
45 import cinderclient
.v2
.client
as cClient_v2
46 from httplib
import HTTPException
47 from neutronclient
.neutron
import client
as neClient_v2
48 from neutronclient
.v2_0
import client
as neClient
49 from neutronclient
.common
import exceptions
as neExceptions
50 from requests
.exceptions
import ConnectionError
52 '''contain the openstack virtual machine status to openmano status'''
53 vmStatus2manoFormat
={'ACTIVE':'ACTIVE',
55 'SUSPENDED': 'SUSPENDED',
58 'ERROR':'ERROR','DELETED':'DELETED'
60 netStatus2manoFormat
={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
63 #global var to have a timeout creating and deleting volumes
67 class vimconnector(vimconn
.vimconnector
):
68 def __init__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
69 '''using common constructor parameters. In this case
70 'url' is the keystone authorization url,
71 'url_admin' is not use
73 self
.osc_api_version
= 'v2.0'
74 if config
.get('APIversion') == 'v3.3':
75 self
.osc_api_version
= 'v3.3'
76 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
, user
, passwd
, log_level
, config
)
81 raise TypeError, 'url param can not be NoneType'
82 self
.k_creds
['auth_url'] = url
83 self
.n_creds
['auth_url'] = url
85 self
.k_creds
['tenant_name'] = tenant_name
86 self
.n_creds
['project_id'] = tenant_name
88 self
.k_creds
['tenant_id'] = tenant_id
89 self
.n_creds
['tenant_id'] = tenant_id
91 self
.k_creds
['username'] = user
92 self
.n_creds
['username'] = user
94 self
.k_creds
['password'] = passwd
95 self
.n_creds
['api_key'] = passwd
96 if self
.osc_api_version
== 'v3.3':
97 self
.k_creds
['project_name'] = tenant_name
98 self
.k_creds
['project_id'] = tenant_id
99 if config
.get('region_name'):
100 self
.k_creds
['region_name'] = config
.get('region_name')
101 self
.n_creds
['region_name'] = config
.get('region_name')
103 self
.reload_client
= True
104 self
.logger
= logging
.getLogger('openmano.vim.openstack')
106 self
.logger
.setLevel( getattr(logging
, log_level
) )
108 def __setitem__(self
,index
, value
):
109 '''Set individuals parameters
110 Throw TypeError, KeyError
112 if index
=='tenant_id':
113 self
.reload_client
=True
114 self
.tenant_id
= value
115 if self
.osc_api_version
== 'v3.3':
117 self
.k_creds
['project_id'] = value
118 self
.n_creds
['project_id'] = value
120 del self
.k_creds
['project_id']
121 del self
.n_creds
['project_id']
124 self
.k_creds
['tenant_id'] = value
125 self
.n_creds
['tenant_id'] = value
127 del self
.k_creds
['tenant_id']
128 del self
.n_creds
['tenant_id']
129 elif index
=='tenant_name':
130 self
.reload_client
=True
131 self
.tenant_name
= value
132 if self
.osc_api_version
== 'v3.3':
134 self
.k_creds
['project_name'] = value
135 self
.n_creds
['project_name'] = value
137 del self
.k_creds
['project_name']
138 del self
.n_creds
['project_name']
141 self
.k_creds
['tenant_name'] = value
142 self
.n_creds
['project_id'] = value
144 del self
.k_creds
['tenant_name']
145 del self
.n_creds
['project_id']
147 self
.reload_client
=True
150 self
.k_creds
['username'] = value
151 self
.n_creds
['username'] = value
153 del self
.k_creds
['username']
154 del self
.n_creds
['username']
155 elif index
=='passwd':
156 self
.reload_client
=True
159 self
.k_creds
['password'] = value
160 self
.n_creds
['api_key'] = value
162 del self
.k_creds
['password']
163 del self
.n_creds
['api_key']
165 self
.reload_client
=True
168 self
.k_creds
['auth_url'] = value
169 self
.n_creds
['auth_url'] = value
171 raise TypeError, 'url param can not be NoneType'
173 vimconn
.vimconnector
.__setitem
__(self
,index
, value
)
175 def _reload_connection(self
):
176 '''Called before any operation, it check if credentials has changed
177 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
179 #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
180 if self
.reload_client
:
182 if len(self
.n_creds
) <4:
183 raise ksExceptions
.ClientException("Not enough parameters to connect to openstack")
184 if self
.osc_api_version
== 'v3.3':
185 self
.nova
= nClient(APIVersion(version_str
='2'), **self
.n_creds
)
186 #TODO To be updated for v3
187 #self.cinder = cClient.Client(**self.n_creds)
188 self
.keystone
= ksClient
.Client(**self
.k_creds
)
189 self
.ne_endpoint
=self
.keystone
.service_catalog
.url_for(service_type
='network', endpoint_type
='publicURL')
190 self
.neutron
= neClient
.Client(APIVersion(version_str
='2'), endpoint_url
=self
.ne_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
)
192 self
.nova
= nClient_v2
.Client('2', **self
.n_creds
)
193 self
.cinder
= cClient_v2
.Client(**self
.n_creds
)
194 self
.keystone
= ksClient_v2
.Client(**self
.k_creds
)
195 self
.ne_endpoint
=self
.keystone
.service_catalog
.url_for(service_type
='network', endpoint_type
='publicURL')
196 self
.neutron
= neClient_v2
.Client('2.0', endpoint_url
=self
.ne_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
)
197 self
.glance_endpoint
= self
.keystone
.service_catalog
.url_for(service_type
='image', endpoint_type
='publicURL')
198 self
.glance
= glClient
.Client(self
.glance_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
) #TODO check k_creds vs n_creds
199 self
.reload_client
= False
201 def __net_os2mano(self
, net_list_dict
):
202 '''Transform the net openstack format to mano format
203 net_list_dict can be a list of dict or a single dict'''
204 if type(net_list_dict
) is dict:
205 net_list_
=(net_list_dict
,)
206 elif type(net_list_dict
) is list:
207 net_list_
=net_list_dict
209 raise TypeError("param net_list_dict must be a list or a dictionary")
210 for net
in net_list_
:
211 if net
.get('provider:network_type') == "vlan":
218 def _format_exception(self
, exception
):
219 '''Transform a keystone, nova, neutron exception into a vimconn exception'''
220 if isinstance(exception
, (HTTPException
, gl1Exceptions
.HTTPException
, gl1Exceptions
.CommunicationError
,
221 ConnectionError
, ksExceptions
.ConnectionError
, neExceptions
.ConnectionFailed
223 raise vimconn
.vimconnConnectionException(type(exception
).__name
__ + ": " + str(exception
))
224 elif isinstance(exception
, (nvExceptions
.ClientException
, ksExceptions
.ClientException
,
225 neExceptions
.NeutronException
, nvExceptions
.BadRequest
)):
226 raise vimconn
.vimconnUnexpectedResponse(type(exception
).__name
__ + ": " + str(exception
))
227 elif isinstance(exception
, (neExceptions
.NetworkNotFoundClient
, nvExceptions
.NotFound
)):
228 raise vimconn
.vimconnNotFoundException(type(exception
).__name
__ + ": " + str(exception
))
229 elif isinstance(exception
, nvExceptions
.Conflict
):
230 raise vimconn
.vimconnConflictException(type(exception
).__name
__ + ": " + str(exception
))
232 raise vimconn
.vimconnConnectionException(type(exception
).__name
__ + ": " + str(exception
))
234 def get_tenant_list(self
, filter_dict
={}):
235 '''Obtain tenants of VIM
236 filter_dict can contain the following keys:
237 name: filter by tenant name
238 id: filter by tenant uuid/id
240 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
242 self
.logger
.debug("Getting tenants from VIM filter: '%s'", str(filter_dict
))
244 self
._reload
_connection
()
245 if self
.osc_api_version
== 'v3.3':
246 project_class_list
=self
.keystone
.projects
.findall(**filter_dict
)
248 project_class_list
=self
.keystone
.tenants
.findall(**filter_dict
)
250 for project
in project_class_list
:
251 project_list
.append(project
.to_dict())
253 except (ksExceptions
.ConnectionError
, ksExceptions
.ClientException
, ConnectionError
) as e
:
254 self
._format
_exception
(e
)
256 def new_tenant(self
, tenant_name
, tenant_description
):
257 '''Adds a new tenant to openstack VIM. Returns the tenant identifier'''
258 self
.logger
.debug("Adding a new tenant name: %s", tenant_name
)
260 self
._reload
_connection
()
261 if self
.osc_api_version
== 'v3.3':
262 project
=self
.keystone
.projects
.create(tenant_name
, tenant_description
)
264 project
=self
.keystone
.tenants
.create(tenant_name
, tenant_description
)
266 except (ksExceptions
.ConnectionError
, ksExceptions
.ClientException
, ConnectionError
) as e
:
267 self
._format
_exception
(e
)
269 def delete_tenant(self
, tenant_id
):
270 '''Delete a tenant from openstack VIM. Returns the old tenant identifier'''
271 self
.logger
.debug("Deleting tenant %s from VIM", tenant_id
)
273 self
._reload
_connection
()
274 if self
.osc_api_version
== 'v3.3':
275 self
.keystone
.projects
.delete(tenant_id
)
277 self
.keystone
.tenants
.delete(tenant_id
)
279 except (ksExceptions
.ConnectionError
, ksExceptions
.ClientException
, ConnectionError
) as e
:
280 self
._format
_exception
(e
)
282 def new_network(self
,net_name
, net_type
, ip_profile
=None, shared
=False, vlan
=None):
283 '''Adds a tenant network to VIM. Returns the network identifier'''
284 self
.logger
.debug("Adding a new network to VIM name '%s', type '%s'", net_name
, net_type
)
285 #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
288 self
._reload
_connection
()
289 network_dict
= {'name': net_name
, 'admin_state_up': True}
290 if net_type
=="data" or net_type
=="ptp":
291 if self
.config
.get('dataplane_physical_net') == None:
292 raise vimconn
.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
293 network_dict
["provider:physical_network"] = self
.config
['dataplane_physical_net'] #"physnet_sriov" #TODO physical
294 network_dict
["provider:network_type"] = "vlan"
296 network_dict
["provider:network_type"] = vlan
297 network_dict
["shared"]=shared
298 new_net
=self
.neutron
.create_network({'network':network_dict
})
300 #create subnetwork, even if there is no profile
303 if 'subnet_address' not in ip_profile
:
304 #Fake subnet is required
305 ip_profile
['subnet_address'] = "192.168.111.0/24"
306 if 'ip_version' not in ip_profile
:
307 ip_profile
['ip_version'] = "IPv4"
308 subnet
={"name":net_name
+"-subnet",
309 "network_id": new_net
["network"]["id"],
310 "ip_version": 4 if ip_profile
['ip_version']=="IPv4" else 6,
311 "cidr": ip_profile
['subnet_address']
313 if 'gateway_address' in ip_profile
:
314 subnet
['gateway_ip'] = ip_profile
['gateway_address']
315 if ip_profile
.get('dns_address'):
316 #TODO: manage dns_address as a list of addresses separated by commas
317 subnet
['dns_nameservers'] = []
318 subnet
['dns_nameservers'].append(ip_profile
['dns_address'])
319 if 'dhcp_enabled' in ip_profile
:
320 subnet
['enable_dhcp'] = False if ip_profile
['dhcp_enabled']=="false" else True
321 if 'dhcp_start_address' in ip_profile
:
322 subnet
['allocation_pools']=[]
323 subnet
['allocation_pools'].append(dict())
324 subnet
['allocation_pools'][0]['start'] = ip_profile
['dhcp_start_address']
325 if 'dhcp_count' in ip_profile
:
326 #parts = ip_profile['dhcp_start_address'].split('.')
327 #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
328 ip_int
= int(netaddr
.IPAddress(ip_profile
['dhcp_start_address']))
329 ip_int
+= ip_profile
['dhcp_count'] - 1
330 ip_str
= str(netaddr
.IPAddress(ip_int
))
331 subnet
['allocation_pools'][0]['end'] = ip_str
332 #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
333 self
.neutron
.create_subnet({"subnet": subnet
} )
334 return new_net
["network"]["id"]
335 except (neExceptions
.ConnectionFailed
, ksExceptions
.ClientException
, neExceptions
.NeutronException
, ConnectionError
) as e
:
337 self
.neutron
.delete_network(new_net
['network']['id'])
338 self
._format
_exception
(e
)
340 def get_network_list(self
, filter_dict
={}):
341 '''Obtain tenant networks of VIM
347 admin_state_up: boolean
349 Returns the network list of dictionaries
351 self
.logger
.debug("Getting network from VIM filter: '%s'", str(filter_dict
))
353 self
._reload
_connection
()
354 if self
.osc_api_version
== 'v3.3' and "tenant_id" in filter_dict
:
355 filter_dict
['project_id'] = filter_dict
.pop('tenant_id')
356 net_dict
=self
.neutron
.list_networks(**filter_dict
)
357 net_list
=net_dict
["networks"]
358 self
.__net
_os
2mano
(net_list
)
360 except (neExceptions
.ConnectionFailed
, ksExceptions
.ClientException
, neExceptions
.NeutronException
, ConnectionError
) as e
:
361 self
._format
_exception
(e
)
363 def get_network(self
, net_id
):
364 '''Obtain details of network from VIM
365 Returns the network information from a network id'''
366 self
.logger
.debug(" Getting tenant network %s from VIM", net_id
)
367 filter_dict
={"id": net_id
}
368 net_list
= self
.get_network_list(filter_dict
)
370 raise vimconn
.vimconnNotFoundException("Network '{}' not found".format(net_id
))
371 elif len(net_list
)>1:
372 raise vimconn
.vimconnConflictException("Found more than one network with this criteria")
375 for subnet_id
in net
.get("subnets", () ):
377 subnet
= self
.neutron
.show_subnet(subnet_id
)
378 except Exception as e
:
379 self
.logger
.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id
, str(e
)))
380 subnet
= {"id": subnet_id
, "fault": str(e
)}
381 subnets
.append(subnet
)
382 net
["subnets"] = subnets
385 def delete_network(self
, net_id
):
386 '''Deletes a tenant network from VIM. Returns the old network identifier'''
387 self
.logger
.debug("Deleting network '%s' from VIM", net_id
)
389 self
._reload
_connection
()
390 #delete VM ports attached to this networks before the network
391 ports
= self
.neutron
.list_ports(network_id
=net_id
)
392 for p
in ports
['ports']:
394 self
.neutron
.delete_port(p
["id"])
395 except Exception as e
:
396 self
.logger
.error("Error deleting port %s: %s", p
["id"], str(e
))
397 self
.neutron
.delete_network(net_id
)
399 except (neExceptions
.ConnectionFailed
, neExceptions
.NetworkNotFoundClient
, neExceptions
.NeutronException
,
400 ksExceptions
.ClientException
, neExceptions
.NeutronException
, ConnectionError
) as e
:
401 self
._format
_exception
(e
)
403 def refresh_nets_status(self
, net_list
):
404 '''Get the status of the networks
405 Params: the list of network identifiers
406 Returns a dictionary with:
407 net_id: #VIM id of this network
408 status: #Mandatory. Text with one of:
409 # DELETED (not found at vim)
410 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
411 # OTHER (Vim reported other status not understood)
412 # ERROR (VIM indicates an ERROR status)
413 # ACTIVE, INACTIVE, DOWN (admin down),
414 # BUILD (on building process)
416 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
417 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
421 for net_id
in net_list
:
424 net_vim
= self
.get_network(net_id
)
425 if net_vim
['status'] in netStatus2manoFormat
:
426 net
["status"] = netStatus2manoFormat
[ net_vim
['status'] ]
428 net
["status"] = "OTHER"
429 net
["error_msg"] = "VIM status reported " + net_vim
['status']
431 if net
['status'] == "ACTIVE" and not net_vim
['admin_state_up']:
432 net
['status'] = 'DOWN'
434 net
['vim_info'] = yaml
.safe_dump(net_vim
, default_flow_style
=True, width
=256)
435 except yaml
.representer
.RepresenterError
:
436 net
['vim_info'] = str(net_vim
)
437 if net_vim
.get('fault'): #TODO
438 net
['error_msg'] = str(net_vim
['fault'])
439 except vimconn
.vimconnNotFoundException
as e
:
440 self
.logger
.error("Exception getting net status: %s", str(e
))
441 net
['status'] = "DELETED"
442 net
['error_msg'] = str(e
)
443 except vimconn
.vimconnException
as e
:
444 self
.logger
.error("Exception getting net status: %s", str(e
))
445 net
['status'] = "VIM_ERROR"
446 net
['error_msg'] = str(e
)
447 net_dict
[net_id
] = net
450 def get_flavor(self
, flavor_id
):
451 '''Obtain flavor details from the VIM. Returns the flavor dict details'''
452 self
.logger
.debug("Getting flavor '%s'", flavor_id
)
454 self
._reload
_connection
()
455 flavor
= self
.nova
.flavors
.find(id=flavor_id
)
456 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
457 return flavor
.to_dict()
458 except (nvExceptions
.NotFound
, nvExceptions
.ClientException
, ksExceptions
.ClientException
, ConnectionError
) as e
:
459 self
._format
_exception
(e
)
461 def new_flavor(self
, flavor_data
, change_name_if_used
=True):
462 '''Adds a tenant flavor to openstack VIM
463 if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition
464 Returns the flavor identifier
466 self
.logger
.debug("Adding flavor '%s'", str(flavor_data
))
470 name
=flavor_data
['name']
471 while retry
<max_retries
:
474 self
._reload
_connection
()
475 if change_name_if_used
:
478 fl
=self
.nova
.flavors
.list()
480 fl_names
.append(f
.name
)
481 while name
in fl_names
:
483 name
= flavor_data
['name']+"-" + str(name_suffix
)
485 ram
= flavor_data
.get('ram',64)
486 vcpus
= flavor_data
.get('vcpus',1)
489 extended
= flavor_data
.get("extended")
491 numas
=extended
.get("numas")
493 numa_nodes
= len(numas
)
495 return -1, "Can not add flavor with more than one numa"
496 numa_properties
= {"hw:numa_nodes":str(numa_nodes
)}
497 numa_properties
["hw:mem_page_size"] = "large"
498 numa_properties
["hw:cpu_policy"] = "dedicated"
499 numa_properties
["hw:numa_mempolicy"] = "strict"
501 #overwrite ram and vcpus
502 ram
= numa
['memory']*1024
503 if 'paired-threads' in numa
:
504 vcpus
= numa
['paired-threads']*2
505 numa_properties
["hw:cpu_threads_policy"] = "prefer"
506 elif 'cores' in numa
:
507 vcpus
= numa
['cores']
508 #numa_properties["hw:cpu_threads_policy"] = "prefer"
509 elif 'threads' in numa
:
510 vcpus
= numa
['threads']
511 numa_properties
["hw:cpu_policy"] = "isolated"
512 for interface
in numa
.get("interfaces",() ):
513 if interface
["dedicated"]=="yes":
514 raise vimconn
.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code
=vimconn
.HTTP_Service_Unavailable
)
515 #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
518 new_flavor
=self
.nova
.flavors
.create(name
,
521 flavor_data
.get('disk',1),
522 is_public
=flavor_data
.get('is_public', True)
526 new_flavor
.set_keys(numa_properties
)
528 except nvExceptions
.Conflict
as e
:
529 if change_name_if_used
and retry
< max_retries
:
531 self
._format
_exception
(e
)
532 #except nvExceptions.BadRequest as e:
533 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, ConnectionError
) as e
:
534 self
._format
_exception
(e
)
536 def delete_flavor(self
,flavor_id
):
537 '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
540 self
._reload
_connection
()
541 self
.nova
.flavors
.delete(flavor_id
)
543 #except nvExceptions.BadRequest as e:
544 except (nvExceptions
.NotFound
, ksExceptions
.ClientException
, nvExceptions
.ClientException
, ConnectionError
) as e
:
545 self
._format
_exception
(e
)
547 def new_image(self
,image_dict
):
549 Adds a tenant image to VIM. imge_dict is a dictionary with:
551 disk_format: qcow2, vhd, vmdk, raw (by default), ...
552 location: path or URI
553 public: "yes" or "no"
554 metadata: metadata of the image
557 #using version 1 of glance client
558 glancev1
= gl1Client
.Client('1',self
.glance_endpoint
, token
=self
.keystone
.auth_token
, **self
.k_creds
) #TODO check k_creds vs n_creds
561 while retry
<max_retries
:
564 self
._reload
_connection
()
565 #determine format http://docs.openstack.org/developer/glance/formats.html
566 if "disk_format" in image_dict
:
567 disk_format
=image_dict
["disk_format"]
568 else: #autodiscover base on extention
569 if image_dict
['location'][-6:]==".qcow2":
571 elif image_dict
['location'][-4:]==".vhd":
573 elif image_dict
['location'][-5:]==".vmdk":
575 elif image_dict
['location'][-4:]==".vdi":
577 elif image_dict
['location'][-4:]==".iso":
579 elif image_dict
['location'][-4:]==".aki":
581 elif image_dict
['location'][-4:]==".ari":
583 elif image_dict
['location'][-4:]==".ami":
587 self
.logger
.debug("new_image: '%s' loading from '%s'", image_dict
['name'], image_dict
['location'])
588 if image_dict
['location'][0:4]=="http":
589 new_image
= glancev1
.images
.create(name
=image_dict
['name'], is_public
=image_dict
.get('public',"yes")=="yes",
590 container_format
="bare", location
=image_dict
['location'], disk_format
=disk_format
)
592 with
open(image_dict
['location']) as fimage
:
593 new_image
= glancev1
.images
.create(name
=image_dict
['name'], is_public
=image_dict
.get('public',"yes")=="yes",
594 container_format
="bare", data
=fimage
, disk_format
=disk_format
)
595 #insert metadata. We cannot use 'new_image.properties.setdefault'
596 #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
597 new_image_nova
=self
.nova
.images
.find(id=new_image
.id)
598 new_image_nova
.metadata
.setdefault('location',image_dict
['location'])
599 metadata_to_load
= image_dict
.get('metadata')
601 for k
,v
in yaml
.load(metadata_to_load
).iteritems():
602 new_image_nova
.metadata
.setdefault(k
,v
)
604 except (nvExceptions
.Conflict
, ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
605 self
._format
_exception
(e
)
606 except (HTTPException
, gl1Exceptions
.HTTPException
, gl1Exceptions
.CommunicationError
, ConnectionError
) as e
:
607 if retry
==max_retries
:
609 self
._format
_exception
(e
)
610 except IOError as e
: #can not open the file
611 raise vimconn
.vimconnConnectionException(type(e
).__name
__ + ": " + str(e
)+ " for " + image_dict
['location'],
612 http_code
=vimconn
.HTTP_Bad_Request
)
614 def delete_image(self
, image_id
):
615 '''Deletes a tenant image from openstack VIM. Returns the old id
618 self
._reload
_connection
()
619 self
.nova
.images
.delete(image_id
)
621 except (nvExceptions
.NotFound
, ksExceptions
.ClientException
, nvExceptions
.ClientException
, gl1Exceptions
.CommunicationError
, ConnectionError
) as e
: #TODO remove
622 self
._format
_exception
(e
)
624 def get_image_id_from_path(self
, path
):
625 '''Get the image id from image path in the VIM database. Returns the image_id'''
627 self
._reload
_connection
()
628 images
= self
.nova
.images
.list()
630 if image
.metadata
.get("location")==path
:
632 raise vimconn
.vimconnNotFoundException("image with location '{}' not found".format( path
))
633 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, gl1Exceptions
.CommunicationError
, ConnectionError
) as e
:
634 self
._format
_exception
(e
)
636 def get_image_list(self
, filter_dict
={}):
637 '''Obtain tenant images from VIM
641 checksum: image checksum
642 Returns the image list of dictionaries:
643 [{<the fields at Filter_dict plus some VIM specific>}, ...]
646 self
.logger
.debug("Getting image list from VIM filter: '%s'", str(filter_dict
))
648 self
._reload
_connection
()
649 filter_dict_os
=filter_dict
.copy()
650 #First we filter by the available filter fields: name, id. The others are removed.
651 filter_dict_os
.pop('checksum',None)
652 image_list
=self
.nova
.images
.findall(**filter_dict_os
)
653 if len(image_list
)==0:
655 #Then we filter by the rest of filter fields: checksum
657 for image
in image_list
:
658 image_dict
=self
.glance
.images
.get(image
.id)
659 if image_dict
['checksum']==filter_dict
.get('checksum'):
660 filtered_list
.append(image
)
662 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, gl1Exceptions
.CommunicationError
, ConnectionError
) as e
:
663 self
._format
_exception
(e
)
665 def new_vminstance(self
,name
,description
,start
,image_id
,flavor_id
,net_list
,cloud_config
=None,disk_list
=None):
666 '''Adds a VM instance to VIM
668 start: indicates if VM must start or boot in pause mode. Ignored
669 image_id,flavor_id: iamge and flavor uuid
670 net_list: list of interfaces, each one is a dictionary with:
672 net_id: network uuid to connect
673 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
674 model: interface model, ignored #TODO
675 mac_address: used for SR-IOV ifaces #TODO for other types
676 use: 'data', 'bridge', 'mgmt'
677 type: 'virtual', 'PF', 'VF', 'VFnotShared'
678 vim_id: filled/added by this function
679 floating_ip: True/False (or it can be None)
680 #TODO ip, security groups
681 Returns the instance identifier
683 self
.logger
.debug("Creating VM image '%s' flavor '%s' nics='%s'",image_id
, flavor_id
,str(net_list
))
687 external_network
=[] #list of external networks to be connected to instance, later on used to create floating_ip
688 self
._reload
_connection
()
689 metadata_vpci
={} #For a specific neutron plugin
691 if not net
.get("net_id"): #skip non connected iface
693 if net
["type"]=="virtual" or net
["type"]=="VF":
695 "network_id": net
["net_id"],
696 "name": net
.get("name"),
697 "admin_state_up": True
699 if net
["type"]=="virtual":
701 metadata_vpci
[ net
["net_id"] ] = [[ net
["vpci"], "" ]]
704 if "VF" not in metadata_vpci
:
705 metadata_vpci
["VF"]=[]
706 metadata_vpci
["VF"].append([ net
["vpci"], "" ])
707 port_dict
["binding:vnic_type"]="direct"
708 if not port_dict
["name"]:
709 port_dict
["name"]=name
710 if net
.get("mac_address"):
711 port_dict
["mac_address"]=net
["mac_address"]
712 if net
.get("port_security") == False:
713 port_dict
["port_security_enabled"]=net
["port_security"]
714 new_port
= self
.neutron
.create_port({"port": port_dict
})
715 net
["mac_adress"] = new_port
["port"]["mac_address"]
716 net
["vim_id"] = new_port
["port"]["id"]
717 net
["ip"] = new_port
["port"].get("fixed_ips", [{}])[0].get("ip_address")
718 net_list_vim
.append({"port-id": new_port
["port"]["id"]})
720 self
.logger
.warn("new_vminstance: Warning, can not connect a passthrough interface ")
721 #TODO insert this when openstack consider passthrough ports as openstack neutron ports
722 if net
.get('floating_ip', False):
723 external_network
.append(net
)
726 metadata
= {"pci_assignement": json
.dumps(metadata_vpci
)}
727 if len(metadata
["pci_assignement"]) >255:
728 #limit the metadata size
729 #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
730 self
.logger
.warn("Metadata deleted since it exceeds the expected length (255) ")
733 self
.logger
.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s",
734 name
, image_id
, flavor_id
, str(net_list_vim
), description
, str(metadata
))
736 security_groups
= self
.config
.get('security_groups')
737 if type(security_groups
) is str:
738 security_groups
= ( security_groups
, )
739 if isinstance(cloud_config
, dict):
740 userdata
="#cloud-config\nusers:\n"
742 if "key-pairs" in cloud_config
:
743 userdata
+= " - default:\n ssh-authorized-keys:\n"
744 for key
in cloud_config
["key-pairs"]:
745 userdata
+= " - '{key}'\n".format(key
=key
)
746 for user
in cloud_config
.get("users",[]):
747 userdata
+= " - name: {name}\n sudo: ALL=(ALL) NOPASSWD:ALL\n".format(name
=user
["name"])
748 if "user-info" in user
:
749 userdata
+= " gecos: {}'\n".format(user
["user-info"])
750 if user
.get("key-pairs"):
751 userdata
+= " ssh-authorized-keys:\n"
752 for key
in user
["key-pairs"]:
753 userdata
+= " - '{key}'\n".format(key
=key
)
754 self
.logger
.debug("userdata: %s", userdata
)
755 elif isinstance(cloud_config
, str):
756 userdata
= cloud_config
760 #Create additional volumes in case these are present in disk_list
761 block_device_mapping
= None
762 base_disk_index
= ord('b')
763 if disk_list
!= None:
764 block_device_mapping
= dict()
765 for disk
in disk_list
:
766 if 'image_id' in disk
:
767 volume
= self
.cinder
.volumes
.create(size
= disk
['size'],name
= name
+ '_vd' +
768 chr(base_disk_index
), imageRef
= disk
['image_id'])
770 volume
= self
.cinder
.volumes
.create(size
=disk
['size'], name
=name
+ '_vd' +
771 chr(base_disk_index
))
772 block_device_mapping
['_vd' + chr(base_disk_index
)] = volume
.id
775 #wait until volumes are with status available
778 while keep_waiting
and elapsed_time
< volume_timeout
:
780 for volume_id
in block_device_mapping
.itervalues():
781 if self
.cinder
.volumes
.get(volume_id
).status
!= 'available':
787 #if we exceeded the timeout rollback
788 if elapsed_time
>= volume_timeout
:
789 #delete the volumes we just created
790 for volume_id
in block_device_mapping
.itervalues():
791 self
.cinder
.volumes
.delete(volume_id
)
793 #delete ports we just created
794 for net_item
in net_list_vim
:
795 if 'port-id' in net_item
:
796 self
.neutron
.delete_port(net_item
['port-id'])
798 raise vimconn
.vimconnException('Timeout creating volumes for instance ' + name
,
799 http_code
=vimconn
.HTTP_Request_Timeout
)
801 server
= self
.nova
.servers
.create(name
, image_id
, flavor_id
, nics
=net_list_vim
, meta
=metadata
,
802 security_groups
=security_groups
,
803 availability_zone
=self
.config
.get('availability_zone'),
804 key_name
=self
.config
.get('keypair'),
806 block_device_mapping
= block_device_mapping
807 ) # , description=description)
808 #print "DONE :-)", server
810 floating_ips
= self
.neutron
.list_floatingips().get("floatingips", ())
811 for floating_network
in external_network
:
812 # wait until vm is active
814 while elapsed_time
< server_timeout
:
815 status
= self
.nova
.servers
.get(server
.id).status
816 if status
== 'ACTIVE':
821 #if we exceeded the timeout rollback
822 if elapsed_time
>= server_timeout
:
823 self
.delete_vminstance(server
.id)
824 raise vimconn
.vimconnException('Timeout creating instance ' + name
,
825 http_code
=vimconn
.HTTP_Request_Timeout
)
828 while(assigned
== False):
830 ip
= floating_ips
.pop(0)
831 if not ip
.get("port_id", False) and ip
.get('tenant_id') == server
.tenant_id
:
832 free_floating_ip
= ip
.get("floating_ip_address")
834 fix_ip
= floating_network
.get('ip')
835 server
.add_floating_ip(free_floating_ip
, fix_ip
)
837 except Exception as e
:
838 self
.delete_vminstance(server
.id)
839 raise vimconn
.vimconnException(type(e
).__name
__ + ": Cannot create floating_ip "+ str(e
), http_code
=vimconn
.HTTP_Conflict
)
841 #Find the external network
842 external_nets
= list()
843 for net
in self
.neutron
.list_networks()['networks']:
844 if net
['router:external']:
845 external_nets
.append(net
)
847 if len(external_nets
) == 0:
848 self
.delete_vminstance(server
.id)
849 raise vimconn
.vimconnException("Cannot create floating_ip automatically since no external "
850 "network is present",
851 http_code
=vimconn
.HTTP_Conflict
)
852 if len(external_nets
) > 1:
853 self
.delete_vminstance(server
.id)
854 raise vimconn
.vimconnException("Cannot create floating_ip automatically since multiple "
855 "external networks are present",
856 http_code
=vimconn
.HTTP_Conflict
)
858 pool_id
= external_nets
[0].get('id')
859 param
= {'floatingip': {'floating_network_id': pool_id
, 'tenant_id': server
.tenant_id
}}
861 #self.logger.debug("Creating floating IP")
862 new_floating_ip
= self
.neutron
.create_floatingip(param
)
863 free_floating_ip
= new_floating_ip
['floatingip']['floating_ip_address']
864 fix_ip
= floating_network
.get('ip')
865 server
.add_floating_ip(free_floating_ip
, fix_ip
)
867 except Exception as e
:
868 self
.delete_vminstance(server
.id)
869 raise vimconn
.vimconnException(type(e
).__name
__ + ": Cannot create floating_ip "+ str(e
), http_code
=vimconn
.HTTP_Conflict
)
872 # except nvExceptions.NotFound as e:
873 # error_value=-vimconn.HTTP_Not_Found
874 # error_text= "vm instance %s not found" % vm_id
875 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, ConnectionError
877 # delete the volumes we just created
878 if block_device_mapping
!= None:
879 for volume_id
in block_device_mapping
.itervalues():
880 self
.cinder
.volumes
.delete(volume_id
)
882 # delete ports we just created
883 for net_item
in net_list_vim
:
884 if 'port-id' in net_item
:
885 self
.neutron
.delete_port(net_item
['port-id'])
886 self
._format
_exception
(e
)
887 except TypeError as e
:
888 raise vimconn
.vimconnException(type(e
).__name
__ + ": "+ str(e
), http_code
=vimconn
.HTTP_Bad_Request
)
890 def get_vminstance(self
,vm_id
):
891 '''Returns the VM instance information from VIM'''
892 #self.logger.debug("Getting VM from VIM")
894 self
._reload
_connection
()
895 server
= self
.nova
.servers
.find(id=vm_id
)
896 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
897 return server
.to_dict()
898 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, nvExceptions
.NotFound
, ConnectionError
) as e
:
899 self
._format
_exception
(e
)
901 def get_vminstance_console(self
,vm_id
, console_type
="vnc"):
903 Get a console for the virtual machine
905 vm_id: uuid of the VM
906 console_type, can be:
907 "novnc" (by default), "xvpvnc" for VNC types,
908 "rdp-html5" for RDP types, "spice-html5" for SPICE types
909 Returns dict with the console parameters:
910 protocol: ssh, ftp, http, https, ...
911 server: usually ip address
912 port: the http, ssh, ... port
913 suffix: extra text, e.g. the http path and query string
915 self
.logger
.debug("Getting VM CONSOLE from VIM")
917 self
._reload
_connection
()
918 server
= self
.nova
.servers
.find(id=vm_id
)
919 if console_type
== None or console_type
== "novnc":
920 console_dict
= server
.get_vnc_console("novnc")
921 elif console_type
== "xvpvnc":
922 console_dict
= server
.get_vnc_console(console_type
)
923 elif console_type
== "rdp-html5":
924 console_dict
= server
.get_rdp_console(console_type
)
925 elif console_type
== "spice-html5":
926 console_dict
= server
.get_spice_console(console_type
)
928 raise vimconn
.vimconnException("console type '{}' not allowed".format(console_type
), http_code
=vimconn
.HTTP_Bad_Request
)
930 console_dict1
= console_dict
.get("console")
932 console_url
= console_dict1
.get("url")
935 protocol_index
= console_url
.find("//")
936 suffix_index
= console_url
[protocol_index
+2:].find("/") + protocol_index
+2
937 port_index
= console_url
[protocol_index
+2:suffix_index
].find(":") + protocol_index
+2
938 if protocol_index
< 0 or port_index
<0 or suffix_index
<0:
939 return -vimconn
.HTTP_Internal_Server_Error
, "Unexpected response from VIM"
940 console_dict
={"protocol": console_url
[0:protocol_index
],
941 "server": console_url
[protocol_index
+2:port_index
],
942 "port": console_url
[port_index
:suffix_index
],
943 "suffix": console_url
[suffix_index
+1:]
947 raise vimconn
.vimconnUnexpectedResponse("Unexpected response from VIM")
949 except (nvExceptions
.NotFound
, ksExceptions
.ClientException
, nvExceptions
.ClientException
, nvExceptions
.BadRequest
, ConnectionError
) as e
:
950 self
._format
_exception
(e
)
952 def delete_vminstance(self
, vm_id
):
953 '''Removes a VM instance from VIM. Returns the old identifier
955 #print "osconnector: Getting VM from VIM"
957 self
._reload
_connection
()
958 #delete VM ports attached to this networks before the virtual machine
959 ports
= self
.neutron
.list_ports(device_id
=vm_id
)
960 for p
in ports
['ports']:
962 self
.neutron
.delete_port(p
["id"])
963 except Exception as e
:
964 self
.logger
.error("Error deleting port: " + type(e
).__name
__ + ": "+ str(e
))
966 #commented because detaching the volumes makes the servers.delete not work properly ?!?
967 #dettach volumes attached
968 server
= self
.nova
.servers
.get(vm_id
)
969 volumes_attached_dict
= server
._info
['os-extended-volumes:volumes_attached']
970 #for volume in volumes_attached_dict:
971 # self.cinder.volumes.detach(volume['id'])
973 self
.nova
.servers
.delete(vm_id
)
976 #Although having detached them should have them in active status
977 #we ensure in this loop
980 while keep_waiting
and elapsed_time
< volume_timeout
:
982 for volume
in volumes_attached_dict
:
983 if self
.cinder
.volumes
.get(volume
['id']).status
!= 'available':
986 self
.cinder
.volumes
.delete(volume
['id'])
992 except (nvExceptions
.NotFound
, ksExceptions
.ClientException
, nvExceptions
.ClientException
, ConnectionError
) as e
:
993 self
._format
_exception
(e
)
994 #TODO insert exception vimconn.HTTP_Unauthorized
995 #if reaching here is because an exception
997 def refresh_vms_status(self
, vm_list
):
998 '''Get the status of the virtual machines and their interfaces/ports
999 Params: the list of VM identifiers
1000 Returns a dictionary with:
1001 vm_id: #VIM id of this Virtual Machine
1002 status: #Mandatory. Text with one of:
1003 # DELETED (not found at vim)
1004 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1005 # OTHER (Vim reported other status not understood)
1006 # ERROR (VIM indicates an ERROR status)
1007 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1008 # CREATING (on building process), ERROR
1009 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1011 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1012 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1014 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1015 mac_address: #Text format XX:XX:XX:XX:XX:XX
1016 vim_net_id: #network id where this interface is connected
1017 vim_interface_id: #interface/port VIM id
1018 ip_address: #null, or text with IPv4, IPv6 address
1021 self
.logger
.debug("refresh_vms status: Getting tenant VM instance information from VIM")
1022 for vm_id
in vm_list
:
1025 vm_vim
= self
.get_vminstance(vm_id
)
1026 if vm_vim
['status'] in vmStatus2manoFormat
:
1027 vm
['status'] = vmStatus2manoFormat
[ vm_vim
['status'] ]
1029 vm
['status'] = "OTHER"
1030 vm
['error_msg'] = "VIM status reported " + vm_vim
['status']
1032 vm
['vim_info'] = yaml
.safe_dump(vm_vim
, default_flow_style
=True, width
=256)
1033 except yaml
.representer
.RepresenterError
:
1034 vm
['vim_info'] = str(vm_vim
)
1035 vm
["interfaces"] = []
1036 if vm_vim
.get('fault'):
1037 vm
['error_msg'] = str(vm_vim
['fault'])
1040 self
._reload
_connection
()
1041 port_dict
=self
.neutron
.list_ports(device_id
=vm_id
)
1042 for port
in port_dict
["ports"]:
1045 interface
['vim_info'] = yaml
.safe_dump(port
, default_flow_style
=True, width
=256)
1046 except yaml
.representer
.RepresenterError
:
1047 interface
['vim_info'] = str(port
)
1048 interface
["mac_address"] = port
.get("mac_address")
1049 interface
["vim_net_id"] = port
["network_id"]
1050 interface
["vim_interface_id"] = port
["id"]
1052 #look for floating ip address
1053 floating_ip_dict
= self
.neutron
.list_floatingips(port_id
=port
["id"])
1054 if floating_ip_dict
.get("floatingips"):
1055 ips
.append(floating_ip_dict
["floatingips"][0].get("floating_ip_address") )
1057 for subnet
in port
["fixed_ips"]:
1058 ips
.append(subnet
["ip_address"])
1059 interface
["ip_address"] = ";".join(ips
)
1060 vm
["interfaces"].append(interface
)
1061 except Exception as e
:
1062 self
.logger
.error("Error getting vm interface information " + type(e
).__name
__ + ": "+ str(e
))
1063 except vimconn
.vimconnNotFoundException
as e
:
1064 self
.logger
.error("Exception getting vm status: %s", str(e
))
1065 vm
['status'] = "DELETED"
1066 vm
['error_msg'] = str(e
)
1067 except vimconn
.vimconnException
as e
:
1068 self
.logger
.error("Exception getting vm status: %s", str(e
))
1069 vm
['status'] = "VIM_ERROR"
1070 vm
['error_msg'] = str(e
)
1074 def action_vminstance(self
, vm_id
, action_dict
):
1075 '''Send and action over a VM instance from VIM
1076 Returns the vm_id if the action was successfully sent to the VIM'''
1077 self
.logger
.debug("Action over VM '%s': %s", vm_id
, str(action_dict
))
1079 self
._reload
_connection
()
1080 server
= self
.nova
.servers
.find(id=vm_id
)
1081 if "start" in action_dict
:
1082 if action_dict
["start"]=="rebuild":
1085 if server
.status
=="PAUSED":
1087 elif server
.status
=="SUSPENDED":
1089 elif server
.status
=="SHUTOFF":
1091 elif "pause" in action_dict
:
1093 elif "resume" in action_dict
:
1095 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1097 elif "forceOff" in action_dict
:
1099 elif "terminate" in action_dict
:
1101 elif "createImage" in action_dict
:
1102 server
.create_image()
1103 #"path":path_schema,
1104 #"description":description_schema,
1105 #"name":name_schema,
1106 #"metadata":metadata_schema,
1107 #"imageRef": id_schema,
1108 #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
1109 elif "rebuild" in action_dict
:
1110 server
.rebuild(server
.image
['id'])
1111 elif "reboot" in action_dict
:
1112 server
.reboot() #reboot_type='SOFT'
1113 elif "console" in action_dict
:
1114 console_type
= action_dict
["console"]
1115 if console_type
== None or console_type
== "novnc":
1116 console_dict
= server
.get_vnc_console("novnc")
1117 elif console_type
== "xvpvnc":
1118 console_dict
= server
.get_vnc_console(console_type
)
1119 elif console_type
== "rdp-html5":
1120 console_dict
= server
.get_rdp_console(console_type
)
1121 elif console_type
== "spice-html5":
1122 console_dict
= server
.get_spice_console(console_type
)
1124 raise vimconn
.vimconnException("console type '{}' not allowed".format(console_type
),
1125 http_code
=vimconn
.HTTP_Bad_Request
)
1127 console_url
= console_dict
["console"]["url"]
1129 protocol_index
= console_url
.find("//")
1130 suffix_index
= console_url
[protocol_index
+2:].find("/") + protocol_index
+2
1131 port_index
= console_url
[protocol_index
+2:suffix_index
].find(":") + protocol_index
+2
1132 if protocol_index
< 0 or port_index
<0 or suffix_index
<0:
1133 raise vimconn
.vimconnException("Unexpected response from VIM " + str(console_dict
))
1134 console_dict2
={"protocol": console_url
[0:protocol_index
],
1135 "server": console_url
[protocol_index
+2 : port_index
],
1136 "port": int(console_url
[port_index
+1 : suffix_index
]),
1137 "suffix": console_url
[suffix_index
+1:]
1139 return console_dict2
1140 except Exception as e
:
1141 raise vimconn
.vimconnException("Unexpected response from VIM " + str(console_dict
))
1144 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
, nvExceptions
.NotFound
, ConnectionError
) as e
:
1145 self
._format
_exception
(e
)
1146 #TODO insert exception vimconn.HTTP_Unauthorized
1150 def new_external_port(self
, port_data
):
1151 #TODO openstack if needed
1152 '''Adds a external port to VIM'''
1153 '''Returns the port identifier'''
1154 return -vimconn
.HTTP_Internal_Server_Error
, "osconnector.new_external_port() not implemented"
1156 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1157 #TODO openstack if needed
1158 '''Connects a external port to a network'''
1159 '''Returns status code of the VIM response'''
1160 return -vimconn
.HTTP_Internal_Server_Error
, "osconnector.connect_port_network() not implemented"
1162 def new_user(self
, user_name
, user_passwd
, tenant_id
=None):
1163 '''Adds a new user to openstack VIM'''
1164 '''Returns the user identifier'''
1165 self
.logger
.debug("osconnector: Adding a new user to VIM")
1167 self
._reload
_connection
()
1168 user
=self
.keystone
.users
.create(user_name
, user_passwd
, tenant_id
=tenant_id
)
1169 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
1171 except ksExceptions
.ConnectionError
as e
:
1172 error_value
=-vimconn
.HTTP_Bad_Request
1173 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1174 except ksExceptions
.ClientException
as e
: #TODO remove
1175 error_value
=-vimconn
.HTTP_Bad_Request
1176 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1177 #TODO insert exception vimconn.HTTP_Unauthorized
1178 #if reaching here is because an exception
1180 self
.logger
.debug("new_user " + error_text
)
1181 return error_value
, error_text
1183 def delete_user(self
, user_id
):
1184 '''Delete a user from openstack VIM'''
1185 '''Returns the user identifier'''
1187 print "osconnector: Deleting a user from VIM"
1189 self
._reload
_connection
()
1190 self
.keystone
.users
.delete(user_id
)
1192 except ksExceptions
.ConnectionError
as e
:
1193 error_value
=-vimconn
.HTTP_Bad_Request
1194 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1195 except ksExceptions
.NotFound
as e
:
1196 error_value
=-vimconn
.HTTP_Not_Found
1197 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1198 except ksExceptions
.ClientException
as e
: #TODO remove
1199 error_value
=-vimconn
.HTTP_Bad_Request
1200 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1201 #TODO insert exception vimconn.HTTP_Unauthorized
1202 #if reaching here is because an exception
1204 print "delete_tenant " + error_text
1205 return error_value
, error_text
1207 def get_hosts_info(self
):
1208 '''Get the information of deployed hosts
1209 Returns the hosts content'''
1211 print "osconnector: Getting Host info from VIM"
1214 self
._reload
_connection
()
1215 hypervisors
= self
.nova
.hypervisors
.list()
1216 for hype
in hypervisors
:
1217 h_list
.append( hype
.to_dict() )
1218 return 1, {"hosts":h_list
}
1219 except nvExceptions
.NotFound
as e
:
1220 error_value
=-vimconn
.HTTP_Not_Found
1221 error_text
= (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1222 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
1223 error_value
=-vimconn
.HTTP_Bad_Request
1224 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1225 #TODO insert exception vimconn.HTTP_Unauthorized
1226 #if reaching here is because an exception
1228 print "get_hosts_info " + error_text
1229 return error_value
, error_text
1231 def get_hosts(self
, vim_tenant
):
1232 '''Get the hosts and deployed instances
1233 Returns the hosts content'''
1234 r
, hype_dict
= self
.get_hosts_info()
1237 hypervisors
= hype_dict
["hosts"]
1239 servers
= self
.nova
.servers
.list()
1240 for hype
in hypervisors
:
1241 for server
in servers
:
1242 if server
.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype
['hypervisor_hostname']:
1244 hype
['vm'].append(server
.id)
1246 hype
['vm'] = [server
.id]
1248 except nvExceptions
.NotFound
as e
:
1249 error_value
=-vimconn
.HTTP_Not_Found
1250 error_text
= (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1251 except (ksExceptions
.ClientException
, nvExceptions
.ClientException
) as e
:
1252 error_value
=-vimconn
.HTTP_Bad_Request
1253 error_text
= type(e
).__name
__ + ": "+ (str(e
) if len(e
.args
)==0 else str(e
.args
[0]))
1254 #TODO insert exception vimconn.HTTP_Unauthorized
1255 #if reaching here is because an exception
1257 print "get_hosts " + error_text
1258 return error_value
, error_text