Feature 1413 resiliency to single component failure
[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, sdn_external_port_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 """
188 Parse the content of HTTP request against a json_schema
189 :param default_schema: The schema to be parsed by default if no version field is found in the client data. In None
190 no validation is done
191 :param version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to
192 obtain the version
193 :param version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value.
194 It can contain a None as key, and this is apply if the client data version does not match any key
195 :return: user_data, used_schema: if the data is successfully decoded and matches the schema.
196 Launch a bottle abort if fails
197 """
198 #print "HEADERS :" + str(bottle.request.headers.items())
199 try:
200 error_text = "Invalid header format "
201 format_type = bottle.request.headers.get('Content-Type', 'application/json')
202 if 'application/json' in format_type:
203 error_text = "Invalid json format "
204 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
205 client_data = json.load(bottle.request.body)
206 #client_data = bottle.request.json()
207 elif 'application/yaml' in format_type:
208 error_text = "Invalid yaml format "
209 client_data = yaml.load(bottle.request.body)
210 elif 'application/xml' in format_type:
211 bottle.abort(501, "Content-Type: application/xml not supported yet.")
212 else:
213 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
214 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
215 return
216 # if client_data == None:
217 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
218 # return
219
220 logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False,
221 tags=False, encoding='utf-8', allow_unicode=True) )
222 # look for the client provider version
223 error_text = "Invalid content "
224 if not default_schema and not version_fields:
225 return client_data, None
226 client_version = None
227 used_schema = None
228 if version_fields != None:
229 client_version = client_data
230 for field in version_fields:
231 if field in client_version:
232 client_version = client_version[field]
233 else:
234 client_version=None
235 break
236 if client_version == None:
237 used_schema = default_schema
238 elif version_dict_schema != None:
239 if client_version in version_dict_schema:
240 used_schema = version_dict_schema[client_version]
241 elif None in version_dict_schema:
242 used_schema = version_dict_schema[None]
243 if used_schema==None:
244 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
245
246 js_v(client_data, used_schema)
247 return client_data, used_schema
248 except (ValueError, yaml.YAMLError) as exc:
249 error_text += str(exc)
250 logger.error(error_text)
251 bottle.abort(HTTP_Bad_Request, error_text)
252 except js_e.ValidationError as exc:
253 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
254 error_pos = ""
255 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
256 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
257 #except:
258 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
259 # raise
260
261 def filter_query_string(qs, http2db, allowed):
262 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
263 Attributes:
264 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
265 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
266 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
267 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
268 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
269 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
270 limit: limit dictated by user with the query string 'limit'. 100 by default
271 abort if not permited, using bottel.abort
272 '''
273 where={}
274 limit=100
275 select=[]
276 #if type(qs) is not bottle.FormsDict:
277 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
278 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
279 for k in qs:
280 if k=='field':
281 select += qs.getall(k)
282 for v in select:
283 if v not in allowed:
284 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
285 elif k=='limit':
286 try:
287 limit=int(qs[k])
288 except:
289 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
290 else:
291 if k not in allowed:
292 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
293 if qs[k]!="null": where[k]=qs[k]
294 else: where[k]=None
295 if len(select)==0: select += allowed
296 #change from http api to database naming
297 for i in range(0,len(select)):
298 k=select[i]
299 if http2db and k in http2db:
300 select[i] = http2db[k]
301 if http2db:
302 change_keys_http2db(where, http2db)
303 #print "filter_query_string", select,where,limit
304
305 return select,where,limit
306
307 @bottle.hook('after_request')
308 def enable_cors():
309 '''Don't know yet if really needed. Keep it just in case'''
310 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
311
312 @bottle.route(url_base + '/version', method='GET')
313 def http_get_version():
314 return nfvo.get_version()
315 #
316 # VNFs
317 #
318
319 @bottle.route(url_base + '/tenants', method='GET')
320 def http_get_tenants():
321 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
322 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
323 ('uuid','name','description','created_at') )
324 try:
325 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
326 #change_keys_http2db(content, http2db_tenant, reverse=True)
327 convert_datetime2str(tenants)
328 data={'tenants' : tenants}
329 return format_out(data)
330 except db_base_Exception as e:
331 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
332 bottle.abort(e.http_code, str(e))
333 except Exception as e:
334 logger.error("Unexpected exception: ", exc_info=True)
335 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
336
337
338 @bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
339 def http_get_tenant_id(tenant_id):
340 '''get tenant details, can use both uuid or name'''
341 #obtain data
342 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
343 try:
344 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
345 #change_keys_http2db(content, http2db_tenant, reverse=True)
346 convert_datetime2str(tenant)
347 data={'tenant' : tenant}
348 return format_out(data)
349 except db_base_Exception as e:
350 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
351 bottle.abort(e.http_code, str(e))
352 except Exception as e:
353 logger.error("Unexpected exception: ", exc_info=True)
354 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
355
356
357 @bottle.route(url_base + '/tenants', method='POST')
358 def http_post_tenants():
359 '''insert a tenant into the catalogue. '''
360 #parse input data
361 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
362 http_content,_ = format_in( tenant_schema )
363 r = utils.remove_extra_items(http_content, tenant_schema)
364 if r:
365 logger.debug("Remove received extra items %s", str(r))
366 try:
367 data = nfvo.new_tenant(mydb, http_content['tenant'])
368 return http_get_tenant_id(data)
369 except (nfvo.NfvoException, db_base_Exception) as e:
370 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
371 bottle.abort(e.http_code, str(e))
372 except Exception as e:
373 logger.error("Unexpected exception: ", exc_info=True)
374 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
375
376
377 @bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
378 def http_edit_tenant_id(tenant_id):
379 '''edit tenant details, can use both uuid or name'''
380 #parse input data
381 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
382 http_content,_ = format_in( tenant_edit_schema )
383 r = utils.remove_extra_items(http_content, tenant_edit_schema)
384 if r:
385 logger.debug("Remove received extra items %s", str(r))
386
387 #obtain data, check that only one exist
388 try:
389 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
390 #edit data
391 tenant_id = tenant['uuid']
392 where={'uuid': tenant['uuid']}
393 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
394 return http_get_tenant_id(tenant_id)
395 except db_base_Exception as e:
396 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
397 bottle.abort(e.http_code, str(e))
398 except Exception as e:
399 logger.error("Unexpected exception: ", exc_info=True)
400 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
401
402
403 @bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
404 def http_delete_tenant_id(tenant_id):
405 '''delete a tenant from database, can use both uuid or name'''
406 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
407 try:
408 data = nfvo.delete_tenant(mydb, tenant_id)
409 return format_out({"result":"tenant " + data + " deleted"})
410 except db_base_Exception as e:
411 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
412 bottle.abort(e.http_code, str(e))
413 except Exception as e:
414 logger.error("Unexpected exception: ", exc_info=True)
415 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
416
417
418 @bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
419 def http_get_datacenters(tenant_id):
420 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
421 try:
422 if tenant_id != 'any':
423 #check valid tenant_id
424 nfvo.check_tenant(mydb, tenant_id)
425 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
426 ('uuid','name','vim_url','type','created_at') )
427 if tenant_id != 'any':
428 where_['nfvo_tenant_id'] = tenant_id
429 if 'created_at' in select_:
430 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
431 if 'created_at' in where_:
432 where_['d.created_at'] = where_.pop('created_at')
433 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
434 SELECT=select_,WHERE=where_,LIMIT=limit_)
435 else:
436 datacenters = mydb.get_rows(FROM='datacenters',
437 SELECT=select_,WHERE=where_,LIMIT=limit_)
438 #change_keys_http2db(content, http2db_tenant, reverse=True)
439 convert_datetime2str(datacenters)
440 data={'datacenters' : datacenters}
441 return format_out(data)
442 except (nfvo.NfvoException, db_base_Exception) as e:
443 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
444 bottle.abort(e.http_code, str(e))
445 except Exception as e:
446 logger.error("Unexpected exception: ", exc_info=True)
447 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
448
449
450 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
451 def http_get_datacenter_id(tenant_id, datacenter_id):
452 '''get datacenter details, can use both uuid or name'''
453 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
454 try:
455 if tenant_id != 'any':
456 #check valid tenant_id
457 nfvo.check_tenant(mydb, tenant_id)
458 #obtain data
459 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
460 where_={}
461 where_[what] = datacenter_id
462 select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
463 if tenant_id != 'any':
464 select_.append("datacenter_tenant_id")
465 where_['td.nfvo_tenant_id']= tenant_id
466 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
467 else:
468 from_='datacenters as d'
469 datacenters = mydb.get_rows(
470 SELECT=select_,
471 FROM=from_,
472 WHERE=where_)
473
474 if len(datacenters)==0:
475 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
476 elif len(datacenters)>1:
477 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
478 datacenter = datacenters[0]
479 if tenant_id != 'any':
480 #get vim tenant info
481 vim_tenants = mydb.get_rows(
482 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
483 FROM="datacenter_tenants",
484 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
485 ORDER_BY=("created", ) )
486 del datacenter["datacenter_tenant_id"]
487 datacenter["vim_tenants"] = vim_tenants
488 for vim_tenant in vim_tenants:
489 if vim_tenant["passwd"]:
490 vim_tenant["passwd"] = "******"
491 if vim_tenant['config'] != None:
492 try:
493 config_dict = yaml.load(vim_tenant['config'])
494 vim_tenant['config'] = config_dict
495 except Exception as e:
496 logger.error("Exception '%s' while trying to load config information", str(e))
497
498 if datacenter['config'] != None:
499 try:
500 config_dict = yaml.load(datacenter['config'])
501 datacenter['config'] = config_dict
502 except Exception as e:
503 logger.error("Exception '%s' while trying to load config information", str(e))
504 #change_keys_http2db(content, http2db_datacenter, reverse=True)
505 convert_datetime2str(datacenter)
506 data={'datacenter' : datacenter}
507 return format_out(data)
508 except (nfvo.NfvoException, db_base_Exception) as e:
509 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
510 bottle.abort(e.http_code, str(e))
511 except Exception as e:
512 logger.error("Unexpected exception: ", exc_info=True)
513 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
514
515
516 @bottle.route(url_base + '/datacenters', method='POST')
517 def http_post_datacenters():
518 '''insert a datacenter into the catalogue. '''
519 #parse input data
520 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
521 http_content,_ = format_in( datacenter_schema )
522 r = utils.remove_extra_items(http_content, datacenter_schema)
523 if r:
524 logger.debug("Remove received extra items %s", str(r))
525 try:
526 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
527 return http_get_datacenter_id('any', data)
528 except (nfvo.NfvoException, db_base_Exception) as e:
529 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
530 bottle.abort(e.http_code, str(e))
531 except Exception as e:
532 logger.error("Unexpected exception: ", exc_info=True)
533 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
534
535
536 @bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
537 def http_edit_datacenter_id(datacenter_id_name):
538 '''edit datacenter details, can use both uuid or name'''
539 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
540 #parse input data
541 http_content,_ = format_in( datacenter_edit_schema )
542 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
543 if r:
544 logger.debug("Remove received extra items %s", str(r))
545
546 try:
547 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
548 return http_get_datacenter_id('any', datacenter_id)
549 except (nfvo.NfvoException, db_base_Exception) as e:
550 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
551 bottle.abort(e.http_code, str(e))
552 except Exception as e:
553 logger.error("Unexpected exception: ", exc_info=True)
554 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
555
556 @bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
557 def http_post_sdn_controller(tenant_id):
558 '''insert a sdn controller into the catalogue. '''
559 #parse input data
560 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
561 http_content,_ = format_in( sdn_controller_schema )
562 try:
563 logger.debug("tenant_id: "+tenant_id)
564 #logger.debug("content: {}".format(http_content['sdn_controller']))
565
566 data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
567 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
568 except (nfvo.NfvoException, db_base_Exception) as e:
569 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
570 bottle.abort(e.http_code, str(e))
571 except Exception as e:
572 logger.error("Unexpected exception: ", exc_info=True)
573 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
574
575 @bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='PUT')
576 def http_put_sdn_controller_update(tenant_id, controller_id):
577 '''Update sdn controller'''
578 #parse input data
579 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
580 http_content,_ = format_in( sdn_controller_edit_schema )
581 # r = utils.remove_extra_items(http_content, datacenter_schema)
582 # if r:
583 # logger.debug("Remove received extra items %s", str(r))
584 try:
585 #logger.debug("tenant_id: "+tenant_id)
586 logger.debug("content: {}".format(http_content['sdn_controller']))
587
588 data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
589 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
590
591 except (nfvo.NfvoException, db_base_Exception) as e:
592 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
593 bottle.abort(e.http_code, str(e))
594 except Exception as e:
595 logger.error("Unexpected exception: ", exc_info=True)
596 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
597
598 @bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
599 def http_get_sdn_controller(tenant_id):
600 '''get sdn controllers list, can use both uuid or name'''
601 try:
602 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
603
604 data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
605 return format_out(data)
606 except (nfvo.NfvoException, db_base_Exception) as e:
607 logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
608 bottle.abort(e.http_code, str(e))
609 except Exception as e:
610 logger.error("Unexpected exception: ", exc_info=True)
611 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
612
613 @bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
614 def http_get_sdn_controller_id(tenant_id, controller_id):
615 '''get sdn controller details, can use both uuid or name'''
616 try:
617 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
618 data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
619 return format_out({"sdn_controllers": data})
620 except (nfvo.NfvoException, db_base_Exception) as e:
621 logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
622 bottle.abort(e.http_code, str(e))
623 except Exception as e:
624 logger.error("Unexpected exception: ", exc_info=True)
625 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
626
627 @bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
628 def http_delete_sdn_controller_id(tenant_id, controller_id):
629 '''delete sdn controller, can use both uuid or name'''
630 try:
631 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
632 data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
633 return format_out(data)
634 except (nfvo.NfvoException, db_base_Exception) as e:
635 logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
636 bottle.abort(e.http_code, str(e))
637 except Exception as e:
638 logger.error("Unexpected exception: ", exc_info=True)
639 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
640
641 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
642 def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
643 '''Set the sdn port mapping for a datacenter. '''
644 #parse input data
645 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
646 http_content, _ = format_in(sdn_port_mapping_schema)
647 # r = utils.remove_extra_items(http_content, datacenter_schema)
648 # if r:
649 # logger.debug("Remove received extra items %s", str(r))
650 try:
651 data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
652 return format_out({"sdn_port_mapping": data})
653 except (nfvo.NfvoException, db_base_Exception) as e:
654 logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
655 bottle.abort(e.http_code, str(e))
656 except Exception as e:
657 logger.error("Unexpected exception: ", exc_info=True)
658 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
659
660 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
661 def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
662 '''get datacenter sdn mapping details, can use both uuid or name'''
663 try:
664 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
665
666 data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
667 return format_out({"sdn_port_mapping": data})
668 except (nfvo.NfvoException, db_base_Exception) as e:
669 logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
670 bottle.abort(e.http_code, str(e))
671 except Exception as e:
672 logger.error("Unexpected exception: ", exc_info=True)
673 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
674
675 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
676 def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
677 '''clean datacenter sdn mapping, can use both uuid or name'''
678 try:
679 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
680 data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
681 return format_out({"result": data})
682 except (nfvo.NfvoException, db_base_Exception) as e:
683 logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
684 bottle.abort(e.http_code, str(e))
685 except Exception as e:
686 logger.error("Unexpected exception: ", exc_info=True)
687 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
688
689 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
690 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
691 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
692 def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
693 '''get datacenter networks, can use both uuid or name'''
694 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
695 #obtain data
696 try:
697 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
698 where_= {"datacenter_id":datacenter_dict['uuid']}
699 if netmap_id:
700 if utils.check_valid_uuid(netmap_id):
701 where_["uuid"] = netmap_id
702 else:
703 where_["name"] = netmap_id
704 netmaps =mydb.get_rows(FROM='datacenter_nets',
705 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
706 WHERE=where_ )
707 convert_datetime2str(netmaps)
708 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
709 if netmap_id and len(netmaps)==1:
710 data={'netmap' : netmaps[0]}
711 elif netmap_id and len(netmaps)==0:
712 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
713 return
714 else:
715 data={'netmaps' : netmaps}
716 return format_out(data)
717 except (nfvo.NfvoException, db_base_Exception) as e:
718 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
719 bottle.abort(e.http_code, str(e))
720 except Exception as e:
721 logger.error("Unexpected exception: ", exc_info=True)
722 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
723
724
725 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
726 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
727 def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
728 '''get datacenter networks, can use both uuid or name'''
729 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
730 #obtain data
731 try:
732 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
733 where_= {"datacenter_id":datacenter_dict['uuid']}
734 if netmap_id:
735 if utils.check_valid_uuid(netmap_id):
736 where_["uuid"] = netmap_id
737 else:
738 where_["name"] = netmap_id
739 #change_keys_http2db(content, http2db_tenant, reverse=True)
740 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
741 if deleted == 0 and netmap_id :
742 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
743 if netmap_id:
744 return format_out({"result": "netmap %s deleted" % netmap_id})
745 else:
746 return format_out({"result": "%d netmap deleted" % deleted})
747 except (nfvo.NfvoException, db_base_Exception) as e:
748 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
749 bottle.abort(e.http_code, str(e))
750 except Exception as e:
751 logger.error("Unexpected exception: ", exc_info=True)
752 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
753
754
755 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
756 def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
757 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
758 try:
759 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
760 convert_datetime2str(netmaps)
761 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
762 data={'netmaps' : netmaps}
763 return format_out(data)
764 except (nfvo.NfvoException, db_base_Exception) as e:
765 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
766 bottle.abort(e.http_code, str(e))
767 except Exception as e:
768 logger.error("Unexpected exception: ", exc_info=True)
769 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
770
771
772 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
773 def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
774 '''creates a new netmap'''
775 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
776 #parse input data
777 http_content,_ = format_in( netmap_new_schema )
778 r = utils.remove_extra_items(http_content, netmap_new_schema)
779 if r:
780 logger.debug("Remove received extra items %s", str(r))
781 try:
782 #obtain data, check that only one exist
783 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
784 convert_datetime2str(netmaps)
785 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
786 data={'netmaps' : netmaps}
787 return format_out(data)
788 except (nfvo.NfvoException, db_base_Exception) as e:
789 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
790 bottle.abort(e.http_code, str(e))
791 except Exception as e:
792 logger.error("Unexpected exception: ", exc_info=True)
793 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
794
795
796 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
797 def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
798 '''edit a netmap'''
799 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
800 #parse input data
801 http_content,_ = format_in( netmap_edit_schema )
802 r = utils.remove_extra_items(http_content, netmap_edit_schema)
803 if r:
804 logger.debug("Remove received extra items %s", str(r))
805
806 #obtain data, check that only one exist
807 try:
808 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
809 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
810 except (nfvo.NfvoException, db_base_Exception) as e:
811 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
812 bottle.abort(e.http_code, str(e))
813 except Exception as e:
814 logger.error("Unexpected exception: ", exc_info=True)
815 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
816
817
818 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
819 def http_action_datacenter_id(tenant_id, datacenter_id):
820 '''perform an action over datacenter, can use both uuid or name'''
821 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
822 #parse input data
823 http_content,_ = format_in( datacenter_action_schema )
824 r = utils.remove_extra_items(http_content, datacenter_action_schema)
825 if r:
826 logger.debug("Remove received extra items %s", str(r))
827 try:
828 #obtain data, check that only one exist
829 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
830 if 'net-update' in http_content:
831 return http_getnetmap_datacenter_id(datacenter_id)
832 else:
833 return format_out(result)
834 except (nfvo.NfvoException, db_base_Exception) as e:
835 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
836 bottle.abort(e.http_code, str(e))
837 except Exception as e:
838 logger.error("Unexpected exception: ", exc_info=True)
839 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
840
841
842 @bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
843 def http_delete_datacenter_id( datacenter_id):
844 '''delete a tenant from database, can use both uuid or name'''
845
846 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
847 try:
848 data = nfvo.delete_datacenter(mydb, datacenter_id)
849 return format_out({"result":"datacenter '" + data + "' deleted"})
850 except (nfvo.NfvoException, db_base_Exception) as e:
851 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
852 bottle.abort(e.http_code, str(e))
853 except Exception as e:
854 logger.error("Unexpected exception: ", exc_info=True)
855 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
856
857
858 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
859 def http_associate_datacenters(tenant_id, datacenter_id):
860 '''associate an existing datacenter to a this tenant. '''
861 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
862 #parse input data
863 http_content,_ = format_in( datacenter_associate_schema )
864 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
865 if r:
866 logger.debug("Remove received extra items %s", str(r))
867 try:
868 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
869 http_content['datacenter'].get('vim_tenant'),
870 http_content['datacenter'].get('vim_tenant_name'),
871 http_content['datacenter'].get('vim_username'),
872 http_content['datacenter'].get('vim_password'),
873 http_content['datacenter'].get('config')
874 )
875 return http_get_datacenter_id(tenant_id, id_)
876 except (nfvo.NfvoException, db_base_Exception) as e:
877 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
878 bottle.abort(e.http_code, str(e))
879 except Exception as e:
880 logger.error("Unexpected exception: ", exc_info=True)
881 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
882
883 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
884 def http_associate_datacenters_edit(tenant_id, datacenter_id):
885 '''associate an existing datacenter to a this tenant. '''
886 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
887 #parse input data
888 http_content,_ = format_in( datacenter_associate_schema )
889 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
890 if r:
891 logger.debug("Remove received extra items %s", str(r))
892 try:
893 id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
894 http_content['datacenter'].get('vim_tenant'),
895 http_content['datacenter'].get('vim_tenant_name'),
896 http_content['datacenter'].get('vim_username'),
897 http_content['datacenter'].get('vim_password'),
898 http_content['datacenter'].get('config')
899 )
900 return http_get_datacenter_id(tenant_id, id_)
901 except (nfvo.NfvoException, db_base_Exception) as e:
902 logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
903 bottle.abort(e.http_code, str(e))
904 except Exception as e:
905 logger.error("Unexpected exception: ", exc_info=True)
906 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
907
908 @bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
909 def http_deassociate_datacenters(tenant_id, datacenter_id):
910 '''deassociate an existing datacenter to a this tenant. '''
911 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
912 try:
913 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
914 return format_out({"result": data})
915 except (nfvo.NfvoException, db_base_Exception) as e:
916 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
917 bottle.abort(e.http_code, str(e))
918 except Exception as e:
919 logger.error("Unexpected exception: ", exc_info=True)
920 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
921
922 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method='POST')
923 def http_post_vim_net_sdn_attach(tenant_id, datacenter_id, network_id):
924 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
925 http_content, _ = format_in(sdn_external_port_schema)
926 try:
927 data = nfvo.vim_net_sdn_attach(mydb, tenant_id, datacenter_id, network_id, http_content)
928 return format_out(data)
929 except (nfvo.NfvoException, db_base_Exception) as e:
930 logger.error("http_post_vim_net_sdn_attach error {}: {}".format(e.http_code, str(e)))
931 bottle.abort(e.http_code, str(e))
932 except Exception as e:
933 logger.error("Unexpected exception: ", exc_info=True)
934 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
935
936 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method='DELETE')
937 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method='DELETE')
938 def http_delete_vim_net_sdn_detach(tenant_id, datacenter_id, network_id, port_id=None):
939 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
940 try:
941 data = nfvo.vim_net_sdn_detach(mydb, tenant_id, datacenter_id, network_id, port_id)
942 return format_out(data)
943 except (nfvo.NfvoException, db_base_Exception) as e:
944 logger.error("http_delete_vim_net_sdn_detach error {}: {}".format(e.http_code, str(e)))
945 bottle.abort(e.http_code, str(e))
946 except Exception as e:
947 logger.error("Unexpected exception: ", exc_info=True)
948 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
949
950 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
951 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
952 def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
953 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
954 try:
955 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
956 return format_out(data)
957 except (nfvo.NfvoException, db_base_Exception) as e:
958 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
959 bottle.abort(e.http_code, str(e))
960 except Exception as e:
961 logger.error("Unexpected exception: ", exc_info=True)
962 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
963
964
965 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
966 def http_del_vim_items(tenant_id, datacenter_id, item, name):
967 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
968 try:
969 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
970 return format_out({"result":data})
971 except (nfvo.NfvoException, db_base_Exception) as e:
972 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
973 bottle.abort(e.http_code, str(e))
974 except Exception as e:
975 logger.error("Unexpected exception: ", exc_info=True)
976 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
977
978
979 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
980 def http_post_vim_items(tenant_id, datacenter_id, item):
981 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
982 http_content,_ = format_in( object_schema )
983 try:
984 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
985 return format_out(data)
986 except (nfvo.NfvoException, db_base_Exception) as e:
987 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
988 bottle.abort(e.http_code, str(e))
989 except Exception as e:
990 logger.error("Unexpected exception: ", exc_info=True)
991 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
992
993
994 @bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
995 def http_get_vnfs(tenant_id):
996 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
997 try:
998 if tenant_id != 'any':
999 #check valid tenant_id
1000 nfvo.check_tenant(mydb, tenant_id)
1001 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
1002 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
1003 where_or = {}
1004 if tenant_id != "any":
1005 where_or["tenant_id"] = tenant_id
1006 where_or["public"] = True
1007 vnfs = mydb.get_rows(FROM='vnfs', SELECT=select_,WHERE=where_,WHERE_OR=where_or, WHERE_AND_OR="AND",LIMIT=limit_)
1008 #change_keys_http2db(content, http2db_vnf, reverse=True)
1009 utils.convert_str2boolean(vnfs, ('public',))
1010 convert_datetime2str(vnfs)
1011 data={'vnfs' : vnfs}
1012 return format_out(data)
1013 except (nfvo.NfvoException, db_base_Exception) as e:
1014 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
1015 bottle.abort(e.http_code, str(e))
1016 except Exception as e:
1017 logger.error("Unexpected exception: ", exc_info=True)
1018 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1019
1020
1021 @bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
1022 def http_get_vnf_id(tenant_id,vnf_id):
1023 '''get vnf details, can use both uuid or name'''
1024 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1025 try:
1026 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
1027 utils.convert_str2boolean(vnf, ('public',))
1028 convert_datetime2str(vnf)
1029 return format_out(vnf)
1030 except (nfvo.NfvoException, db_base_Exception) as e:
1031 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
1032 bottle.abort(e.http_code, str(e))
1033 except Exception as e:
1034 logger.error("Unexpected exception: ", exc_info=True)
1035 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1036
1037
1038 @bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
1039 def http_post_vnfs(tenant_id):
1040 """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
1041 :param tenant_id: tenant that this vnf belongs to
1042 :return:
1043 """
1044 # print "Parsing the YAML file of the VNF"
1045 # parse input data
1046 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1047 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
1048 r = utils.remove_extra_items(http_content, used_schema)
1049 if r:
1050 logger.debug("Remove received extra items %s", str(r))
1051 try:
1052 if used_schema == vnfd_schema_v01:
1053 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
1054 elif used_schema == vnfd_schema_v02:
1055 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
1056 else:
1057 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1058 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
1059 return http_get_vnf_id(tenant_id, vnf_id)
1060 except (nfvo.NfvoException, db_base_Exception) as e:
1061 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1062 bottle.abort(e.http_code, str(e))
1063 except Exception as e:
1064 logger.error("Unexpected exception: ", exc_info=True)
1065 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1066
1067
1068 @bottle.route(url_base + '/v3/<tenant_id>/vnfd', method='POST')
1069 def http_post_vnfs_v3(tenant_id):
1070 """
1071 Insert one or several VNFs in the catalog, following OSM IM
1072 :param tenant_id: tenant owner of the VNF
1073 :return: The detailed list of inserted VNFs, following the old format
1074 """
1075 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1076 http_content, _ = format_in(None)
1077 try:
1078 vnfd_uuid_list = nfvo.new_vnfd_v3(mydb, tenant_id, http_content)
1079 vnfd_list = []
1080 for vnfd_uuid in vnfd_uuid_list:
1081 vnf = nfvo.get_vnf_id(mydb, tenant_id, vnfd_uuid)
1082 utils.convert_str2boolean(vnf, ('public',))
1083 convert_datetime2str(vnf)
1084 vnfd_list.append(vnf["vnf"])
1085 return format_out({"vnfd": vnfd_list})
1086 except (nfvo.NfvoException, db_base_Exception) as e:
1087 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1088 bottle.abort(e.http_code, str(e))
1089 except Exception as e:
1090 logger.error("Unexpected exception: ", exc_info=True)
1091 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1092
1093 @bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
1094 def http_delete_vnf_id(tenant_id, vnf_id):
1095 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
1096 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1097 #check valid tenant_id and deletes the vnf, including images,
1098 try:
1099 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
1100 #print json.dumps(data, indent=4)
1101 return format_out({"result":"VNF " + data + " deleted"})
1102 except (nfvo.NfvoException, db_base_Exception) as e:
1103 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
1104 bottle.abort(e.http_code, str(e))
1105 except Exception as e:
1106 logger.error("Unexpected exception: ", exc_info=True)
1107 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1108
1109
1110 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1111 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1112 @bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
1113 def http_get_hosts(tenant_id, datacenter):
1114 '''get the tidvim host hopology from the vim.'''
1115 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1116 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1117 try:
1118 if datacenter == 'treeview':
1119 data = nfvo.get_hosts(mydb, tenant_id)
1120 else:
1121 #openmano-gui is using a hardcoded value for the datacenter
1122 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
1123
1124 if result < 0:
1125 #print "http_get_hosts error %d %s" % (-result, data)
1126 bottle.abort(-result, data)
1127 else:
1128 convert_datetime2str(data)
1129 #print json.dumps(data, indent=4)
1130 return format_out(data)
1131 except (nfvo.NfvoException, db_base_Exception) as e:
1132 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
1133 bottle.abort(e.http_code, str(e))
1134 except Exception as e:
1135 logger.error("Unexpected exception: ", exc_info=True)
1136 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1137
1138
1139 @bottle.route(url_base + '/<path:path>', method='OPTIONS')
1140 def http_options_deploy(path):
1141 '''For some reason GUI web ask for OPTIONS that must be responded'''
1142 #TODO: check correct path, and correct headers request
1143 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1144 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1145 bottle.response.set_header('Accept','application/yaml,application/json')
1146 bottle.response.set_header('Content-Type','application/yaml,application/json')
1147 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
1148 bottle.response.set_header('Access-Control-Allow-Origin','*')
1149 return
1150
1151 @bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
1152 def http_post_deploy(tenant_id):
1153 '''post topology deploy.'''
1154 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1155
1156 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
1157 #r = utils.remove_extra_items(http_content, used_schema)
1158 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
1159 #print "http_post_deploy input: ", http_content
1160
1161 try:
1162 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1163 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
1164 #print json.dumps(data, indent=4)
1165 return format_out(instance)
1166 except (nfvo.NfvoException, db_base_Exception) as e:
1167 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
1168 bottle.abort(e.http_code, str(e))
1169 except Exception as e:
1170 logger.error("Unexpected exception: ", exc_info=True)
1171 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1172
1173
1174 @bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
1175 def http_post_verify(tenant_id):
1176 #TODO:
1177 # '''post topology verify'''
1178 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
1179 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1180 return
1181
1182 #
1183 # SCENARIOS
1184 #
1185
1186 @bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
1187 def http_post_scenarios(tenant_id):
1188 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
1189 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1190 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02, "0.3": nsd_schema_v03})
1191 #r = utils.remove_extra_items(http_content, used_schema)
1192 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
1193 #print "http_post_scenarios input: ", http_content
1194 try:
1195 if used_schema == nsd_schema_v01:
1196 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1197 elif used_schema == nsd_schema_v02:
1198 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
1199 elif used_schema == nsd_schema_v03:
1200 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
1201 else:
1202 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1203 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
1204 #print json.dumps(data, indent=4)
1205 #return format_out(data)
1206 return http_get_scenario_id(tenant_id, scenario_id)
1207 except (nfvo.NfvoException, db_base_Exception) as e:
1208 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
1209 bottle.abort(e.http_code, str(e))
1210 except Exception as e:
1211 logger.error("Unexpected exception: ", exc_info=True)
1212 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1213
1214 @bottle.route(url_base + '/v3/<tenant_id>/nsd', method='POST')
1215 def http_post_nsds_v3(tenant_id):
1216 """
1217 Insert one or several NSDs in the catalog, following OSM IM
1218 :param tenant_id: tenant owner of the NSD
1219 :return: The detailed list of inserted NSDs, following the old format
1220 """
1221 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1222 http_content, _ = format_in(None)
1223 try:
1224 nsd_uuid_list = nfvo.new_nsd_v3(mydb, tenant_id, http_content)
1225 nsd_list = []
1226 for nsd_uuid in nsd_uuid_list:
1227 scenario = mydb.get_scenario(nsd_uuid, tenant_id)
1228 convert_datetime2str(scenario)
1229 nsd_list.append(scenario)
1230 data = {'nsd': nsd_list}
1231 return format_out(data)
1232 except (nfvo.NfvoException, db_base_Exception) as e:
1233 logger.error("http_post_nsds_v3 error {}: {}".format(e.http_code, str(e)))
1234 bottle.abort(e.http_code, str(e))
1235 except Exception as e:
1236 logger.error("Unexpected exception: ", exc_info=True)
1237 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1238
1239
1240 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
1241 def http_post_scenario_action(tenant_id, scenario_id):
1242 '''take an action over a scenario'''
1243 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1244 # parse input data
1245 http_content, _ = format_in(scenario_action_schema)
1246 r = utils.remove_extra_items(http_content, scenario_action_schema)
1247 if r:
1248 logger.debug("Remove received extra items %s", str(r))
1249 try:
1250 # check valid tenant_id
1251 nfvo.check_tenant(mydb, tenant_id)
1252 if "start" in http_content:
1253 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1254 http_content['start'].get('description',http_content['start']['instance_name']),
1255 http_content['start'].get('datacenter') )
1256 return format_out(data)
1257 elif "deploy" in http_content: #Equivalent to start
1258 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1259 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1260 http_content['deploy'].get('datacenter') )
1261 return format_out(data)
1262 elif "reserve" in http_content: #Reserve resources
1263 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1264 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1265 http_content['reserve'].get('datacenter'), startvms=False )
1266 return format_out(data)
1267 elif "verify" in http_content: #Equivalent to start and then delete
1268 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1269 http_content['verify'].get('description',http_content['verify']['instance_name']),
1270 http_content['verify'].get('datacenter'), startvms=False )
1271 instance_id = data['uuid']
1272 nfvo.delete_instance(mydb, tenant_id,instance_id)
1273 return format_out({"result":"Verify OK"})
1274 except (nfvo.NfvoException, db_base_Exception) as e:
1275 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1276 bottle.abort(e.http_code, str(e))
1277 except Exception as e:
1278 logger.error("Unexpected exception: ", exc_info=True)
1279 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1280
1281
1282 @bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1283 def http_get_scenarios(tenant_id):
1284 '''get scenarios list'''
1285 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1286 try:
1287 #check valid tenant_id
1288 if tenant_id != "any":
1289 nfvo.check_tenant(mydb, tenant_id)
1290 #obtain data
1291 s,w,l=filter_query_string(bottle.request.query, None,
1292 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
1293 where_or={}
1294 if tenant_id != "any":
1295 where_or["tenant_id"] = tenant_id
1296 where_or["public"] = True
1297 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1298 convert_datetime2str(scenarios)
1299 utils.convert_str2boolean(scenarios, ('public',) )
1300 data={'scenarios':scenarios}
1301 #print json.dumps(scenarios, indent=4)
1302 return format_out(data)
1303 except (nfvo.NfvoException, db_base_Exception) as e:
1304 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1305 bottle.abort(e.http_code, str(e))
1306 except Exception as e:
1307 logger.error("Unexpected exception: ", exc_info=True)
1308 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1309
1310
1311 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1312 def http_get_scenario_id(tenant_id, scenario_id):
1313 '''get scenario details, can use both uuid or name'''
1314 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1315 try:
1316 #check valid tenant_id
1317 if tenant_id != "any":
1318 nfvo.check_tenant(mydb, tenant_id)
1319 #obtain data
1320 scenario = mydb.get_scenario(scenario_id, tenant_id)
1321 convert_datetime2str(scenario)
1322 data={'scenario' : scenario}
1323 return format_out(data)
1324 except (nfvo.NfvoException, db_base_Exception) as e:
1325 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1326 bottle.abort(e.http_code, str(e))
1327 except Exception as e:
1328 logger.error("Unexpected exception: ", exc_info=True)
1329 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1330
1331
1332 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1333 def http_delete_scenario_id(tenant_id, scenario_id):
1334 '''delete a scenario from database, can use both uuid or name'''
1335 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1336 try:
1337 #check valid tenant_id
1338 if tenant_id != "any":
1339 nfvo.check_tenant(mydb, tenant_id)
1340 #obtain data
1341 data = mydb.delete_scenario(scenario_id, tenant_id)
1342 #print json.dumps(data, indent=4)
1343 return format_out({"result":"scenario " + data + " deleted"})
1344 except (nfvo.NfvoException, db_base_Exception) as e:
1345 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1346 bottle.abort(e.http_code, str(e))
1347 except Exception as e:
1348 logger.error("Unexpected exception: ", exc_info=True)
1349 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1350
1351
1352 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1353 def http_put_scenario_id(tenant_id, scenario_id):
1354 '''edit an existing scenario id'''
1355 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1356 http_content,_ = format_in( scenario_edit_schema )
1357 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1358 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1359 #print "http_put_scenario_id input: ", http_content
1360 try:
1361 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
1362 #print json.dumps(data, indent=4)
1363 #return format_out(data)
1364 return http_get_scenario_id(tenant_id, scenario_id)
1365 except (nfvo.NfvoException, db_base_Exception) as e:
1366 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1367 bottle.abort(e.http_code, str(e))
1368 except Exception as e:
1369 logger.error("Unexpected exception: ", exc_info=True)
1370 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1371
1372 @bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1373 def http_post_instances(tenant_id):
1374 '''create an instance-scenario'''
1375 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1376 # parse input data
1377 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1378 r = utils.remove_extra_items(http_content, used_schema)
1379 if r is not None:
1380 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
1381 try:
1382 #check valid tenant_id
1383 if tenant_id != "any":
1384 nfvo.check_tenant(mydb, tenant_id)
1385 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
1386 return format_out(data)
1387 except (nfvo.NfvoException, db_base_Exception) as e:
1388 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1389 bottle.abort(e.http_code, str(e))
1390 except Exception as e:
1391 logger.error("Unexpected exception: ", exc_info=True)
1392 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1393
1394 #
1395 # INSTANCES
1396 #
1397 @bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1398 def http_get_instances(tenant_id):
1399 '''get instance list'''
1400 try:
1401 #check valid tenant_id
1402 if tenant_id != "any":
1403 nfvo.check_tenant(mydb, tenant_id)
1404 #obtain data
1405 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1406 if tenant_id != "any":
1407 w['tenant_id'] = tenant_id
1408 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1409 convert_datetime2str(instances)
1410 utils.convert_str2boolean(instances, ('public',) )
1411 data={'instances':instances}
1412 return format_out(data)
1413 except (nfvo.NfvoException, db_base_Exception) as e:
1414 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1415 bottle.abort(e.http_code, str(e))
1416 except Exception as e:
1417 logger.error("Unexpected exception: ", exc_info=True)
1418 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1419
1420
1421 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1422 def http_get_instance_id(tenant_id, instance_id):
1423 '''get instances details, can use both uuid or name'''
1424 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1425 try:
1426 #check valid tenant_id
1427 if tenant_id != "any":
1428 nfvo.check_tenant(mydb, tenant_id)
1429 if tenant_id == "any":
1430 tenant_id = None
1431 #obtain data (first time is only to check that the instance exists)
1432 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1433 try:
1434 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1435 except (nfvo.NfvoException, db_base_Exception) as e:
1436 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
1437 # obtain data with results upated
1438 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1439 # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
1440 for vnf in instance.get("vnfs", ()):
1441 for vm in vnf.get("vms", ()):
1442 for iface in vm.get("interfaces", ()):
1443 if iface.get("ip_address"):
1444 index = iface["ip_address"].find(";")
1445 if index >= 0:
1446 iface["ip_address"] = iface["ip_address"][:index]
1447 convert_datetime2str(instance)
1448 # print json.dumps(instance, indent=4)
1449 return format_out(instance)
1450 except (nfvo.NfvoException, db_base_Exception) as e:
1451 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1452 bottle.abort(e.http_code, str(e))
1453 except Exception as e:
1454 logger.error("Unexpected exception: ", exc_info=True)
1455 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1456
1457
1458 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1459 def http_delete_instance_id(tenant_id, instance_id):
1460 '''delete instance from VIM and from database, can use both uuid or name'''
1461 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1462 try:
1463 #check valid tenant_id
1464 if tenant_id != "any":
1465 nfvo.check_tenant(mydb, tenant_id)
1466 if tenant_id == "any":
1467 tenant_id = None
1468 #obtain data
1469 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
1470 return format_out({"result":message})
1471 except (nfvo.NfvoException, db_base_Exception) as e:
1472 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1473 bottle.abort(e.http_code, str(e))
1474 except Exception as e:
1475 logger.error("Unexpected exception: ", exc_info=True)
1476 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1477
1478
1479 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1480 def http_post_instance_scenario_action(tenant_id, instance_id):
1481 """
1482 take an action over a scenario instance
1483 :param tenant_id: tenant where user belongs to
1484 :param instance_id: instance indentity
1485 :return:
1486 """
1487 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1488 # parse input data
1489 http_content, _ = format_in(instance_scenario_action_schema)
1490 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1491 if r:
1492 logger.debug("Remove received extra items %s", str(r))
1493 try:
1494 #check valid tenant_id
1495 if tenant_id != "any":
1496 nfvo.check_tenant(mydb, tenant_id)
1497
1498 #print "http_post_instance_scenario_action input: ", http_content
1499 #obtain data
1500 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1501 instance_id = instance["uuid"]
1502
1503 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
1504 return format_out(data)
1505 except (nfvo.NfvoException, db_base_Exception) as e:
1506 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1507 bottle.abort(e.http_code, str(e))
1508 except Exception as e:
1509 logger.error("Unexpected exception: ", exc_info=True)
1510 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1511
1512
1513 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='GET')
1514 @bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action/<action_id>', method='GET')
1515 def http_get_instance_scenario_action(tenant_id, instance_id, action_id=None):
1516 """
1517 List the actions done over an instance, or the action details
1518 :param tenant_id: tenant where user belongs to. Can be "any" to ignore
1519 :param instance_id: instance id, can be "any" to get actions of all instances
1520 :return:
1521 """
1522 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1523 try:
1524 # check valid tenant_id
1525 if tenant_id != "any":
1526 nfvo.check_tenant(mydb, tenant_id)
1527 data = nfvo.instance_action_get(mydb, tenant_id, instance_id, action_id)
1528 return format_out(data)
1529 except (nfvo.NfvoException, db_base_Exception) as e:
1530 logger.error("http_get_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1531 bottle.abort(e.http_code, str(e))
1532 except Exception as e:
1533 logger.error("Unexpected exception: ", exc_info=True)
1534 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1535
1536
1537 @bottle.error(400)
1538 @bottle.error(401)
1539 @bottle.error(404)
1540 @bottle.error(403)
1541 @bottle.error(405)
1542 @bottle.error(406)
1543 @bottle.error(409)
1544 @bottle.error(503)
1545 @bottle.error(500)
1546 def error400(error):
1547 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1548 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1549 return format_out(e)
1550