blob: f9a2185c6e630c1862f192ea5ce34ba2a5eb049d [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
garciadeblas2299e3b2017-01-26 14:35:55 +000037import random
tierno7edb6752016-03-21 17:37:52 +010038
ahmadsa95baa272016-11-30 09:14:11 +050039from novaclient import client as nClient_v2, exceptions as nvExceptions, api_versions as APIVersion
40import keystoneclient.v2_0.client as ksClient_v2
41from novaclient.v2.client import Client as nClient
42import keystoneclient.v3.client as ksClient
tierno7edb6752016-03-21 17:37:52 +010043import keystoneclient.exceptions as ksExceptions
44import glanceclient.v2.client as glClient
45import glanceclient.client as gl1Client
46import glanceclient.exc as gl1Exceptions
montesmoreno0c8def02016-12-22 12:16:23 +000047import cinderclient.v2.client as cClient_v2
tierno7edb6752016-03-21 17:37:52 +010048from httplib import HTTPException
ahmadsa95baa272016-11-30 09:14:11 +050049from neutronclient.neutron import client as neClient_v2
50from neutronclient.v2_0 import client as neClient
tierno7edb6752016-03-21 17:37:52 +010051from neutronclient.common import exceptions as neExceptions
52from requests.exceptions import ConnectionError
53
54'''contain the openstack virtual machine status to openmano status'''
55vmStatus2manoFormat={'ACTIVE':'ACTIVE',
56 'PAUSED':'PAUSED',
57 'SUSPENDED': 'SUSPENDED',
58 'SHUTOFF':'INACTIVE',
59 'BUILD':'BUILD',
60 'ERROR':'ERROR','DELETED':'DELETED'
61 }
62netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
63 }
64
montesmoreno0c8def02016-12-22 12:16:23 +000065#global var to have a timeout creating and deleting volumes
66volume_timeout = 60
montesmoreno2a1fc4e2017-01-09 16:46:04 +000067server_timeout = 60
montesmoreno0c8def02016-12-22 12:16:23 +000068
tierno7edb6752016-03-21 17:37:52 +010069class vimconnector(vimconn.vimconnector):
tiernofe789902016-09-29 14:20:44 +000070 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 +010071 '''using common constructor parameters. In this case
72 'url' is the keystone authorization url,
73 'url_admin' is not use
74 '''
ahmadsa95baa272016-11-30 09:14:11 +050075 self.osc_api_version = 'v2.0'
76 if config.get('APIversion') == 'v3.3':
77 self.osc_api_version = 'v3.3'
tiernoae4a8d12016-07-08 12:30:39 +020078 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
tierno7edb6752016-03-21 17:37:52 +010079
80 self.k_creds={}
81 self.n_creds={}
tiernoc75a5dc2017-01-18 15:53:44 +010082 if self.config.get("insecure"):
83 self.k_creds["insecure"] = True
84 self.n_creds["insecure"] = True
tierno7edb6752016-03-21 17:37:52 +010085 if not url:
86 raise TypeError, 'url param can not be NoneType'
87 self.k_creds['auth_url'] = url
88 self.n_creds['auth_url'] = url
tierno392f2852016-05-13 12:28:55 +020089 if tenant_name:
90 self.k_creds['tenant_name'] = tenant_name
91 self.n_creds['project_id'] = tenant_name
92 if tenant_id:
93 self.k_creds['tenant_id'] = tenant_id
94 self.n_creds['tenant_id'] = tenant_id
tierno7edb6752016-03-21 17:37:52 +010095 if user:
96 self.k_creds['username'] = user
97 self.n_creds['username'] = user
98 if passwd:
99 self.k_creds['password'] = passwd
100 self.n_creds['api_key'] = passwd
ahmadsa95baa272016-11-30 09:14:11 +0500101 if self.osc_api_version == 'v3.3':
102 self.k_creds['project_name'] = tenant_name
103 self.k_creds['project_id'] = tenant_id
montesmorenocf227142017-01-12 12:24:21 +0000104 if config.get('region_name'):
105 self.k_creds['region_name'] = config.get('region_name')
106 self.n_creds['region_name'] = config.get('region_name')
montesmoreno0c8def02016-12-22 12:16:23 +0000107
tierno7edb6752016-03-21 17:37:52 +0100108 self.reload_client = True
tierno73ad9e42016-09-12 18:11:11 +0200109 self.logger = logging.getLogger('openmano.vim.openstack')
tiernofe789902016-09-29 14:20:44 +0000110 if log_level:
111 self.logger.setLevel( getattr(logging, log_level) )
tierno7edb6752016-03-21 17:37:52 +0100112
113 def __setitem__(self,index, value):
114 '''Set individuals parameters
115 Throw TypeError, KeyError
116 '''
tierno392f2852016-05-13 12:28:55 +0200117 if index=='tenant_id':
tierno7edb6752016-03-21 17:37:52 +0100118 self.reload_client=True
tierno392f2852016-05-13 12:28:55 +0200119 self.tenant_id = value
ahmadsa95baa272016-11-30 09:14:11 +0500120 if self.osc_api_version == 'v3.3':
121 if value:
122 self.k_creds['project_id'] = value
123 self.n_creds['project_id'] = value
124 else:
125 del self.k_creds['project_id']
126 del self.n_creds['project_id']
tierno392f2852016-05-13 12:28:55 +0200127 else:
ahmadsa95baa272016-11-30 09:14:11 +0500128 if value:
129 self.k_creds['tenant_id'] = value
130 self.n_creds['tenant_id'] = value
131 else:
132 del self.k_creds['tenant_id']
133 del self.n_creds['tenant_id']
tierno392f2852016-05-13 12:28:55 +0200134 elif index=='tenant_name':
135 self.reload_client=True
136 self.tenant_name = value
ahmadsa95baa272016-11-30 09:14:11 +0500137 if self.osc_api_version == 'v3.3':
138 if value:
139 self.k_creds['project_name'] = value
140 self.n_creds['project_name'] = value
141 else:
142 del self.k_creds['project_name']
143 del self.n_creds['project_name']
tierno7edb6752016-03-21 17:37:52 +0100144 else:
ahmadsa95baa272016-11-30 09:14:11 +0500145 if value:
146 self.k_creds['tenant_name'] = value
147 self.n_creds['project_id'] = value
148 else:
149 del self.k_creds['tenant_name']
150 del self.n_creds['project_id']
tierno7edb6752016-03-21 17:37:52 +0100151 elif index=='user':
152 self.reload_client=True
153 self.user = value
154 if value:
155 self.k_creds['username'] = value
156 self.n_creds['username'] = value
157 else:
158 del self.k_creds['username']
159 del self.n_creds['username']
160 elif index=='passwd':
161 self.reload_client=True
162 self.passwd = value
163 if value:
164 self.k_creds['password'] = value
165 self.n_creds['api_key'] = value
166 else:
167 del self.k_creds['password']
168 del self.n_creds['api_key']
169 elif index=='url':
170 self.reload_client=True
171 self.url = value
172 if value:
173 self.k_creds['auth_url'] = value
174 self.n_creds['auth_url'] = value
175 else:
176 raise TypeError, 'url param can not be NoneType'
177 else:
178 vimconn.vimconnector.__setitem__(self,index, value)
179
180 def _reload_connection(self):
181 '''Called before any operation, it check if credentials has changed
182 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
183 '''
184 #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
185 if self.reload_client:
186 #test valid params
187 if len(self.n_creds) <4:
188 raise ksExceptions.ClientException("Not enough parameters to connect to openstack")
ahmadsa95baa272016-11-30 09:14:11 +0500189 if self.osc_api_version == 'v3.3':
190 self.nova = nClient(APIVersion(version_str='2'), **self.n_creds)
montesmoreno0c8def02016-12-22 12:16:23 +0000191 #TODO To be updated for v3
192 #self.cinder = cClient.Client(**self.n_creds)
ahmadsa95baa272016-11-30 09:14:11 +0500193 self.keystone = ksClient.Client(**self.k_creds)
194 self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
195 self.neutron = neClient.Client(APIVersion(version_str='2'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
196 else:
197 self.nova = nClient_v2.Client('2', **self.n_creds)
montesmoreno0c8def02016-12-22 12:16:23 +0000198 self.cinder = cClient_v2.Client(**self.n_creds)
ahmadsa95baa272016-11-30 09:14:11 +0500199 self.keystone = ksClient_v2.Client(**self.k_creds)
200 self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
201 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 +0100202 self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL')
203 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 +0100204 self.reload_client = False
ahmadsa95baa272016-11-30 09:14:11 +0500205
tierno7edb6752016-03-21 17:37:52 +0100206 def __net_os2mano(self, net_list_dict):
207 '''Transform the net openstack format to mano format
208 net_list_dict can be a list of dict or a single dict'''
209 if type(net_list_dict) is dict:
210 net_list_=(net_list_dict,)
211 elif type(net_list_dict) is list:
212 net_list_=net_list_dict
213 else:
214 raise TypeError("param net_list_dict must be a list or a dictionary")
215 for net in net_list_:
216 if net.get('provider:network_type') == "vlan":
217 net['type']='data'
218 else:
219 net['type']='bridge'
tiernoae4a8d12016-07-08 12:30:39 +0200220
221
222
223 def _format_exception(self, exception):
224 '''Transform a keystone, nova, neutron exception into a vimconn exception'''
225 if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
tierno8e995ce2016-09-22 08:13:00 +0000226 ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed
227 )):
tiernoae4a8d12016-07-08 12:30:39 +0200228 raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
229 elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException,
230 neExceptions.NeutronException, nvExceptions.BadRequest)):
231 raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + str(exception))
232 elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)):
233 raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception))
234 elif isinstance(exception, nvExceptions.Conflict):
235 raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception))
236 else: # ()
237 raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
238
239 def get_tenant_list(self, filter_dict={}):
240 '''Obtain tenants of VIM
241 filter_dict can contain the following keys:
242 name: filter by tenant name
243 id: filter by tenant uuid/id
244 <other VIM specific>
245 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
246 '''
ahmadsa95baa272016-11-30 09:14:11 +0500247 self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
tiernoae4a8d12016-07-08 12:30:39 +0200248 try:
249 self._reload_connection()
montesmoreno0c8def02016-12-22 12:16:23 +0000250 if self.osc_api_version == 'v3.3':
ahmadsa95baa272016-11-30 09:14:11 +0500251 project_class_list=self.keystone.projects.findall(**filter_dict)
252 else:
253 project_class_list=self.keystone.tenants.findall(**filter_dict)
254 project_list=[]
255 for project in project_class_list:
256 project_list.append(project.to_dict())
257 return project_list
tierno8e995ce2016-09-22 08:13:00 +0000258 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200259 self._format_exception(e)
260
261 def new_tenant(self, tenant_name, tenant_description):
262 '''Adds a new tenant to openstack VIM. Returns the tenant identifier'''
263 self.logger.debug("Adding a new tenant name: %s", tenant_name)
264 try:
265 self._reload_connection()
ahmadsa95baa272016-11-30 09:14:11 +0500266 if self.osc_api_version == 'v3.3':
267 project=self.keystone.projects.create(tenant_name, tenant_description)
268 else:
269 project=self.keystone.tenants.create(tenant_name, tenant_description)
270 return project.id
tierno8e995ce2016-09-22 08:13:00 +0000271 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200272 self._format_exception(e)
273
274 def delete_tenant(self, tenant_id):
275 '''Delete a tenant from openstack VIM. Returns the old tenant identifier'''
276 self.logger.debug("Deleting tenant %s from VIM", tenant_id)
277 try:
278 self._reload_connection()
montesmoreno0c8def02016-12-22 12:16:23 +0000279 if self.osc_api_version == 'v3.3':
ahmadsa95baa272016-11-30 09:14:11 +0500280 self.keystone.projects.delete(tenant_id)
281 else:
282 self.keystone.tenants.delete(tenant_id)
tiernoae4a8d12016-07-08 12:30:39 +0200283 return tenant_id
tierno8e995ce2016-09-22 08:13:00 +0000284 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200285 self._format_exception(e)
ahmadsa95baa272016-11-30 09:14:11 +0500286
garciadeblas9f8456e2016-09-05 05:02:59 +0200287 def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
tiernoae4a8d12016-07-08 12:30:39 +0200288 '''Adds a tenant network to VIM. Returns the network identifier'''
289 self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
garciadeblasedca7b32016-09-29 14:01:52 +0000290 #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
tierno7edb6752016-03-21 17:37:52 +0100291 try:
garciadeblasedca7b32016-09-29 14:01:52 +0000292 new_net = None
tierno7edb6752016-03-21 17:37:52 +0100293 self._reload_connection()
294 network_dict = {'name': net_name, 'admin_state_up': True}
295 if net_type=="data" or net_type=="ptp":
296 if self.config.get('dataplane_physical_net') == None:
tiernoae4a8d12016-07-08 12:30:39 +0200297 raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
tierno7edb6752016-03-21 17:37:52 +0100298 network_dict["provider:physical_network"] = self.config['dataplane_physical_net'] #"physnet_sriov" #TODO physical
299 network_dict["provider:network_type"] = "vlan"
300 if vlan!=None:
301 network_dict["provider:network_type"] = vlan
tiernoae4a8d12016-07-08 12:30:39 +0200302 network_dict["shared"]=shared
tierno7edb6752016-03-21 17:37:52 +0100303 new_net=self.neutron.create_network({'network':network_dict})
304 #print new_net
garciadeblas9f8456e2016-09-05 05:02:59 +0200305 #create subnetwork, even if there is no profile
306 if not ip_profile:
307 ip_profile = {}
308 if 'subnet_address' not in ip_profile:
garciadeblas2299e3b2017-01-26 14:35:55 +0000309 #Fake subnet is required
310 subnet_rand = random.randint(0, 255)
311 ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
garciadeblas9f8456e2016-09-05 05:02:59 +0200312 if 'ip_version' not in ip_profile:
313 ip_profile['ip_version'] = "IPv4"
tierno7edb6752016-03-21 17:37:52 +0100314 subnet={"name":net_name+"-subnet",
315 "network_id": new_net["network"]["id"],
garciadeblas9f8456e2016-09-05 05:02:59 +0200316 "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
317 "cidr": ip_profile['subnet_address']
tierno7edb6752016-03-21 17:37:52 +0100318 }
garciadeblas9f8456e2016-09-05 05:02:59 +0200319 if 'gateway_address' in ip_profile:
320 subnet['gateway_ip'] = ip_profile['gateway_address']
garciadeblasedca7b32016-09-29 14:01:52 +0000321 if ip_profile.get('dns_address'):
garciadeblas9f8456e2016-09-05 05:02:59 +0200322 #TODO: manage dns_address as a list of addresses separated by commas
323 subnet['dns_nameservers'] = []
324 subnet['dns_nameservers'].append(ip_profile['dns_address'])
325 if 'dhcp_enabled' in ip_profile:
326 subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True
327 if 'dhcp_start_address' in ip_profile:
328 subnet['allocation_pools']=[]
329 subnet['allocation_pools'].append(dict())
330 subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
331 if 'dhcp_count' in ip_profile:
332 #parts = ip_profile['dhcp_start_address'].split('.')
333 #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
334 ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
garciadeblas21d795b2016-09-29 17:31:46 +0200335 ip_int += ip_profile['dhcp_count'] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200336 ip_str = str(netaddr.IPAddress(ip_int))
337 subnet['allocation_pools'][0]['end'] = ip_str
garciadeblasedca7b32016-09-29 14:01:52 +0000338 #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
tierno7edb6752016-03-21 17:37:52 +0100339 self.neutron.create_subnet({"subnet": subnet} )
tiernoae4a8d12016-07-08 12:30:39 +0200340 return new_net["network"]["id"]
tierno8e995ce2016-09-22 08:13:00 +0000341 except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000342 if new_net:
343 self.neutron.delete_network(new_net['network']['id'])
tiernoae4a8d12016-07-08 12:30:39 +0200344 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100345
346 def get_network_list(self, filter_dict={}):
347 '''Obtain tenant networks of VIM
348 Filter_dict can be:
349 name: network name
350 id: network uuid
351 shared: boolean
352 tenant_id: tenant
353 admin_state_up: boolean
354 status: 'ACTIVE'
355 Returns the network list of dictionaries
356 '''
tiernoae4a8d12016-07-08 12:30:39 +0200357 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
tierno7edb6752016-03-21 17:37:52 +0100358 try:
359 self._reload_connection()
montesmoreno0c8def02016-12-22 12:16:23 +0000360 if self.osc_api_version == 'v3.3' and "tenant_id" in filter_dict:
ahmadsa95baa272016-11-30 09:14:11 +0500361 filter_dict['project_id'] = filter_dict.pop('tenant_id')
tierno7edb6752016-03-21 17:37:52 +0100362 net_dict=self.neutron.list_networks(**filter_dict)
363 net_list=net_dict["networks"]
364 self.__net_os2mano(net_list)
tiernoae4a8d12016-07-08 12:30:39 +0200365 return net_list
tierno8e995ce2016-09-22 08:13:00 +0000366 except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200367 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100368
tiernoae4a8d12016-07-08 12:30:39 +0200369 def get_network(self, net_id):
370 '''Obtain details of network from VIM
371 Returns the network information from a network id'''
372 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno7edb6752016-03-21 17:37:52 +0100373 filter_dict={"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +0200374 net_list = self.get_network_list(filter_dict)
tierno7edb6752016-03-21 17:37:52 +0100375 if len(net_list)==0:
tiernoae4a8d12016-07-08 12:30:39 +0200376 raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id))
tierno7edb6752016-03-21 17:37:52 +0100377 elif len(net_list)>1:
tiernoae4a8d12016-07-08 12:30:39 +0200378 raise vimconn.vimconnConflictException("Found more than one network with this criteria")
tierno7edb6752016-03-21 17:37:52 +0100379 net = net_list[0]
380 subnets=[]
381 for subnet_id in net.get("subnets", () ):
382 try:
383 subnet = self.neutron.show_subnet(subnet_id)
384 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200385 self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e)))
386 subnet = {"id": subnet_id, "fault": str(e)}
tierno7edb6752016-03-21 17:37:52 +0100387 subnets.append(subnet)
388 net["subnets"] = subnets
tiernoae4a8d12016-07-08 12:30:39 +0200389 return net
tierno7edb6752016-03-21 17:37:52 +0100390
tiernoae4a8d12016-07-08 12:30:39 +0200391 def delete_network(self, net_id):
392 '''Deletes a tenant network from VIM. Returns the old network identifier'''
393 self.logger.debug("Deleting network '%s' from VIM", net_id)
tierno7edb6752016-03-21 17:37:52 +0100394 try:
395 self._reload_connection()
396 #delete VM ports attached to this networks before the network
397 ports = self.neutron.list_ports(network_id=net_id)
398 for p in ports['ports']:
399 try:
400 self.neutron.delete_port(p["id"])
401 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200402 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
tierno7edb6752016-03-21 17:37:52 +0100403 self.neutron.delete_network(net_id)
tiernoae4a8d12016-07-08 12:30:39 +0200404 return net_id
405 except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
tierno8e995ce2016-09-22 08:13:00 +0000406 ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200407 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100408
tiernoae4a8d12016-07-08 12:30:39 +0200409 def refresh_nets_status(self, net_list):
410 '''Get the status of the networks
411 Params: the list of network identifiers
412 Returns a dictionary with:
413 net_id: #VIM id of this network
414 status: #Mandatory. Text with one of:
415 # DELETED (not found at vim)
416 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
417 # OTHER (Vim reported other status not understood)
418 # ERROR (VIM indicates an ERROR status)
419 # ACTIVE, INACTIVE, DOWN (admin down),
420 # BUILD (on building process)
421 #
422 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
423 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
424
425 '''
426 net_dict={}
427 for net_id in net_list:
428 net = {}
429 try:
430 net_vim = self.get_network(net_id)
431 if net_vim['status'] in netStatus2manoFormat:
432 net["status"] = netStatus2manoFormat[ net_vim['status'] ]
433 else:
434 net["status"] = "OTHER"
435 net["error_msg"] = "VIM status reported " + net_vim['status']
436
tierno8e995ce2016-09-22 08:13:00 +0000437 if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
tiernoae4a8d12016-07-08 12:30:39 +0200438 net['status'] = 'DOWN'
tierno8e995ce2016-09-22 08:13:00 +0000439 try:
440 net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256)
441 except yaml.representer.RepresenterError:
442 net['vim_info'] = str(net_vim)
tiernoae4a8d12016-07-08 12:30:39 +0200443 if net_vim.get('fault'): #TODO
444 net['error_msg'] = str(net_vim['fault'])
445 except vimconn.vimconnNotFoundException as e:
446 self.logger.error("Exception getting net status: %s", str(e))
447 net['status'] = "DELETED"
448 net['error_msg'] = str(e)
449 except vimconn.vimconnException as e:
450 self.logger.error("Exception getting net status: %s", str(e))
451 net['status'] = "VIM_ERROR"
452 net['error_msg'] = str(e)
453 net_dict[net_id] = net
454 return net_dict
455
456 def get_flavor(self, flavor_id):
457 '''Obtain flavor details from the VIM. Returns the flavor dict details'''
458 self.logger.debug("Getting flavor '%s'", flavor_id)
tierno7edb6752016-03-21 17:37:52 +0100459 try:
460 self._reload_connection()
461 flavor = self.nova.flavors.find(id=flavor_id)
462 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +0200463 return flavor.to_dict()
tierno8e995ce2016-09-22 08:13:00 +0000464 except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200465 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100466
tiernoae4a8d12016-07-08 12:30:39 +0200467 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno7edb6752016-03-21 17:37:52 +0100468 '''Adds a tenant flavor to openstack VIM
tiernoae4a8d12016-07-08 12:30:39 +0200469 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 +0100470 Returns the flavor identifier
471 '''
tiernoae4a8d12016-07-08 12:30:39 +0200472 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno7edb6752016-03-21 17:37:52 +0100473 retry=0
tiernoae4a8d12016-07-08 12:30:39 +0200474 max_retries=3
tierno7edb6752016-03-21 17:37:52 +0100475 name_suffix = 0
tiernoae4a8d12016-07-08 12:30:39 +0200476 name=flavor_data['name']
477 while retry<max_retries:
tierno7edb6752016-03-21 17:37:52 +0100478 retry+=1
479 try:
480 self._reload_connection()
481 if change_name_if_used:
482 #get used names
483 fl_names=[]
484 fl=self.nova.flavors.list()
485 for f in fl:
486 fl_names.append(f.name)
487 while name in fl_names:
488 name_suffix += 1
tiernoae4a8d12016-07-08 12:30:39 +0200489 name = flavor_data['name']+"-" + str(name_suffix)
tierno7edb6752016-03-21 17:37:52 +0100490
tiernoae4a8d12016-07-08 12:30:39 +0200491 ram = flavor_data.get('ram',64)
492 vcpus = flavor_data.get('vcpus',1)
tierno7edb6752016-03-21 17:37:52 +0100493 numa_properties=None
494
tiernoae4a8d12016-07-08 12:30:39 +0200495 extended = flavor_data.get("extended")
tierno7edb6752016-03-21 17:37:52 +0100496 if extended:
497 numas=extended.get("numas")
498 if numas:
499 numa_nodes = len(numas)
500 if numa_nodes > 1:
501 return -1, "Can not add flavor with more than one numa"
502 numa_properties = {"hw:numa_nodes":str(numa_nodes)}
503 numa_properties["hw:mem_page_size"] = "large"
504 numa_properties["hw:cpu_policy"] = "dedicated"
505 numa_properties["hw:numa_mempolicy"] = "strict"
506 for numa in numas:
507 #overwrite ram and vcpus
508 ram = numa['memory']*1024
509 if 'paired-threads' in numa:
510 vcpus = numa['paired-threads']*2
511 numa_properties["hw:cpu_threads_policy"] = "prefer"
512 elif 'cores' in numa:
513 vcpus = numa['cores']
514 #numa_properties["hw:cpu_threads_policy"] = "prefer"
515 elif 'threads' in numa:
516 vcpus = numa['threads']
517 numa_properties["hw:cpu_policy"] = "isolated"
518 for interface in numa.get("interfaces",() ):
519 if interface["dedicated"]=="yes":
tierno809a7802016-07-08 13:31:24 +0200520 raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
tierno7edb6752016-03-21 17:37:52 +0100521 #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
522
523 #create flavor
524 new_flavor=self.nova.flavors.create(name,
525 ram,
526 vcpus,
tiernoae4a8d12016-07-08 12:30:39 +0200527 flavor_data.get('disk',1),
528 is_public=flavor_data.get('is_public', True)
tierno7edb6752016-03-21 17:37:52 +0100529 )
530 #add metadata
531 if numa_properties:
532 new_flavor.set_keys(numa_properties)
tiernoae4a8d12016-07-08 12:30:39 +0200533 return new_flavor.id
tierno7edb6752016-03-21 17:37:52 +0100534 except nvExceptions.Conflict as e:
tiernoae4a8d12016-07-08 12:30:39 +0200535 if change_name_if_used and retry < max_retries:
tierno7edb6752016-03-21 17:37:52 +0100536 continue
tiernoae4a8d12016-07-08 12:30:39 +0200537 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100538 #except nvExceptions.BadRequest as e:
539 except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200540 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100541
tiernoae4a8d12016-07-08 12:30:39 +0200542 def delete_flavor(self,flavor_id):
543 '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
tierno7edb6752016-03-21 17:37:52 +0100544 '''
tiernoae4a8d12016-07-08 12:30:39 +0200545 try:
546 self._reload_connection()
547 self.nova.flavors.delete(flavor_id)
548 return flavor_id
549 #except nvExceptions.BadRequest as e:
tierno8e995ce2016-09-22 08:13:00 +0000550 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200551 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100552
tiernoae4a8d12016-07-08 12:30:39 +0200553 def new_image(self,image_dict):
tierno7edb6752016-03-21 17:37:52 +0100554 '''
tiernoae4a8d12016-07-08 12:30:39 +0200555 Adds a tenant image to VIM. imge_dict is a dictionary with:
556 name: name
557 disk_format: qcow2, vhd, vmdk, raw (by default), ...
558 location: path or URI
559 public: "yes" or "no"
560 metadata: metadata of the image
561 Returns the image_id
tierno7edb6752016-03-21 17:37:52 +0100562 '''
tierno7edb6752016-03-21 17:37:52 +0100563 #using version 1 of glance client
564 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 +0200565 retry=0
566 max_retries=3
567 while retry<max_retries:
tierno7edb6752016-03-21 17:37:52 +0100568 retry+=1
569 try:
570 self._reload_connection()
571 #determine format http://docs.openstack.org/developer/glance/formats.html
572 if "disk_format" in image_dict:
573 disk_format=image_dict["disk_format"]
garciadeblas14480452017-01-10 13:08:07 +0100574 else: #autodiscover based on extension
tierno7edb6752016-03-21 17:37:52 +0100575 if image_dict['location'][-6:]==".qcow2":
576 disk_format="qcow2"
577 elif image_dict['location'][-4:]==".vhd":
578 disk_format="vhd"
579 elif image_dict['location'][-5:]==".vmdk":
580 disk_format="vmdk"
581 elif image_dict['location'][-4:]==".vdi":
582 disk_format="vdi"
583 elif image_dict['location'][-4:]==".iso":
584 disk_format="iso"
585 elif image_dict['location'][-4:]==".aki":
586 disk_format="aki"
587 elif image_dict['location'][-4:]==".ari":
588 disk_format="ari"
589 elif image_dict['location'][-4:]==".ami":
590 disk_format="ami"
591 else:
592 disk_format="raw"
tiernoae4a8d12016-07-08 12:30:39 +0200593 self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location'])
tierno7edb6752016-03-21 17:37:52 +0100594 if image_dict['location'][0:4]=="http":
595 new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
596 container_format="bare", location=image_dict['location'], disk_format=disk_format)
597 else: #local path
598 with open(image_dict['location']) as fimage:
599 new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
600 container_format="bare", data=fimage, disk_format=disk_format)
601 #insert metadata. We cannot use 'new_image.properties.setdefault'
602 #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata
603 new_image_nova=self.nova.images.find(id=new_image.id)
604 new_image_nova.metadata.setdefault('location',image_dict['location'])
605 metadata_to_load = image_dict.get('metadata')
606 if metadata_to_load:
607 for k,v in yaml.load(metadata_to_load).iteritems():
608 new_image_nova.metadata.setdefault(k,v)
tiernoae4a8d12016-07-08 12:30:39 +0200609 return new_image.id
610 except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
611 self._format_exception(e)
tierno8e995ce2016-09-22 08:13:00 +0000612 except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200613 if retry==max_retries:
614 continue
615 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100616 except IOError as e: #can not open the file
tiernoae4a8d12016-07-08 12:30:39 +0200617 raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'],
618 http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +0100619
tiernoae4a8d12016-07-08 12:30:39 +0200620 def delete_image(self, image_id):
621 '''Deletes a tenant image from openstack VIM. Returns the old id
tierno7edb6752016-03-21 17:37:52 +0100622 '''
tiernoae4a8d12016-07-08 12:30:39 +0200623 try:
624 self._reload_connection()
625 self.nova.images.delete(image_id)
626 return image_id
tierno8e995ce2016-09-22 08:13:00 +0000627 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove
tiernoae4a8d12016-07-08 12:30:39 +0200628 self._format_exception(e)
629
630 def get_image_id_from_path(self, path):
garciadeblasb69fa9f2016-09-28 12:04:10 +0200631 '''Get the image id from image path in the VIM database. Returns the image_id'''
tiernoae4a8d12016-07-08 12:30:39 +0200632 try:
633 self._reload_connection()
634 images = self.nova.images.list()
635 for image in images:
636 if image.metadata.get("location")==path:
637 return image.id
638 raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
tierno8e995ce2016-09-22 08:13:00 +0000639 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200640 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100641
garciadeblasb69fa9f2016-09-28 12:04:10 +0200642 def get_image_list(self, filter_dict={}):
643 '''Obtain tenant images from VIM
644 Filter_dict can be:
645 id: image id
646 name: image name
647 checksum: image checksum
648 Returns the image list of dictionaries:
649 [{<the fields at Filter_dict plus some VIM specific>}, ...]
650 List can be empty
651 '''
652 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
653 try:
654 self._reload_connection()
655 filter_dict_os=filter_dict.copy()
656 #First we filter by the available filter fields: name, id. The others are removed.
657 filter_dict_os.pop('checksum',None)
658 image_list=self.nova.images.findall(**filter_dict_os)
659 if len(image_list)==0:
660 return []
661 #Then we filter by the rest of filter fields: checksum
662 filtered_list = []
663 for image in image_list:
tierno4540ea52017-01-18 17:44:32 +0100664 image_class=self.glance.images.get(image.id)
665 if 'checksum' not in filter_dict or image_class['checksum']==filter_dict.get('checksum'):
666 filtered_list.append(image_class.copy())
garciadeblasb69fa9f2016-09-28 12:04:10 +0200667 return filtered_list
668 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
669 self._format_exception(e)
670
montesmoreno0c8def02016-12-22 12:16:23 +0000671 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 +0100672 '''Adds a VM instance to VIM
673 Params:
674 start: indicates if VM must start or boot in pause mode. Ignored
675 image_id,flavor_id: iamge and flavor uuid
676 net_list: list of interfaces, each one is a dictionary with:
677 name:
678 net_id: network uuid to connect
679 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
680 model: interface model, ignored #TODO
681 mac_address: used for SR-IOV ifaces #TODO for other types
682 use: 'data', 'bridge', 'mgmt'
683 type: 'virtual', 'PF', 'VF', 'VFnotShared'
684 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +0500685 floating_ip: True/False (or it can be None)
tierno7edb6752016-03-21 17:37:52 +0100686 #TODO ip, security groups
tiernoae4a8d12016-07-08 12:30:39 +0200687 Returns the instance identifier
tierno7edb6752016-03-21 17:37:52 +0100688 '''
tiernoae4a8d12016-07-08 12:30:39 +0200689 self.logger.debug("Creating VM image '%s' flavor '%s' nics='%s'",image_id, flavor_id,str(net_list))
tierno7edb6752016-03-21 17:37:52 +0100690 try:
tierno6e116232016-07-18 13:01:40 +0200691 metadata={}
tierno7edb6752016-03-21 17:37:52 +0100692 net_list_vim=[]
ahmadsaf853d452016-12-22 11:33:47 +0500693 external_network=[] #list of external networks to be connected to instance, later on used to create floating_ip
tierno7edb6752016-03-21 17:37:52 +0100694 self._reload_connection()
tiernoae4a8d12016-07-08 12:30:39 +0200695 metadata_vpci={} #For a specific neutron plugin
tierno7edb6752016-03-21 17:37:52 +0100696 for net in net_list:
697 if not net.get("net_id"): #skip non connected iface
698 continue
ahmadsaf853d452016-12-22 11:33:47 +0500699 if net["type"]=="virtual" or net["type"]=="VF":
tierno7edb6752016-03-21 17:37:52 +0100700 port_dict={
ahmadsaf853d452016-12-22 11:33:47 +0500701 "network_id": net["net_id"],
702 "name": net.get("name"),
703 "admin_state_up": True
704 }
705 if net["type"]=="virtual":
706 if "vpci" in net:
707 metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
708 else: # for VF
709 if "vpci" in net:
710 if "VF" not in metadata_vpci:
711 metadata_vpci["VF"]=[]
712 metadata_vpci["VF"].append([ net["vpci"], "" ])
713 port_dict["binding:vnic_type"]="direct"
tierno7edb6752016-03-21 17:37:52 +0100714 if not port_dict["name"]:
ahmadsaf853d452016-12-22 11:33:47 +0500715 port_dict["name"]=name
tierno7edb6752016-03-21 17:37:52 +0100716 if net.get("mac_address"):
717 port_dict["mac_address"]=net["mac_address"]
montesmorenocf227142017-01-12 12:24:21 +0000718 if net.get("port_security") == False:
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000719 port_dict["port_security_enabled"]=net["port_security"]
tierno7edb6752016-03-21 17:37:52 +0100720 new_port = self.neutron.create_port({"port": port_dict })
721 net["mac_adress"] = new_port["port"]["mac_address"]
722 net["vim_id"] = new_port["port"]["id"]
ahmadsaf853d452016-12-22 11:33:47 +0500723 net["ip"] = new_port["port"].get("fixed_ips", [{}])[0].get("ip_address")
tierno7edb6752016-03-21 17:37:52 +0100724 net_list_vim.append({"port-id": new_port["port"]["id"]})
ahmadsaf853d452016-12-22 11:33:47 +0500725 else: # for PF
726 self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ")
727 #TODO insert this when openstack consider passthrough ports as openstack neutron ports
728 if net.get('floating_ip', False):
tiernof8383b82017-01-18 15:49:48 +0100729 net['exit_on_floating_ip_error'] = True
ahmadsaf853d452016-12-22 11:33:47 +0500730 external_network.append(net)
tiernof8383b82017-01-18 15:49:48 +0100731 elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'):
732 net['exit_on_floating_ip_error'] = False
733 external_network.append(net)
734
tierno7edb6752016-03-21 17:37:52 +0100735 if metadata_vpci:
736 metadata = {"pci_assignement": json.dumps(metadata_vpci)}
tiernoafbced42016-07-23 01:43:53 +0200737 if len(metadata["pci_assignement"]) >255:
tierno6e116232016-07-18 13:01:40 +0200738 #limit the metadata size
739 #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
740 self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
741 metadata = {}
tierno7edb6752016-03-21 17:37:52 +0100742
tiernoae4a8d12016-07-08 12:30:39 +0200743 self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s' metadata %s",
744 name, image_id, flavor_id, str(net_list_vim), description, str(metadata))
tierno7edb6752016-03-21 17:37:52 +0100745
746 security_groups = self.config.get('security_groups')
747 if type(security_groups) is str:
748 security_groups = ( security_groups, )
tierno36c0b172017-01-12 18:32:28 +0100749 #cloud config
750 userdata=None
751 config_drive = None
tiernoa4e1a6e2016-08-31 14:19:40 +0200752 if isinstance(cloud_config, dict):
tierno36c0b172017-01-12 18:32:28 +0100753 if cloud_config.get("user-data"):
754 userdata=cloud_config["user-data"]
755 if cloud_config.get("boot-data-drive") != None:
756 config_drive = cloud_config["boot-data-drive"]
757 if cloud_config.get("config-files") or cloud_config.get("users") or cloud_config.get("key-pairs"):
758 if userdata:
759 raise vimconn.vimconnConflictException("Cloud-config cannot contain both 'userdata' and 'config-files'/'users'/'key-pairs'")
760 userdata_dict={}
761 #default user
762 if cloud_config.get("key-pairs"):
763 userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
764 userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"] }]
765 if cloud_config.get("users"):
tierno01d0bf52017-01-25 14:27:20 +0100766 if "users" not in userdata_dict:
tierno36c0b172017-01-12 18:32:28 +0100767 userdata_dict["users"] = [ "default" ]
768 for user in cloud_config["users"]:
769 user_info = {
770 "name" : user["name"],
771 "sudo": "ALL = (ALL)NOPASSWD:ALL"
772 }
773 if "user-info" in user:
774 user_info["gecos"] = user["user-info"]
775 if user.get("key-pairs"):
776 user_info["ssh-authorized-keys"] = user["key-pairs"]
777 userdata_dict["users"].append(user_info)
778
779 if cloud_config.get("config-files"):
780 userdata_dict["write_files"] = []
781 for file in cloud_config["config-files"]:
782 file_info = {
783 "path" : file["dest"],
784 "content": file["content"]
785 }
786 if file.get("encoding"):
787 file_info["encoding"] = file["encoding"]
788 if file.get("permissions"):
789 file_info["permissions"] = file["permissions"]
790 if file.get("owner"):
791 file_info["owner"] = file["owner"]
792 userdata_dict["write_files"].append(file_info)
793 userdata = "#cloud-config\n"
794 userdata += yaml.safe_dump(userdata_dict, indent=4, default_flow_style=False)
tiernoa4e1a6e2016-08-31 14:19:40 +0200795 self.logger.debug("userdata: %s", userdata)
796 elif isinstance(cloud_config, str):
797 userdata = cloud_config
montesmoreno0c8def02016-12-22 12:16:23 +0000798
799 #Create additional volumes in case these are present in disk_list
800 block_device_mapping = None
801 base_disk_index = ord('b')
802 if disk_list != None:
803 block_device_mapping = dict()
804 for disk in disk_list:
805 if 'image_id' in disk:
806 volume = self.cinder.volumes.create(size = disk['size'],name = name + '_vd' +
807 chr(base_disk_index), imageRef = disk['image_id'])
808 else:
809 volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
810 chr(base_disk_index))
811 block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
812 base_disk_index += 1
813
814 #wait until volumes are with status available
815 keep_waiting = True
816 elapsed_time = 0
817 while keep_waiting and elapsed_time < volume_timeout:
818 keep_waiting = False
819 for volume_id in block_device_mapping.itervalues():
820 if self.cinder.volumes.get(volume_id).status != 'available':
821 keep_waiting = True
822 if keep_waiting:
823 time.sleep(1)
824 elapsed_time += 1
825
826 #if we exceeded the timeout rollback
827 if elapsed_time >= volume_timeout:
828 #delete the volumes we just created
829 for volume_id in block_device_mapping.itervalues():
830 self.cinder.volumes.delete(volume_id)
831
832 #delete ports we just created
833 for net_item in net_list_vim:
834 if 'port-id' in net_item:
montesmorenocf227142017-01-12 12:24:21 +0000835 self.neutron.delete_port(net_item['port-id'])
montesmoreno0c8def02016-12-22 12:16:23 +0000836
837 raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
838 http_code=vimconn.HTTP_Request_Timeout)
839
tierno7edb6752016-03-21 17:37:52 +0100840 server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, meta=metadata,
montesmoreno0c8def02016-12-22 12:16:23 +0000841 security_groups=security_groups,
842 availability_zone=self.config.get('availability_zone'),
843 key_name=self.config.get('keypair'),
844 userdata=userdata,
tierno36c0b172017-01-12 18:32:28 +0100845 config_drive = config_drive,
montesmoreno0c8def02016-12-22 12:16:23 +0000846 block_device_mapping = block_device_mapping
847 ) # , description=description)
tiernoae4a8d12016-07-08 12:30:39 +0200848 #print "DONE :-)", server
ahmadsaf853d452016-12-22 11:33:47 +0500849 pool_id = None
850 floating_ips = self.neutron.list_floatingips().get("floatingips", ())
851 for floating_network in external_network:
tiernof8383b82017-01-18 15:49:48 +0100852 try:
853 # wait until vm is active
854 elapsed_time = 0
855 while elapsed_time < server_timeout:
856 status = self.nova.servers.get(server.id).status
857 if status == 'ACTIVE':
858 break
859 time.sleep(1)
860 elapsed_time += 1
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000861
tiernof8383b82017-01-18 15:49:48 +0100862 #if we exceeded the timeout rollback
863 if elapsed_time >= server_timeout:
864 raise vimconn.vimconnException('Timeout creating instance ' + name,
865 http_code=vimconn.HTTP_Request_Timeout)
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000866
tiernof8383b82017-01-18 15:49:48 +0100867 assigned = False
868 while(assigned == False):
869 if floating_ips:
870 ip = floating_ips.pop(0)
871 if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
872 free_floating_ip = ip.get("floating_ip_address")
873 try:
874 fix_ip = floating_network.get('ip')
875 server.add_floating_ip(free_floating_ip, fix_ip)
876 assigned = True
877 except Exception as e:
878 raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
879 else:
880 #Find the external network
881 external_nets = list()
882 for net in self.neutron.list_networks()['networks']:
883 if net['router:external']:
884 external_nets.append(net)
885
886 if len(external_nets) == 0:
887 raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
888 "network is present",
889 http_code=vimconn.HTTP_Conflict)
890 if len(external_nets) > 1:
891 raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
892 "external networks are present",
893 http_code=vimconn.HTTP_Conflict)
894
895 pool_id = external_nets[0].get('id')
896 param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
ahmadsaf853d452016-12-22 11:33:47 +0500897 try:
tiernof8383b82017-01-18 15:49:48 +0100898 #self.logger.debug("Creating floating IP")
899 new_floating_ip = self.neutron.create_floatingip(param)
900 free_floating_ip = new_floating_ip['floatingip']['floating_ip_address']
ahmadsaf853d452016-12-22 11:33:47 +0500901 fix_ip = floating_network.get('ip')
902 server.add_floating_ip(free_floating_ip, fix_ip)
tiernof8383b82017-01-18 15:49:48 +0100903 assigned=True
ahmadsaf853d452016-12-22 11:33:47 +0500904 except Exception as e:
tiernof8383b82017-01-18 15:49:48 +0100905 raise vimconn.vimconnException(type(e).__name__ + ": Cannot assign floating_ip "+ str(e), http_code=vimconn.HTTP_Conflict)
906 except Exception as e:
907 if not floating_network['exit_on_floating_ip_error']:
908 self.logger.warn("Cannot create floating_ip. %s", str(e))
909 continue
910 self.delete_vminstance(server.id)
911 raise
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000912
tiernoae4a8d12016-07-08 12:30:39 +0200913 return server.id
tierno7edb6752016-03-21 17:37:52 +0100914# except nvExceptions.NotFound as e:
915# error_value=-vimconn.HTTP_Not_Found
916# error_text= "vm instance %s not found" % vm_id
tiernof8383b82017-01-18 15:49:48 +0100917 except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
montesmoreno2a1fc4e2017-01-09 16:46:04 +0000918 # delete the volumes we just created
919 if block_device_mapping != None:
920 for volume_id in block_device_mapping.itervalues():
921 self.cinder.volumes.delete(volume_id)
922
923 # delete ports we just created
924 for net_item in net_list_vim:
925 if 'port-id' in net_item:
montesmorenocf227142017-01-12 12:24:21 +0000926 self.neutron.delete_port(net_item['port-id'])
tiernoae4a8d12016-07-08 12:30:39 +0200927 self._format_exception(e)
928 except TypeError as e:
929 raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +0100930
tiernoae4a8d12016-07-08 12:30:39 +0200931 def get_vminstance(self,vm_id):
tierno7edb6752016-03-21 17:37:52 +0100932 '''Returns the VM instance information from VIM'''
tiernoae4a8d12016-07-08 12:30:39 +0200933 #self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +0100934 try:
935 self._reload_connection()
936 server = self.nova.servers.find(id=vm_id)
937 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +0200938 return server.to_dict()
tierno8e995ce2016-09-22 08:13:00 +0000939 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200940 self._format_exception(e)
941
942 def get_vminstance_console(self,vm_id, console_type="vnc"):
tierno7edb6752016-03-21 17:37:52 +0100943 '''
944 Get a console for the virtual machine
945 Params:
946 vm_id: uuid of the VM
947 console_type, can be:
948 "novnc" (by default), "xvpvnc" for VNC types,
949 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +0200950 Returns dict with the console parameters:
951 protocol: ssh, ftp, http, https, ...
952 server: usually ip address
953 port: the http, ssh, ... port
954 suffix: extra text, e.g. the http path and query string
tierno7edb6752016-03-21 17:37:52 +0100955 '''
tiernoae4a8d12016-07-08 12:30:39 +0200956 self.logger.debug("Getting VM CONSOLE from VIM")
tierno7edb6752016-03-21 17:37:52 +0100957 try:
958 self._reload_connection()
959 server = self.nova.servers.find(id=vm_id)
960 if console_type == None or console_type == "novnc":
961 console_dict = server.get_vnc_console("novnc")
962 elif console_type == "xvpvnc":
963 console_dict = server.get_vnc_console(console_type)
964 elif console_type == "rdp-html5":
965 console_dict = server.get_rdp_console(console_type)
966 elif console_type == "spice-html5":
967 console_dict = server.get_spice_console(console_type)
968 else:
tiernoae4a8d12016-07-08 12:30:39 +0200969 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +0100970
971 console_dict1 = console_dict.get("console")
972 if console_dict1:
973 console_url = console_dict1.get("url")
974 if console_url:
975 #parse console_url
976 protocol_index = console_url.find("//")
977 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
978 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
979 if protocol_index < 0 or port_index<0 or suffix_index<0:
980 return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
981 console_dict={"protocol": console_url[0:protocol_index],
982 "server": console_url[protocol_index+2:port_index],
983 "port": console_url[port_index:suffix_index],
984 "suffix": console_url[suffix_index+1:]
985 }
986 protocol_index += 2
tiernoae4a8d12016-07-08 12:30:39 +0200987 return console_dict
988 raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
tierno7edb6752016-03-21 17:37:52 +0100989
tierno8e995ce2016-09-22 08:13:00 +0000990 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200991 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100992
tiernoae4a8d12016-07-08 12:30:39 +0200993 def delete_vminstance(self, vm_id):
994 '''Removes a VM instance from VIM. Returns the old identifier
tierno7edb6752016-03-21 17:37:52 +0100995 '''
tiernoae4a8d12016-07-08 12:30:39 +0200996 #print "osconnector: Getting VM from VIM"
tierno7edb6752016-03-21 17:37:52 +0100997 try:
998 self._reload_connection()
999 #delete VM ports attached to this networks before the virtual machine
1000 ports = self.neutron.list_ports(device_id=vm_id)
1001 for p in ports['ports']:
1002 try:
1003 self.neutron.delete_port(p["id"])
1004 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +02001005 self.logger.error("Error deleting port: " + type(e).__name__ + ": "+ str(e))
montesmoreno0c8def02016-12-22 12:16:23 +00001006
1007 #commented because detaching the volumes makes the servers.delete not work properly ?!?
1008 #dettach volumes attached
1009 server = self.nova.servers.get(vm_id)
1010 volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']
1011 #for volume in volumes_attached_dict:
1012 # self.cinder.volumes.detach(volume['id'])
1013
tierno7edb6752016-03-21 17:37:52 +01001014 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00001015
1016 #delete volumes.
1017 #Although having detached them should have them in active status
1018 #we ensure in this loop
1019 keep_waiting = True
1020 elapsed_time = 0
1021 while keep_waiting and elapsed_time < volume_timeout:
1022 keep_waiting = False
1023 for volume in volumes_attached_dict:
1024 if self.cinder.volumes.get(volume['id']).status != 'available':
1025 keep_waiting = True
1026 else:
1027 self.cinder.volumes.delete(volume['id'])
1028 if keep_waiting:
1029 time.sleep(1)
1030 elapsed_time += 1
1031
tiernoae4a8d12016-07-08 12:30:39 +02001032 return vm_id
tierno8e995ce2016-09-22 08:13:00 +00001033 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001034 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001035 #TODO insert exception vimconn.HTTP_Unauthorized
1036 #if reaching here is because an exception
tierno7edb6752016-03-21 17:37:52 +01001037
tiernoae4a8d12016-07-08 12:30:39 +02001038 def refresh_vms_status(self, vm_list):
1039 '''Get the status of the virtual machines and their interfaces/ports
1040 Params: the list of VM identifiers
1041 Returns a dictionary with:
1042 vm_id: #VIM id of this Virtual Machine
1043 status: #Mandatory. Text with one of:
1044 # DELETED (not found at vim)
1045 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1046 # OTHER (Vim reported other status not understood)
1047 # ERROR (VIM indicates an ERROR status)
1048 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1049 # CREATING (on building process), ERROR
1050 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1051 #
1052 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1053 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1054 interfaces:
1055 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1056 mac_address: #Text format XX:XX:XX:XX:XX:XX
1057 vim_net_id: #network id where this interface is connected
1058 vim_interface_id: #interface/port VIM id
1059 ip_address: #null, or text with IPv4, IPv6 address
tierno7edb6752016-03-21 17:37:52 +01001060 '''
tiernoae4a8d12016-07-08 12:30:39 +02001061 vm_dict={}
1062 self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM")
1063 for vm_id in vm_list:
1064 vm={}
1065 try:
1066 vm_vim = self.get_vminstance(vm_id)
1067 if vm_vim['status'] in vmStatus2manoFormat:
1068 vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ]
tierno7edb6752016-03-21 17:37:52 +01001069 else:
tiernoae4a8d12016-07-08 12:30:39 +02001070 vm['status'] = "OTHER"
1071 vm['error_msg'] = "VIM status reported " + vm_vim['status']
tierno8e995ce2016-09-22 08:13:00 +00001072 try:
1073 vm['vim_info'] = yaml.safe_dump(vm_vim, default_flow_style=True, width=256)
1074 except yaml.representer.RepresenterError:
1075 vm['vim_info'] = str(vm_vim)
tiernoae4a8d12016-07-08 12:30:39 +02001076 vm["interfaces"] = []
1077 if vm_vim.get('fault'):
1078 vm['error_msg'] = str(vm_vim['fault'])
1079 #get interfaces
tierno7edb6752016-03-21 17:37:52 +01001080 try:
tiernoae4a8d12016-07-08 12:30:39 +02001081 self._reload_connection()
1082 port_dict=self.neutron.list_ports(device_id=vm_id)
1083 for port in port_dict["ports"]:
1084 interface={}
tierno8e995ce2016-09-22 08:13:00 +00001085 try:
1086 interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
1087 except yaml.representer.RepresenterError:
1088 interface['vim_info'] = str(port)
tiernoae4a8d12016-07-08 12:30:39 +02001089 interface["mac_address"] = port.get("mac_address")
1090 interface["vim_net_id"] = port["network_id"]
1091 interface["vim_interface_id"] = port["id"]
1092 ips=[]
1093 #look for floating ip address
1094 floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
1095 if floating_ip_dict.get("floatingips"):
1096 ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
tierno7edb6752016-03-21 17:37:52 +01001097
tiernoae4a8d12016-07-08 12:30:39 +02001098 for subnet in port["fixed_ips"]:
1099 ips.append(subnet["ip_address"])
1100 interface["ip_address"] = ";".join(ips)
1101 vm["interfaces"].append(interface)
1102 except Exception as e:
1103 self.logger.error("Error getting vm interface information " + type(e).__name__ + ": "+ str(e))
1104 except vimconn.vimconnNotFoundException as e:
1105 self.logger.error("Exception getting vm status: %s", str(e))
1106 vm['status'] = "DELETED"
1107 vm['error_msg'] = str(e)
1108 except vimconn.vimconnException as e:
1109 self.logger.error("Exception getting vm status: %s", str(e))
1110 vm['status'] = "VIM_ERROR"
1111 vm['error_msg'] = str(e)
1112 vm_dict[vm_id] = vm
1113 return vm_dict
tierno7edb6752016-03-21 17:37:52 +01001114
tiernoae4a8d12016-07-08 12:30:39 +02001115 def action_vminstance(self, vm_id, action_dict):
tierno7edb6752016-03-21 17:37:52 +01001116 '''Send and action over a VM instance from VIM
tiernoae4a8d12016-07-08 12:30:39 +02001117 Returns the vm_id if the action was successfully sent to the VIM'''
1118 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
tierno7edb6752016-03-21 17:37:52 +01001119 try:
1120 self._reload_connection()
1121 server = self.nova.servers.find(id=vm_id)
1122 if "start" in action_dict:
1123 if action_dict["start"]=="rebuild":
1124 server.rebuild()
1125 else:
1126 if server.status=="PAUSED":
1127 server.unpause()
1128 elif server.status=="SUSPENDED":
1129 server.resume()
1130 elif server.status=="SHUTOFF":
1131 server.start()
1132 elif "pause" in action_dict:
1133 server.pause()
1134 elif "resume" in action_dict:
1135 server.resume()
1136 elif "shutoff" in action_dict or "shutdown" in action_dict:
1137 server.stop()
1138 elif "forceOff" in action_dict:
1139 server.stop() #TODO
1140 elif "terminate" in action_dict:
1141 server.delete()
1142 elif "createImage" in action_dict:
1143 server.create_image()
1144 #"path":path_schema,
1145 #"description":description_schema,
1146 #"name":name_schema,
1147 #"metadata":metadata_schema,
1148 #"imageRef": id_schema,
1149 #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
1150 elif "rebuild" in action_dict:
1151 server.rebuild(server.image['id'])
1152 elif "reboot" in action_dict:
1153 server.reboot() #reboot_type='SOFT'
1154 elif "console" in action_dict:
1155 console_type = action_dict["console"]
1156 if console_type == None or console_type == "novnc":
1157 console_dict = server.get_vnc_console("novnc")
1158 elif console_type == "xvpvnc":
1159 console_dict = server.get_vnc_console(console_type)
1160 elif console_type == "rdp-html5":
1161 console_dict = server.get_rdp_console(console_type)
1162 elif console_type == "spice-html5":
1163 console_dict = server.get_spice_console(console_type)
1164 else:
tiernoae4a8d12016-07-08 12:30:39 +02001165 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type),
1166 http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +01001167 try:
1168 console_url = console_dict["console"]["url"]
1169 #parse console_url
1170 protocol_index = console_url.find("//")
1171 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
1172 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
1173 if protocol_index < 0 or port_index<0 or suffix_index<0:
tiernoae4a8d12016-07-08 12:30:39 +02001174 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
tierno7edb6752016-03-21 17:37:52 +01001175 console_dict2={"protocol": console_url[0:protocol_index],
1176 "server": console_url[protocol_index+2 : port_index],
1177 "port": int(console_url[port_index+1 : suffix_index]),
1178 "suffix": console_url[suffix_index+1:]
1179 }
tiernoae4a8d12016-07-08 12:30:39 +02001180 return console_dict2
1181 except Exception as e:
1182 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
tierno7edb6752016-03-21 17:37:52 +01001183
tiernoae4a8d12016-07-08 12:30:39 +02001184 return vm_id
tierno8e995ce2016-09-22 08:13:00 +00001185 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001186 self._format_exception(e)
1187 #TODO insert exception vimconn.HTTP_Unauthorized
1188
1189#NOT USED FUNCTIONS
1190
1191 def new_external_port(self, port_data):
1192 #TODO openstack if needed
1193 '''Adds a external port to VIM'''
1194 '''Returns the port identifier'''
1195 return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
1196
1197 def connect_port_network(self, port_id, network_id, admin=False):
1198 #TODO openstack if needed
1199 '''Connects a external port to a network'''
1200 '''Returns status code of the VIM response'''
1201 return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
1202
1203 def new_user(self, user_name, user_passwd, tenant_id=None):
1204 '''Adds a new user to openstack VIM'''
1205 '''Returns the user identifier'''
1206 self.logger.debug("osconnector: Adding a new user to VIM")
1207 try:
1208 self._reload_connection()
1209 user=self.keystone.users.create(user_name, user_passwd, tenant_id=tenant_id)
1210 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
1211 return user.id
1212 except ksExceptions.ConnectionError as e:
1213 error_value=-vimconn.HTTP_Bad_Request
1214 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1215 except ksExceptions.ClientException as e: #TODO remove
tierno7edb6752016-03-21 17:37:52 +01001216 error_value=-vimconn.HTTP_Bad_Request
1217 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1218 #TODO insert exception vimconn.HTTP_Unauthorized
1219 #if reaching here is because an exception
1220 if self.debug:
tiernoae4a8d12016-07-08 12:30:39 +02001221 self.logger.debug("new_user " + error_text)
tierno7edb6752016-03-21 17:37:52 +01001222 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02001223
1224 def delete_user(self, user_id):
1225 '''Delete a user from openstack VIM'''
1226 '''Returns the user identifier'''
1227 if self.debug:
1228 print "osconnector: Deleting a user from VIM"
1229 try:
1230 self._reload_connection()
1231 self.keystone.users.delete(user_id)
1232 return 1, user_id
1233 except ksExceptions.ConnectionError as e:
1234 error_value=-vimconn.HTTP_Bad_Request
1235 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1236 except ksExceptions.NotFound as e:
1237 error_value=-vimconn.HTTP_Not_Found
1238 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1239 except ksExceptions.ClientException as e: #TODO remove
1240 error_value=-vimconn.HTTP_Bad_Request
1241 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1242 #TODO insert exception vimconn.HTTP_Unauthorized
1243 #if reaching here is because an exception
1244 if self.debug:
1245 print "delete_tenant " + error_text
1246 return error_value, error_text
1247
tierno7edb6752016-03-21 17:37:52 +01001248 def get_hosts_info(self):
1249 '''Get the information of deployed hosts
1250 Returns the hosts content'''
1251 if self.debug:
1252 print "osconnector: Getting Host info from VIM"
1253 try:
1254 h_list=[]
1255 self._reload_connection()
1256 hypervisors = self.nova.hypervisors.list()
1257 for hype in hypervisors:
1258 h_list.append( hype.to_dict() )
1259 return 1, {"hosts":h_list}
1260 except nvExceptions.NotFound as e:
1261 error_value=-vimconn.HTTP_Not_Found
1262 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1263 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1264 error_value=-vimconn.HTTP_Bad_Request
1265 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1266 #TODO insert exception vimconn.HTTP_Unauthorized
1267 #if reaching here is because an exception
1268 if self.debug:
1269 print "get_hosts_info " + error_text
1270 return error_value, error_text
1271
1272 def get_hosts(self, vim_tenant):
1273 '''Get the hosts and deployed instances
1274 Returns the hosts content'''
1275 r, hype_dict = self.get_hosts_info()
1276 if r<0:
1277 return r, hype_dict
1278 hypervisors = hype_dict["hosts"]
1279 try:
1280 servers = self.nova.servers.list()
1281 for hype in hypervisors:
1282 for server in servers:
1283 if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
1284 if 'vm' in hype:
1285 hype['vm'].append(server.id)
1286 else:
1287 hype['vm'] = [server.id]
1288 return 1, hype_dict
1289 except nvExceptions.NotFound as e:
1290 error_value=-vimconn.HTTP_Not_Found
1291 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1292 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1293 error_value=-vimconn.HTTP_Bad_Request
1294 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1295 #TODO insert exception vimconn.HTTP_Unauthorized
1296 #if reaching here is because an exception
1297 if self.debug:
1298 print "get_hosts " + error_text
1299 return error_value, error_text
1300
tierno7edb6752016-03-21 17:37:52 +01001301