blob: cd40882ff2edf5a9cd757878d739cf26fa308d96 [file] [log] [blame]
tierno7edb6752016-03-21 17:37:52 +01001# -*- coding: utf-8 -*-
2
3##
4# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5# This file is part of openmano
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact with: nfvlabs@tid.es
22##
23
24'''
25HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
26and will use the nfvo.py module to run the appropriate method.
27Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
28'''
29__author__="Alfonso Tierno, Gerardo Garcia"
30__date__ ="$17-sep-2014 09:07:15$"
31
32import bottle
33import yaml
34import json
35import threading
36import time
tiernof97fd272016-07-11 14:32:37 +020037import logging
tierno7edb6752016-03-21 17:37:52 +010038
39from jsonschema import validate as js_v, exceptions as js_e
40from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
garciadeblas9f8456e2016-09-05 05:02:59 +020041 nsd_schema_v01, nsd_schema_v02, nsd_schema_v03, scenario_edit_schema, \
garciadeblas0c317ee2016-08-29 12:33:06 +020042 scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
tierno7edb6752016-03-21 17:37:52 +010043 tenant_schema, tenant_edit_schema,\
44 datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
45 object_schema, netmap_new_schema, netmap_edit_schema
46import nfvo
tierno42fcc3b2016-07-06 17:20:40 +020047import utils
tiernof97fd272016-07-11 14:32:37 +020048from db_base import db_base_Exception
49from functools import wraps
tierno7edb6752016-03-21 17:37:52 +010050
51global mydb
52global url_base
tiernof97fd272016-07-11 14:32:37 +020053global logger
tierno7edb6752016-03-21 17:37:52 +010054url_base="/openmano"
tierno73ad9e42016-09-12 18:11:11 +020055logger = None
tierno7edb6752016-03-21 17:37:52 +010056
57HTTP_Bad_Request = 400
58HTTP_Unauthorized = 401
59HTTP_Not_Found = 404
60HTTP_Forbidden = 403
61HTTP_Method_Not_Allowed = 405
62HTTP_Not_Acceptable = 406
63HTTP_Service_Unavailable = 503
64HTTP_Internal_Server_Error= 500
65
66def delete_nulls(var):
67 if type(var) is dict:
68 for k in var.keys():
69 if var[k] is None: del var[k]
70 elif type(var[k]) is dict or type(var[k]) is list or type(var[k]) is tuple:
71 if delete_nulls(var[k]): del var[k]
72 if len(var) == 0: return True
73 elif type(var) is list or type(var) is tuple:
74 for k in var:
75 if type(k) is dict: delete_nulls(k)
76 if len(var) == 0: return True
77 return False
78
79def convert_datetime2str(var):
80 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
81 It enters recursively in the dict var finding this kind of variables
82 '''
83 if type(var) is dict:
84 for k,v in var.items():
85 if type(v) is float and k in ("created_at", "modified_at"):
86 var[k] = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(v) )
87 elif type(v) is dict or type(v) is list or type(v) is tuple:
88 convert_datetime2str(v)
89 if len(var) == 0: return True
90 elif type(var) is list or type(var) is tuple:
91 for v in var:
92 convert_datetime2str(v)
93
tiernof97fd272016-07-11 14:32:37 +020094def log_to_logger(fn):
95 '''
96 Wrap a Bottle request so that a log line is emitted after it's handled.
97 (This decorator can be extended to take the desired logger as a param.)
98 '''
99 @wraps(fn)
100 def _log_to_logger(*args, **kwargs):
101 actual_response = fn(*args, **kwargs)
102 # modify this to log exactly what you need:
103 logger.info('FROM %s %s %s %s' % (bottle.request.remote_addr,
104 bottle.request.method,
105 bottle.request.url,
106 bottle.response.status))
107 return actual_response
108 return _log_to_logger
tierno7edb6752016-03-21 17:37:52 +0100109
110class httpserver(threading.Thread):
111 def __init__(self, db, admin=False, host='localhost', port=9090):
112 #global url_base
113 global mydb
tiernof97fd272016-07-11 14:32:37 +0200114 global logger
tierno7edb6752016-03-21 17:37:52 +0100115 #initialization
tierno73ad9e42016-09-12 18:11:11 +0200116 if not logger:
117 logger = logging.getLogger('openmano.http')
tierno7edb6752016-03-21 17:37:52 +0100118 threading.Thread.__init__(self)
119 self.host = host
120 self.port = port #Port where the listen service must be started
121 if admin==True:
122 self.name = "http_admin"
123 else:
124 self.name = "http"
125 #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
126 mydb = db
127 #self.first_usable_connection_index = 10
128 #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
129 #Ensure that when the main program exits the thread will also exit
130 self.daemon = True
131 self.setDaemon(True)
132
133 def run(self):
tiernof97fd272016-07-11 14:32:37 +0200134 bottle.install(log_to_logger)
135 bottle.run(host=self.host, port=self.port, debug=False, quiet=True)
tierno7edb6752016-03-21 17:37:52 +0100136
137def run_bottle(db, host_='localhost', port_=9090):
138 '''used for launching in main thread, so that it can be debugged'''
139 global mydb
140 mydb = db
141 bottle.run(host=host_, port=port_, debug=True) #quiet=True
142
143
144@bottle.route(url_base + '/', method='GET')
145def http_get():
tiernoefd80c92016-09-16 14:17:46 +0200146 #print
tierno7edb6752016-03-21 17:37:52 +0100147 return 'works' #TODO: to be completed
148
149#
150# Util functions
151#
152
153def change_keys_http2db(data, http_db, reverse=False):
154 '''Change keys of dictionary data acording to the key_dict values
155 This allow change from http interface names to database names.
156 When reverse is True, the change is otherwise
157 Attributes:
158 data: can be a dictionary or a list
159 http_db: is a dictionary with hhtp names as keys and database names as value
160 reverse: by default change is done from http api to database. If True change is done otherwise
161 Return: None, but data is modified'''
162 if type(data) is tuple or type(data) is list:
163 for d in data:
164 change_keys_http2db(d, http_db, reverse)
165 elif type(data) is dict or type(data) is bottle.FormsDict:
166 if reverse:
167 for k,v in http_db.items():
168 if v in data: data[k]=data.pop(v)
169 else:
170 for k,v in http_db.items():
171 if k in data: data[v]=data.pop(k)
172
173def format_out(data):
174 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
tiernoefd80c92016-09-16 14:17:46 +0200175 logger.debug("OUT: " + yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
tierno7edb6752016-03-21 17:37:52 +0100176 if 'application/yaml' in bottle.request.headers.get('Accept'):
177 bottle.response.content_type='application/yaml'
tierno7edb6752016-03-21 17:37:52 +0100178 return yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) #, canonical=True, default_style='"'
179 else: #by default json
180 bottle.response.content_type='application/json'
181 #return data #json no style
182 return json.dumps(data, indent=4) + "\n"
183
184def format_in(default_schema, version_fields=None, version_dict_schema=None):
185 ''' Parse the content of HTTP request against a json_schema
186 Parameters
187 default_schema: The schema to be parsed by default if no version field is found in the client data
188 version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
189 version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
190 It can contain a None as key, and this is apply if the client data version does not match any key
191 Return:
192 user_data, used_schema: if the data is successfully decoded and matches the schema
193 launch a bottle abort if fails
194 '''
195 #print "HEADERS :" + str(bottle.request.headers.items())
196 try:
197 error_text = "Invalid header format "
198 format_type = bottle.request.headers.get('Content-Type', 'application/json')
199 if 'application/json' in format_type:
200 error_text = "Invalid json format "
201 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
202 client_data = json.load(bottle.request.body)
203 #client_data = bottle.request.json()
204 elif 'application/yaml' in format_type:
205 error_text = "Invalid yaml format "
206 client_data = yaml.load(bottle.request.body)
207 elif 'application/xml' in format_type:
208 bottle.abort(501, "Content-Type: application/xml not supported yet.")
209 else:
tiernoefd80c92016-09-16 14:17:46 +0200210 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
tierno7edb6752016-03-21 17:37:52 +0100211 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
212 return
213 #if client_data == None:
214 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
215 # return
216
tiernoefd80c92016-09-16 14:17:46 +0200217 logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
tierno7edb6752016-03-21 17:37:52 +0100218 #look for the client provider version
219 error_text = "Invalid content "
220 client_version = None
221 used_schema = None
222 if version_fields != None:
223 client_version = client_data
224 for field in version_fields:
225 if field in client_version:
226 client_version = client_version[field]
227 else:
228 client_version=None
229 break
230 if client_version==None:
231 used_schema=default_schema
232 elif version_dict_schema!=None:
233 if client_version in version_dict_schema:
234 used_schema = version_dict_schema[client_version]
235 elif None in version_dict_schema:
236 used_schema = version_dict_schema[None]
237 if used_schema==None:
238 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
239
240 js_v(client_data, used_schema)
241 return client_data, used_schema
242 except (ValueError, yaml.YAMLError) as exc:
243 error_text += str(exc)
tiernoefd80c92016-09-16 14:17:46 +0200244 logger.error(error_text)
tierno7edb6752016-03-21 17:37:52 +0100245 bottle.abort(HTTP_Bad_Request, error_text)
246 except js_e.ValidationError as exc:
tiernoefd80c92016-09-16 14:17:46 +0200247 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
tierno7edb6752016-03-21 17:37:52 +0100248 error_pos = ""
249 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
250 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
251 #except:
252 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
253 # raise
254
255def filter_query_string(qs, http2db, allowed):
256 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
257 Attributes:
258 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
259 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
260 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
261 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
262 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
263 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
264 limit: limit dictated by user with the query string 'limit'. 100 by default
265 abort if not permited, using bottel.abort
266 '''
267 where={}
268 limit=100
269 select=[]
tiernof97fd272016-07-11 14:32:37 +0200270 #if type(qs) is not bottle.FormsDict:
271 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
272 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
273 for k in qs:
274 if k=='field':
275 select += qs.getall(k)
276 for v in select:
277 if v not in allowed:
278 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
279 elif k=='limit':
280 try:
281 limit=int(qs[k])
282 except:
283 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
284 else:
285 if k not in allowed:
286 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
287 if qs[k]!="null": where[k]=qs[k]
288 else: where[k]=None
tierno7edb6752016-03-21 17:37:52 +0100289 if len(select)==0: select += allowed
290 #change from http api to database naming
291 for i in range(0,len(select)):
292 k=select[i]
293 if http2db and k in http2db:
294 select[i] = http2db[k]
295 if http2db:
296 change_keys_http2db(where, http2db)
tiernof97fd272016-07-11 14:32:37 +0200297 #print "filter_query_string", select,where,limit
tierno7edb6752016-03-21 17:37:52 +0100298
299 return select,where,limit
300
301@bottle.hook('after_request')
302def enable_cors():
303 '''Don't know yet if really needed. Keep it just in case'''
304 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
305
306#
307# VNFs
308#
309
310@bottle.route(url_base + '/tenants', method='GET')
311def http_get_tenants():
tiernof97fd272016-07-11 14:32:37 +0200312 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100313 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
314 ('uuid','name','description','created_at') )
tiernof97fd272016-07-11 14:32:37 +0200315 try:
316 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100317 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200318 convert_datetime2str(tenants)
319 data={'tenants' : tenants}
tierno7edb6752016-03-21 17:37:52 +0100320 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200321 except db_base_Exception as e:
322 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
323 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000324 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000325 logger.error("Unexpected exception: ", exc_info=True)
326 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000327
tierno7edb6752016-03-21 17:37:52 +0100328
329@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
330def http_get_tenant_id(tenant_id):
331 '''get tenant details, can use both uuid or name'''
332 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200333 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
334 try:
335 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
336 #change_keys_http2db(content, http2db_tenant, reverse=True)
337 convert_datetime2str(tenant)
338 data={'tenant' : tenant}
339 return format_out(data)
340 except db_base_Exception as e:
341 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
342 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000343 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000344 logger.error("Unexpected exception: ", exc_info=True)
345 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000346
tierno7edb6752016-03-21 17:37:52 +0100347
348@bottle.route(url_base + '/tenants', method='POST')
349def http_post_tenants():
350 '''insert a tenant into the catalogue. '''
351 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200352 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100353 http_content,_ = format_in( tenant_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200354 r = utils.remove_extra_items(http_content, tenant_schema)
tiernoefd80c92016-09-16 14:17:46 +0200355 if r:
356 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200357 try:
358 data = nfvo.new_tenant(mydb, http_content['tenant'])
tierno7edb6752016-03-21 17:37:52 +0100359 return http_get_tenant_id(data)
tiernof97fd272016-07-11 14:32:37 +0200360 except (nfvo.NfvoException, db_base_Exception) as e:
361 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
362 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000363 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000364 logger.error("Unexpected exception: ", exc_info=True)
365 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000366
tierno7edb6752016-03-21 17:37:52 +0100367
368@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
369def http_edit_tenant_id(tenant_id):
370 '''edit tenant details, can use both uuid or name'''
371 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200372 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100373 http_content,_ = format_in( tenant_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200374 r = utils.remove_extra_items(http_content, tenant_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200375 if r:
376 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100377
378 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200379 try:
380 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
381 #edit data
382 tenant_id = tenant['uuid']
383 where={'uuid': tenant['uuid']}
384 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
385 return http_get_tenant_id(tenant_id)
386 except db_base_Exception as e:
387 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
388 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000389 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000390 logger.error("Unexpected exception: ", exc_info=True)
391 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000392
tierno7edb6752016-03-21 17:37:52 +0100393
394@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
395def http_delete_tenant_id(tenant_id):
396 '''delete a tenant from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200397 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
398 try:
399 data = nfvo.delete_tenant(mydb, tenant_id)
tierno7edb6752016-03-21 17:37:52 +0100400 return format_out({"result":"tenant " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200401 except db_base_Exception as e:
402 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
403 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000404 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000405 logger.error("Unexpected exception: ", exc_info=True)
406 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000407
tierno7edb6752016-03-21 17:37:52 +0100408
409@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
410def http_get_datacenters(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200411 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
412 try:
413 if tenant_id != 'any':
414 #check valid tenant_id
415 nfvo.check_tenant(mydb, tenant_id)
416 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
417 ('uuid','name','vim_url','type','created_at') )
418 if tenant_id != 'any':
419 where_['nfvo_tenant_id'] = tenant_id
420 if 'created_at' in select_:
421 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
422 if 'created_at' in where_:
423 where_['d.created_at'] = where_.pop('created_at')
424 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
425 SELECT=select_,WHERE=where_,LIMIT=limit_)
426 else:
427 datacenters = mydb.get_rows(FROM='datacenters',
428 SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100429 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200430 convert_datetime2str(datacenters)
431 data={'datacenters' : datacenters}
tierno7edb6752016-03-21 17:37:52 +0100432 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200433 except (nfvo.NfvoException, db_base_Exception) as e:
434 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
435 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000436 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000437 logger.error("Unexpected exception: ", exc_info=True)
438 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000439
tierno7edb6752016-03-21 17:37:52 +0100440
441@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
442def http_get_datacenter_id(tenant_id, datacenter_id):
443 '''get datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200444 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
445 try:
446 if tenant_id != 'any':
447 #check valid tenant_id
448 nfvo.check_tenant(mydb, tenant_id)
449 #obtain data
450 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
451 where_={}
452 where_[what] = datacenter_id
453 select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'config', 'description', 'd.created_at as created_at']
454 if tenant_id != 'any':
455 select_.append("datacenter_tenant_id")
456 where_['td.nfvo_tenant_id']= tenant_id
457 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
458 else:
459 from_='datacenters as d'
460 datacenters = mydb.get_rows(
461 SELECT=select_,
462 FROM=from_,
463 WHERE=where_)
464
465 if len(datacenters)==0:
466 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
467 elif len(datacenters)>1:
468 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
469 datacenter = datacenters[0]
470 if tenant_id != 'any':
471 #get vim tenant info
472 vim_tenants = mydb.get_rows(
473 SELECT=("vim_tenant_name", "vim_tenant_id", "user"),
474 FROM="datacenter_tenants",
475 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
476 ORDER_BY=("created", ) )
477 del datacenter["datacenter_tenant_id"]
478 datacenter["vim_tenants"] = vim_tenants
479
480 if datacenter['config'] != None:
481 try:
482 config_dict = yaml.load(datacenter['config'])
483 datacenter['config'] = config_dict
484 except Exception, e:
tiernoefd80c92016-09-16 14:17:46 +0200485 logger.error("Exception '%s' while trying to load config information", str(e))
tiernof97fd272016-07-11 14:32:37 +0200486 #change_keys_http2db(content, http2db_datacenter, reverse=True)
487 convert_datetime2str(datacenter)
488 data={'datacenter' : datacenter}
489 return format_out(data)
490 except (nfvo.NfvoException, db_base_Exception) as e:
491 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
492 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000493 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000494 logger.error("Unexpected exception: ", exc_info=True)
495 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
496
tierno7edb6752016-03-21 17:37:52 +0100497
498@bottle.route(url_base + '/datacenters', method='POST')
499def http_post_datacenters():
500 '''insert a tenant into the catalogue. '''
501 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200502 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100503 http_content,_ = format_in( datacenter_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200504 r = utils.remove_extra_items(http_content, datacenter_schema)
tiernoefd80c92016-09-16 14:17:46 +0200505 if r:
506 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200507 try:
508 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100509 return http_get_datacenter_id('any', data)
tiernof97fd272016-07-11 14:32:37 +0200510 except (nfvo.NfvoException, db_base_Exception) as e:
511 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
512 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000513 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000514 logger.error("Unexpected exception: ", exc_info=True)
515 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
516
tierno7edb6752016-03-21 17:37:52 +0100517
518@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
519def http_edit_datacenter_id(datacenter_id_name):
520 '''edit datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200521 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100522 #parse input data
523 http_content,_ = format_in( datacenter_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200524 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200525 if r:
526 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100527
tiernof97fd272016-07-11 14:32:37 +0200528 try:
529 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100530 return http_get_datacenter_id('any', datacenter_id)
tiernof97fd272016-07-11 14:32:37 +0200531 except (nfvo.NfvoException, db_base_Exception) as e:
532 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
533 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000534 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000535 logger.error("Unexpected exception: ", exc_info=True)
536 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
537
tierno7edb6752016-03-21 17:37:52 +0100538
539@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
540@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
541@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
542def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
543 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200544 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100545 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200546 try:
547 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
548 where_= {"datacenter_id":datacenter_dict['uuid']}
549 if netmap_id:
550 if utils.check_valid_uuid(netmap_id):
551 where_["uuid"] = netmap_id
552 else:
553 where_["name"] = netmap_id
554 netmaps =mydb.get_rows(FROM='datacenter_nets',
555 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
556 WHERE=where_ )
557 convert_datetime2str(netmaps)
558 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
559 if netmap_id and len(netmaps)==1:
560 data={'netmap' : netmaps[0]}
561 elif netmap_id and len(netmaps)==0:
562 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
563 return
tierno7edb6752016-03-21 17:37:52 +0100564 else:
tiernof97fd272016-07-11 14:32:37 +0200565 data={'netmaps' : netmaps}
566 return format_out(data)
567 except (nfvo.NfvoException, db_base_Exception) as e:
568 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
569 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000570 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000571 logger.error("Unexpected exception: ", exc_info=True)
572 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
573
tierno7edb6752016-03-21 17:37:52 +0100574
575@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
576@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
577def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
578 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200579 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100580 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200581 try:
582 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
583 where_= {"datacenter_id":datacenter_dict['uuid']}
584 if netmap_id:
585 if utils.check_valid_uuid(netmap_id):
586 where_["uuid"] = netmap_id
587 else:
588 where_["name"] = netmap_id
589 #change_keys_http2db(content, http2db_tenant, reverse=True)
590 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
591 if deleted == 0 and netmap_id :
592 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
593 if netmap_id:
594 return format_out({"result": "netmap %s deleted" % netmap_id})
tierno7edb6752016-03-21 17:37:52 +0100595 else:
tiernof97fd272016-07-11 14:32:37 +0200596 return format_out({"result": "%d netmap deleted" % deleted})
597 except (nfvo.NfvoException, db_base_Exception) as e:
598 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
599 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000600 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000601 logger.error("Unexpected exception: ", exc_info=True)
602 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100603
604
605@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
606def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
tiernof97fd272016-07-11 14:32:37 +0200607 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
608 try:
609 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
610 convert_datetime2str(netmaps)
611 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
612 data={'netmaps' : netmaps}
613 return format_out(data)
614 except (nfvo.NfvoException, db_base_Exception) as e:
615 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
616 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000617 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000618 logger.error("Unexpected exception: ", exc_info=True)
619 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
620
tierno7edb6752016-03-21 17:37:52 +0100621
622@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
623def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
624 '''creates a new netmap'''
tiernof97fd272016-07-11 14:32:37 +0200625 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100626 #parse input data
627 http_content,_ = format_in( netmap_new_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200628 r = utils.remove_extra_items(http_content, netmap_new_schema)
tiernoefd80c92016-09-16 14:17:46 +0200629 if r:
630 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200631 try:
632 #obtain data, check that only one exist
633 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
634 convert_datetime2str(netmaps)
635 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
636 data={'netmaps' : netmaps}
637 return format_out(data)
638 except (nfvo.NfvoException, db_base_Exception) as e:
639 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
640 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000641 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000642 logger.error("Unexpected exception: ", exc_info=True)
643 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
644
tierno7edb6752016-03-21 17:37:52 +0100645
646@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
647def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
648 '''edit a netmap'''
tiernof97fd272016-07-11 14:32:37 +0200649 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100650 #parse input data
651 http_content,_ = format_in( netmap_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200652 r = utils.remove_extra_items(http_content, netmap_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200653 if r:
654 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100655
656 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200657 try:
658 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
tierno7edb6752016-03-21 17:37:52 +0100659 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
tiernof97fd272016-07-11 14:32:37 +0200660 except (nfvo.NfvoException, db_base_Exception) as e:
661 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
662 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000663 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000664 logger.error("Unexpected exception: ", exc_info=True)
665 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100666
667
668@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
669def http_action_datacenter_id(tenant_id, datacenter_id):
670 '''perform an action over datacenter, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200671 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100672 #parse input data
673 http_content,_ = format_in( datacenter_action_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200674 r = utils.remove_extra_items(http_content, datacenter_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200675 if r:
676 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200677 try:
678 #obtain data, check that only one exist
679 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
680 if 'net-update' in http_content:
681 return http_getnetmap_datacenter_id(datacenter_id)
682 else:
683 return format_out(result)
684 except (nfvo.NfvoException, db_base_Exception) as e:
685 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
686 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000687 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000688 logger.error("Unexpected exception: ", exc_info=True)
689 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100690
691
692@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
693def http_delete_datacenter_id( datacenter_id):
694 '''delete a tenant from database, can use both uuid or name'''
695
tiernof97fd272016-07-11 14:32:37 +0200696 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
697 try:
698 data = nfvo.delete_datacenter(mydb, datacenter_id)
699 return format_out({"result":"datacenter '" + data + "' deleted"})
700 except (nfvo.NfvoException, db_base_Exception) as e:
701 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
702 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000703 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000704 logger.error("Unexpected exception: ", exc_info=True)
705 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
706
tierno7edb6752016-03-21 17:37:52 +0100707
708@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
709def http_associate_datacenters(tenant_id, datacenter_id):
710 '''associate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200711 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100712 #parse input data
713 http_content,_ = format_in( datacenter_associate_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200714 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
tiernoefd80c92016-09-16 14:17:46 +0200715 if r:
716 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200717 try:
718 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
719 http_content['datacenter'].get('vim_tenant'),
720 http_content['datacenter'].get('vim_tenant_name'),
721 http_content['datacenter'].get('vim_username'),
722 http_content['datacenter'].get('vim_password')
723 )
724 return http_get_datacenter_id(tenant_id, id_)
725 except (nfvo.NfvoException, db_base_Exception) as e:
726 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
727 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000728 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000729 logger.error("Unexpected exception: ", exc_info=True)
730 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
731
tierno7edb6752016-03-21 17:37:52 +0100732
733@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
734def http_deassociate_datacenters(tenant_id, datacenter_id):
735 '''deassociate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200736 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
737 try:
738 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
739 return format_out({"result": data})
740 except (nfvo.NfvoException, db_base_Exception) as e:
741 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
742 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000743 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000744 logger.error("Unexpected exception: ", exc_info=True)
745 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
746
tierno7edb6752016-03-21 17:37:52 +0100747
tierno7edb6752016-03-21 17:37:52 +0100748@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
749@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
750def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
tiernof97fd272016-07-11 14:32:37 +0200751 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
752 try:
753 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100754 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200755 except (nfvo.NfvoException, db_base_Exception) as e:
756 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
757 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000758 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000759 logger.error("Unexpected exception: ", exc_info=True)
760 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
761
tierno7edb6752016-03-21 17:37:52 +0100762
763@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
764def http_del_vim_items(tenant_id, datacenter_id, item, name):
tiernof97fd272016-07-11 14:32:37 +0200765 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
766 try:
767 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100768 return format_out({"result":data})
tiernof97fd272016-07-11 14:32:37 +0200769 except (nfvo.NfvoException, db_base_Exception) as e:
770 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
771 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000772 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000773 logger.error("Unexpected exception: ", exc_info=True)
774 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
775
tierno65a9b0c2016-09-28 14:57:25 +0000776
tierno7edb6752016-03-21 17:37:52 +0100777@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
778def http_post_vim_items(tenant_id, datacenter_id, item):
tiernof97fd272016-07-11 14:32:37 +0200779 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100780 http_content,_ = format_in( object_schema )
tiernof97fd272016-07-11 14:32:37 +0200781 try:
782 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
tierno7edb6752016-03-21 17:37:52 +0100783 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200784 except (nfvo.NfvoException, db_base_Exception) as e:
785 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
786 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000787 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000788 logger.error("Unexpected exception: ", exc_info=True)
789 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
790
tierno7edb6752016-03-21 17:37:52 +0100791
792@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
793def http_get_vnfs(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200794 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
795 try:
796 if tenant_id != 'any':
797 #check valid tenant_id
798 nfvo.check_tenant(mydb, tenant_id)
799 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
800 ('uuid','name','description','public', "tenant_id", "created_at") )
801 where_or = {}
802 if tenant_id != "any":
803 where_or["tenant_id"] = tenant_id
804 where_or["public"] = True
805 vnfs = mydb.get_rows(FROM='vnfs', SELECT=select_,WHERE=where_,WHERE_OR=where_or, WHERE_AND_OR="AND",LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100806 #change_keys_http2db(content, http2db_vnf, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200807 utils.convert_str2boolean(vnfs, ('public',))
808 convert_datetime2str(vnfs)
809 data={'vnfs' : vnfs}
tierno7edb6752016-03-21 17:37:52 +0100810 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200811 except (nfvo.NfvoException, db_base_Exception) as e:
812 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
813 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000814 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000815 logger.error("Unexpected exception: ", exc_info=True)
816 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
817
tierno7edb6752016-03-21 17:37:52 +0100818
819@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
820def http_get_vnf_id(tenant_id,vnf_id):
821 '''get vnf details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200822 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
823 try:
824 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
825 utils.convert_str2boolean(vnf, ('public',))
826 convert_datetime2str(vnf)
827 return format_out(vnf)
828 except (nfvo.NfvoException, db_base_Exception) as e:
829 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
830 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000831 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000832 logger.error("Unexpected exception: ", exc_info=True)
833 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
834
tierno7edb6752016-03-21 17:37:52 +0100835
836@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
837def http_post_vnfs(tenant_id):
838 '''insert a vnf into the catalogue. Creates the flavor and images in the VIM, and creates the VNF and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +0200839 #print "Parsing the YAML file of the VNF"
tierno7edb6752016-03-21 17:37:52 +0100840 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200841 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +0200842 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +0200843 r = utils.remove_extra_items(http_content, used_schema)
tiernoefd80c92016-09-16 14:17:46 +0200844 if r:
845 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200846 try:
tierno4319dad2016-09-05 12:11:11 +0200847 if used_schema == vnfd_schema_v01:
garciadeblas9f8456e2016-09-05 05:02:59 +0200848 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
tierno4319dad2016-09-05 12:11:11 +0200849 elif used_schema == vnfd_schema_v02:
garciadeblas9f8456e2016-09-05 05:02:59 +0200850 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
851 else:
852 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
853 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tiernof97fd272016-07-11 14:32:37 +0200854 return http_get_vnf_id(tenant_id, vnf_id)
855 except (nfvo.NfvoException, db_base_Exception) as e:
856 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
857 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000858 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000859 logger.error("Unexpected exception: ", exc_info=True)
860 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
861
tierno7edb6752016-03-21 17:37:52 +0100862
863@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
864def http_delete_vnf_id(tenant_id,vnf_id):
865 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200866 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100867 #check valid tenant_id and deletes the vnf, including images,
tiernof97fd272016-07-11 14:32:37 +0200868 try:
869 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
tierno7edb6752016-03-21 17:37:52 +0100870 #print json.dumps(data, indent=4)
871 return format_out({"result":"VNF " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200872 except (nfvo.NfvoException, db_base_Exception) as e:
873 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
874 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000875 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000876 logger.error("Unexpected exception: ", exc_info=True)
877 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
878
tierno7edb6752016-03-21 17:37:52 +0100879
880#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
881#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
882@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
883def http_get_hosts(tenant_id, datacenter):
884 '''get the tidvim host hopology from the vim.'''
tiernof97fd272016-07-11 14:32:37 +0200885 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
886 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
887 try:
888 if datacenter == 'treeview':
889 data = nfvo.get_hosts(mydb, tenant_id)
890 else:
891 #openmano-gui is using a hardcoded value for the datacenter
892 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
893
894 if result < 0:
tiernoefd80c92016-09-16 14:17:46 +0200895 #print "http_get_hosts error %d %s" % (-result, data)
tiernof97fd272016-07-11 14:32:37 +0200896 bottle.abort(-result, data)
897 else:
898 convert_datetime2str(data)
tiernoefd80c92016-09-16 14:17:46 +0200899 #print json.dumps(data, indent=4)
tiernof97fd272016-07-11 14:32:37 +0200900 return format_out(data)
901 except (nfvo.NfvoException, db_base_Exception) as e:
garciadeblas9f8456e2016-09-05 05:02:59 +0200902 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
tiernof97fd272016-07-11 14:32:37 +0200903 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000904 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000905 logger.error("Unexpected exception: ", exc_info=True)
906 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100907
908
909@bottle.route(url_base + '/<path:path>', method='OPTIONS')
910def http_options_deploy(path):
911 '''For some reason GUI web ask for OPTIONS that must be responded'''
912 #TODO: check correct path, and correct headers request
tiernof97fd272016-07-11 14:32:37 +0200913 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100914 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
915 bottle.response.set_header('Accept','application/yaml,application/json')
916 bottle.response.set_header('Content-Type','application/yaml,application/json')
917 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
918 bottle.response.set_header('Access-Control-Allow-Origin','*')
919 return
920
921@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
922def http_post_deploy(tenant_id):
923 '''post topology deploy.'''
tiernof97fd272016-07-11 14:32:37 +0200924 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100925
tierno66aa0372016-07-06 17:31:12 +0200926 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +0200927 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +0100928 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +0200929 #print "http_post_deploy input: ", http_content
tierno7edb6752016-03-21 17:37:52 +0100930
tiernof97fd272016-07-11 14:32:37 +0200931 try:
932 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
933 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
934 #print json.dumps(data, indent=4)
935 return format_out(instance)
936 except (nfvo.NfvoException, db_base_Exception) as e:
937 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
938 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000939 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000940 logger.error("Unexpected exception: ", exc_info=True)
941 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
942
tierno7edb6752016-03-21 17:37:52 +0100943
944@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
945def http_post_verify(tenant_id):
946 #TODO:
947# '''post topology verify'''
948# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
tiernof97fd272016-07-11 14:32:37 +0200949 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100950 return
951
952#
953# SCENARIOS
954#
955
956@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
957def http_post_scenarios(tenant_id):
958 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +0200959 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +0200960 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02, "0.3": nsd_schema_v03})
tierno42fcc3b2016-07-06 17:20:40 +0200961 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +0100962 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +0200963 #print "http_post_scenarios input: ", http_content
964 try:
tierno4319dad2016-09-05 12:11:11 +0200965 if used_schema == nsd_schema_v01:
tiernof97fd272016-07-11 14:32:37 +0200966 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +0200967 elif used_schema == nsd_schema_v02:
tiernof97fd272016-07-11 14:32:37 +0200968 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +0200969 elif used_schema == nsd_schema_v03:
garciadeblas9f8456e2016-09-05 05:02:59 +0200970 scenario_id = nfvo.new_scenario_v03(mydb, tenant_id, http_content)
971 else:
972 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
973 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tierno7edb6752016-03-21 17:37:52 +0100974 #print json.dumps(data, indent=4)
975 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200976 return http_get_scenario_id(tenant_id, scenario_id)
977 except (nfvo.NfvoException, db_base_Exception) as e:
978 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
979 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000980 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000981 logger.error("Unexpected exception: ", exc_info=True)
982 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
983
tierno7edb6752016-03-21 17:37:52 +0100984
985@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
986def http_post_scenario_action(tenant_id, scenario_id):
987 '''take an action over a scenario'''
tiernof97fd272016-07-11 14:32:37 +0200988 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100989 #check valid tenant_id
tiernof97fd272016-07-11 14:32:37 +0200990 try:
991 nfvo.check_tenant(mydb, tenant_id)
992 #parse input data
993 http_content,_ = format_in( scenario_action_schema )
994 r = utils.remove_extra_items(http_content, scenario_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200995 if r:
996 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200997 if "start" in http_content:
998 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
999 http_content['start'].get('description',http_content['start']['instance_name']),
1000 http_content['start'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001001 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001002 elif "deploy" in http_content: #Equivalent to start
1003 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1004 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1005 http_content['deploy'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001006 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001007 elif "reserve" in http_content: #Reserve resources
1008 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1009 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1010 http_content['reserve'].get('datacenter'), startvms=False )
tierno7edb6752016-03-21 17:37:52 +01001011 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001012 elif "verify" in http_content: #Equivalent to start and then delete
1013 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1014 http_content['verify'].get('description',http_content['verify']['instance_name']),
1015 http_content['verify'].get('datacenter'), startvms=False )
1016 instance_id = data['uuid']
1017 nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001018 return format_out({"result":"Verify OK"})
tiernof97fd272016-07-11 14:32:37 +02001019 except (nfvo.NfvoException, db_base_Exception) as e:
1020 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1021 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001022 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001023 logger.error("Unexpected exception: ", exc_info=True)
1024 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1025
tierno7edb6752016-03-21 17:37:52 +01001026
1027@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1028def http_get_scenarios(tenant_id):
1029 '''get scenarios list'''
tiernof97fd272016-07-11 14:32:37 +02001030 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1031 try:
1032 #check valid tenant_id
1033 if tenant_id != "any":
1034 nfvo.check_tenant(mydb, tenant_id)
1035 #obtain data
1036 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
1037 where_or={}
1038 if tenant_id != "any":
1039 where_or["tenant_id"] = tenant_id
1040 where_or["public"] = True
1041 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1042 convert_datetime2str(scenarios)
1043 utils.convert_str2boolean(scenarios, ('public',) )
1044 data={'scenarios':scenarios}
tierno7edb6752016-03-21 17:37:52 +01001045 #print json.dumps(scenarios, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001046 return format_out(data)
1047 except (nfvo.NfvoException, db_base_Exception) as e:
1048 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1049 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001050 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001051 logger.error("Unexpected exception: ", exc_info=True)
1052 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1053
tierno7edb6752016-03-21 17:37:52 +01001054
1055@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1056def http_get_scenario_id(tenant_id, scenario_id):
1057 '''get scenario details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001058 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1059 try:
1060 #check valid tenant_id
1061 if tenant_id != "any":
1062 nfvo.check_tenant(mydb, tenant_id)
1063 #obtain data
1064 scenario = mydb.get_scenario(scenario_id, tenant_id)
1065 convert_datetime2str(scenario)
1066 data={'scenario' : scenario}
tierno7edb6752016-03-21 17:37:52 +01001067 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001068 except (nfvo.NfvoException, db_base_Exception) as e:
1069 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1070 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001071 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001072 logger.error("Unexpected exception: ", exc_info=True)
1073 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1074
tierno7edb6752016-03-21 17:37:52 +01001075
1076@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1077def http_delete_scenario_id(tenant_id, scenario_id):
1078 '''delete a scenario from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001079 try:
1080 #check valid tenant_id
1081 if tenant_id != "any":
1082 nfvo.check_tenant(mydb, tenant_id)
1083 #obtain data
1084 data = mydb.delete_scenario(scenario_id, tenant_id)
tierno7edb6752016-03-21 17:37:52 +01001085 #print json.dumps(data, indent=4)
1086 return format_out({"result":"scenario " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001087 except (nfvo.NfvoException, db_base_Exception) as e:
1088 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1089 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001090 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001091 logger.error("Unexpected exception: ", exc_info=True)
1092 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001093
1094
1095@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1096def http_put_scenario_id(tenant_id, scenario_id):
1097 '''edit an existing scenario id'''
tiernof97fd272016-07-11 14:32:37 +02001098 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001099 http_content,_ = format_in( scenario_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +02001100 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
tierno7edb6752016-03-21 17:37:52 +01001101 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001102 #print "http_put_scenario_id input: ", http_content
1103 try:
1104 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001105 #print json.dumps(data, indent=4)
1106 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001107 return http_get_scenario_id(tenant_id, scenario_id)
1108 except (nfvo.NfvoException, db_base_Exception) as e:
1109 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1110 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001111 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001112 logger.error("Unexpected exception: ", exc_info=True)
1113 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001114
1115@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1116def http_post_instances(tenant_id):
garciadeblasedca7b32016-09-29 14:01:52 +00001117 '''create an instance-scenario'''
tiernof97fd272016-07-11 14:32:37 +02001118 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1119 try:
1120 #check valid tenant_id
1121 if tenant_id != "any":
1122 nfvo.check_tenant(mydb, tenant_id)
1123 #parse input data
garciadeblas0c317ee2016-08-29 12:33:06 +02001124 http_content,used_schema = format_in( instance_scenario_create_schema_v01)
tiernof97fd272016-07-11 14:32:37 +02001125 r = utils.remove_extra_items(http_content, used_schema)
tiernoa4e1a6e2016-08-31 14:19:40 +02001126 if r is not None:
1127 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001128 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
tierno7edb6752016-03-21 17:37:52 +01001129 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001130 except (nfvo.NfvoException, db_base_Exception) as e:
1131 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1132 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001133 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001134 logger.error("Unexpected exception: ", exc_info=True)
1135 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001136
1137#
1138# INSTANCES
1139#
1140@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1141def http_get_instances(tenant_id):
1142 '''get instance list'''
tiernof97fd272016-07-11 14:32:37 +02001143 try:
1144 #check valid tenant_id
1145 if tenant_id != "any":
1146 nfvo.check_tenant(mydb, tenant_id)
1147 #obtain data
1148 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1149 if tenant_id != "any":
1150 w['tenant_id'] = tenant_id
1151 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1152 convert_datetime2str(instances)
1153 utils.convert_str2boolean(instances, ('public',) )
1154 data={'instances':instances}
1155 return format_out(data)
1156 except (nfvo.NfvoException, db_base_Exception) as e:
1157 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1158 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001159 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001160 logger.error("Unexpected exception: ", exc_info=True)
1161 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1162
tierno7edb6752016-03-21 17:37:52 +01001163
1164@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1165def http_get_instance_id(tenant_id, instance_id):
1166 '''get instances details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001167 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1168 try:
1169 #check valid tenant_id
1170 if tenant_id != "any":
1171 nfvo.check_tenant(mydb, tenant_id)
1172 if tenant_id == "any":
1173 tenant_id = None
1174 #obtain data (first time is only to check that the instance exists)
1175 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1176 try:
1177 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1178 except (nfvo.NfvoException, db_base_Exception) as e:
1179 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
1180 #obtain data with results upated
1181 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1182 convert_datetime2str(instance)
1183 #print json.dumps(instance, indent=4)
1184 return format_out(instance)
1185 except (nfvo.NfvoException, db_base_Exception) as e:
1186 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1187 bottle.abort(e.http_code, str(e))
tierno8e995ce2016-09-22 08:13:00 +00001188 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001189 logger.error("Unexpected exception: ", exc_info=True)
1190 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1191
tierno7edb6752016-03-21 17:37:52 +01001192
1193@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1194def http_delete_instance_id(tenant_id, instance_id):
1195 '''delete instance from VIM and from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001196 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1197 try:
1198 #check valid tenant_id
1199 if tenant_id != "any":
1200 nfvo.check_tenant(mydb, tenant_id)
1201 if tenant_id == "any":
1202 tenant_id = None
1203 #obtain data
1204 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001205 return format_out({"result":message})
tiernof97fd272016-07-11 14:32:37 +02001206 except (nfvo.NfvoException, db_base_Exception) as e:
1207 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1208 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001209 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001210 logger.error("Unexpected exception: ", exc_info=True)
1211 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1212
tierno7edb6752016-03-21 17:37:52 +01001213
1214@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1215def http_post_instance_scenario_action(tenant_id, instance_id):
1216 '''take an action over a scenario instance'''
tiernof97fd272016-07-11 14:32:37 +02001217 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1218 try:
1219 #check valid tenant_id
1220 if tenant_id != "any":
1221 nfvo.check_tenant(mydb, tenant_id)
1222
1223 #parse input data
1224 http_content,_ = format_in( instance_scenario_action_schema )
1225 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
tiernoefd80c92016-09-16 14:17:46 +02001226 if r:
1227 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001228 #print "http_post_instance_scenario_action input: ", http_content
1229 #obtain data
1230 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1231 instance_id = instance["uuid"]
1232
1233 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001234 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001235 except (nfvo.NfvoException, db_base_Exception) as e:
1236 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1237 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001238 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001239 logger.error("Unexpected exception: ", exc_info=True)
1240 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001241
1242
1243@bottle.error(400)
1244@bottle.error(401)
1245@bottle.error(404)
1246@bottle.error(403)
1247@bottle.error(405)
1248@bottle.error(406)
1249@bottle.error(409)
1250@bottle.error(503)
1251@bottle.error(500)
1252def error400(error):
1253 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1254 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1255 return format_out(e)
1256