blob: 0cd9b19357922aba59bbb2d5808680952f1ffae1 [file] [log] [blame]
tierno7edb6752016-03-21 17:37:52 +01001# -*- coding: utf-8 -*-
2
3##
4# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5# This file is part of openmano
6# All Rights Reserved.
7#
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
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
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
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact with: nfvlabs@tid.es
22##
23
24'''
25osconnector implements all the methods to interact with openstack using the python-client.
26'''
montesmoreno0c8def02016-12-22 12:16:23 +000027__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research"
28__date__ ="$22-jun-2014 11:19:29$"
tierno7edb6752016-03-21 17:37:52 +010029
30import vimconn
31import json
32import yaml
tiernoae4a8d12016-07-08 12:30:39 +020033import logging
garciadeblas9f8456e2016-09-05 05:02:59 +020034import netaddr
montesmoreno0c8def02016-12-22 12:16:23 +000035import time
tierno36c0b172017-01-12 18:32:28 +010036import yaml
tierno7edb6752016-03-21 17:37:52 +010037
ahmadsa95baa272016-11-30 09:14:11 +050038from novaclient import client as nClient_v2, exceptions as nvExceptions, api_versions as APIVersion
39import keystoneclient.v2_0.client as ksClient_v2
40from novaclient.v2.client import Client as nClient
41import keystoneclient.v3.client as ksClient
tierno7edb6752016-03-21 17:37:52 +010042import keystoneclient.exceptions as ksExceptions
43import glanceclient.v2.client as glClient
44import glanceclient.client as gl1Client
45import glanceclient.exc as gl1Exceptions
montesmoreno0c8def02016-12-22 12:16:23 +000046import cinderclient.v2.client as cClient_v2
tierno7edb6752016-03-21 17:37:52 +010047from httplib import HTTPException
ahmadsa95baa272016-11-30 09:14:11 +050048from neutronclient.neutron import client as neClient_v2
49from neutronclient.v2_0 import client as neClient
tierno7edb6752016-03-21 17:37:52 +010050from neutronclient.common import exceptions as neExceptions
51from requests.exceptions import ConnectionError
52
53'''contain the openstack virtual machine status to openmano status'''
54vmStatus2manoFormat={'ACTIVE':'ACTIVE',
55 'PAUSED':'PAUSED',
56 'SUSPENDED': 'SUSPENDED',
57 'SHUTOFF':'INACTIVE',
58 'BUILD':'BUILD',
59 'ERROR':'ERROR','DELETED':'DELETED'
60 }
61netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
62 }
63
montesmoreno0c8def02016-12-22 12:16:23 +000064#global var to have a timeout creating and deleting volumes
65volume_timeout = 60
montesmoreno2a1fc4e2017-01-09 16:46:04 +000066server_timeout = 60
montesmoreno0c8def02016-12-22 12:16:23 +000067
tierno7edb6752016-03-21 17:37:52 +010068class vimconnector(vimconn.vimconnector):
tiernofe789902016-09-29 14:20:44 +000069 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, config={}):
tierno7edb6752016-03-21 17:37:52 +010070 '''using common constructor parameters. In this case
71 'url' is the keystone authorization url,
72 'url_admin' is not use
73 '''
ahmadsa95baa272016-11-30 09:14:11 +050074 self.osc_api_version = 'v2.0'
75 if config.get('APIversion') == 'v3.3':
76 self.osc_api_version = 'v3.3'
tiernoae4a8d12016-07-08 12:30:39 +020077 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
tierno7edb6752016-03-21 17:37:52 +010078
79 self.k_creds={}
80 self.n_creds={}
81 if not url:
82 raise TypeError, 'url param can not be NoneType'
83 self.k_creds['auth_url'] = url
84 self.n_creds['auth_url'] = url
tierno392f2852016-05-13 12:28:55 +020085 if tenant_name:
86 self.k_creds['tenant_name'] = tenant_name
87 self.n_creds['project_id'] = tenant_name
88 if tenant_id:
89 self.k_creds['tenant_id'] = tenant_id
90 self.n_creds['tenant_id'] = tenant_id
tierno7edb6752016-03-21 17:37:52 +010091 if user:
92 self.k_creds['username'] = user
93 self.n_creds['username'] = user
94 if passwd:
95 self.k_creds['password'] = passwd
96 self.n_creds['api_key'] = passwd
ahmadsa95baa272016-11-30 09:14:11 +050097 if self.osc_api_version == 'v3.3':
98 self.k_creds['project_name'] = tenant_name
99 self.k_creds['project_id'] = tenant_id
montesmorenocf227142017-01-12 12:24:21 +0000100 if config.get('region_name'):
101 self.k_creds['region_name'] = config.get('region_name')
102 self.n_creds['region_name'] = config.get('region_name')
montesmoreno0c8def02016-12-22 12:16:23 +0000103
tierno7edb6752016-03-21 17:37:52 +0100104 self.reload_client = True
tierno73ad9e42016-09-12 18:11:11 +0200105 self.logger = logging.getLogger('openmano.vim.openstack')
tiernofe789902016-09-29 14:20:44 +0000106 if log_level:
107 self.logger.setLevel( getattr(logging, log_level) )
tierno7edb6752016-03-21 17:37:52 +0100108
109 def __setitem__(self,index, value):
110 '''Set individuals parameters
111 Throw TypeError, KeyError
112 '''
tierno392f2852016-05-13 12:28:55 +0200113 if index=='tenant_id':
tierno7edb6752016-03-21 17:37:52 +0100114 self.reload_client=True
tierno392f2852016-05-13 12:28:55 +0200115 self.tenant_id = value
ahmadsa95baa272016-11-30 09:14:11 +0500116 if self.osc_api_version == 'v3.3':
117 if value:
118 self.k_creds['project_id'] = value
119 self.n_creds['project_id'] = value
120 else:
121 del self.k_creds['project_id']
122 del self.n_creds['project_id']
tierno392f2852016-05-13 12:28:55 +0200123 else:
ahmadsa95baa272016-11-30 09:14:11 +0500124 if value:
125 self.k_creds['tenant_id'] = value
126 self.n_creds['tenant_id'] = value
127 else:
128 del self.k_creds['tenant_id']
129 del self.n_creds['tenant_id']
tierno392f2852016-05-13 12:28:55 +0200130 elif index=='tenant_name':
131 self.reload_client=True
132 self.tenant_name = value
ahmadsa95baa272016-11-30 09:14:11 +0500133 if self.osc_api_version == 'v3.3':
134 if value:
135 self.k_creds['project_name'] = value
136 self.n_creds['project_name'] = value
137 else:
138 del self.k_creds['project_name']
139 del self.n_creds['project_name']
tierno7edb6752016-03-21 17:37:52 +0100140 else:
ahmadsa95baa272016-11-30 09:14:11 +0500141 if value:
142 self.k_creds['tenant_name'] = value
143 self.n_creds['project_id'] = value
144 else:
145 del self.k_creds['tenant_name']
146 del self.n_creds['project_id']
tierno7edb6752016-03-21 17:37:52 +0100147 elif index=='user':
148 self.reload_client=True
149 self.user = value
150 if value:
151 self.k_creds['username'] = value
152 self.n_creds['username'] = value
153 else:
154 del self.k_creds['username']
155 del self.n_creds['username']
156 elif index=='passwd':
157 self.reload_client=True
158 self.passwd = value
159 if value:
160 self.k_creds['password'] = value
161 self.n_creds['api_key'] = value
162 else:
163 del self.k_creds['password']
164 del self.n_creds['api_key']
165 elif index=='url':
166 self.reload_client=True
167 self.url = value
168 if value:
169 self.k_creds['auth_url'] = value
170 self.n_creds['auth_url'] = value
171 else:
172 raise TypeError, 'url param can not be NoneType'
173 else:
174 vimconn.vimconnector.__setitem__(self,index, value)
175
176 def _reload_connection(self):
177 '''Called before any operation, it check if credentials has changed
178 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
179 '''
180 #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
181 if self.reload_client:
182 #test valid params
183 if len(self.n_creds) <4:
184 raise ksExceptions.ClientException("Not enough parameters to connect to openstack")
ahmadsa95baa272016-11-30 09:14:11 +0500185 if self.osc_api_version == 'v3.3':
186 self.nova = nClient(APIVersion(version_str='2'), **self.n_creds)
montesmoreno0c8def02016-12-22 12:16:23 +0000187 #TODO To be updated for v3
188 #self.cinder = cClient.Client(**self.n_creds)
ahmadsa95baa272016-11-30 09:14:11 +0500189 self.keystone = ksClient.Client(**self.k_creds)
190 self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
191 self.neutron = neClient.Client(APIVersion(version_str='2'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
192 else:
193 self.nova = nClient_v2.Client('2', **self.n_creds)
montesmoreno0c8def02016-12-22 12:16:23 +0000194 self.cinder = cClient_v2.Client(**self.n_creds)
ahmadsa95baa272016-11-30 09:14:11 +0500195 self.keystone = ksClient_v2.Client(**self.k_creds)
196 self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
197 self.neutron = neClient_v2.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
tierno7edb6752016-03-21 17:37:52 +0100198 self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL')
199 self.glance = glClient.Client(self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
tierno7edb6752016-03-21 17:37:52 +0100200 self.reload_client = False
ahmadsa95baa272016-11-30 09:14:11 +0500201
tierno7edb6752016-03-21 17:37:52 +0100202 def __net_os2mano(self, net_list_dict):
203 '''Transform the net openstack format to mano format
204 net_list_dict can be a list of dict or a single dict'''
205 if type(net_list_dict) is dict:
206 net_list_=(net_list_dict,)
207 elif type(net_list_dict) is list:
208 net_list_=net_list_dict
209 else:
210 raise TypeError("param net_list_dict must be a list or a dictionary")
211 for net in net_list_:
212 if net.get('provider:network_type') == "vlan":
213 net['type']='data'
214 else:
215 net['type']='bridge'
tiernoae4a8d12016-07-08 12:30:39 +0200216
217
218
219 def _format_exception(self, exception):
220 '''Transform a keystone, nova, neutron exception into a vimconn exception'''
221 if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
tierno8e995ce2016-09-22 08:13:00 +0000222 ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed
223 )):
tiernoae4a8d12016-07-08 12:30:39 +0200224 raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
225 elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException,
226 neExceptions.NeutronException, nvExceptions.BadRequest)):
227 raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + str(exception))
228 elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)):
229 raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception))
230 elif isinstance(exception, nvExceptions.Conflict):
231 raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception))
232 else: # ()
233 raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
234
235 def get_tenant_list(self, filter_dict={}):
236 '''Obtain tenants of VIM
237 filter_dict can contain the following keys:
238 name: filter by tenant name
239 id: filter by tenant uuid/id
240 <other VIM specific>
241 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
242 '''
ahmadsa95baa272016-11-30 09:14:11 +0500243 self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
tiernoae4a8d12016-07-08 12:30:39 +0200244 try:
245 self._reload_connection()
montesmoreno0c8def02016-12-22 12:16:23 +0000246 if self.osc_api_version == 'v3.3':
ahmadsa95baa272016-11-30 09:14:11 +0500247 project_class_list=self.keystone.projects.findall(**filter_dict)
248 else:
249 project_class_list=self.keystone.tenants.findall(**filter_dict)
250 project_list=[]
251 for project in project_class_list:
252 project_list.append(project.to_dict())
253 return project_list
tierno8e995ce2016-09-22 08:13:00 +0000254 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200255 self._format_exception(e)
256
257 def new_tenant(self, tenant_name, tenant_description):
258 '''Adds a new tenant to openstack VIM. Returns the tenant identifier'''
259 self.logger.debug("Adding a new tenant name: %s", tenant_name)
260 try:
261 self._reload_connection()
ahmadsa95baa272016-11-30 09:14:11 +0500262 if self.osc_api_version == 'v3.3':
263 project=self.keystone.projects.create(tenant_name, tenant_description)
264 else:
265 project=self.keystone.tenants.create(tenant_name, tenant_description)
266 return project.id
tierno8e995ce2016-09-22 08:13:00 +0000267 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200268 self._format_exception(e)
269
270 def delete_tenant(self, tenant_id):
271 '''Delete a tenant from openstack VIM. Returns the old tenant identifier'''
272 self.logger.debug("Deleting tenant %s from VIM", tenant_id)
273 try:
274 self._reload_connection()
montesmoreno0c8def02016-12-22 12:16:23 +0000275 if self.osc_api_version == 'v3.3':
ahmadsa95baa272016-11-30 09:14:11 +0500276 self.keystone.projects.delete(tenant_id)
277 else:
278 self.keystone.tenants.delete(tenant_id)
tiernoae4a8d12016-07-08 12:30:39 +0200279 return tenant_id
tierno8e995ce2016-09-22 08:13:00 +0000280 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200281 self._format_exception(e)
ahmadsa95baa272016-11-30 09:14:11 +0500282
garciadeblas9f8456e2016-09-05 05:02:59 +0200283 def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
tiernoae4a8d12016-07-08 12:30:39 +0200284 '''Adds a tenant network to VIM. Returns the network identifier'''
285 self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
garciadeblasedca7b32016-09-29 14:01:52 +0000286 #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
tierno7edb6752016-03-21 17:37:52 +0100287 try:
garciadeblasedca7b32016-09-29 14:01:52 +0000288 new_net = None
tierno7edb6752016-03-21 17:37:52 +0100289 self._reload_connection()
290 network_dict = {'name': net_name, 'admin_state_up': True}
291 if net_type=="data" or net_type=="ptp":
292 if self.config.get('dataplane_physical_net') == None:
tiernoae4a8d12016-07-08 12:30:39 +0200293 raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
tierno7edb6752016-03-21 17:37:52 +0100294 network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical
295 network_dict["provider:network_type"] = "vlan"
296 if vlan!=None:
297 network_dict["provider:network_type"] = vlan
tiernoae4a8d12016-07-08 12:30:39 +0200298 network_dict["shared"]=shared
tierno7edb6752016-03-21 17:37:52 +0100299 new_net=self.neutron.create_network({'network':network_dict})
300 #print new_net
garciadeblas9f8456e2016-09-05 05:02:59 +0200301 #create subnetwork, even if there is no profile
302 if not ip_profile:
303 ip_profile = {}
304 if 'subnet_address' not in ip_profile:
305 #Fake subnet is required
306 ip_profile['subnet_address'] = "192.168.111.0/24"
307 if 'ip_version' not in ip_profile:
308 ip_profile['ip_version'] = "IPv4"
tierno7edb6752016-03-21 17:37:52 +0100309 subnet={"name":net_name+"-subnet",
310 "network_id": new_net["network"]["id"],
garciadeblas9f8456e2016-09-05 05:02:59 +0200311 "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
312 "cidr": ip_profile['subnet_address']
tierno7edb6752016-03-21 17:37:52 +0100313 }
garciadeblas9f8456e2016-09-05 05:02:59 +0200314 if 'gateway_address' in ip_profile:
315 subnet['gateway_ip'] = ip_profile['gateway_address']
garciadeblasedca7b32016-09-29 14:01:52 +0000316 if ip_profile.get('dns_address'):
garciadeblas9f8456e2016-09-05 05:02:59 +0200317 #TODO: manage dns_address as a list of addresses separated by commas
318 subnet['dns_nameservers'] = []
319 subnet['dns_nameservers'].append(ip_profile['dns_address'])
320 if 'dhcp_enabled' in ip_profile:
321 subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True
322 if 'dhcp_start_address' in ip_profile:
323 subnet['allocation_pools']=[]
324 subnet['allocation_pools'].append(dict())
325 subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
326 if 'dhcp_count' in ip_profile:
327 #parts = ip_profile['dhcp_start_address'].split('.')
328 #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
329 ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
garciadeblas21d795b2016-09-29 17:31:46 +0200330 ip_int += ip_profile['dhcp_count'] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200331 ip_str = str(netaddr.IPAddress(ip_int))
332 subnet['allocation_pools'][0]['end'] = ip_str
garciadeblasedca7b32016-09-29 14:01:52 +0000333 #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
tierno7edb6752016-03-21 17:37:52 +0100334 self.neutron.create_subnet({"subnet": subnet} )
tiernoae4a8d12016-07-08 12:30:39 +0200335 return new_net["network"]["id"]
tierno8e995ce2016-09-22 08:13:00 +0000336 except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000337 if new_net:
338 self.neutron.delete_network(new_net['network']['id'])
tiernoae4a8d12016-07-08 12:30:39 +0200339 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100340
341 def get_network_list(self, filter_dict={}):
342 '''Obtain tenant networks of VIM
343 Filter_dict can be:
344 name: network name
345 id: network uuid
346 shared: boolean
347 tenant_id: tenant
348 admin_state_up: boolean
349 status: 'ACTIVE'
350 Returns the network list of dictionaries
351 '''
tiernoae4a8d12016-07-08 12:30:39 +0200352 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
tierno7edb6752016-03-21 17:37:52 +0100353 try:
354 self._reload_connection()
montesmoreno0c8def02016-12-22 12:16:23 +0000355 if self.osc_api_version == 'v3.3' and "tenant_id" in filter_dict:
ahmadsa95baa272016-11-30 09:14:11 +0500356 filter_dict['project_id'] = filter_dict.pop('tenant_id')
tierno7edb6752016-03-21 17:37:52 +0100357 net_dict=self.neutron.list_networks(**filter_dict)
358 net_list=net_dict["networks"]
359 self.__net_os2mano(net_list)
tiernoae4a8d12016-07-08 12:30:39 +0200360 return net_list
tierno8e995ce2016-09-22 08:13:00 +0000361 except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200362 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100363
tiernoae4a8d12016-07-08 12:30:39 +0200364 def get_network(self, net_id):
365 '''Obtain details of network from VIM
366 Returns the network information from a network id'''
367 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno7edb6752016-03-21 17:37:52 +0100368 filter_dict={"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +0200369 net_list = self.get_network_list(filter_dict)
tierno7edb6752016-03-21 17:37:52 +0100370 if len(net_list)==0:
tiernoae4a8d12016-07-08 12:30:39 +0200371 raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id))
tierno7edb6752016-03-21 17:37:52 +0100372 elif len(net_list)>1:
tiernoae4a8d12016-07-08 12:30:39 +0200373 raise vimconn.vimconnConflictException("Found more than one network with this criteria")
tierno7edb6752016-03-21 17:37:52 +0100374 net = net_list[0]
375 subnets=[]
376 for subnet_id in net.get("subnets", () ):
377 try:
378 subnet = self.neutron.show_subnet(subnet_id)
379 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200380 self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e)))
381 subnet = {"id": subnet_id, "fault": str(e)}
tierno7edb6752016-03-21 17:37:52 +0100382 subnets.append(subnet)
383 net["subnets"] = subnets
tiernoae4a8d12016-07-08 12:30:39 +0200384 return net
tierno7edb6752016-03-21 17:37:52 +0100385
tiernoae4a8d12016-07-08 12:30:39 +0200386 def delete_network(self, net_id):
387 '''Deletes a tenant network from VIM. Returns the old network identifier'''
388 self.logger.debug("Deleting network '%s' from VIM", net_id)
tierno7edb6752016-03-21 17:37:52 +0100389 try:
390 self._reload_connection()
391 #delete VM ports attached to this networks before the network
392 ports = self.neutron.list_ports(network_id=net_id)
393 for p in ports['ports']:
394 try:
395 self.neutron.delete_port(p["id"])
396 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200397 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
tierno7edb6752016-03-21 17:37:52 +0100398 self.neutron.delete_network(net_id)
tiernoae4a8d12016-07-08 12:30:39 +0200399 return net_id
400 except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
tierno8e995ce2016-09-22 08:13:00 +0000401 ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200402 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100403
tiernoae4a8d12016-07-08 12:30:39 +0200404 def refresh_nets_status(self, net_list):
405 '''Get the status of the networks
406 Params: the list of network identifiers
407 Returns a dictionary with:
408 net_id: #VIM id of this network
409 status: #Mandatory. Text with one of:
410 # DELETED (not found at vim)
411 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
412 # OTHER (Vim reported other status not understood)
413 # ERROR (VIM indicates an ERROR status)
414 # ACTIVE, INACTIVE, DOWN (admin down),
415 # BUILD (on building process)
416 #
417 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
418 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
419
420 '''
421 net_dict={}
422 for net_id in net_list:
423 net = {}
424 try:
425 net_vim = self.get_network(net_id)
426 if net_vim['status'] in netStatus2manoFormat:
427 net["status"] = netStatus2manoFormat[ net_vim['status'] ]
428 else:
429 net["status"] = "OTHER"
430 net["error_msg"] = "VIM status reported " + net_vim['status']
431
tierno8e995ce2016-09-22 08:13:00 +0000432 if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
tiernoae4a8d12016-07-08 12:30:39 +0200433 net['status'] = 'DOWN'
tierno8e995ce2016-09-22 08:13:00 +0000434 try:
435 net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256)
436 except yaml.representer.RepresenterError:
437 net['vim_info'] = str(net_vim)
tiernoae4a8d12016-07-08 12:30:39 +0200438 if net_vim.get('fault'): #TODO
439 net['error_msg'] = str(net_vim['fault'])
440 except vimconn.vimconnNotFoundException as e:
441 self.logger.error("Exception getting net status: %s", str(e))
442 net['status'] = "DELETED"
443 net['error_msg'] = str(e)
444 except vimconn.vimconnException as e:
445 self.logger.error("Exception getting net status: %s", str(e))
446 net['status'] = "VIM_ERROR"
447 net['error_msg'] = str(e)
448 net_dict[net_id] = net
449 return net_dict
450
451 def get_flavor(self, flavor_id):
452 '''Obtain flavor details from the VIM. Returns the flavor dict details'''
453 self.logger.debug("Getting flavor '%s'", flavor_id)
tierno7edb6752016-03-21 17:37:52 +0100454 try:
455 self._reload_connection()
456 flavor = self.nova.flavors.find(id=flavor_id)
457 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +0200458 return flavor.to_dict()
tierno8e995ce2016-09-22 08:13:00 +0000459 except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200460 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100461
tiernoae4a8d12016-07-08 12:30:39 +0200462 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno7edb6752016-03-21 17:37:52 +0100463 '''Adds a tenant flavor to openstack VIM
tiernoae4a8d12016-07-08 12:30:39 +0200464 if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition
tierno7edb6752016-03-21 17:37:52 +0100465 Returns the flavor identifier
466 '''
tiernoae4a8d12016-07-08 12:30:39 +0200467 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno7edb6752016-03-21 17:37:52 +0100468 retry=0
tiernoae4a8d12016-07-08 12:30:39 +0200469 max_retries=3
tierno7edb6752016-03-21 17:37:52 +0100470 name_suffix = 0
tiernoae4a8d12016-07-08 12:30:39 +0200471 name=flavor_data['name']
472 while retry<max_retries:
tierno7edb6752016-03-21 17:37:52 +0100473 retry+=1
474 try:
475 self._reload_connection()
476 if change_name_if_used:
477 #get used names
478 fl_names=[]
479 fl=self.nova.flavors.list()
480 for f in fl:
481 fl_names.append(f.name)
482 while name in fl_names:
483 name_suffix += 1
tiernoae4a8d12016-07-08 12:30:39 +0200484 name = flavor_data['name']+"-" + str(name_suffix)
tierno7edb6752016-03-21 17:37:52 +0100485
tiernoae4a8d12016-07-08 12:30:39 +0200486 ram = flavor_data.get('ram',64)
487 vcpus = flavor_data.get('vcpus',1)
tierno7edb6752016-03-21 17:37:52 +0100488 numa_properties=None
489
tiernoae4a8d12016-07-08 12:30:39 +0200490 extended = flavor_data.get("extended")
tierno7edb6752016-03-21 17:37:52 +0100491 if extended:
492 numas=extended.get("numas")
493 if numas:
494 numa_nodes = len(numas)
495 if numa_nodes > 1:
496 return -1, "Can not add flavor with more than one numa"
497 numa_properties = {"hw:numa_nodes":str(numa_nodes)}
498 numa_properties["hw:mem_page_size"] = "large"
499 numa_properties["hw:cpu_policy"] = "dedicated"
500 numa_properties["hw:numa_mempolicy"] = "strict"
501 for numa in numas:
502 #overwrite ram and vcpus
503 ram = numa['memory']*1024
504 if 'paired-threads' in numa:
505 vcpus = numa['paired-threads']*2
506 numa_properties["hw:cpu_threads_policy"] = "prefer"
507 elif 'cores' in numa:
508 vcpus = numa['cores']
509 #numa_properties["hw:cpu_threads_policy"] = "prefer"
510 elif 'threads' in numa:
511 vcpus = numa['threads']
512 numa_properties["hw:cpu_policy"] = "isolated"
513 for interface in numa.get("interfaces",() ):
514 if interface["dedicated"]=="yes":
tierno809a7802016-07-08 13:31:24 +0200515 raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
tierno7edb6752016-03-21 17:37:52 +0100516 #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
517
518 #create flavor
519 new_flavor=self.nova.flavors.create(name,
520 ram,
521 vcpus,
tiernoae4a8d12016-07-08 12:30:39 +0200522 flavor_data.get('disk',1),
523 is_public=flavor_data.get('is_public', True)
tierno7edb6752016-03-21 17:37:52 +0100524 )
525 #add metadata
526 if numa_properties:
527 new_flavor.set_keys(numa_properties)
tiernoae4a8d12016-07-08 12:30:39 +0200528 return new_flavor.id
tierno7edb6752016-03-21 17:37:52 +0100529 except nvExceptions.Conflict as e:
tiernoae4a8d12016-07-08 12:30:39 +0200530 if change_name_if_used and retry < max_retries:
tierno7edb6752016-03-21 17:37:52 +0100531 continue
tiernoae4a8d12016-07-08 12:30:39 +0200532 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100533 #except nvExceptions.BadRequest as e:
534 except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200535 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100536
tiernoae4a8d12016-07-08 12:30:39 +0200537 def delete_flavor(self,flavor_id):
538 '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
tierno7edb6752016-03-21 17:37:52 +0100539 '''
tiernoae4a8d12016-07-08 12:30:39 +0200540 try:
541 self._reload_connection()
542 self.nova.flavors.delete(flavor_id)
543 return flavor_id
544 #except nvExceptions.BadRequest as e:
tierno8e995ce2016-09-22 08:13:00 +0000545 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200546 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100547
tiernoae4a8d12016-07-08 12:30:39 +0200548 def new_image(self,image_dict):
tierno7edb6752016-03-21 17:37:52 +0100549 '''
tiernoae4a8d12016-07-08 12:30:39 +0200550 Adds a tenant image to VIM. imge_dict is a dictionary with:
551 name: name
552 disk_format: qcow2, vhd, vmdk, raw (by default), ...
553 location: path or URI
554 public: "yes" or "no"
555 metadata: metadata of the image
556 Returns the image_id
tierno7edb6752016-03-21 17:37:52 +0100557 '''
tierno7edb6752016-03-21 17:37:52 +0100558 #using version 1 of glance client
559 glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds
tiernoae4a8d12016-07-08 12:30:39 +0200560 retry=0
561 max_retries=3
562 while retry<max_retries:
tierno7edb6752016-03-21 17:37:52 +0100563 retry+=1
564 try:
565 self._reload_connection()
566 #determine format http://docs.openstack.org/developer/glance/formats.html
567 if "disk_format" in image_dict:
568 disk_format=image_dict["disk_format"]
garciadeblas14480452017-01-10 13:08:07 +0100569 else: #autodiscover based on extension
tierno7edb6752016-03-21 17:37:52 +0100570 if image_dict['location'][-6:]==".qcow2":
571 disk_format="qcow2"
572 elif image_dict['location'][-4:]==".vhd":
573 disk_format="vhd"
574 elif image_dict['location'][-5:]==".vmdk":
575 disk_format="vmdk"
576 elif image_dict['location'][-4:]==".vdi":
577 disk_format="vdi"
578 elif image_dict['location'][-4:]==".iso":
579 disk_format="iso"
580 elif image_dict['location'][-4:]==".aki":
581 disk_format="aki"
582 elif image_dict['location'][-4:]==".ari":
583 disk_format="ari"
584 elif image_dict['location'][-4:]==".ami":
585 disk_format="ami"
586 else:
587 disk_format="raw"
tiernoae4a8d12016-07-08 12:30:39 +0200588 self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location'])
tierno7edb6752016-03-21 17:37:52 +0100589 if image_dict['location'][0:4]=="http":
590 new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
591 container_format="bare", location=image_dict['location'], disk_format=disk_format)
592 else: #local path
593 with open(image_dict['location']) as fimage:
594 new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
595 container_format="bare", data=fimage, disk_format=disk_format)
596 #insert metadata. We cannot use 'new_image.properties.setdefault'
597 #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
598 new_image_nova=self.nova.images.find(id=new_image.id)
599 new_image_nova.metadata.setdefault('location',image_dict['location'])
600 metadata_to_load = image_dict.get('metadata')
601 if metadata_to_load:
602 for k,v in yaml.load(metadata_to_load).iteritems():
603 new_image_nova.metadata.setdefault(k,v)
tiernoae4a8d12016-07-08 12:30:39 +0200604 return new_image.id
605 except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
606 self._format_exception(e)
tierno8e995ce2016-09-22 08:13:00 +0000607 except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200608 if retry==max_retries:
609 continue
610 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100611 except IOError as e: #can not open the file
tiernoae4a8d12016-07-08 12:30:39 +0200612 raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'],
613 http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +0100614
tiernoae4a8d12016-07-08 12:30:39 +0200615 def delete_image(self, image_id):
616 '''Deletes a tenant image from openstack VIM. Returns the old id
tierno7edb6752016-03-21 17:37:52 +0100617 '''
tiernoae4a8d12016-07-08 12:30:39 +0200618 try:
619 self._reload_connection()
620 self.nova.images.delete(image_id)
621 return image_id
tierno8e995ce2016-09-22 08:13:00 +0000622 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove
tiernoae4a8d12016-07-08 12:30:39 +0200623 self._format_exception(e)
624
625 def get_image_id_from_path(self, path):
garciadeblasb69fa9f2016-09-28 12:04:10 +0200626 '''Get the image id from image path in the VIM database. Returns the image_id'''
tiernoae4a8d12016-07-08 12:30:39 +0200627 try:
628 self._reload_connection()
629 images = self.nova.images.list()
630 for image in images:
631 if image.metadata.get("location")==path:
632 return image.id
633 raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
tierno8e995ce2016-09-22 08:13:00 +0000634 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200635 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100636
garciadeblasb69fa9f2016-09-28 12:04:10 +0200637 def get_image_list(self, filter_dict={}):
638 '''Obtain tenant images from VIM
639 Filter_dict can be:
640 id: image id
641 name: image name
642 checksum: image checksum
643 Returns the image list of dictionaries:
644 [{<the fields at Filter_dict plus some VIM specific>}, ...]
645 List can be empty
646 '''
647 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
648 try:
649 self._reload_connection()
650 filter_dict_os=filter_dict.copy()
651 #First we filter by the available filter fields: name, id. The others are removed.
652 filter_dict_os.pop('checksum',None)
653 image_list=self.nova.images.findall(**filter_dict_os)
654 if len(image_list)==0:
655 return []
656 #Then we filter by the rest of filter fields: checksum
657 filtered_list = []
658 for image in image_list:
garciadeblasbb6a1ed2016-09-30 14:02:09 +0000659 image_dict=self.glance.images.get(image.id)
garciadeblas14480452017-01-10 13:08:07 +0100660 if 'checksum' not in filter_dict or image_dict['checksum']==filter_dict.get('checksum'):
661 filtered_list.append(image_dict)
garciadeblasb69fa9f2016-09-28 12:04:10 +0200662 return filtered_list
663 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
664 self._format_exception(e)
665
montesmoreno0c8def02016-12-22 12:16:23 +0000666 def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None,disk_list=None):
tierno7edb6752016-03-21 17:37:52 +0100667 '''Adds a VM instance to VIM
668 Params:
669 start: indicates if VM must start or boot in pause mode. Ignored
670 image_id,flavor_id: iamge and flavor uuid
671 net_list: list of interfaces, each one is a dictionary with:
672 name:
673 net_id: network uuid to connect
674 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
675 model: interface model, ignored #TODO
676 mac_address: used for SR-IOV ifaces #TODO for other types
677 use: 'data', 'bridge', 'mgmt'
678 type: 'virtual', 'PF', 'VF', 'VFnotShared'
679 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +0500680 floating_ip: True/False (or it can be None)
tierno7edb6752016-03-21 17:37:52 +0100681 #TODO ip, security groups
tiernoae4a8d12016-07-08 12:30:39 +0200682 Returns the instance identifier
tierno7edb6752016-03-21 17:37:52 +0100683 '''
tiernoae4a8d12016-07-08 12:30:39 +0200684 self.logger.debug("Creating VM image '%s' flavor '%s' nics='%s'",image_id, flavor_id,str(net_list))
tierno7edb6752016-03-21 17:37:52 +0100685 try:
tierno6e116232016-07-18 13:01:40 +0200686 metadata={}
tierno7edb6752016-03-21 17:37:52 +0100687 net_list_vim=[]
ahmadsaf853d452016-12-22 11:33:47 +0500688 external_network=[] #list of external networks to be connected to instance, later on used to create floating_ip
tierno7edb6752016-03-21 17:37:52 +0100689 self._reload_connection()
tiernoae4a8d12016-07-08 12:30:39 +0200690 metadata_vpci={} #For a specific neutron plugin
tierno7edb6752016-03-21 17:37:52 +0100691 for net in net_list:
692 if not net.get("net_id"): #skip non connected iface
693 continue
ahmadsaf853d452016-12-22 11:33:47 +0500694 if net["type"]=="virtual" or net["type"]=="VF":
tierno7edb6752016-03-21 17:37:52 +0100695 port_dict={
ahmadsaf853d452016-12-22 11:33:47 +0500696 "network_id": net["net_id"],
697 "name": net.get("name"),
698 "admin_state_up": True
699 }
700 if net["type"]=="virtual":
701 if "vpci" in net:
702 metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
703 else: # for VF
704 if "vpci" in net:
705 if "VF" not in metadata_vpci:
706 metadata_vpci["VF"]=[]
707 metadata_vpci["VF"].append([ net["vpci"], "" ])
708 port_dict["binding:vnic_type"]="direct"
tierno7edb6752016-03-21 17:37:52 +0100709 if not port_dict["name"]:
ahmadsaf853d452016-12-22 11:33:47 +0500710 port_dict["name"]=name
tierno7edb6752016-03-21 17:37:52 +0100711 if net.get("mac_address"):
712 port_dict["mac_address"]=net["mac_address"]
montesmorenocf227142017-01-12 12:24:21 +0000713 if net.get("port_security") == False:
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000714 port_dict["port_security_enabled"]=net["port_security"]
tierno7edb6752016-03-21 17:37:52 +0100715 new_port = self.neutron.create_port({"port": port_dict })
716 net["mac_adress"] = new_port["port"]["mac_address"]
717 net["vim_id"] = new_port["port"]["id"]
ahmadsaf853d452016-12-22 11:33:47 +0500718 net["ip"] = new_port["port"].get("fixed_ips", [{}])[0].get("ip_address")
tierno7edb6752016-03-21 17:37:52 +0100719 net_list_vim.append({"port-id": new_port["port"]["id"]})
ahmadsaf853d452016-12-22 11:33:47 +0500720 else: # for PF
721 self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ")
722 #TODO insert this when openstack consider passthrough ports as openstack neutron ports
723 if net.get('floating_ip', False):
724 external_network.append(net)
725
tierno7edb6752016-03-21 17:37:52 +0100726 if metadata_vpci:
727 metadata = {"pci_assignement": json.dumps(metadata_vpci)}
tiernoafbced42016-07-23 01:43:53 +0200728 if len(metadata["pci_assignement"]) >255:
tierno6e116232016-07-18 13:01:40 +0200729 #limit the metadata size
730 #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
731 self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
732 metadata = {}
tierno7edb6752016-03-21 17:37:52 +0100733
tiernoae4a8d12016-07-08 12:30:39 +0200734 self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s",
735 name, image_id, flavor_id, str(net_list_vim), description, str(metadata))
tierno7edb6752016-03-21 17:37:52 +0100736
737 security_groups = self.config.get('security_groups')
738 if type(security_groups) is str:
739 security_groups = ( security_groups, )
tierno36c0b172017-01-12 18:32:28 +0100740 #cloud config
741 userdata=None
742 config_drive = None
tiernoa4e1a6e2016-08-31 14:19:40 +0200743 if isinstance(cloud_config, dict):
tierno36c0b172017-01-12 18:32:28 +0100744 if cloud_config.get("user-data"):
745 userdata=cloud_config["user-data"]
746 if cloud_config.get("boot-data-drive") != None:
747 config_drive = cloud_config["boot-data-drive"]
748 if cloud_config.get("config-files") or cloud_config.get("users") or cloud_config.get("key-pairs"):
749 if userdata:
750 raise vimconn.vimconnConflictException("Cloud-config cannot contain both 'userdata' and 'config-files'/'users'/'key-pairs'")
751 userdata_dict={}
752 #default user
753 if cloud_config.get("key-pairs"):
754 userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
755 userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"] }]
756 if cloud_config.get("users"):
757 if "users" not in cloud_config:
758 userdata_dict["users"] = [ "default" ]
759 for user in cloud_config["users"]:
760 user_info = {
761 "name" : user["name"],
762 "sudo": "ALL = (ALL)NOPASSWD:ALL"
763 }
764 if "user-info" in user:
765 user_info["gecos"] = user["user-info"]
766 if user.get("key-pairs"):
767 user_info["ssh-authorized-keys"] = user["key-pairs"]
768 userdata_dict["users"].append(user_info)
769
770 if cloud_config.get("config-files"):
771 userdata_dict["write_files"] = []
772 for file in cloud_config["config-files"]:
773 file_info = {
774 "path" : file["dest"],
775 "content": file["content"]
776 }
777 if file.get("encoding"):
778 file_info["encoding"] = file["encoding"]
779 if file.get("permissions"):
780 file_info["permissions"] = file["permissions"]
781 if file.get("owner"):
782 file_info["owner"] = file["owner"]
783 userdata_dict["write_files"].append(file_info)
784 userdata = "#cloud-config\n"
785 userdata += yaml.safe_dump(userdata_dict, indent=4, default_flow_style=False)
tiernoa4e1a6e2016-08-31 14:19:40 +0200786 self.logger.debug("userdata: %s", userdata)
787 elif isinstance(cloud_config, str):
788 userdata = cloud_config
montesmoreno0c8def02016-12-22 12:16:23 +0000789
790 #Create additional volumes in case these are present in disk_list
791 block_device_mapping = None
792 base_disk_index = ord('b')
793 if disk_list != None:
794 block_device_mapping = dict()
795 for disk in disk_list:
796 if 'image_id' in disk:
797 volume = self.cinder.volumes.create(size = disk['size'],name = name + '_vd' +
798 chr(base_disk_index), imageRef = disk['image_id'])
799 else:
800 volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
801 chr(base_disk_index))
802 block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
803 base_disk_index += 1
804
805 #wait until volumes are with status available
806 keep_waiting = True
807 elapsed_time = 0
808 while keep_waiting and elapsed_time < volume_timeout:
809 keep_waiting = False
810 for volume_id in block_device_mapping.itervalues():
811 if self.cinder.volumes.get(volume_id).status != 'available':
812 keep_waiting = True
813 if keep_waiting:
814 time.sleep(1)
815 elapsed_time += 1
816
817 #if we exceeded the timeout rollback
818 if elapsed_time >= volume_timeout:
819 #delete the volumes we just created
820 for volume_id in block_device_mapping.itervalues():
821 self.cinder.volumes.delete(volume_id)
822
823 #delete ports we just created
824 for net_item in net_list_vim:
825 if 'port-id' in net_item:
montesmorenocf227142017-01-12 12:24:21 +0000826 self.neutron.delete_port(net_item['port-id'])
montesmoreno0c8def02016-12-22 12:16:23 +0000827
828 raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
829 http_code=vimconn.HTTP_Request_Timeout)
830
tierno7edb6752016-03-21 17:37:52 +0100831 server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, meta=metadata,
montesmoreno0c8def02016-12-22 12:16:23 +0000832 security_groups=security_groups,
833 availability_zone=self.config.get('availability_zone'),
834 key_name=self.config.get('keypair'),
835 userdata=userdata,
tierno36c0b172017-01-12 18:32:28 +0100836 config_drive = config_drive,
montesmoreno0c8def02016-12-22 12:16:23 +0000837 block_device_mapping = block_device_mapping
838 ) # , description=description)
tiernoae4a8d12016-07-08 12:30:39 +0200839 #print "DONE :-)", server
ahmadsaf853d452016-12-22 11:33:47 +0500840 pool_id = None
841 floating_ips = self.neutron.list_floatingips().get("floatingips", ())
842 for floating_network in external_network:
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000843 # wait until vm is active
844 elapsed_time = 0
845 while elapsed_time < server_timeout:
846 status = self.nova.servers.get(server.id).status
847 if status == 'ACTIVE':
848 break
849 time.sleep(1)
850 elapsed_time += 1
851
852 #if we exceeded the timeout rollback
853 if elapsed_time >= server_timeout:
854 self.delete_vminstance(server.id)
855 raise vimconn.vimconnException('Timeout creating instance ' + name,
856 http_code=vimconn.HTTP_Request_Timeout)
857
ahmadsaf853d452016-12-22 11:33:47 +0500858 assigned = False
859 while(assigned == False):
860 if floating_ips:
861 ip = floating_ips.pop(0)
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000862 if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
ahmadsaf853d452016-12-22 11:33:47 +0500863 free_floating_ip = ip.get("floating_ip_address")
864 try:
865 fix_ip = floating_network.get('ip')
866 server.add_floating_ip(free_floating_ip, fix_ip)
867 assigned = True
868 except Exception as e:
869 self.delete_vminstance(server.id)
870 raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
871 else:
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000872 #Find the external network
873 external_nets = list()
874 for net in self.neutron.list_networks()['networks']:
875 if net['router:external']:
876 external_nets.append(net)
877
878 if len(external_nets) == 0:
879 self.delete_vminstance(server.id)
880 raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
881 "network is present",
882 http_code=vimconn.HTTP_Conflict)
883 if len(external_nets) > 1:
884 self.delete_vminstance(server.id)
885 raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
886 "external networks are present",
887 http_code=vimconn.HTTP_Conflict)
888
889 pool_id = external_nets[0].get('id')
890 param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
ahmadsaf853d452016-12-22 11:33:47 +0500891 try:
892 #self.logger.debug("Creating floating IP")
893 new_floating_ip = self.neutron.create_floatingip(param)
894 free_floating_ip = new_floating_ip['floatingip']['floating_ip_address']
895 fix_ip = floating_network.get('ip')
896 server.add_floating_ip(free_floating_ip, fix_ip)
897 assigned=True
898 except Exception as e:
899 self.delete_vminstance(server.id)
900 raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
tierno7edb6752016-03-21 17:37:52 +0100901
tiernoae4a8d12016-07-08 12:30:39 +0200902 return server.id
tierno7edb6752016-03-21 17:37:52 +0100903# except nvExceptions.NotFound as e:
904# error_value=-vimconn.HTTP_Not_Found
905# error_text= "vm instance %s not found" % vm_id
tierno8e995ce2016-09-22 08:13:00 +0000906 except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError
907 ) as e:
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000908 # delete the volumes we just created
909 if block_device_mapping != None:
910 for volume_id in block_device_mapping.itervalues():
911 self.cinder.volumes.delete(volume_id)
912
913 # delete ports we just created
914 for net_item in net_list_vim:
915 if 'port-id' in net_item:
montesmorenocf227142017-01-12 12:24:21 +0000916 self.neutron.delete_port(net_item['port-id'])
tiernoae4a8d12016-07-08 12:30:39 +0200917 self._format_exception(e)
918 except TypeError as e:
919 raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +0100920
tiernoae4a8d12016-07-08 12:30:39 +0200921 def get_vminstance(self,vm_id):
tierno7edb6752016-03-21 17:37:52 +0100922 '''Returns the VM instance information from VIM'''
tiernoae4a8d12016-07-08 12:30:39 +0200923 #self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +0100924 try:
925 self._reload_connection()
926 server = self.nova.servers.find(id=vm_id)
927 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +0200928 return server.to_dict()
tierno8e995ce2016-09-22 08:13:00 +0000929 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200930 self._format_exception(e)
931
932 def get_vminstance_console(self,vm_id, console_type="vnc"):
tierno7edb6752016-03-21 17:37:52 +0100933 '''
934 Get a console for the virtual machine
935 Params:
936 vm_id: uuid of the VM
937 console_type, can be:
938 "novnc" (by default), "xvpvnc" for VNC types,
939 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +0200940 Returns dict with the console parameters:
941 protocol: ssh, ftp, http, https, ...
942 server: usually ip address
943 port: the http, ssh, ... port
944 suffix: extra text, e.g. the http path and query string
tierno7edb6752016-03-21 17:37:52 +0100945 '''
tiernoae4a8d12016-07-08 12:30:39 +0200946 self.logger.debug("Getting VM CONSOLE from VIM")
tierno7edb6752016-03-21 17:37:52 +0100947 try:
948 self._reload_connection()
949 server = self.nova.servers.find(id=vm_id)
950 if console_type == None or console_type == "novnc":
951 console_dict = server.get_vnc_console("novnc")
952 elif console_type == "xvpvnc":
953 console_dict = server.get_vnc_console(console_type)
954 elif console_type == "rdp-html5":
955 console_dict = server.get_rdp_console(console_type)
956 elif console_type == "spice-html5":
957 console_dict = server.get_spice_console(console_type)
958 else:
tiernoae4a8d12016-07-08 12:30:39 +0200959 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +0100960
961 console_dict1 = console_dict.get("console")
962 if console_dict1:
963 console_url = console_dict1.get("url")
964 if console_url:
965 #parse console_url
966 protocol_index = console_url.find("//")
967 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
968 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
969 if protocol_index < 0 or port_index<0 or suffix_index<0:
970 return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
971 console_dict={"protocol": console_url[0:protocol_index],
972 "server": console_url[protocol_index+2:port_index],
973 "port": console_url[port_index:suffix_index],
974 "suffix": console_url[suffix_index+1:]
975 }
976 protocol_index += 2
tiernoae4a8d12016-07-08 12:30:39 +0200977 return console_dict
978 raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
tierno7edb6752016-03-21 17:37:52 +0100979
tierno8e995ce2016-09-22 08:13:00 +0000980 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200981 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100982
tiernoae4a8d12016-07-08 12:30:39 +0200983 def delete_vminstance(self, vm_id):
984 '''Removes a VM instance from VIM. Returns the old identifier
tierno7edb6752016-03-21 17:37:52 +0100985 '''
tiernoae4a8d12016-07-08 12:30:39 +0200986 #print "osconnector: Getting VM from VIM"
tierno7edb6752016-03-21 17:37:52 +0100987 try:
988 self._reload_connection()
989 #delete VM ports attached to this networks before the virtual machine
990 ports = self.neutron.list_ports(device_id=vm_id)
991 for p in ports['ports']:
992 try:
993 self.neutron.delete_port(p["id"])
994 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200995 self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e))
montesmoreno0c8def02016-12-22 12:16:23 +0000996
997 #commented because detaching the volumes makes the servers.delete not work properly ?!?
998 #dettach volumes attached
999 server = self.nova.servers.get(vm_id)
1000 volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']
1001 #for volume in volumes_attached_dict:
1002 # self.cinder.volumes.detach(volume['id'])
1003
tierno7edb6752016-03-21 17:37:52 +01001004 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00001005
1006 #delete volumes.
1007 #Although having detached them should have them in active status
1008 #we ensure in this loop
1009 keep_waiting = True
1010 elapsed_time = 0
1011 while keep_waiting and elapsed_time < volume_timeout:
1012 keep_waiting = False
1013 for volume in volumes_attached_dict:
1014 if self.cinder.volumes.get(volume['id']).status != 'available':
1015 keep_waiting = True
1016 else:
1017 self.cinder.volumes.delete(volume['id'])
1018 if keep_waiting:
1019 time.sleep(1)
1020 elapsed_time += 1
1021
tiernoae4a8d12016-07-08 12:30:39 +02001022 return vm_id
tierno8e995ce2016-09-22 08:13:00 +00001023 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001024 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001025 #TODO insert exception vimconn.HTTP_Unauthorized
1026 #if reaching here is because an exception
tierno7edb6752016-03-21 17:37:52 +01001027
tiernoae4a8d12016-07-08 12:30:39 +02001028 def refresh_vms_status(self, vm_list):
1029 '''Get the status of the virtual machines and their interfaces/ports
1030 Params: the list of VM identifiers
1031 Returns a dictionary with:
1032 vm_id: #VIM id of this Virtual Machine
1033 status: #Mandatory. Text with one of:
1034 # DELETED (not found at vim)
1035 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1036 # OTHER (Vim reported other status not understood)
1037 # ERROR (VIM indicates an ERROR status)
1038 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1039 # CREATING (on building process), ERROR
1040 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1041 #
1042 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1043 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1044 interfaces:
1045 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1046 mac_address: #Text format XX:XX:XX:XX:XX:XX
1047 vim_net_id: #network id where this interface is connected
1048 vim_interface_id: #interface/port VIM id
1049 ip_address: #null, or text with IPv4, IPv6 address
tierno7edb6752016-03-21 17:37:52 +01001050 '''
tiernoae4a8d12016-07-08 12:30:39 +02001051 vm_dict={}
1052 self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM")
1053 for vm_id in vm_list:
1054 vm={}
1055 try:
1056 vm_vim = self.get_vminstance(vm_id)
1057 if vm_vim['status'] in vmStatus2manoFormat:
1058 vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ]
tierno7edb6752016-03-21 17:37:52 +01001059 else:
tiernoae4a8d12016-07-08 12:30:39 +02001060 vm['status'] = "OTHER"
1061 vm['error_msg'] = "VIM status reported " + vm_vim['status']
tierno8e995ce2016-09-22 08:13:00 +00001062 try:
1063 vm['vim_info'] = yaml.safe_dump(vm_vim, default_flow_style=True, width=256)
1064 except yaml.representer.RepresenterError:
1065 vm['vim_info'] = str(vm_vim)
tiernoae4a8d12016-07-08 12:30:39 +02001066 vm["interfaces"] = []
1067 if vm_vim.get('fault'):
1068 vm['error_msg'] = str(vm_vim['fault'])
1069 #get interfaces
tierno7edb6752016-03-21 17:37:52 +01001070 try:
tiernoae4a8d12016-07-08 12:30:39 +02001071 self._reload_connection()
1072 port_dict=self.neutron.list_ports(device_id=vm_id)
1073 for port in port_dict["ports"]:
1074 interface={}
tierno8e995ce2016-09-22 08:13:00 +00001075 try:
1076 interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
1077 except yaml.representer.RepresenterError:
1078 interface['vim_info'] = str(port)
tiernoae4a8d12016-07-08 12:30:39 +02001079 interface["mac_address"] = port.get("mac_address")
1080 interface["vim_net_id"] = port["network_id"]
1081 interface["vim_interface_id"] = port["id"]
1082 ips=[]
1083 #look for floating ip address
1084 floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
1085 if floating_ip_dict.get("floatingips"):
1086 ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
tierno7edb6752016-03-21 17:37:52 +01001087
tiernoae4a8d12016-07-08 12:30:39 +02001088 for subnet in port["fixed_ips"]:
1089 ips.append(subnet["ip_address"])
1090 interface["ip_address"] = ";".join(ips)
1091 vm["interfaces"].append(interface)
1092 except Exception as e:
1093 self.logger.error("Error getting vm interface information " + type(e).__name__ + ": "+ str(e))
1094 except vimconn.vimconnNotFoundException as e:
1095 self.logger.error("Exception getting vm status: %s", str(e))
1096 vm['status'] = "DELETED"
1097 vm['error_msg'] = str(e)
1098 except vimconn.vimconnException as e:
1099 self.logger.error("Exception getting vm status: %s", str(e))
1100 vm['status'] = "VIM_ERROR"
1101 vm['error_msg'] = str(e)
1102 vm_dict[vm_id] = vm
1103 return vm_dict
tierno7edb6752016-03-21 17:37:52 +01001104
tiernoae4a8d12016-07-08 12:30:39 +02001105 def action_vminstance(self, vm_id, action_dict):
tierno7edb6752016-03-21 17:37:52 +01001106 '''Send and action over a VM instance from VIM
tiernoae4a8d12016-07-08 12:30:39 +02001107 Returns the vm_id if the action was successfully sent to the VIM'''
1108 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
tierno7edb6752016-03-21 17:37:52 +01001109 try:
1110 self._reload_connection()
1111 server = self.nova.servers.find(id=vm_id)
1112 if "start" in action_dict:
1113 if action_dict["start"]=="rebuild":
1114 server.rebuild()
1115 else:
1116 if server.status=="PAUSED":
1117 server.unpause()
1118 elif server.status=="SUSPENDED":
1119 server.resume()
1120 elif server.status=="SHUTOFF":
1121 server.start()
1122 elif "pause" in action_dict:
1123 server.pause()
1124 elif "resume" in action_dict:
1125 server.resume()
1126 elif "shutoff" in action_dict or "shutdown" in action_dict:
1127 server.stop()
1128 elif "forceOff" in action_dict:
1129 server.stop() #TODO
1130 elif "terminate" in action_dict:
1131 server.delete()
1132 elif "createImage" in action_dict:
1133 server.create_image()
1134 #"path":path_schema,
1135 #"description":description_schema,
1136 #"name":name_schema,
1137 #"metadata":metadata_schema,
1138 #"imageRef": id_schema,
1139 #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
1140 elif "rebuild" in action_dict:
1141 server.rebuild(server.image['id'])
1142 elif "reboot" in action_dict:
1143 server.reboot() #reboot_type='SOFT'
1144 elif "console" in action_dict:
1145 console_type = action_dict["console"]
1146 if console_type == None or console_type == "novnc":
1147 console_dict = server.get_vnc_console("novnc")
1148 elif console_type == "xvpvnc":
1149 console_dict = server.get_vnc_console(console_type)
1150 elif console_type == "rdp-html5":
1151 console_dict = server.get_rdp_console(console_type)
1152 elif console_type == "spice-html5":
1153 console_dict = server.get_spice_console(console_type)
1154 else:
tiernoae4a8d12016-07-08 12:30:39 +02001155 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type),
1156 http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +01001157 try:
1158 console_url = console_dict["console"]["url"]
1159 #parse console_url
1160 protocol_index = console_url.find("//")
1161 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
1162 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
1163 if protocol_index < 0 or port_index<0 or suffix_index<0:
tiernoae4a8d12016-07-08 12:30:39 +02001164 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
tierno7edb6752016-03-21 17:37:52 +01001165 console_dict2={"protocol": console_url[0:protocol_index],
1166 "server": console_url[protocol_index+2 : port_index],
1167 "port": int(console_url[port_index+1 : suffix_index]),
1168 "suffix": console_url[suffix_index+1:]
1169 }
tiernoae4a8d12016-07-08 12:30:39 +02001170 return console_dict2
1171 except Exception as e:
1172 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
tierno7edb6752016-03-21 17:37:52 +01001173
tiernoae4a8d12016-07-08 12:30:39 +02001174 return vm_id
tierno8e995ce2016-09-22 08:13:00 +00001175 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001176 self._format_exception(e)
1177 #TODO insert exception vimconn.HTTP_Unauthorized
1178
1179#NOT USED FUNCTIONS
1180
1181 def new_external_port(self, port_data):
1182 #TODO openstack if needed
1183 '''Adds a external port to VIM'''
1184 '''Returns the port identifier'''
1185 return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
1186
1187 def connect_port_network(self, port_id, network_id, admin=False):
1188 #TODO openstack if needed
1189 '''Connects a external port to a network'''
1190 '''Returns status code of the VIM response'''
1191 return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
1192
1193 def new_user(self, user_name, user_passwd, tenant_id=None):
1194 '''Adds a new user to openstack VIM'''
1195 '''Returns the user identifier'''
1196 self.logger.debug("osconnector: Adding a new user to VIM")
1197 try:
1198 self._reload_connection()
1199 user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id)
1200 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
1201 return user.id
1202 except ksExceptions.ConnectionError as e:
1203 error_value=-vimconn.HTTP_Bad_Request
1204 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1205 except ksExceptions.ClientException as e: #TODO remove
tierno7edb6752016-03-21 17:37:52 +01001206 error_value=-vimconn.HTTP_Bad_Request
1207 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1208 #TODO insert exception vimconn.HTTP_Unauthorized
1209 #if reaching here is because an exception
1210 if self.debug:
tiernoae4a8d12016-07-08 12:30:39 +02001211 self.logger.debug("new_user " + error_text)
tierno7edb6752016-03-21 17:37:52 +01001212 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02001213
1214 def delete_user(self, user_id):
1215 '''Delete a user from openstack VIM'''
1216 '''Returns the user identifier'''
1217 if self.debug:
1218 print "osconnector: Deleting a user from VIM"
1219 try:
1220 self._reload_connection()
1221 self.keystone.users.delete(user_id)
1222 return 1, user_id
1223 except ksExceptions.ConnectionError as e:
1224 error_value=-vimconn.HTTP_Bad_Request
1225 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1226 except ksExceptions.NotFound as e:
1227 error_value=-vimconn.HTTP_Not_Found
1228 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1229 except ksExceptions.ClientException as e: #TODO remove
1230 error_value=-vimconn.HTTP_Bad_Request
1231 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1232 #TODO insert exception vimconn.HTTP_Unauthorized
1233 #if reaching here is because an exception
1234 if self.debug:
1235 print "delete_tenant " + error_text
1236 return error_value, error_text
1237
tierno7edb6752016-03-21 17:37:52 +01001238 def get_hosts_info(self):
1239 '''Get the information of deployed hosts
1240 Returns the hosts content'''
1241 if self.debug:
1242 print "osconnector: Getting Host info from VIM"
1243 try:
1244 h_list=[]
1245 self._reload_connection()
1246 hypervisors = self.nova.hypervisors.list()
1247 for hype in hypervisors:
1248 h_list.append( hype.to_dict() )
1249 return 1, {"hosts":h_list}
1250 except nvExceptions.NotFound as e:
1251 error_value=-vimconn.HTTP_Not_Found
1252 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1253 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1254 error_value=-vimconn.HTTP_Bad_Request
1255 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1256 #TODO insert exception vimconn.HTTP_Unauthorized
1257 #if reaching here is because an exception
1258 if self.debug:
1259 print "get_hosts_info " + error_text
1260 return error_value, error_text
1261
1262 def get_hosts(self, vim_tenant):
1263 '''Get the hosts and deployed instances
1264 Returns the hosts content'''
1265 r, hype_dict = self.get_hosts_info()
1266 if r<0:
1267 return r, hype_dict
1268 hypervisors = hype_dict["hosts"]
1269 try:
1270 servers = self.nova.servers.list()
1271 for hype in hypervisors:
1272 for server in servers:
1273 if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
1274 if 'vm' in hype:
1275 hype['vm'].append(server.id)
1276 else:
1277 hype['vm'] = [server.id]
1278 return 1, hype_dict
1279 except nvExceptions.NotFound as e:
1280 error_value=-vimconn.HTTP_Not_Found
1281 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1282 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1283 error_value=-vimconn.HTTP_Bad_Request
1284 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1285 #TODO insert exception vimconn.HTTP_Unauthorized
1286 #if reaching here is because an exception
1287 if self.debug:
1288 print "get_hosts " + error_text
1289 return error_value, error_text
1290
tierno7edb6752016-03-21 17:37:52 +01001291