Add version primitive at http server and client
[osm/RO.git] / osm_ro / httpserver.py
1 # -*- 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 '''
25 HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
26 and will use the nfvo.py module to run the appropriate method.
27 Every 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
32 import bottle
33 import yaml
34 import json
35 import threading
36 import time
37 import logging
38
39 from jsonschema import validate as js_v, exceptions as js_e
40 from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
41 nsd_schema_v01, nsd_schema_v02, nsd_schema_v03, scenario_edit_schema, \
42 scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
43 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, sdn_controller_schema, sdn_controller_edit_schema, \
46 sdn_port_mapping_schema
47
48 import nfvo
49 import utils
50 from db_base import db_base_Exception
51 from functools import wraps
52
53 global mydb
54 global url_base
55 global logger
56 url_base="/openmano"
57 logger = None
58
59 HTTP_Bad_Request = 400
60 HTTP_Unauthorized = 401
61 HTTP_Not_Found = 404
62 HTTP_Forbidden = 403
63 HTTP_Method_Not_Allowed = 405
64 HTTP_Not_Acceptable = 406
65 HTTP_Service_Unavailable = 503
66 HTTP_Internal_Server_Error= 500
67
68 def delete_nulls(var):
69 if type(var) is dict:
70 for k in var.keys():
71 if var[k] is None: del var[k]
72 elif type(var[k]) is dict or type(var[k]) is list or type(var[k]) is tuple:
73 if delete_nulls(var[k]): del var[k]
74 if len(var) == 0: return True
75 elif type(var) is list or type(var) is tuple:
76 for k in var:
77 if type(k) is dict: delete_nulls(k)
78 if len(var) == 0: return True
79 return False
80
81 def convert_datetime2str(var):
82 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
83 It enters recursively in the dict var finding this kind of variables
84 '''
85 if type(var) is dict:
86 for k,v in var.items():
87 if type(v) is float and k in ("created_at", "modified_at"):
88 var[k] = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(v) )
89 elif type(v) is dict or type(v) is list or type(v) is tuple:
90 convert_datetime2str(v)
91 if len(var) == 0: return True
92 elif type(var) is list or type(var) is tuple:
93 for v in var:
94 convert_datetime2str(v)
95
96 def log_to_logger(fn):
97 '''
98 Wrap a Bottle request so that a log line is emitted after it's handled.
99 (This decorator can be extended to take the desired logger as a param.)
100 '''
101 @wraps(fn)
102 def _log_to_logger(*args, **kwargs):
103 actual_response = fn(*args, **kwargs)
104 # modify this to log exactly what you need:
105 logger.info('FROM %s %s %s %s' % (bottle.request.remote_addr,
106 bottle.request.method,
107 bottle.request.url,
108 bottle.response.status))
109 return actual_response
110 return _log_to_logger
111
112 class httpserver(threading.Thread):
113 def __init__(self, db, admin=False, host='localhost', port=9090):
114 #global url_base
115 global mydb
116 global logger
117 #initialization
118 if not logger:
119 logger = logging.getLogger('openmano.http')
120 threading.Thread.__init__(self)
121 self.host = host
122 self.port = port #Port where the listen service must be started
123 if admin==True:
124 self.name = "http_admin"
125 else:
126 self.name = "http"
127 #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
128 mydb = db
129 #self.first_usable_connection_index = 10
130 #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
131 #Ensure that when the main program exits the thread will also exit
132 self.daemon = True
133 self.setDaemon(True)
134
135 def run(self):
136 bottle.install(log_to_logger)
137 bottle.run(host=self.host, port=self.port, debug=False, quiet=True)
138
139 def run_bottle(db, host_='localhost', port_=9090):
140 '''used for launching in main thread, so that it can be debugged'''
141 global mydb
142 mydb = db
143 bottle.run(host=host_, port=port_, debug=True) #quiet=True
144
145
146 @bottle.route(url_base + '/', method='GET')
147 def http_get():
148 #print
149 return 'works' #TODO: to be completed
150
151 #
152 # Util functions
153 #
154
155 def change_keys_http2db(data, http_db, reverse=False):
156 '''Change keys of dictionary data acording to the key_dict values
157 This allow change from http interface names to database names.
158 When reverse is True, the change is otherwise
159 Attributes:
160 data: can be a dictionary or a list
161 http_db: is a dictionary with hhtp names as keys and database names as value
162 reverse: by default change is done from http api to database. If True change is done otherwise
163 Return: None, but data is modified'''
164 if type(data) is tuple or type(data) is list:
165 for d in data:
166 change_keys_http2db(d, http_db, reverse)
167 elif type(data) is dict or type(data) is bottle.FormsDict:
168 if reverse:
169 for k,v in http_db.items():
170 if v in data: data[k]=data.pop(v)
171 else:
172 for k,v in http_db.items():
173 if k in data: data[v]=data.pop(k)
174
175 def format_out(data):
176 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
177 logger.debug("OUT: " + yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
178 if 'application/yaml' in bottle.request.headers.get('Accept'):
179 bottle.response.content_type='application/yaml'
180 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='"'
181 else: #by default json
182 bottle.response.content_type='application/json'
183 #return data #json no style
184 return json.dumps(data, indent=4) + "\n"
185
186 def format_in(default_schema, version_fields=None, version_dict_schema=None):
187 ''' Parse the content of HTTP request against a json_schema
188 Parameters
189 default_schema: The schema to be parsed by default if no version field is found in the client data
190 version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
191 version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
192 It can contain a None as key, and this is apply if the client data version does not match any key
193 Return:
194 user_data, used_schema: if the data is successfully decoded and matches the schema
195 launch a bottle abort if fails
196 '''
197 #print "HEADERS :" + str(bottle.request.headers.items())
198 try:
199 error_text = "Invalid header format "
200 format_type = bottle.request.headers.get('Content-Type', 'application/json')
201 if 'application/json' in format_type:
202 error_text = "Invalid json format "
203 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
204 client_data = json.load(bottle.request.body)
205 #client_data = bottle.request.json()
206 elif 'application/yaml' in format_type:
207 error_text = "Invalid yaml format "
208 client_data = yaml.load(bottle.request.body)
209 elif 'application/xml' in format_type:
210 bottle.abort(501, "Content-Type: application/xml not supported yet.")
211 else:
212 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
213 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
214 return
215 #if client_data == None:
216 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
217 # return
218
219 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) )
220 #look for the client provider version
221 error_text = "Invalid content "
222 client_version = None
223 used_schema = None
224 if version_fields != None:
225 client_version = client_data
226 for field in version_fields:
227 if field in client_version:
228 client_version = client_version[field]
229 else:
230 client_version=None
231 break
232 if client_version==None:
233 used_schema=default_schema
234 elif version_dict_schema!=None:
235 if client_version in version_dict_schema:
236 used_schema = version_dict_schema[client_version]
237 elif None in version_dict_schema:
238 used_schema = version_dict_schema[None]
239 if used_schema==None:
240 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
241
242 js_v(client_data, used_schema)
243 return client_data, used_schema
244 except (ValueError, yaml.YAMLError) as exc:
245 error_text += str(exc)
246 logger.error(error_text)
247 bottle.abort(HTTP_Bad_Request, error_text)
248 except js_e.ValidationError as exc:
249 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
250 error_pos = ""
251 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
252 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
253 #except:
254 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
255 # raise
256
257 def filter_query_string(qs, http2db, allowed):
258 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
259 Attributes:
260 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
261 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
262 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
263 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
264 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
265 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
266 limit: limit dictated by user with the query string 'limit'. 100 by default
267 abort if not permited, using bottel.abort
268 '''
269 where={}
270 limit=100
271 select=[]
272 #if type(qs) is not bottle.FormsDict:
273 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
274 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
275 for k in qs:
276 if k=='field':
277 select += qs.getall(k)
278 for v in select:
279 if v not in allowed:
280 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
281 elif k=='limit':
282 try:
283 limit=int(qs[k])
284 except:
285 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
286 else:
287 if k not in allowed:
288 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
289 if qs[k]!="null": where[k]=qs[k]
290 else: where[k]=None
291 if len(select)==0: select += allowed
292 #change from http api to database naming
293 for i in range(0,len(select)):
294 k=select[i]
295 if http2db and k in http2db:
296 select[i] = http2db[k]
297 if http2db:
298 change_keys_http2db(where, http2db)
299 #print "filter_query_string", select,where,limit
300
301 return select,where,limit
302
303 @bottle.hook('after_request')
304 def enable_cors():
305 '''Don't know yet if really needed. Keep it just in case'''
306 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
307
308 @bottle.route(url_base + '/version', method='GET')
309 def http_get_version():
310 return nfvo.get_version()
311 #
312 # VNFs
313 #
314
315 @bottle.route(url_base + '/tenants', method='GET')
316 def http_get_tenants():
317 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
318 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
319 ('uuid','name','description','created_at') )
320 try:
321 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
322 #change_keys_http2db(content, http2db_tenant, reverse=True)
323 convert_datetime2str(tenants)
324 data={'tenants' : tenants}
325 return format_out(data)
326 except db_base_Exception as e:
327 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
328 bottle.abort(e.http_code, str(e))
329 except Exception as e:
330 logger.error("Unexpected exception: ", exc_info=True)
331 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
332
333
334 @bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
335 def http_get_tenant_id(tenant_id):
336 '''get tenant details, can use both uuid or name'''
337 #obtain data
338 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
339 try:
340 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
341 #change_keys_http2db(content, http2db_tenant, reverse=True)
342 convert_datetime2str(tenant)
343 data={'tenant' : tenant}
344 return format_out(data)
345 except db_base_Exception as e:
346 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
347 bottle.abort(e.http_code, str(e))
348 except Exception as e:
349 logger.error("Unexpected exception: ", exc_info=True)
350 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
351
352
353 @bottle.route(url_base + '/tenants', method='POST')
354 def http_post_tenants():
355 '''insert a tenant into the catalogue. '''
356 #parse input data
357 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
358 http_content,_ = format_in( tenant_schema )
359 r = utils.remove_extra_items(http_content, tenant_schema)
360 if r:
361 logger.debug("Remove received extra items %s", str(r))
362 try:
363 data = nfvo.new_tenant(mydb, http_content['tenant'])
364 return http_get_tenant_id(data)
365 except (nfvo.NfvoException, db_base_Exception) as e:
366 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
367 bottle.abort(e.http_code, str(e))
368 except Exception as e:
369 logger.error("Unexpected exception: ", exc_info=True)
370 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
371
372
373 @bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
374 def http_edit_tenant_id(tenant_id):
375 '''edit tenant details, can use both uuid or name'''
376 #parse input data
377 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
378 http_content,_ = format_in( tenant_edit_schema )
379 r = utils.remove_extra_items(http_content, tenant_edit_schema)
380 if r:
381 logger.debug("Remove received extra items %s", str(r))
382
383 #obtain data, check that only one exist
384 try:
385 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
386 #edit data
387 tenant_id = tenant['uuid']
388 where={'uuid': tenant['uuid']}
389 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
390 return http_get_tenant_id(tenant_id)
391 except db_base_Exception as e:
392 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
393 bottle.abort(e.http_code, str(e))
394 except Exception as e:
395 logger.error("Unexpected exception: ", exc_info=True)
396 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
397
398
399 @bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
400 def http_delete_tenant_id(tenant_id):
401 '''delete a tenant from database, can use both uuid or name'''
402 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
403 try:
404 data = nfvo.delete_tenant(mydb, tenant_id)
405 return format_out({"result":"tenant " + data + " deleted"})
406 except db_base_Exception as e:
407 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
408 bottle.abort(e.http_code, str(e))
409 except Exception as e:
410 logger.error("Unexpected exception: ", exc_info=True)
411 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
412
413
414 @bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
415 def http_get_datacenters(tenant_id):
416 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
417 try:
418 if tenant_id != 'any':
419 #check valid tenant_id
420 nfvo.check_tenant(mydb, tenant_id)
421 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
422 ('uuid','name','vim_url','type','created_at') )
423 if tenant_id != 'any':
424 where_['nfvo_tenant_id'] = tenant_id
425 if 'created_at' in select_:
426 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
427 if 'created_at' in where_:
428 where_['d.created_at'] = where_.pop('created_at')
429 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
430 SELECT=select_,WHERE=where_,LIMIT=limit_)
431 else:
432 datacenters = mydb.get_rows(FROM='datacenters',
433 SELECT=select_,WHERE=where_,LIMIT=limit_)
434 #change_keys_http2db(content, http2db_tenant, reverse=True)
435 convert_datetime2str(datacenters)
436 data={'datacenters' : datacenters}
437 return format_out(data)
438 except (nfvo.NfvoException, db_base_Exception) as e:
439 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
440 bottle.abort(e.http_code, str(e))
441 except Exception as e:
442 logger.error("Unexpected exception: ", exc_info=True)
443 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
444
445
446 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
447 def http_get_datacenter_id(tenant_id, datacenter_id):
448 '''get datacenter details, can use both uuid or name'''
449 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
450 try:
451 if tenant_id != 'any':
452 #check valid tenant_id
453 nfvo.check_tenant(mydb, tenant_id)
454 #obtain data
455 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
456 where_={}
457 where_[what] = datacenter_id
458 select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
459 if tenant_id != 'any':
460 select_.append("datacenter_tenant_id")
461 where_['td.nfvo_tenant_id']= tenant_id
462 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
463 else:
464 from_='datacenters as d'
465 datacenters = mydb.get_rows(
466 SELECT=select_,
467 FROM=from_,
468 WHERE=where_)
469
470 if len(datacenters)==0:
471 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
472 elif len(datacenters)>1:
473 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
474 datacenter = datacenters[0]
475 if tenant_id != 'any':
476 #get vim tenant info
477 vim_tenants = mydb.get_rows(
478 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
479 FROM="datacenter_tenants",
480 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
481 ORDER_BY=("created", ) )
482 del datacenter["datacenter_tenant_id"]
483 datacenter["vim_tenants"] = vim_tenants
484 for vim_tenant in vim_tenants:
485 if vim_tenant["passwd"]:
486 vim_tenant["passwd"] = "******"
487 if vim_tenant['config'] != None:
488 try:
489 config_dict = yaml.load(vim_tenant['config'])
490 vim_tenant['config'] = config_dict
491 except Exception as e:
492 logger.error("Exception '%s' while trying to load config information", str(e))
493
494 if datacenter['config'] != None:
495 try:
496 config_dict = yaml.load(datacenter['config'])
497 datacenter['config'] = config_dict
498 except Exception as e:
499 logger.error("Exception '%s' while trying to load config information", str(e))
500 #change_keys_http2db(content, http2db_datacenter, reverse=True)
501 convert_datetime2str(datacenter)
502 data={'datacenter' : datacenter}
503 return format_out(data)
504 except (nfvo.NfvoException, db_base_Exception) as e:
505 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
506 bottle.abort(e.http_code, str(e))
507 except Exception as e:
508 logger.error("Unexpected exception: ", exc_info=True)
509 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
510
511
512 @bottle.route(url_base + '/datacenters', method='POST')
513 def http_post_datacenters():
514 '''insert a datacenter into the catalogue. '''
515 #parse input data
516 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
517 http_content,_ = format_in( datacenter_schema )
518 r = utils.remove_extra_items(http_content, datacenter_schema)
519 if r:
520 logger.debug("Remove received extra items %s", str(r))
521 try:
522 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
523 return http_get_datacenter_id('any', data)
524 except (nfvo.NfvoException, db_base_Exception) as e:
525 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
526 bottle.abort(e.http_code, str(e))
527 except Exception as e:
528 logger.error("Unexpected exception: ", exc_info=True)
529 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
530
531
532 @bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
533 def http_edit_datacenter_id(datacenter_id_name):
534 '''edit datacenter details, can use both uuid or name'''
535 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
536 #parse input data
537 http_content,_ = format_in( datacenter_edit_schema )
538 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
539 if r:
540 logger.debug("Remove received extra items %s", str(r))
541
542 try:
543 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
544 return http_get_datacenter_id('any', datacenter_id)
545 except (nfvo.NfvoException, db_base_Exception) as e:
546 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
547 bottle.abort(e.http_code, str(e))
548 except Exception as e:
549 logger.error("Unexpected exception: ", exc_info=True)
550 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
551
552 @bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
553 def http_post_sdn_controller(tenant_id):
554 '''insert a sdn controller into the catalogue. '''
555 #parse input data
556 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
557 http_content,_ = format_in( sdn_controller_schema )
558 try:
559 logger.debug("tenant_id: "+tenant_id)
560 #logger.debug("content: {}".format(http_content['sdn_controller']))
561
562 data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
563 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
564 except (nfvo.NfvoException, db_base_Exception) as e:
565 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
566 bottle.abort(e.http_code, str(e))
567 except Exception as e:
568 logger.error("Unexpected exception: ", exc_info=True)
569 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
570
571 @bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='PUT')
572 def http_put_sdn_controller_update(tenant_id, controller_id):
573 '''Update sdn controller'''
574 #parse input data
575 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
576 http_content,_ = format_in( sdn_controller_edit_schema )
577 # r = utils.remove_extra_items(http_content, datacenter_schema)
578 # if r:
579 # logger.debug("Remove received extra items %s", str(r))
580 try:
581 #logger.debug("tenant_id: "+tenant_id)
582 logger.debug("content: {}".format(http_content['sdn_controller']))
583
584 data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
585 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
586
587 except (nfvo.NfvoException, db_base_Exception) as e:
588 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
589 bottle.abort(e.http_code, str(e))
590 except Exception as e:
591 logger.error("Unexpected exception: ", exc_info=True)
592 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
593
594 @bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
595 def http_get_sdn_controller(tenant_id):
596 '''get sdn controllers list, can use both uuid or name'''
597 try:
598 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
599
600 data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
601 return format_out(data)
602 except (nfvo.NfvoException, db_base_Exception) as e:
603 logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
604 bottle.abort(e.http_code, str(e))
605 except Exception as e:
606 logger.error("Unexpected exception: ", exc_info=True)
607 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
608
609 @bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
610 def http_get_sdn_controller_id(tenant_id, controller_id):
611 '''get sdn controller details, can use both uuid or name'''
612 try:
613 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
614 data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
615 return format_out({"sdn_controllers": data})
616 except (nfvo.NfvoException, db_base_Exception) as e:
617 logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
618 bottle.abort(e.http_code, str(e))
619 except Exception as e:
620 logger.error("Unexpected exception: ", exc_info=True)
621 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
622
623 @bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
624 def http_delete_sdn_controller_id(tenant_id, controller_id):
625 '''delete sdn controller, can use both uuid or name'''
626 try:
627 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
628 data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
629 return format_out(data)
630 except (nfvo.NfvoException, db_base_Exception) as e:
631 logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
632 bottle.abort(e.http_code, str(e))
633 except Exception as e:
634 logger.error("Unexpected exception: ", exc_info=True)
635 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
636
637 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
638 def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
639 '''Set the sdn port mapping for a datacenter. '''
640 #parse input data
641 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
642 http_content, _ = format_in(sdn_port_mapping_schema)
643 # r = utils.remove_extra_items(http_content, datacenter_schema)
644 # if r:
645 # logger.debug("Remove received extra items %s", str(r))
646 try:
647 data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
648 return format_out({"sdn_port_mapping": data})
649 except (nfvo.NfvoException, db_base_Exception) as e:
650 logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
651 bottle.abort(e.http_code, str(e))
652 except Exception as e:
653 logger.error("Unexpected exception: ", exc_info=True)
654 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
655
656 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
657 def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
658 '''get datacenter sdn mapping details, can use both uuid or name'''
659 try:
660 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
661
662 data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
663 return format_out({"sdn_port_mapping": data})
664 except (nfvo.NfvoException, db_base_Exception) as e:
665 logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
666 bottle.abort(e.http_code, str(e))
667 except Exception as e:
668 logger.error("Unexpected exception: ", exc_info=True)
669 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
670
671 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
672 def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
673 '''clean datacenter sdn mapping, can use both uuid or name'''
674 try:
675 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
676 data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
677 return format_out({"result": data})
678 except (nfvo.NfvoException, db_base_Exception) as e:
679 logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
680 bottle.abort(e.http_code, str(e))
681 except Exception as e:
682 logger.error("Unexpected exception: ", exc_info=True)
683 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
684
685 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
686 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
687 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
688 def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
689 '''get datacenter networks, can use both uuid or name'''
690 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
691 #obtain data
692 try:
693 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
694 where_= {"datacenter_id":datacenter_dict['uuid']}
695 if netmap_id:
696 if utils.check_valid_uuid(netmap_id):
697 where_["uuid"] = netmap_id
698 else:
699 where_["name"] = netmap_id
700 netmaps =mydb.get_rows(FROM='datacenter_nets',
701 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
702 WHERE=where_ )
703 convert_datetime2str(netmaps)
704 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
705 if netmap_id and len(netmaps)==1:
706 data={'netmap' : netmaps[0]}
707 elif netmap_id and len(netmaps)==0:
708 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
709 return
710 else:
711 data={'netmaps' : netmaps}
712 return format_out(data)
713 except (nfvo.NfvoException, db_base_Exception) as e:
714 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
715 bottle.abort(e.http_code, str(e))
716 except Exception as e:
717 logger.error("Unexpected exception: ", exc_info=True)
718 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
719
720
721 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
722 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
723 def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
724 '''get datacenter networks, can use both uuid or name'''
725 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
726 #obtain data
727 try:
728 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
729 where_= {"datacenter_id":datacenter_dict['uuid']}
730 if netmap_id:
731 if utils.check_valid_uuid(netmap_id):
732 where_["uuid"] = netmap_id
733 else:
734 where_["name"] = netmap_id
735 #change_keys_http2db(content, http2db_tenant, reverse=True)
736 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
737 if deleted == 0 and netmap_id :
738 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
739 if netmap_id:
740 return format_out({"result": "netmap %s deleted" % netmap_id})
741 else:
742 return format_out({"result": "%d netmap deleted" % deleted})
743 except (nfvo.NfvoException, db_base_Exception) as e:
744 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
745 bottle.abort(e.http_code, str(e))
746 except Exception as e:
747 logger.error("Unexpected exception: ", exc_info=True)
748 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
749
750
751 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
752 def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
753 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
754 try:
755 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
756 convert_datetime2str(netmaps)
757 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
758 data={'netmaps' : netmaps}
759 return format_out(data)
760 except (nfvo.NfvoException, db_base_Exception) as e:
761 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
762 bottle.abort(e.http_code, str(e))
763 except Exception as e:
764 logger.error("Unexpected exception: ", exc_info=True)
765 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
766
767
768 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
769 def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
770 '''creates a new netmap'''
771 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
772 #parse input data
773 http_content,_ = format_in( netmap_new_schema )
774 r = utils.remove_extra_items(http_content, netmap_new_schema)
775 if r:
776 logger.debug("Remove received extra items %s", str(r))
777 try:
778 #obtain data, check that only one exist
779 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
780 convert_datetime2str(netmaps)
781 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
782 data={'netmaps' : netmaps}
783 return format_out(data)
784 except (nfvo.NfvoException, db_base_Exception) as e:
785 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
786 bottle.abort(e.http_code, str(e))
787 except Exception as e:
788 logger.error("Unexpected exception: ", exc_info=True)
789 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
790
791
792 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
793 def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
794 '''edit a netmap'''
795 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
796 #parse input data
797 http_content,_ = format_in( netmap_edit_schema )
798 r = utils.remove_extra_items(http_content, netmap_edit_schema)
799 if r:
800 logger.debug("Remove received extra items %s", str(r))
801
802 #obtain data, check that only one exist
803 try:
804 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
805 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
806 except (nfvo.NfvoException, db_base_Exception) as e:
807 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
808 bottle.abort(e.http_code, str(e))
809 except Exception as e:
810 logger.error("Unexpected exception: ", exc_info=True)
811 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
812
813
814 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
815 def http_action_datacenter_id(tenant_id, datacenter_id):
816 '''perform an action over datacenter, can use both uuid or name'''
817 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
818 #parse input data
819 http_content,_ = format_in( datacenter_action_schema )
820 r = utils.remove_extra_items(http_content, datacenter_action_schema)
821 if r:
822 logger.debug("Remove received extra items %s", str(r))
823 try:
824 #obtain data, check that only one exist
825 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
826 if 'net-update' in http_content:
827 return http_getnetmap_datacenter_id(datacenter_id)
828 else:
829 return format_out(result)
830 except (nfvo.NfvoException, db_base_Exception) as e:
831 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
832 bottle.abort(e.http_code, str(e))
833 except Exception as e:
834 logger.error("Unexpected exception: ", exc_info=True)
835 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
836
837
838 @bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
839 def http_delete_datacenter_id( datacenter_id):
840 '''delete a tenant from database, can use both uuid or name'''
841
842 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
843 try:
844 data = nfvo.delete_datacenter(mydb, datacenter_id)
845 return format_out({"result":"datacenter '" + data + "' deleted"})
846 except (nfvo.NfvoException, db_base_Exception) as e:
847 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
848 bottle.abort(e.http_code, str(e))
849 except Exception as e:
850 logger.error("Unexpected exception: ", exc_info=True)
851 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
852
853
854 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
855 def http_associate_datacenters(tenant_id, datacenter_id):
856 '''associate an existing datacenter to a this tenant. '''
857 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
858 #parse input data
859 http_content,_ = format_in( datacenter_associate_schema )
860 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
861 if r:
862 logger.debug("Remove received extra items %s", str(r))
863 try:
864 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
865 http_content['datacenter'].get('vim_tenant'),
866 http_content['datacenter'].get('vim_tenant_name'),
867 http_content['datacenter'].get('vim_username'),
868 http_content['datacenter'].get('vim_password'),
869 http_content['datacenter'].get('config')
870 )
871 return http_get_datacenter_id(tenant_id, id_)
872 except (nfvo.NfvoException, db_base_Exception) as e:
873 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
874 bottle.abort(e.http_code, str(e))
875 except Exception as e:
876 logger.error("Unexpected exception: ", exc_info=True)
877 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
878
879 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
880 def http_associate_datacenters_edit(tenant_id, datacenter_id):
881 '''associate an existing datacenter to a this tenant. '''
882 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
883 #parse input data
884 http_content,_ = format_in( datacenter_associate_schema )
885 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
886 if r:
887 logger.debug("Remove received extra items %s", str(r))
888 try:
889 id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
890 http_content['datacenter'].get('vim_tenant'),
891 http_content['datacenter'].get('vim_tenant_name'),
892 http_content['datacenter'].get('vim_username'),
893 http_content['datacenter'].get('vim_password'),
894 http_content['datacenter'].get('config')
895 )
896 return http_get_datacenter_id(tenant_id, id_)
897 except (nfvo.NfvoException, db_base_Exception) as e:
898 logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
899 bottle.abort(e.http_code, str(e))
900 except Exception as e:
901 logger.error("Unexpected exception: ", exc_info=True)
902 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
903
904 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
905 def http_deassociate_datacenters(tenant_id, datacenter_id):
906 '''deassociate an existing datacenter to a this tenant. '''
907 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
908 try:
909 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
910 return format_out({"result": data})
911 except (nfvo.NfvoException, db_base_Exception) as e:
912 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
913 bottle.abort(e.http_code, str(e))
914 except Exception as e:
915 logger.error("Unexpected exception: ", exc_info=True)
916 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
917
918
919 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
920 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
921 def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
922 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
923 try:
924 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
925 return format_out(data)
926 except (nfvo.NfvoException, db_base_Exception) as e:
927 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
928 bottle.abort(e.http_code, str(e))
929 except Exception as e:
930 logger.error("Unexpected exception: ", exc_info=True)
931 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
932
933
934 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
935 def http_del_vim_items(tenant_id, datacenter_id, item, name):
936 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
937 try:
938 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
939 return format_out({"result":data})
940 except (nfvo.NfvoException, db_base_Exception) as e:
941 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
942 bottle.abort(e.http_code, str(e))
943 except Exception as e:
944 logger.error("Unexpected exception: ", exc_info=True)
945 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
946
947
948 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
949 def http_post_vim_items(tenant_id, datacenter_id, item):
950 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
951 http_content,_ = format_in( object_schema )
952 try:
953 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
954 return format_out(data)
955 except (nfvo.NfvoException, db_base_Exception) as e:
956 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
957 bottle.abort(e.http_code, str(e))
958 except Exception as e:
959 logger.error("Unexpected exception: ", exc_info=True)
960 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
961
962
963 @bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
964 def http_get_vnfs(tenant_id):
965 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
966 try:
967 if tenant_id != 'any':
968 #check valid tenant_id
969 nfvo.check_tenant(mydb, tenant_id)
970 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
971 ('uuid','name','description','public', "tenant_id", "created_at") )
972 where_or = {}
973 if tenant_id != "any":
974 where_or["tenant_id"] = tenant_id
975 where_or["public"] = True
976 vnfs = mydb.get_rows(FROM='vnfs', SELECT=select_,WHERE=where_,WHERE_OR=where_or, WHERE_AND_OR="AND",LIMIT=limit_)
977 #change_keys_http2db(content, http2db_vnf, reverse=True)
978 utils.convert_str2boolean(vnfs, ('public',))
979 convert_datetime2str(vnfs)
980 data={'vnfs' : vnfs}
981 return format_out(data)
982 except (nfvo.NfvoException, db_base_Exception) as e:
983 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
984 bottle.abort(e.http_code, str(e))
985 except Exception as e:
986 logger.error("Unexpected exception: ", exc_info=True)
987 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
988
989
990 @bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
991 def http_get_vnf_id(tenant_id,vnf_id):
992 '''get vnf details, can use both uuid or name'''
993 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
994 try:
995 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
996 utils.convert_str2boolean(vnf, ('public',))
997 convert_datetime2str(vnf)
998 return format_out(vnf)
999 except (nfvo.NfvoException, db_base_Exception) as e:
1000 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
1001 bottle.abort(e.http_code, str(e))
1002 except Exception as e:
1003 logger.error("Unexpected exception: ", exc_info=True)
1004 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1005
1006
1007 @bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
1008 def http_post_vnfs(tenant_id):
1009 '''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'''
1010 #print "Parsing the YAML file of the VNF"
1011 #parse input data
1012 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1013 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
1014 r = utils.remove_extra_items(http_content, used_schema)
1015 if r:
1016 logger.debug("Remove received extra items %s", str(r))
1017 try:
1018 if used_schema == vnfd_schema_v01:
1019 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
1020 elif used_schema == vnfd_schema_v02:
1021 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
1022 else:
1023 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1024 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
1025 return http_get_vnf_id(tenant_id, vnf_id)
1026 except (nfvo.NfvoException, db_base_Exception) as e:
1027 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1028 bottle.abort(e.http_code, str(e))
1029 except Exception as e:
1030 logger.error("Unexpected exception: ", exc_info=True)
1031 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1032
1033
1034 @bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
1035 def http_delete_vnf_id(tenant_id,vnf_id):
1036 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
1037 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1038 #check valid tenant_id and deletes the vnf, including images,
1039 try:
1040 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
1041 #print json.dumps(data, indent=4)
1042 return format_out({"result":"VNF " + data + " deleted"})
1043 except (nfvo.NfvoException, db_base_Exception) as e:
1044 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
1045 bottle.abort(e.http_code, str(e))
1046 except Exception as e:
1047 logger.error("Unexpected exception: ", exc_info=True)
1048 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1049
1050
1051 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1052 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1053 @bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
1054 def http_get_hosts(tenant_id, datacenter):
1055 '''get the tidvim host hopology from the vim.'''
1056 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1057 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1058 try:
1059 if datacenter == 'treeview':
1060 data = nfvo.get_hosts(mydb, tenant_id)
1061 else:
1062 #openmano-gui is using a hardcoded value for the datacenter
1063 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
1064
1065 if result < 0:
1066 #print "http_get_hosts error %d %s" % (-result, data)
1067 bottle.abort(-result, data)
1068 else:
1069 convert_datetime2str(data)
1070 #print json.dumps(data, indent=4)
1071 return format_out(data)
1072 except (nfvo.NfvoException, db_base_Exception) as e:
1073 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
1074 bottle.abort(e.http_code, str(e))
1075 except Exception as e:
1076 logger.error("Unexpected exception: ", exc_info=True)
1077 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1078
1079
1080 @bottle.route(url_base + '/<path:path>', method='OPTIONS')
1081 def http_options_deploy(path):
1082 '''For some reason GUI web ask for OPTIONS that must be responded'''
1083 #TODO: check correct path, and correct headers request
1084 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1085 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1086 bottle.response.set_header('Accept','application/yaml,application/json')
1087 bottle.response.set_header('Content-Type','application/yaml,application/json')
1088 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
1089 bottle.response.set_header('Access-Control-Allow-Origin','*')
1090 return
1091
1092 @bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
1093 def http_post_deploy(tenant_id):
1094 '''post topology deploy.'''
1095 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1096
1097 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
1098 #r = utils.remove_extra_items(http_content, used_schema)
1099 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
1100 #print "http_post_deploy input: ", http_content
1101
1102 try:
1103 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1104 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
1105 #print json.dumps(data, indent=4)
1106 return format_out(instance)
1107 except (nfvo.NfvoException, db_base_Exception) as e:
1108 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
1109 bottle.abort(e.http_code, str(e))
1110 except Exception as e:
1111 logger.error("Unexpected exception: ", exc_info=True)
1112 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1113
1114
1115 @bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
1116 def http_post_verify(tenant_id):
1117 #TODO:
1118 # '''post topology verify'''
1119 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
1120 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1121 return
1122
1123 #
1124 # SCENARIOS
1125 #
1126
1127 @bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
1128 def http_post_scenarios(tenant_id):
1129 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
1130 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1131 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02, "0.3": nsd_schema_v03})
1132 #r = utils.remove_extra_items(http_content, used_schema)
1133 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
1134 #print "http_post_scenarios input: ", http_content
1135 try:
1136 if used_schema == nsd_schema_v01:
1137 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1138 elif used_schema == nsd_schema_v02:
1139 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
1140 elif used_schema == nsd_schema_v03:
1141 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
1142 else:
1143 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1144 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
1145 #print json.dumps(data, indent=4)
1146 #return format_out(data)
1147 return http_get_scenario_id(tenant_id, scenario_id)
1148 except (nfvo.NfvoException, db_base_Exception) as e:
1149 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
1150 bottle.abort(e.http_code, str(e))
1151 except Exception as e:
1152 logger.error("Unexpected exception: ", exc_info=True)
1153 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1154
1155
1156 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
1157 def http_post_scenario_action(tenant_id, scenario_id):
1158 '''take an action over a scenario'''
1159 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1160 # parse input data
1161 http_content, _ = format_in(scenario_action_schema)
1162 r = utils.remove_extra_items(http_content, scenario_action_schema)
1163 if r:
1164 logger.debug("Remove received extra items %s", str(r))
1165 try:
1166 # check valid tenant_id
1167 nfvo.check_tenant(mydb, tenant_id)
1168 if "start" in http_content:
1169 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1170 http_content['start'].get('description',http_content['start']['instance_name']),
1171 http_content['start'].get('datacenter') )
1172 return format_out(data)
1173 elif "deploy" in http_content: #Equivalent to start
1174 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1175 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1176 http_content['deploy'].get('datacenter') )
1177 return format_out(data)
1178 elif "reserve" in http_content: #Reserve resources
1179 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1180 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1181 http_content['reserve'].get('datacenter'), startvms=False )
1182 return format_out(data)
1183 elif "verify" in http_content: #Equivalent to start and then delete
1184 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1185 http_content['verify'].get('description',http_content['verify']['instance_name']),
1186 http_content['verify'].get('datacenter'), startvms=False )
1187 instance_id = data['uuid']
1188 nfvo.delete_instance(mydb, tenant_id,instance_id)
1189 return format_out({"result":"Verify OK"})
1190 except (nfvo.NfvoException, db_base_Exception) as e:
1191 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1192 bottle.abort(e.http_code, str(e))
1193 except Exception as e:
1194 logger.error("Unexpected exception: ", exc_info=True)
1195 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1196
1197
1198 @bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1199 def http_get_scenarios(tenant_id):
1200 '''get scenarios list'''
1201 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1202 try:
1203 #check valid tenant_id
1204 if tenant_id != "any":
1205 nfvo.check_tenant(mydb, tenant_id)
1206 #obtain data
1207 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
1208 where_or={}
1209 if tenant_id != "any":
1210 where_or["tenant_id"] = tenant_id
1211 where_or["public"] = True
1212 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1213 convert_datetime2str(scenarios)
1214 utils.convert_str2boolean(scenarios, ('public',) )
1215 data={'scenarios':scenarios}
1216 #print json.dumps(scenarios, indent=4)
1217 return format_out(data)
1218 except (nfvo.NfvoException, db_base_Exception) as e:
1219 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1220 bottle.abort(e.http_code, str(e))
1221 except Exception as e:
1222 logger.error("Unexpected exception: ", exc_info=True)
1223 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1224
1225
1226 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1227 def http_get_scenario_id(tenant_id, scenario_id):
1228 '''get scenario details, can use both uuid or name'''
1229 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1230 try:
1231 #check valid tenant_id
1232 if tenant_id != "any":
1233 nfvo.check_tenant(mydb, tenant_id)
1234 #obtain data
1235 scenario = mydb.get_scenario(scenario_id, tenant_id)
1236 convert_datetime2str(scenario)
1237 data={'scenario' : scenario}
1238 return format_out(data)
1239 except (nfvo.NfvoException, db_base_Exception) as e:
1240 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1241 bottle.abort(e.http_code, str(e))
1242 except Exception as e:
1243 logger.error("Unexpected exception: ", exc_info=True)
1244 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1245
1246
1247 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1248 def http_delete_scenario_id(tenant_id, scenario_id):
1249 '''delete a scenario from database, can use both uuid or name'''
1250 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1251 try:
1252 #check valid tenant_id
1253 if tenant_id != "any":
1254 nfvo.check_tenant(mydb, tenant_id)
1255 #obtain data
1256 data = mydb.delete_scenario(scenario_id, tenant_id)
1257 #print json.dumps(data, indent=4)
1258 return format_out({"result":"scenario " + data + " deleted"})
1259 except (nfvo.NfvoException, db_base_Exception) as e:
1260 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1261 bottle.abort(e.http_code, str(e))
1262 except Exception as e:
1263 logger.error("Unexpected exception: ", exc_info=True)
1264 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1265
1266
1267 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1268 def http_put_scenario_id(tenant_id, scenario_id):
1269 '''edit an existing scenario id'''
1270 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1271 http_content,_ = format_in( scenario_edit_schema )
1272 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1273 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1274 #print "http_put_scenario_id input: ", http_content
1275 try:
1276 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
1277 #print json.dumps(data, indent=4)
1278 #return format_out(data)
1279 return http_get_scenario_id(tenant_id, scenario_id)
1280 except (nfvo.NfvoException, db_base_Exception) as e:
1281 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1282 bottle.abort(e.http_code, str(e))
1283 except Exception as e:
1284 logger.error("Unexpected exception: ", exc_info=True)
1285 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1286
1287 @bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1288 def http_post_instances(tenant_id):
1289 '''create an instance-scenario'''
1290 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1291 # parse input data
1292 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1293 r = utils.remove_extra_items(http_content, used_schema)
1294 if r is not None:
1295 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
1296 try:
1297 #check valid tenant_id
1298 if tenant_id != "any":
1299 nfvo.check_tenant(mydb, tenant_id)
1300 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
1301 return format_out(data)
1302 except (nfvo.NfvoException, db_base_Exception) as e:
1303 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1304 bottle.abort(e.http_code, str(e))
1305 except Exception as e:
1306 logger.error("Unexpected exception: ", exc_info=True)
1307 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1308
1309 #
1310 # INSTANCES
1311 #
1312 @bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1313 def http_get_instances(tenant_id):
1314 '''get instance list'''
1315 try:
1316 #check valid tenant_id
1317 if tenant_id != "any":
1318 nfvo.check_tenant(mydb, tenant_id)
1319 #obtain data
1320 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1321 if tenant_id != "any":
1322 w['tenant_id'] = tenant_id
1323 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1324 convert_datetime2str(instances)
1325 utils.convert_str2boolean(instances, ('public',) )
1326 data={'instances':instances}
1327 return format_out(data)
1328 except (nfvo.NfvoException, db_base_Exception) as e:
1329 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1330 bottle.abort(e.http_code, str(e))
1331 except Exception as e:
1332 logger.error("Unexpected exception: ", exc_info=True)
1333 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1334
1335
1336 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1337 def http_get_instance_id(tenant_id, instance_id):
1338 '''get instances details, can use both uuid or name'''
1339 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1340 try:
1341 #check valid tenant_id
1342 if tenant_id != "any":
1343 nfvo.check_tenant(mydb, tenant_id)
1344 if tenant_id == "any":
1345 tenant_id = None
1346 #obtain data (first time is only to check that the instance exists)
1347 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1348 try:
1349 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1350 except (nfvo.NfvoException, db_base_Exception) as e:
1351 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
1352 #obtain data with results upated
1353 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1354 convert_datetime2str(instance)
1355 #print json.dumps(instance, indent=4)
1356 return format_out(instance)
1357 except (nfvo.NfvoException, db_base_Exception) as e:
1358 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1359 bottle.abort(e.http_code, str(e))
1360 except Exception as e:
1361 logger.error("Unexpected exception: ", exc_info=True)
1362 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1363
1364
1365 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1366 def http_delete_instance_id(tenant_id, instance_id):
1367 '''delete instance from VIM and from database, can use both uuid or name'''
1368 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1369 try:
1370 #check valid tenant_id
1371 if tenant_id != "any":
1372 nfvo.check_tenant(mydb, tenant_id)
1373 if tenant_id == "any":
1374 tenant_id = None
1375 #obtain data
1376 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
1377 return format_out({"result":message})
1378 except (nfvo.NfvoException, db_base_Exception) as e:
1379 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1380 bottle.abort(e.http_code, str(e))
1381 except Exception as e:
1382 logger.error("Unexpected exception: ", exc_info=True)
1383 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1384
1385
1386 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1387 def http_post_instance_scenario_action(tenant_id, instance_id):
1388 '''take an action over a scenario instance'''
1389 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1390 # parse input data
1391 http_content, _ = format_in(instance_scenario_action_schema)
1392 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1393 if r:
1394 logger.debug("Remove received extra items %s", str(r))
1395 try:
1396 #check valid tenant_id
1397 if tenant_id != "any":
1398 nfvo.check_tenant(mydb, tenant_id)
1399
1400 #print "http_post_instance_scenario_action input: ", http_content
1401 #obtain data
1402 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1403 instance_id = instance["uuid"]
1404
1405 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
1406 return format_out(data)
1407 except (nfvo.NfvoException, db_base_Exception) as e:
1408 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1409 bottle.abort(e.http_code, str(e))
1410 except Exception as e:
1411 logger.error("Unexpected exception: ", exc_info=True)
1412 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1413
1414
1415 @bottle.error(400)
1416 @bottle.error(401)
1417 @bottle.error(404)
1418 @bottle.error(403)
1419 @bottle.error(405)
1420 @bottle.error(406)
1421 @bottle.error(409)
1422 @bottle.error(503)
1423 @bottle.error(500)
1424 def error400(error):
1425 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1426 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1427 return format_out(e)
1428