blob: a0216e12f2f209853703f77e240bf54565b57291 [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'''
25HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
26and will use the nfvo.py module to run the appropriate method.
27Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
28'''
29__author__="Alfonso Tierno, Gerardo Garcia"
30__date__ ="$17-sep-2014 09:07:15$"
31
32import bottle
33import yaml
34import json
35import threading
36import time
tiernof97fd272016-07-11 14:32:37 +020037import logging
tierno7edb6752016-03-21 17:37:52 +010038
39from jsonschema import validate as js_v, exceptions as js_e
40from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
garciadeblas9f8456e2016-09-05 05:02:59 +020041 nsd_schema_v01, nsd_schema_v02, nsd_schema_v03, scenario_edit_schema, \
garciadeblas0c317ee2016-08-29 12:33:06 +020042 scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
tierno7edb6752016-03-21 17:37:52 +010043 tenant_schema, tenant_edit_schema,\
44 datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
45 object_schema, netmap_new_schema, netmap_edit_schema
46import nfvo
tierno42fcc3b2016-07-06 17:20:40 +020047import utils
tiernof97fd272016-07-11 14:32:37 +020048from db_base import db_base_Exception
49from functools import wraps
tierno7edb6752016-03-21 17:37:52 +010050
51global mydb
52global url_base
tiernof97fd272016-07-11 14:32:37 +020053global logger
tierno7edb6752016-03-21 17:37:52 +010054url_base="/openmano"
tierno73ad9e42016-09-12 18:11:11 +020055logger = None
tierno7edb6752016-03-21 17:37:52 +010056
57HTTP_Bad_Request = 400
58HTTP_Unauthorized = 401
59HTTP_Not_Found = 404
60HTTP_Forbidden = 403
61HTTP_Method_Not_Allowed = 405
62HTTP_Not_Acceptable = 406
63HTTP_Service_Unavailable = 503
64HTTP_Internal_Server_Error= 500
65
66def delete_nulls(var):
67 if type(var) is dict:
68 for k in var.keys():
69 if var[k] is None: del var[k]
70 elif type(var[k]) is dict or type(var[k]) is list or type(var[k]) is tuple:
71 if delete_nulls(var[k]): del var[k]
72 if len(var) == 0: return True
73 elif type(var) is list or type(var) is tuple:
74 for k in var:
75 if type(k) is dict: delete_nulls(k)
76 if len(var) == 0: return True
77 return False
78
79def convert_datetime2str(var):
80 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
81 It enters recursively in the dict var finding this kind of variables
82 '''
83 if type(var) is dict:
84 for k,v in var.items():
85 if type(v) is float and k in ("created_at", "modified_at"):
86 var[k] = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(v) )
87 elif type(v) is dict or type(v) is list or type(v) is tuple:
88 convert_datetime2str(v)
89 if len(var) == 0: return True
90 elif type(var) is list or type(var) is tuple:
91 for v in var:
92 convert_datetime2str(v)
93
tiernof97fd272016-07-11 14:32:37 +020094def log_to_logger(fn):
95 '''
96 Wrap a Bottle request so that a log line is emitted after it's handled.
97 (This decorator can be extended to take the desired logger as a param.)
98 '''
99 @wraps(fn)
100 def _log_to_logger(*args, **kwargs):
101 actual_response = fn(*args, **kwargs)
102 # modify this to log exactly what you need:
103 logger.info('FROM %s %s %s %s' % (bottle.request.remote_addr,
104 bottle.request.method,
105 bottle.request.url,
106 bottle.response.status))
107 return actual_response
108 return _log_to_logger
tierno7edb6752016-03-21 17:37:52 +0100109
110class httpserver(threading.Thread):
111 def __init__(self, db, admin=False, host='localhost', port=9090):
112 #global url_base
113 global mydb
tiernof97fd272016-07-11 14:32:37 +0200114 global logger
tierno7edb6752016-03-21 17:37:52 +0100115 #initialization
tierno73ad9e42016-09-12 18:11:11 +0200116 if not logger:
117 logger = logging.getLogger('openmano.http')
tierno7edb6752016-03-21 17:37:52 +0100118 threading.Thread.__init__(self)
119 self.host = host
120 self.port = port #Port where the listen service must be started
121 if admin==True:
122 self.name = "http_admin"
123 else:
124 self.name = "http"
125 #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
126 mydb = db
127 #self.first_usable_connection_index = 10
128 #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
129 #Ensure that when the main program exits the thread will also exit
130 self.daemon = True
131 self.setDaemon(True)
132
133 def run(self):
tiernof97fd272016-07-11 14:32:37 +0200134 bottle.install(log_to_logger)
135 bottle.run(host=self.host, port=self.port, debug=False, quiet=True)
tierno7edb6752016-03-21 17:37:52 +0100136
137def run_bottle(db, host_='localhost', port_=9090):
138 '''used for launching in main thread, so that it can be debugged'''
139 global mydb
140 mydb = db
141 bottle.run(host=host_, port=port_, debug=True) #quiet=True
142
143
144@bottle.route(url_base + '/', method='GET')
145def http_get():
tiernoefd80c92016-09-16 14:17:46 +0200146 #print
tierno7edb6752016-03-21 17:37:52 +0100147 return 'works' #TODO: to be completed
148
149#
150# Util functions
151#
152
153def change_keys_http2db(data, http_db, reverse=False):
154 '''Change keys of dictionary data acording to the key_dict values
155 This allow change from http interface names to database names.
156 When reverse is True, the change is otherwise
157 Attributes:
158 data: can be a dictionary or a list
159 http_db: is a dictionary with hhtp names as keys and database names as value
160 reverse: by default change is done from http api to database. If True change is done otherwise
161 Return: None, but data is modified'''
162 if type(data) is tuple or type(data) is list:
163 for d in data:
164 change_keys_http2db(d, http_db, reverse)
165 elif type(data) is dict or type(data) is bottle.FormsDict:
166 if reverse:
167 for k,v in http_db.items():
168 if v in data: data[k]=data.pop(v)
169 else:
170 for k,v in http_db.items():
171 if k in data: data[v]=data.pop(k)
172
173def format_out(data):
174 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
tiernoefd80c92016-09-16 14:17:46 +0200175 logger.debug("OUT: " + yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
tierno7edb6752016-03-21 17:37:52 +0100176 if 'application/yaml' in bottle.request.headers.get('Accept'):
177 bottle.response.content_type='application/yaml'
tierno7edb6752016-03-21 17:37:52 +0100178 return yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) #, canonical=True, default_style='"'
179 else: #by default json
180 bottle.response.content_type='application/json'
181 #return data #json no style
182 return json.dumps(data, indent=4) + "\n"
183
184def format_in(default_schema, version_fields=None, version_dict_schema=None):
185 ''' Parse the content of HTTP request against a json_schema
186 Parameters
187 default_schema: The schema to be parsed by default if no version field is found in the client data
188 version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
189 version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
190 It can contain a None as key, and this is apply if the client data version does not match any key
191 Return:
192 user_data, used_schema: if the data is successfully decoded and matches the schema
193 launch a bottle abort if fails
194 '''
195 #print "HEADERS :" + str(bottle.request.headers.items())
196 try:
197 error_text = "Invalid header format "
198 format_type = bottle.request.headers.get('Content-Type', 'application/json')
199 if 'application/json' in format_type:
200 error_text = "Invalid json format "
201 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
202 client_data = json.load(bottle.request.body)
203 #client_data = bottle.request.json()
204 elif 'application/yaml' in format_type:
205 error_text = "Invalid yaml format "
206 client_data = yaml.load(bottle.request.body)
207 elif 'application/xml' in format_type:
208 bottle.abort(501, "Content-Type: application/xml not supported yet.")
209 else:
tiernoefd80c92016-09-16 14:17:46 +0200210 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
tierno7edb6752016-03-21 17:37:52 +0100211 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
212 return
213 #if client_data == None:
214 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
215 # return
216
tiernoefd80c92016-09-16 14:17:46 +0200217 logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
tierno7edb6752016-03-21 17:37:52 +0100218 #look for the client provider version
219 error_text = "Invalid content "
220 client_version = None
221 used_schema = None
222 if version_fields != None:
223 client_version = client_data
224 for field in version_fields:
225 if field in client_version:
226 client_version = client_version[field]
227 else:
228 client_version=None
229 break
230 if client_version==None:
231 used_schema=default_schema
232 elif version_dict_schema!=None:
233 if client_version in version_dict_schema:
234 used_schema = version_dict_schema[client_version]
235 elif None in version_dict_schema:
236 used_schema = version_dict_schema[None]
237 if used_schema==None:
238 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
239
240 js_v(client_data, used_schema)
241 return client_data, used_schema
242 except (ValueError, yaml.YAMLError) as exc:
243 error_text += str(exc)
tiernoefd80c92016-09-16 14:17:46 +0200244 logger.error(error_text)
tierno7edb6752016-03-21 17:37:52 +0100245 bottle.abort(HTTP_Bad_Request, error_text)
246 except js_e.ValidationError as exc:
tiernoefd80c92016-09-16 14:17:46 +0200247 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
tierno7edb6752016-03-21 17:37:52 +0100248 error_pos = ""
249 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
250 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
251 #except:
252 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
253 # raise
254
255def filter_query_string(qs, http2db, allowed):
256 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
257 Attributes:
258 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
259 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
260 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
261 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
262 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
263 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
264 limit: limit dictated by user with the query string 'limit'. 100 by default
265 abort if not permited, using bottel.abort
266 '''
267 where={}
268 limit=100
269 select=[]
tiernof97fd272016-07-11 14:32:37 +0200270 #if type(qs) is not bottle.FormsDict:
271 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
272 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
273 for k in qs:
274 if k=='field':
275 select += qs.getall(k)
276 for v in select:
277 if v not in allowed:
278 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
279 elif k=='limit':
280 try:
281 limit=int(qs[k])
282 except:
283 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
284 else:
285 if k not in allowed:
286 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
287 if qs[k]!="null": where[k]=qs[k]
288 else: where[k]=None
tierno7edb6752016-03-21 17:37:52 +0100289 if len(select)==0: select += allowed
290 #change from http api to database naming
291 for i in range(0,len(select)):
292 k=select[i]
293 if http2db and k in http2db:
294 select[i] = http2db[k]
295 if http2db:
296 change_keys_http2db(where, http2db)
tiernof97fd272016-07-11 14:32:37 +0200297 #print "filter_query_string", select,where,limit
tierno7edb6752016-03-21 17:37:52 +0100298
299 return select,where,limit
300
301@bottle.hook('after_request')
302def enable_cors():
303 '''Don't know yet if really needed. Keep it just in case'''
304 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
305
306#
307# VNFs
308#
309
310@bottle.route(url_base + '/tenants', method='GET')
311def http_get_tenants():
tiernof97fd272016-07-11 14:32:37 +0200312 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100313 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
314 ('uuid','name','description','created_at') )
tiernof97fd272016-07-11 14:32:37 +0200315 try:
316 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100317 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200318 convert_datetime2str(tenants)
319 data={'tenants' : tenants}
tierno7edb6752016-03-21 17:37:52 +0100320 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200321 except db_base_Exception as e:
322 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
323 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000324 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000325 logger.error("Unexpected exception: ", exc_info=True)
326 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000327
tierno7edb6752016-03-21 17:37:52 +0100328
329@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
330def http_get_tenant_id(tenant_id):
331 '''get tenant details, can use both uuid or name'''
332 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200333 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
334 try:
335 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
336 #change_keys_http2db(content, http2db_tenant, reverse=True)
337 convert_datetime2str(tenant)
338 data={'tenant' : tenant}
339 return format_out(data)
340 except db_base_Exception as e:
341 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
342 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000343 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000344 logger.error("Unexpected exception: ", exc_info=True)
345 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000346
tierno7edb6752016-03-21 17:37:52 +0100347
348@bottle.route(url_base + '/tenants', method='POST')
349def http_post_tenants():
350 '''insert a tenant into the catalogue. '''
351 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200352 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100353 http_content,_ = format_in( tenant_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200354 r = utils.remove_extra_items(http_content, tenant_schema)
tiernoefd80c92016-09-16 14:17:46 +0200355 if r:
356 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200357 try:
358 data = nfvo.new_tenant(mydb, http_content['tenant'])
tierno7edb6752016-03-21 17:37:52 +0100359 return http_get_tenant_id(data)
tiernof97fd272016-07-11 14:32:37 +0200360 except (nfvo.NfvoException, db_base_Exception) as e:
361 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
362 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000363 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000364 logger.error("Unexpected exception: ", exc_info=True)
365 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000366
tierno7edb6752016-03-21 17:37:52 +0100367
368@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
369def http_edit_tenant_id(tenant_id):
370 '''edit tenant details, can use both uuid or name'''
371 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200372 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100373 http_content,_ = format_in( tenant_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200374 r = utils.remove_extra_items(http_content, tenant_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200375 if r:
376 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100377
378 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200379 try:
380 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
381 #edit data
382 tenant_id = tenant['uuid']
383 where={'uuid': tenant['uuid']}
384 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
385 return http_get_tenant_id(tenant_id)
386 except db_base_Exception as e:
387 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
388 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000389 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000390 logger.error("Unexpected exception: ", exc_info=True)
391 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000392
tierno7edb6752016-03-21 17:37:52 +0100393
394@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
395def http_delete_tenant_id(tenant_id):
396 '''delete a tenant from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200397 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
398 try:
399 data = nfvo.delete_tenant(mydb, tenant_id)
tierno7edb6752016-03-21 17:37:52 +0100400 return format_out({"result":"tenant " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200401 except db_base_Exception as e:
402 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
403 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000404 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000405 logger.error("Unexpected exception: ", exc_info=True)
406 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000407
tierno7edb6752016-03-21 17:37:52 +0100408
409@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
410def http_get_datacenters(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200411 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
412 try:
413 if tenant_id != 'any':
414 #check valid tenant_id
415 nfvo.check_tenant(mydb, tenant_id)
416 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
417 ('uuid','name','vim_url','type','created_at') )
418 if tenant_id != 'any':
419 where_['nfvo_tenant_id'] = tenant_id
420 if 'created_at' in select_:
421 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
422 if 'created_at' in where_:
423 where_['d.created_at'] = where_.pop('created_at')
424 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
425 SELECT=select_,WHERE=where_,LIMIT=limit_)
426 else:
427 datacenters = mydb.get_rows(FROM='datacenters',
428 SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100429 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200430 convert_datetime2str(datacenters)
431 data={'datacenters' : datacenters}
tierno7edb6752016-03-21 17:37:52 +0100432 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200433 except (nfvo.NfvoException, db_base_Exception) as e:
434 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
435 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000436 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000437 logger.error("Unexpected exception: ", exc_info=True)
438 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000439
tierno7edb6752016-03-21 17:37:52 +0100440
441@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
442def http_get_datacenter_id(tenant_id, datacenter_id):
443 '''get datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200444 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
445 try:
446 if tenant_id != 'any':
447 #check valid tenant_id
448 nfvo.check_tenant(mydb, tenant_id)
449 #obtain data
450 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
451 where_={}
452 where_[what] = datacenter_id
tierno8008c3a2016-10-13 15:34:28 +0000453 select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
tiernof97fd272016-07-11 14:32:37 +0200454 if tenant_id != 'any':
455 select_.append("datacenter_tenant_id")
456 where_['td.nfvo_tenant_id']= tenant_id
457 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
458 else:
459 from_='datacenters as d'
460 datacenters = mydb.get_rows(
461 SELECT=select_,
462 FROM=from_,
463 WHERE=where_)
464
465 if len(datacenters)==0:
466 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
467 elif len(datacenters)>1:
468 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
469 datacenter = datacenters[0]
470 if tenant_id != 'any':
471 #get vim tenant info
472 vim_tenants = mydb.get_rows(
tierno8008c3a2016-10-13 15:34:28 +0000473 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
tiernof97fd272016-07-11 14:32:37 +0200474 FROM="datacenter_tenants",
475 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
476 ORDER_BY=("created", ) )
477 del datacenter["datacenter_tenant_id"]
478 datacenter["vim_tenants"] = vim_tenants
tierno8008c3a2016-10-13 15:34:28 +0000479 for vim_tenant in vim_tenants:
480 if vim_tenant["passwd"]:
481 vim_tenant["passwd"] = "******"
482 if vim_tenant['config'] != None:
483 try:
484 config_dict = yaml.load(vim_tenant['config'])
485 vim_tenant['config'] = config_dict
venkatamahesh6ecca182017-01-27 23:04:40 +0530486 except Exception as e:
tierno8008c3a2016-10-13 15:34:28 +0000487 logger.error("Exception '%s' while trying to load config information", str(e))
488
tiernof97fd272016-07-11 14:32:37 +0200489 if datacenter['config'] != None:
490 try:
491 config_dict = yaml.load(datacenter['config'])
492 datacenter['config'] = config_dict
venkatamahesh6ecca182017-01-27 23:04:40 +0530493 except Exception as e:
tiernoefd80c92016-09-16 14:17:46 +0200494 logger.error("Exception '%s' while trying to load config information", str(e))
tiernof97fd272016-07-11 14:32:37 +0200495 #change_keys_http2db(content, http2db_datacenter, reverse=True)
496 convert_datetime2str(datacenter)
497 data={'datacenter' : datacenter}
498 return format_out(data)
499 except (nfvo.NfvoException, db_base_Exception) as e:
500 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
501 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000502 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000503 logger.error("Unexpected exception: ", exc_info=True)
504 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
505
tierno7edb6752016-03-21 17:37:52 +0100506
507@bottle.route(url_base + '/datacenters', method='POST')
508def http_post_datacenters():
509 '''insert a tenant into the catalogue. '''
510 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200511 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100512 http_content,_ = format_in( datacenter_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200513 r = utils.remove_extra_items(http_content, datacenter_schema)
tiernoefd80c92016-09-16 14:17:46 +0200514 if r:
515 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200516 try:
517 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100518 return http_get_datacenter_id('any', data)
tiernof97fd272016-07-11 14:32:37 +0200519 except (nfvo.NfvoException, db_base_Exception) as e:
520 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
521 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000522 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000523 logger.error("Unexpected exception: ", exc_info=True)
524 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
525
tierno7edb6752016-03-21 17:37:52 +0100526
527@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
528def http_edit_datacenter_id(datacenter_id_name):
529 '''edit datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200530 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100531 #parse input data
532 http_content,_ = format_in( datacenter_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200533 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200534 if r:
535 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100536
tiernof97fd272016-07-11 14:32:37 +0200537 try:
538 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100539 return http_get_datacenter_id('any', datacenter_id)
tiernof97fd272016-07-11 14:32:37 +0200540 except (nfvo.NfvoException, db_base_Exception) as e:
541 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
542 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000543 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000544 logger.error("Unexpected exception: ", exc_info=True)
545 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
546
tierno7edb6752016-03-21 17:37:52 +0100547
548@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
549@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
550@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
551def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
552 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200553 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100554 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200555 try:
556 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
557 where_= {"datacenter_id":datacenter_dict['uuid']}
558 if netmap_id:
559 if utils.check_valid_uuid(netmap_id):
560 where_["uuid"] = netmap_id
561 else:
562 where_["name"] = netmap_id
563 netmaps =mydb.get_rows(FROM='datacenter_nets',
564 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
565 WHERE=where_ )
566 convert_datetime2str(netmaps)
567 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
568 if netmap_id and len(netmaps)==1:
569 data={'netmap' : netmaps[0]}
570 elif netmap_id and len(netmaps)==0:
571 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
572 return
tierno7edb6752016-03-21 17:37:52 +0100573 else:
tiernof97fd272016-07-11 14:32:37 +0200574 data={'netmaps' : netmaps}
575 return format_out(data)
576 except (nfvo.NfvoException, db_base_Exception) as e:
577 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
578 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000579 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000580 logger.error("Unexpected exception: ", exc_info=True)
581 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
582
tierno7edb6752016-03-21 17:37:52 +0100583
584@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
585@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
586def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
587 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200588 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100589 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200590 try:
591 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
592 where_= {"datacenter_id":datacenter_dict['uuid']}
593 if netmap_id:
594 if utils.check_valid_uuid(netmap_id):
595 where_["uuid"] = netmap_id
596 else:
597 where_["name"] = netmap_id
598 #change_keys_http2db(content, http2db_tenant, reverse=True)
599 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
600 if deleted == 0 and netmap_id :
601 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
602 if netmap_id:
603 return format_out({"result": "netmap %s deleted" % netmap_id})
tierno7edb6752016-03-21 17:37:52 +0100604 else:
tiernof97fd272016-07-11 14:32:37 +0200605 return format_out({"result": "%d netmap deleted" % deleted})
606 except (nfvo.NfvoException, db_base_Exception) as e:
607 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
608 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000609 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000610 logger.error("Unexpected exception: ", exc_info=True)
611 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100612
613
614@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
615def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
tiernof97fd272016-07-11 14:32:37 +0200616 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
617 try:
618 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
619 convert_datetime2str(netmaps)
620 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
621 data={'netmaps' : netmaps}
622 return format_out(data)
623 except (nfvo.NfvoException, db_base_Exception) as e:
624 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
625 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000626 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000627 logger.error("Unexpected exception: ", exc_info=True)
628 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
629
tierno7edb6752016-03-21 17:37:52 +0100630
631@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
632def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
633 '''creates a new netmap'''
tiernof97fd272016-07-11 14:32:37 +0200634 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100635 #parse input data
636 http_content,_ = format_in( netmap_new_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200637 r = utils.remove_extra_items(http_content, netmap_new_schema)
tiernoefd80c92016-09-16 14:17:46 +0200638 if r:
639 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200640 try:
641 #obtain data, check that only one exist
642 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
643 convert_datetime2str(netmaps)
644 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
645 data={'netmaps' : netmaps}
646 return format_out(data)
647 except (nfvo.NfvoException, db_base_Exception) as e:
648 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
649 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000650 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000651 logger.error("Unexpected exception: ", exc_info=True)
652 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
653
tierno7edb6752016-03-21 17:37:52 +0100654
655@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
656def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
657 '''edit a netmap'''
tiernof97fd272016-07-11 14:32:37 +0200658 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100659 #parse input data
660 http_content,_ = format_in( netmap_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200661 r = utils.remove_extra_items(http_content, netmap_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200662 if r:
663 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100664
665 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200666 try:
667 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
tierno7edb6752016-03-21 17:37:52 +0100668 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
tiernof97fd272016-07-11 14:32:37 +0200669 except (nfvo.NfvoException, db_base_Exception) as e:
670 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
671 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000672 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000673 logger.error("Unexpected exception: ", exc_info=True)
674 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100675
676
677@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
678def http_action_datacenter_id(tenant_id, datacenter_id):
679 '''perform an action over datacenter, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200680 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100681 #parse input data
682 http_content,_ = format_in( datacenter_action_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200683 r = utils.remove_extra_items(http_content, datacenter_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200684 if r:
685 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200686 try:
687 #obtain data, check that only one exist
688 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
689 if 'net-update' in http_content:
690 return http_getnetmap_datacenter_id(datacenter_id)
691 else:
692 return format_out(result)
693 except (nfvo.NfvoException, db_base_Exception) as e:
694 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
695 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000696 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000697 logger.error("Unexpected exception: ", exc_info=True)
698 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100699
700
701@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
702def http_delete_datacenter_id( datacenter_id):
703 '''delete a tenant from database, can use both uuid or name'''
704
tiernof97fd272016-07-11 14:32:37 +0200705 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
706 try:
707 data = nfvo.delete_datacenter(mydb, datacenter_id)
708 return format_out({"result":"datacenter '" + data + "' deleted"})
709 except (nfvo.NfvoException, db_base_Exception) as e:
710 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
711 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000712 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000713 logger.error("Unexpected exception: ", exc_info=True)
714 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
715
tierno7edb6752016-03-21 17:37:52 +0100716
717@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
718def http_associate_datacenters(tenant_id, datacenter_id):
719 '''associate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200720 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100721 #parse input data
722 http_content,_ = format_in( datacenter_associate_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200723 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
tiernoefd80c92016-09-16 14:17:46 +0200724 if r:
725 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200726 try:
727 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
728 http_content['datacenter'].get('vim_tenant'),
729 http_content['datacenter'].get('vim_tenant_name'),
730 http_content['datacenter'].get('vim_username'),
tierno8008c3a2016-10-13 15:34:28 +0000731 http_content['datacenter'].get('vim_password'),
732 http_content['datacenter'].get('config')
733 )
tiernof97fd272016-07-11 14:32:37 +0200734 return http_get_datacenter_id(tenant_id, id_)
735 except (nfvo.NfvoException, db_base_Exception) as e:
736 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
737 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000738 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000739 logger.error("Unexpected exception: ", exc_info=True)
740 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
741
tierno7edb6752016-03-21 17:37:52 +0100742
743@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
744def http_deassociate_datacenters(tenant_id, datacenter_id):
745 '''deassociate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200746 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
747 try:
748 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
749 return format_out({"result": data})
750 except (nfvo.NfvoException, db_base_Exception) as e:
751 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
752 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000753 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000754 logger.error("Unexpected exception: ", exc_info=True)
755 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
756
tierno7edb6752016-03-21 17:37:52 +0100757
tierno7edb6752016-03-21 17:37:52 +0100758@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
759@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
760def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
tiernof97fd272016-07-11 14:32:37 +0200761 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
762 try:
763 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100764 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200765 except (nfvo.NfvoException, db_base_Exception) as e:
766 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
767 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000768 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000769 logger.error("Unexpected exception: ", exc_info=True)
770 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
771
tierno7edb6752016-03-21 17:37:52 +0100772
773@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
774def http_del_vim_items(tenant_id, datacenter_id, item, name):
tiernof97fd272016-07-11 14:32:37 +0200775 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
776 try:
777 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100778 return format_out({"result":data})
tiernof97fd272016-07-11 14:32:37 +0200779 except (nfvo.NfvoException, db_base_Exception) as e:
780 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
781 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000782 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000783 logger.error("Unexpected exception: ", exc_info=True)
784 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
785
tierno65a9b0c2016-09-28 14:57:25 +0000786
tierno7edb6752016-03-21 17:37:52 +0100787@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
788def http_post_vim_items(tenant_id, datacenter_id, item):
tiernof97fd272016-07-11 14:32:37 +0200789 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100790 http_content,_ = format_in( object_schema )
tiernof97fd272016-07-11 14:32:37 +0200791 try:
792 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
tierno7edb6752016-03-21 17:37:52 +0100793 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200794 except (nfvo.NfvoException, db_base_Exception) as e:
795 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
796 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000797 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000798 logger.error("Unexpected exception: ", exc_info=True)
799 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
800
tierno7edb6752016-03-21 17:37:52 +0100801
802@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
803def http_get_vnfs(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200804 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
805 try:
806 if tenant_id != 'any':
807 #check valid tenant_id
808 nfvo.check_tenant(mydb, tenant_id)
809 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
810 ('uuid','name','description','public', "tenant_id", "created_at") )
811 where_or = {}
812 if tenant_id != "any":
813 where_or["tenant_id"] = tenant_id
814 where_or["public"] = True
815 vnfs = mydb.get_rows(FROM='vnfs', SELECT=select_,WHERE=where_,WHERE_OR=where_or, WHERE_AND_OR="AND",LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100816 #change_keys_http2db(content, http2db_vnf, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200817 utils.convert_str2boolean(vnfs, ('public',))
818 convert_datetime2str(vnfs)
819 data={'vnfs' : vnfs}
tierno7edb6752016-03-21 17:37:52 +0100820 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200821 except (nfvo.NfvoException, db_base_Exception) as e:
822 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
823 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000824 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000825 logger.error("Unexpected exception: ", exc_info=True)
826 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
827
tierno7edb6752016-03-21 17:37:52 +0100828
829@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
830def http_get_vnf_id(tenant_id,vnf_id):
831 '''get vnf details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200832 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
833 try:
834 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
835 utils.convert_str2boolean(vnf, ('public',))
836 convert_datetime2str(vnf)
837 return format_out(vnf)
838 except (nfvo.NfvoException, db_base_Exception) as e:
839 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
840 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000841 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000842 logger.error("Unexpected exception: ", exc_info=True)
843 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
844
tierno7edb6752016-03-21 17:37:52 +0100845
846@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
847def http_post_vnfs(tenant_id):
848 '''insert a vnf into the catalogue. Creates the flavor and images in the VIM, and creates the VNF and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +0200849 #print "Parsing the YAML file of the VNF"
tierno7edb6752016-03-21 17:37:52 +0100850 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200851 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +0200852 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +0200853 r = utils.remove_extra_items(http_content, used_schema)
tiernoefd80c92016-09-16 14:17:46 +0200854 if r:
855 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200856 try:
tierno4319dad2016-09-05 12:11:11 +0200857 if used_schema == vnfd_schema_v01:
garciadeblas9f8456e2016-09-05 05:02:59 +0200858 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
tierno4319dad2016-09-05 12:11:11 +0200859 elif used_schema == vnfd_schema_v02:
garciadeblas9f8456e2016-09-05 05:02:59 +0200860 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
861 else:
862 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
863 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tiernof97fd272016-07-11 14:32:37 +0200864 return http_get_vnf_id(tenant_id, vnf_id)
865 except (nfvo.NfvoException, db_base_Exception) as e:
866 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
867 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000868 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000869 logger.error("Unexpected exception: ", exc_info=True)
870 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
871
tierno7edb6752016-03-21 17:37:52 +0100872
873@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
874def http_delete_vnf_id(tenant_id,vnf_id):
875 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200876 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100877 #check valid tenant_id and deletes the vnf, including images,
tiernof97fd272016-07-11 14:32:37 +0200878 try:
879 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
tierno7edb6752016-03-21 17:37:52 +0100880 #print json.dumps(data, indent=4)
881 return format_out({"result":"VNF " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200882 except (nfvo.NfvoException, db_base_Exception) as e:
883 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
884 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000885 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000886 logger.error("Unexpected exception: ", exc_info=True)
887 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
888
tierno7edb6752016-03-21 17:37:52 +0100889
890#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
891#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
892@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
893def http_get_hosts(tenant_id, datacenter):
894 '''get the tidvim host hopology from the vim.'''
tiernof97fd272016-07-11 14:32:37 +0200895 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
896 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
897 try:
898 if datacenter == 'treeview':
899 data = nfvo.get_hosts(mydb, tenant_id)
900 else:
901 #openmano-gui is using a hardcoded value for the datacenter
902 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
903
904 if result < 0:
tiernoefd80c92016-09-16 14:17:46 +0200905 #print "http_get_hosts error %d %s" % (-result, data)
tiernof97fd272016-07-11 14:32:37 +0200906 bottle.abort(-result, data)
907 else:
908 convert_datetime2str(data)
tiernoefd80c92016-09-16 14:17:46 +0200909 #print json.dumps(data, indent=4)
tiernof97fd272016-07-11 14:32:37 +0200910 return format_out(data)
911 except (nfvo.NfvoException, db_base_Exception) as e:
garciadeblas9f8456e2016-09-05 05:02:59 +0200912 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
tiernof97fd272016-07-11 14:32:37 +0200913 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000914 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000915 logger.error("Unexpected exception: ", exc_info=True)
916 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100917
918
919@bottle.route(url_base + '/<path:path>', method='OPTIONS')
920def http_options_deploy(path):
921 '''For some reason GUI web ask for OPTIONS that must be responded'''
922 #TODO: check correct path, and correct headers request
tiernof97fd272016-07-11 14:32:37 +0200923 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100924 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
925 bottle.response.set_header('Accept','application/yaml,application/json')
926 bottle.response.set_header('Content-Type','application/yaml,application/json')
927 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
928 bottle.response.set_header('Access-Control-Allow-Origin','*')
929 return
930
931@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
932def http_post_deploy(tenant_id):
933 '''post topology deploy.'''
tiernof97fd272016-07-11 14:32:37 +0200934 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100935
tierno66aa0372016-07-06 17:31:12 +0200936 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +0200937 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +0100938 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +0200939 #print "http_post_deploy input: ", http_content
tierno7edb6752016-03-21 17:37:52 +0100940
tiernof97fd272016-07-11 14:32:37 +0200941 try:
942 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
943 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
944 #print json.dumps(data, indent=4)
945 return format_out(instance)
946 except (nfvo.NfvoException, db_base_Exception) as e:
947 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
948 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000949 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000950 logger.error("Unexpected exception: ", exc_info=True)
951 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
952
tierno7edb6752016-03-21 17:37:52 +0100953
954@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
955def http_post_verify(tenant_id):
956 #TODO:
957# '''post topology verify'''
958# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
tiernof97fd272016-07-11 14:32:37 +0200959 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100960 return
961
962#
963# SCENARIOS
964#
965
966@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
967def http_post_scenarios(tenant_id):
968 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +0200969 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +0200970 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02, "0.3": nsd_schema_v03})
tierno42fcc3b2016-07-06 17:20:40 +0200971 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +0100972 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +0200973 #print "http_post_scenarios input: ", http_content
974 try:
tierno4319dad2016-09-05 12:11:11 +0200975 if used_schema == nsd_schema_v01:
tiernof97fd272016-07-11 14:32:37 +0200976 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +0200977 elif used_schema == nsd_schema_v02:
tierno5bb59dc2017-02-13 14:53:54 +0100978 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
tierno4319dad2016-09-05 12:11:11 +0200979 elif used_schema == nsd_schema_v03:
tierno5bb59dc2017-02-13 14:53:54 +0100980 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
garciadeblas9f8456e2016-09-05 05:02:59 +0200981 else:
982 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
983 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tierno7edb6752016-03-21 17:37:52 +0100984 #print json.dumps(data, indent=4)
985 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200986 return http_get_scenario_id(tenant_id, scenario_id)
987 except (nfvo.NfvoException, db_base_Exception) as e:
988 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
989 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000990 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000991 logger.error("Unexpected exception: ", exc_info=True)
992 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
993
tierno7edb6752016-03-21 17:37:52 +0100994
995@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
996def http_post_scenario_action(tenant_id, scenario_id):
997 '''take an action over a scenario'''
tiernof97fd272016-07-11 14:32:37 +0200998 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +0100999 # parse input data
1000 http_content, _ = format_in(scenario_action_schema)
1001 r = utils.remove_extra_items(http_content, scenario_action_schema)
1002 if r:
1003 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001004 try:
tierno551e5322017-01-19 16:16:26 +01001005 # check valid tenant_id
1006 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001007 if "start" in http_content:
1008 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1009 http_content['start'].get('description',http_content['start']['instance_name']),
1010 http_content['start'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001011 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001012 elif "deploy" in http_content: #Equivalent to start
1013 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1014 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1015 http_content['deploy'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001016 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001017 elif "reserve" in http_content: #Reserve resources
1018 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1019 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1020 http_content['reserve'].get('datacenter'), startvms=False )
tierno7edb6752016-03-21 17:37:52 +01001021 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001022 elif "verify" in http_content: #Equivalent to start and then delete
1023 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1024 http_content['verify'].get('description',http_content['verify']['instance_name']),
1025 http_content['verify'].get('datacenter'), startvms=False )
1026 instance_id = data['uuid']
1027 nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001028 return format_out({"result":"Verify OK"})
tiernof97fd272016-07-11 14:32:37 +02001029 except (nfvo.NfvoException, db_base_Exception) as e:
1030 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1031 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001032 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001033 logger.error("Unexpected exception: ", exc_info=True)
1034 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1035
tierno7edb6752016-03-21 17:37:52 +01001036
1037@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1038def http_get_scenarios(tenant_id):
1039 '''get scenarios list'''
tiernof97fd272016-07-11 14:32:37 +02001040 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1041 try:
1042 #check valid tenant_id
1043 if tenant_id != "any":
1044 nfvo.check_tenant(mydb, tenant_id)
1045 #obtain data
1046 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
1047 where_or={}
1048 if tenant_id != "any":
1049 where_or["tenant_id"] = tenant_id
1050 where_or["public"] = True
1051 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1052 convert_datetime2str(scenarios)
1053 utils.convert_str2boolean(scenarios, ('public',) )
1054 data={'scenarios':scenarios}
tierno7edb6752016-03-21 17:37:52 +01001055 #print json.dumps(scenarios, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001056 return format_out(data)
1057 except (nfvo.NfvoException, db_base_Exception) as e:
1058 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1059 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001060 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001061 logger.error("Unexpected exception: ", exc_info=True)
1062 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1063
tierno7edb6752016-03-21 17:37:52 +01001064
1065@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1066def http_get_scenario_id(tenant_id, scenario_id):
1067 '''get scenario details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001068 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1069 try:
1070 #check valid tenant_id
1071 if tenant_id != "any":
1072 nfvo.check_tenant(mydb, tenant_id)
1073 #obtain data
1074 scenario = mydb.get_scenario(scenario_id, tenant_id)
1075 convert_datetime2str(scenario)
1076 data={'scenario' : scenario}
tierno7edb6752016-03-21 17:37:52 +01001077 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001078 except (nfvo.NfvoException, db_base_Exception) as e:
1079 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1080 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001081 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001082 logger.error("Unexpected exception: ", exc_info=True)
1083 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1084
tierno7edb6752016-03-21 17:37:52 +01001085
1086@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1087def http_delete_scenario_id(tenant_id, scenario_id):
1088 '''delete a scenario from database, can use both uuid or name'''
tierno664691a2017-01-31 12:43:46 +01001089 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tiernof97fd272016-07-11 14:32:37 +02001090 try:
1091 #check valid tenant_id
1092 if tenant_id != "any":
tierno664691a2017-01-31 12:43:46 +01001093 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001094 #obtain data
1095 data = mydb.delete_scenario(scenario_id, tenant_id)
tierno7edb6752016-03-21 17:37:52 +01001096 #print json.dumps(data, indent=4)
1097 return format_out({"result":"scenario " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001098 except (nfvo.NfvoException, db_base_Exception) as e:
1099 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1100 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001101 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001102 logger.error("Unexpected exception: ", exc_info=True)
1103 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001104
1105
1106@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1107def http_put_scenario_id(tenant_id, scenario_id):
1108 '''edit an existing scenario id'''
tiernof97fd272016-07-11 14:32:37 +02001109 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001110 http_content,_ = format_in( scenario_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +02001111 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
tierno7edb6752016-03-21 17:37:52 +01001112 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001113 #print "http_put_scenario_id input: ", http_content
1114 try:
1115 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001116 #print json.dumps(data, indent=4)
1117 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001118 return http_get_scenario_id(tenant_id, scenario_id)
1119 except (nfvo.NfvoException, db_base_Exception) as e:
1120 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1121 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001122 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001123 logger.error("Unexpected exception: ", exc_info=True)
1124 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001125
1126@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1127def http_post_instances(tenant_id):
garciadeblasedca7b32016-09-29 14:01:52 +00001128 '''create an instance-scenario'''
tiernof97fd272016-07-11 14:32:37 +02001129 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001130 # parse input data
1131 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1132 r = utils.remove_extra_items(http_content, used_schema)
1133 if r is not None:
1134 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001135 try:
1136 #check valid tenant_id
1137 if tenant_id != "any":
1138 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001139 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
tierno7edb6752016-03-21 17:37:52 +01001140 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001141 except (nfvo.NfvoException, db_base_Exception) as e:
1142 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1143 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001144 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001145 logger.error("Unexpected exception: ", exc_info=True)
1146 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001147
1148#
1149# INSTANCES
1150#
1151@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1152def http_get_instances(tenant_id):
1153 '''get instance list'''
tiernof97fd272016-07-11 14:32:37 +02001154 try:
1155 #check valid tenant_id
1156 if tenant_id != "any":
1157 nfvo.check_tenant(mydb, tenant_id)
1158 #obtain data
1159 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1160 if tenant_id != "any":
1161 w['tenant_id'] = tenant_id
1162 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1163 convert_datetime2str(instances)
1164 utils.convert_str2boolean(instances, ('public',) )
1165 data={'instances':instances}
1166 return format_out(data)
1167 except (nfvo.NfvoException, db_base_Exception) as e:
1168 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1169 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001170 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001171 logger.error("Unexpected exception: ", exc_info=True)
1172 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1173
tierno7edb6752016-03-21 17:37:52 +01001174
1175@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1176def http_get_instance_id(tenant_id, instance_id):
1177 '''get instances details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001178 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1179 try:
1180 #check valid tenant_id
1181 if tenant_id != "any":
1182 nfvo.check_tenant(mydb, tenant_id)
1183 if tenant_id == "any":
1184 tenant_id = None
1185 #obtain data (first time is only to check that the instance exists)
1186 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1187 try:
1188 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1189 except (nfvo.NfvoException, db_base_Exception) as e:
1190 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
1191 #obtain data with results upated
1192 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1193 convert_datetime2str(instance)
1194 #print json.dumps(instance, indent=4)
1195 return format_out(instance)
1196 except (nfvo.NfvoException, db_base_Exception) as e:
1197 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1198 bottle.abort(e.http_code, str(e))
tierno8e995ce2016-09-22 08:13:00 +00001199 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001200 logger.error("Unexpected exception: ", exc_info=True)
1201 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1202
tierno7edb6752016-03-21 17:37:52 +01001203
1204@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1205def http_delete_instance_id(tenant_id, instance_id):
1206 '''delete instance from VIM and from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001207 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1208 try:
1209 #check valid tenant_id
1210 if tenant_id != "any":
1211 nfvo.check_tenant(mydb, tenant_id)
1212 if tenant_id == "any":
1213 tenant_id = None
1214 #obtain data
1215 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001216 return format_out({"result":message})
tiernof97fd272016-07-11 14:32:37 +02001217 except (nfvo.NfvoException, db_base_Exception) as e:
1218 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1219 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001220 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001221 logger.error("Unexpected exception: ", exc_info=True)
1222 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1223
tierno7edb6752016-03-21 17:37:52 +01001224
1225@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1226def http_post_instance_scenario_action(tenant_id, instance_id):
1227 '''take an action over a scenario instance'''
tiernof97fd272016-07-11 14:32:37 +02001228 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001229 # parse input data
1230 http_content, _ = format_in(instance_scenario_action_schema)
1231 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1232 if r:
1233 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001234 try:
1235 #check valid tenant_id
1236 if tenant_id != "any":
1237 nfvo.check_tenant(mydb, tenant_id)
1238
tiernof97fd272016-07-11 14:32:37 +02001239 #print "http_post_instance_scenario_action input: ", http_content
1240 #obtain data
1241 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1242 instance_id = instance["uuid"]
1243
1244 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001245 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001246 except (nfvo.NfvoException, db_base_Exception) as e:
1247 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1248 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001249 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001250 logger.error("Unexpected exception: ", exc_info=True)
1251 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001252
1253
1254@bottle.error(400)
1255@bottle.error(401)
1256@bottle.error(404)
1257@bottle.error(403)
1258@bottle.error(405)
1259@bottle.error(406)
1260@bottle.error(409)
1261@bottle.error(503)
1262@bottle.error(500)
1263def error400(error):
1264 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1265 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1266 return format_out(e)
1267