blob: 1f08c4680962c4e2e15edec7cf465b24c5e0a003 [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
Marco Ceppic4629bd2017-10-26 14:35:46 +020040from .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,\
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +010045 object_schema, netmap_new_schema, netmap_edit_schema, sdn_controller_schema, sdn_controller_edit_schema, \
Pablo Montes Moreno6aa0b2b2017-05-23 18:33:12 +020046 sdn_port_mapping_schema, sdn_external_port_schema
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +010047
Marco Ceppic4629bd2017-10-26 14:35:46 +020048from . import nfvo
49from . import utils
50from .db_base import db_base_Exception
tiernof97fd272016-07-11 14:32:37 +020051from functools import wraps
tierno7edb6752016-03-21 17:37:52 +010052
53global mydb
54global url_base
tiernof97fd272016-07-11 14:32:37 +020055global logger
tierno7edb6752016-03-21 17:37:52 +010056url_base="/openmano"
tierno73ad9e42016-09-12 18:11:11 +020057logger = None
tierno7edb6752016-03-21 17:37:52 +010058
59HTTP_Bad_Request = 400
60HTTP_Unauthorized = 401
61HTTP_Not_Found = 404
62HTTP_Forbidden = 403
63HTTP_Method_Not_Allowed = 405
64HTTP_Not_Acceptable = 406
65HTTP_Service_Unavailable = 503
66HTTP_Internal_Server_Error= 500
67
68def delete_nulls(var):
69 if type(var) is dict:
Marco Ceppic4629bd2017-10-26 14:35:46 +020070 for k in list(var.keys()):
tierno7edb6752016-03-21 17:37:52 +010071 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
81def 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:
Marco Ceppic4629bd2017-10-26 14:35:46 +020086 for k,v in list(var.items()):
tierno7edb6752016-03-21 17:37:52 +010087 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
tiernof97fd272016-07-11 14:32:37 +020096def 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
tierno7edb6752016-03-21 17:37:52 +0100111
112class httpserver(threading.Thread):
113 def __init__(self, db, admin=False, host='localhost', port=9090):
114 #global url_base
115 global mydb
tiernof97fd272016-07-11 14:32:37 +0200116 global logger
tierno7edb6752016-03-21 17:37:52 +0100117 #initialization
tierno73ad9e42016-09-12 18:11:11 +0200118 if not logger:
119 logger = logging.getLogger('openmano.http')
tierno7edb6752016-03-21 17:37:52 +0100120 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):
tiernof97fd272016-07-11 14:32:37 +0200136 bottle.install(log_to_logger)
137 bottle.run(host=self.host, port=self.port, debug=False, quiet=True)
tierno7edb6752016-03-21 17:37:52 +0100138
139def 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')
147def http_get():
tiernoefd80c92016-09-16 14:17:46 +0200148 #print
tierno7edb6752016-03-21 17:37:52 +0100149 return 'works' #TODO: to be completed
150
151#
152# Util functions
153#
154
155def 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:
Marco Ceppic4629bd2017-10-26 14:35:46 +0200169 for k,v in list(http_db.items()):
tierno7edb6752016-03-21 17:37:52 +0100170 if v in data: data[k]=data.pop(v)
171 else:
Marco Ceppic4629bd2017-10-26 14:35:46 +0200172 for k,v in list(http_db.items()):
tierno7edb6752016-03-21 17:37:52 +0100173 if k in data: data[v]=data.pop(k)
174
175def format_out(data):
176 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
tiernoefd80c92016-09-16 14:17:46 +0200177 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 +0100178 if 'application/yaml' in bottle.request.headers.get('Accept'):
179 bottle.response.content_type='application/yaml'
tierno7edb6752016-03-21 17:37:52 +0100180 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
gcalvinoc62cfa52017-10-05 18:21:25 +0200186def format_in(default_schema, version_fields=None, version_dict_schema=None, confidential_data=False):
tiernof1ba57e2017-09-07 12:23:19 +0200187 """
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 """
tierno7edb6752016-03-21 17:37:52 +0100198 #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:
tiernoefd80c92016-09-16 14:17:46 +0200213 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
tierno7edb6752016-03-21 17:37:52 +0100214 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
215 return
tiernof1ba57e2017-09-07 12:23:19 +0200216 # if client_data == None:
tierno7edb6752016-03-21 17:37:52 +0100217 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
218 # return
gcalvinoc62cfa52017-10-05 18:21:25 +0200219 if confidential_data:
220 logger.debug('IN: %s', remove_clear_passwd (yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False,
221 tags=False, encoding='utf-8', allow_unicode=True)))
222 else:
223 logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False,
tiernof1ba57e2017-09-07 12:23:19 +0200224 tags=False, encoding='utf-8', allow_unicode=True) )
225 # look for the client provider version
tierno7edb6752016-03-21 17:37:52 +0100226 error_text = "Invalid content "
tiernof1ba57e2017-09-07 12:23:19 +0200227 if not default_schema and not version_fields:
228 return client_data, None
tierno7edb6752016-03-21 17:37:52 +0100229 client_version = None
230 used_schema = None
231 if version_fields != None:
232 client_version = client_data
233 for field in version_fields:
234 if field in client_version:
235 client_version = client_version[field]
236 else:
237 client_version=None
238 break
tiernof1ba57e2017-09-07 12:23:19 +0200239 if client_version == None:
240 used_schema = default_schema
241 elif version_dict_schema != None:
tierno7edb6752016-03-21 17:37:52 +0100242 if client_version in version_dict_schema:
243 used_schema = version_dict_schema[client_version]
244 elif None in version_dict_schema:
245 used_schema = version_dict_schema[None]
246 if used_schema==None:
247 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
248
249 js_v(client_data, used_schema)
250 return client_data, used_schema
251 except (ValueError, yaml.YAMLError) as exc:
252 error_text += str(exc)
tiernoefd80c92016-09-16 14:17:46 +0200253 logger.error(error_text)
tierno7edb6752016-03-21 17:37:52 +0100254 bottle.abort(HTTP_Bad_Request, error_text)
255 except js_e.ValidationError as exc:
tiernoefd80c92016-09-16 14:17:46 +0200256 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
tierno7edb6752016-03-21 17:37:52 +0100257 error_pos = ""
258 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
259 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
260 #except:
261 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
262 # raise
263
264def filter_query_string(qs, http2db, allowed):
265 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
266 Attributes:
267 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
268 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
269 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
270 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
271 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
272 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
273 limit: limit dictated by user with the query string 'limit'. 100 by default
274 abort if not permited, using bottel.abort
275 '''
276 where={}
277 limit=100
278 select=[]
tiernof97fd272016-07-11 14:32:37 +0200279 #if type(qs) is not bottle.FormsDict:
280 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
281 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
282 for k in qs:
283 if k=='field':
284 select += qs.getall(k)
285 for v in select:
286 if v not in allowed:
287 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
288 elif k=='limit':
289 try:
290 limit=int(qs[k])
291 except:
292 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
293 else:
294 if k not in allowed:
295 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
296 if qs[k]!="null": where[k]=qs[k]
297 else: where[k]=None
tierno7edb6752016-03-21 17:37:52 +0100298 if len(select)==0: select += allowed
299 #change from http api to database naming
300 for i in range(0,len(select)):
301 k=select[i]
302 if http2db and k in http2db:
303 select[i] = http2db[k]
304 if http2db:
305 change_keys_http2db(where, http2db)
tiernof97fd272016-07-11 14:32:37 +0200306 #print "filter_query_string", select,where,limit
tierno7edb6752016-03-21 17:37:52 +0100307
308 return select,where,limit
309
310@bottle.hook('after_request')
311def enable_cors():
312 '''Don't know yet if really needed. Keep it just in case'''
313 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
314
tierno6ddeded2017-05-16 15:40:26 +0200315@bottle.route(url_base + '/version', method='GET')
316def http_get_version():
317 return nfvo.get_version()
tierno7edb6752016-03-21 17:37:52 +0100318#
319# VNFs
320#
321
322@bottle.route(url_base + '/tenants', method='GET')
323def http_get_tenants():
tiernof97fd272016-07-11 14:32:37 +0200324 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100325 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
326 ('uuid','name','description','created_at') )
tiernof97fd272016-07-11 14:32:37 +0200327 try:
328 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100329 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200330 convert_datetime2str(tenants)
331 data={'tenants' : tenants}
tierno7edb6752016-03-21 17:37:52 +0100332 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200333 except bottle.HTTPError:
334 raise
tiernof97fd272016-07-11 14:32:37 +0200335 except db_base_Exception as e:
336 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
337 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000338 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000339 logger.error("Unexpected exception: ", exc_info=True)
340 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000341
tierno7edb6752016-03-21 17:37:52 +0100342
343@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
344def http_get_tenant_id(tenant_id):
345 '''get tenant details, can use both uuid or name'''
346 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200347 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
348 try:
gcalvinoc62cfa52017-10-05 18:21:25 +0200349 from_ = 'nfvo_tenants'
350 select_, where_, limit_ = filter_query_string(bottle.request.query, None,
351 ('uuid', 'name', 'description', 'created_at'))
tiernod2b560b2017-10-11 10:30:18 +0200352 what = 'uuid' if utils.check_valid_uuid(tenant_id) else 'name'
353 where_[what] = tenant_id
tierno9c22f2d2017-10-09 16:23:55 +0200354 tenants = mydb.get_rows(FROM=from_, SELECT=select_,WHERE=where_)
tiernof97fd272016-07-11 14:32:37 +0200355 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernod2b560b2017-10-11 10:30:18 +0200356 if len(tenants) == 0:
357 bottle.abort(HTTP_Not_Found, "No tenant found with {}='{}'".format(what, tenant_id))
358 elif len(tenants) > 1:
359 bottle.abort(HTTP_Bad_Request, "More than one tenant found with {}='{}'".format(what, tenant_id))
360 convert_datetime2str(tenants[0])
361 data = {'tenant': tenants[0]}
tiernof97fd272016-07-11 14:32:37 +0200362 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200363 except bottle.HTTPError:
364 raise
tiernof97fd272016-07-11 14:32:37 +0200365 except db_base_Exception as e:
366 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
367 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000368 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000369 logger.error("Unexpected exception: ", exc_info=True)
370 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000371
tierno7edb6752016-03-21 17:37:52 +0100372
373@bottle.route(url_base + '/tenants', method='POST')
374def http_post_tenants():
375 '''insert a tenant into the catalogue. '''
376 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200377 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100378 http_content,_ = format_in( tenant_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200379 r = utils.remove_extra_items(http_content, tenant_schema)
tiernoefd80c92016-09-16 14:17:46 +0200380 if r:
381 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200382 try:
383 data = nfvo.new_tenant(mydb, http_content['tenant'])
tierno7edb6752016-03-21 17:37:52 +0100384 return http_get_tenant_id(data)
tiernod2b560b2017-10-11 10:30:18 +0200385 except bottle.HTTPError:
386 raise
tiernof97fd272016-07-11 14:32:37 +0200387 except (nfvo.NfvoException, db_base_Exception) as e:
388 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
389 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000390 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000391 logger.error("Unexpected exception: ", exc_info=True)
392 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000393
tierno7edb6752016-03-21 17:37:52 +0100394
395@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
396def http_edit_tenant_id(tenant_id):
397 '''edit tenant details, can use both uuid or name'''
398 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200399 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100400 http_content,_ = format_in( tenant_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200401 r = utils.remove_extra_items(http_content, tenant_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200402 if r:
403 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100404
405 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200406 try:
407 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
408 #edit data
409 tenant_id = tenant['uuid']
410 where={'uuid': tenant['uuid']}
411 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
412 return http_get_tenant_id(tenant_id)
tiernod2b560b2017-10-11 10:30:18 +0200413 except bottle.HTTPError:
414 raise
tiernof97fd272016-07-11 14:32:37 +0200415 except db_base_Exception as e:
416 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
417 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000418 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000419 logger.error("Unexpected exception: ", exc_info=True)
420 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000421
tierno7edb6752016-03-21 17:37:52 +0100422
423@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
424def http_delete_tenant_id(tenant_id):
425 '''delete a tenant from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200426 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
427 try:
428 data = nfvo.delete_tenant(mydb, tenant_id)
tierno7edb6752016-03-21 17:37:52 +0100429 return format_out({"result":"tenant " + data + " deleted"})
tiernod2b560b2017-10-11 10:30:18 +0200430 except bottle.HTTPError:
431 raise
tiernof97fd272016-07-11 14:32:37 +0200432 except db_base_Exception as e:
433 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
434 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000435 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000436 logger.error("Unexpected exception: ", exc_info=True)
437 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000438
tierno7edb6752016-03-21 17:37:52 +0100439
440@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
441def http_get_datacenters(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200442 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
443 try:
444 if tenant_id != 'any':
445 #check valid tenant_id
446 nfvo.check_tenant(mydb, tenant_id)
447 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
448 ('uuid','name','vim_url','type','created_at') )
449 if tenant_id != 'any':
450 where_['nfvo_tenant_id'] = tenant_id
451 if 'created_at' in select_:
452 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
453 if 'created_at' in where_:
454 where_['d.created_at'] = where_.pop('created_at')
455 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
456 SELECT=select_,WHERE=where_,LIMIT=limit_)
457 else:
458 datacenters = mydb.get_rows(FROM='datacenters',
459 SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100460 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200461 convert_datetime2str(datacenters)
462 data={'datacenters' : datacenters}
tierno7edb6752016-03-21 17:37:52 +0100463 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200464 except bottle.HTTPError:
465 raise
tiernof97fd272016-07-11 14:32:37 +0200466 except (nfvo.NfvoException, db_base_Exception) as e:
467 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
468 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000469 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000470 logger.error("Unexpected exception: ", exc_info=True)
471 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000472
tierno7edb6752016-03-21 17:37:52 +0100473
474@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
475def http_get_datacenter_id(tenant_id, datacenter_id):
476 '''get datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200477 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
478 try:
479 if tenant_id != 'any':
480 #check valid tenant_id
481 nfvo.check_tenant(mydb, tenant_id)
482 #obtain data
483 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
484 where_={}
485 where_[what] = datacenter_id
tierno8008c3a2016-10-13 15:34:28 +0000486 select_=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
tiernof97fd272016-07-11 14:32:37 +0200487 if tenant_id != 'any':
488 select_.append("datacenter_tenant_id")
489 where_['td.nfvo_tenant_id']= tenant_id
490 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
491 else:
492 from_='datacenters as d'
493 datacenters = mydb.get_rows(
494 SELECT=select_,
495 FROM=from_,
496 WHERE=where_)
497
498 if len(datacenters)==0:
499 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
500 elif len(datacenters)>1:
501 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
502 datacenter = datacenters[0]
503 if tenant_id != 'any':
504 #get vim tenant info
505 vim_tenants = mydb.get_rows(
tierno8008c3a2016-10-13 15:34:28 +0000506 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
tiernof97fd272016-07-11 14:32:37 +0200507 FROM="datacenter_tenants",
508 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
509 ORDER_BY=("created", ) )
510 del datacenter["datacenter_tenant_id"]
511 datacenter["vim_tenants"] = vim_tenants
tierno8008c3a2016-10-13 15:34:28 +0000512 for vim_tenant in vim_tenants:
513 if vim_tenant["passwd"]:
514 vim_tenant["passwd"] = "******"
515 if vim_tenant['config'] != None:
516 try:
517 config_dict = yaml.load(vim_tenant['config'])
518 vim_tenant['config'] = config_dict
gcalvinoc62cfa52017-10-05 18:21:25 +0200519 if vim_tenant['config'].get('admin_password'):
520 vim_tenant['config']['admin_password'] = "******"
521 if vim_tenant['config'].get('vcenter_password'):
522 vim_tenant['config']['vcenter_password'] = "******"
523 if vim_tenant['config'].get('nsx_password'):
524 vim_tenant['config']['nsx_password'] = "******"
venkatamahesh6ecca182017-01-27 23:04:40 +0530525 except Exception as e:
tierno8008c3a2016-10-13 15:34:28 +0000526 logger.error("Exception '%s' while trying to load config information", str(e))
527
tiernof97fd272016-07-11 14:32:37 +0200528 if datacenter['config'] != None:
529 try:
530 config_dict = yaml.load(datacenter['config'])
531 datacenter['config'] = config_dict
gcalvinoc62cfa52017-10-05 18:21:25 +0200532 if datacenter['config'].get('admin_password'):
533 datacenter['config']['admin_password'] = "******"
534 if datacenter['config'].get('vcenter_password'):
535 datacenter['config']['vcenter_password'] = "******"
536 if datacenter['config'].get('nsx_password'):
537 datacenter['config']['nsx_password'] = "******"
venkatamahesh6ecca182017-01-27 23:04:40 +0530538 except Exception as e:
tiernoefd80c92016-09-16 14:17:46 +0200539 logger.error("Exception '%s' while trying to load config information", str(e))
tiernof97fd272016-07-11 14:32:37 +0200540 #change_keys_http2db(content, http2db_datacenter, reverse=True)
541 convert_datetime2str(datacenter)
542 data={'datacenter' : datacenter}
543 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200544 except bottle.HTTPError:
545 raise
tiernof97fd272016-07-11 14:32:37 +0200546 except (nfvo.NfvoException, db_base_Exception) as e:
547 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
548 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000549 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000550 logger.error("Unexpected exception: ", exc_info=True)
551 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
552
tierno7edb6752016-03-21 17:37:52 +0100553
554@bottle.route(url_base + '/datacenters', method='POST')
555def http_post_datacenters():
garciadeblasc27b0462017-03-22 18:57:47 +0100556 '''insert a datacenter into the catalogue. '''
tierno7edb6752016-03-21 17:37:52 +0100557 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200558 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
gcalvinoc62cfa52017-10-05 18:21:25 +0200559 http_content,_ = format_in(datacenter_schema, confidential_data=True)
tierno42fcc3b2016-07-06 17:20:40 +0200560 r = utils.remove_extra_items(http_content, datacenter_schema)
tiernoefd80c92016-09-16 14:17:46 +0200561 if r:
562 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200563 try:
564 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100565 return http_get_datacenter_id('any', data)
tiernod2b560b2017-10-11 10:30:18 +0200566 except bottle.HTTPError:
567 raise
tiernof97fd272016-07-11 14:32:37 +0200568 except (nfvo.NfvoException, db_base_Exception) as e:
569 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
570 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000571 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000572 logger.error("Unexpected exception: ", exc_info=True)
573 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
574
tierno7edb6752016-03-21 17:37:52 +0100575
576@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
577def http_edit_datacenter_id(datacenter_id_name):
578 '''edit datacenter details, 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 #parse input data
581 http_content,_ = format_in( datacenter_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200582 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200583 if r:
584 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100585
tiernof97fd272016-07-11 14:32:37 +0200586 try:
587 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100588 return http_get_datacenter_id('any', datacenter_id)
tiernod2b560b2017-10-11 10:30:18 +0200589 except bottle.HTTPError:
590 raise
tiernof97fd272016-07-11 14:32:37 +0200591 except (nfvo.NfvoException, db_base_Exception) as e:
592 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
593 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000594 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000595 logger.error("Unexpected exception: ", exc_info=True)
596 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
597
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100598@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
599def http_post_sdn_controller(tenant_id):
600 '''insert a sdn controller into the catalogue. '''
601 #parse input data
602 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
603 http_content,_ = format_in( sdn_controller_schema )
604 try:
605 logger.debug("tenant_id: "+tenant_id)
606 #logger.debug("content: {}".format(http_content['sdn_controller']))
607
608 data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
609 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
tiernod2b560b2017-10-11 10:30:18 +0200610 except bottle.HTTPError:
611 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100612 except (nfvo.NfvoException, db_base_Exception) as e:
613 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
614 bottle.abort(e.http_code, str(e))
615 except Exception as e:
616 logger.error("Unexpected exception: ", exc_info=True)
617 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
618
619@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='PUT')
620def http_put_sdn_controller_update(tenant_id, controller_id):
621 '''Update sdn controller'''
622 #parse input data
623 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
624 http_content,_ = format_in( sdn_controller_edit_schema )
625# r = utils.remove_extra_items(http_content, datacenter_schema)
626# if r:
627# logger.debug("Remove received extra items %s", str(r))
628 try:
629 #logger.debug("tenant_id: "+tenant_id)
630 logger.debug("content: {}".format(http_content['sdn_controller']))
631
632 data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
633 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
634
tiernod2b560b2017-10-11 10:30:18 +0200635 except bottle.HTTPError:
636 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100637 except (nfvo.NfvoException, db_base_Exception) as e:
638 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
639 bottle.abort(e.http_code, str(e))
640 except Exception as e:
641 logger.error("Unexpected exception: ", exc_info=True)
642 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
643
644@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
645def http_get_sdn_controller(tenant_id):
646 '''get sdn controllers list, can use both uuid or name'''
647 try:
648 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
649
650 data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
651 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200652 except bottle.HTTPError:
653 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100654 except (nfvo.NfvoException, db_base_Exception) as e:
655 logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
656 bottle.abort(e.http_code, str(e))
657 except Exception as e:
658 logger.error("Unexpected exception: ", exc_info=True)
659 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
660
661@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
662def http_get_sdn_controller_id(tenant_id, controller_id):
663 '''get sdn controller details, can use both uuid or name'''
664 try:
665 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
666 data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
667 return format_out({"sdn_controllers": data})
tiernod2b560b2017-10-11 10:30:18 +0200668 except bottle.HTTPError:
669 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100670 except (nfvo.NfvoException, db_base_Exception) as e:
671 logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
672 bottle.abort(e.http_code, str(e))
673 except Exception as e:
674 logger.error("Unexpected exception: ", exc_info=True)
675 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
676
677@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
678def http_delete_sdn_controller_id(tenant_id, controller_id):
679 '''delete sdn controller, can use both uuid or name'''
680 try:
681 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
682 data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
683 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200684 except bottle.HTTPError:
685 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100686 except (nfvo.NfvoException, db_base_Exception) as e:
687 logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
688 bottle.abort(e.http_code, str(e))
689 except Exception as e:
690 logger.error("Unexpected exception: ", exc_info=True)
691 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
692
693@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
694def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
695 '''Set the sdn port mapping for a datacenter. '''
696 #parse input data
697 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
698 http_content, _ = format_in(sdn_port_mapping_schema)
699# r = utils.remove_extra_items(http_content, datacenter_schema)
700# if r:
701# logger.debug("Remove received extra items %s", str(r))
702 try:
703 data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
704 return format_out({"sdn_port_mapping": data})
tiernod2b560b2017-10-11 10:30:18 +0200705 except bottle.HTTPError:
706 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100707 except (nfvo.NfvoException, db_base_Exception) as e:
708 logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
709 bottle.abort(e.http_code, str(e))
710 except Exception as e:
711 logger.error("Unexpected exception: ", exc_info=True)
712 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
713
714@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
715def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
716 '''get datacenter sdn mapping details, can use both uuid or name'''
717 try:
718 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
719
720 data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
721 return format_out({"sdn_port_mapping": data})
tiernod2b560b2017-10-11 10:30:18 +0200722 except bottle.HTTPError:
723 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100724 except (nfvo.NfvoException, db_base_Exception) as e:
725 logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
726 bottle.abort(e.http_code, str(e))
727 except Exception as e:
728 logger.error("Unexpected exception: ", exc_info=True)
729 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
730
731@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
732def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
733 '''clean datacenter sdn mapping, can use both uuid or name'''
734 try:
735 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
736 data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
737 return format_out({"result": data})
tiernod2b560b2017-10-11 10:30:18 +0200738 except bottle.HTTPError:
739 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100740 except (nfvo.NfvoException, db_base_Exception) as e:
741 logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
742 bottle.abort(e.http_code, str(e))
743 except Exception as e:
744 logger.error("Unexpected exception: ", exc_info=True)
745 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100746
747@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
748@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
749@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
750def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
751 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200752 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100753 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200754 try:
755 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
756 where_= {"datacenter_id":datacenter_dict['uuid']}
757 if netmap_id:
758 if utils.check_valid_uuid(netmap_id):
759 where_["uuid"] = netmap_id
760 else:
761 where_["name"] = netmap_id
762 netmaps =mydb.get_rows(FROM='datacenter_nets',
763 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
764 WHERE=where_ )
765 convert_datetime2str(netmaps)
766 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
767 if netmap_id and len(netmaps)==1:
768 data={'netmap' : netmaps[0]}
769 elif netmap_id and len(netmaps)==0:
Marco Ceppic4629bd2017-10-26 14:35:46 +0200770 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join([str(x[0])+": "+str(x[1]) for x in iter(where_.items())]) )
tiernof97fd272016-07-11 14:32:37 +0200771 return
tierno7edb6752016-03-21 17:37:52 +0100772 else:
tiernof97fd272016-07-11 14:32:37 +0200773 data={'netmaps' : netmaps}
774 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200775 except bottle.HTTPError:
776 raise
tiernof97fd272016-07-11 14:32:37 +0200777 except (nfvo.NfvoException, db_base_Exception) as e:
778 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
779 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000780 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000781 logger.error("Unexpected exception: ", exc_info=True)
782 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
783
tierno7edb6752016-03-21 17:37:52 +0100784
785@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
786@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
787def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
788 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200789 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100790 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200791 try:
792 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
793 where_= {"datacenter_id":datacenter_dict['uuid']}
794 if netmap_id:
795 if utils.check_valid_uuid(netmap_id):
796 where_["uuid"] = netmap_id
797 else:
798 where_["name"] = netmap_id
799 #change_keys_http2db(content, http2db_tenant, reverse=True)
800 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
tiernod2b560b2017-10-11 10:30:18 +0200801 if deleted == 0 and netmap_id:
Marco Ceppic4629bd2017-10-26 14:35:46 +0200802 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join([str(x[0])+": "+str(x[1]) for x in iter(where_.items())]) )
tiernof97fd272016-07-11 14:32:37 +0200803 if netmap_id:
804 return format_out({"result": "netmap %s deleted" % netmap_id})
tierno7edb6752016-03-21 17:37:52 +0100805 else:
tiernof97fd272016-07-11 14:32:37 +0200806 return format_out({"result": "%d netmap deleted" % deleted})
tiernod2b560b2017-10-11 10:30:18 +0200807 except bottle.HTTPError:
808 raise
tiernof97fd272016-07-11 14:32:37 +0200809 except (nfvo.NfvoException, db_base_Exception) as e:
810 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
811 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000812 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000813 logger.error("Unexpected exception: ", exc_info=True)
814 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100815
816
817@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
818def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
tiernof97fd272016-07-11 14:32:37 +0200819 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
820 try:
821 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
822 convert_datetime2str(netmaps)
823 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
824 data={'netmaps' : netmaps}
825 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200826 except bottle.HTTPError:
827 raise
tiernof97fd272016-07-11 14:32:37 +0200828 except (nfvo.NfvoException, db_base_Exception) as e:
829 logger.error("http_uploadnetmap_datacenter_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>/datacenters/<datacenter_id>/netmaps', method='POST')
837def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
838 '''creates a new netmap'''
tiernof97fd272016-07-11 14:32:37 +0200839 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100840 #parse input data
841 http_content,_ = format_in( netmap_new_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200842 r = utils.remove_extra_items(http_content, netmap_new_schema)
tiernoefd80c92016-09-16 14:17:46 +0200843 if r:
844 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200845 try:
846 #obtain data, check that only one exist
847 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
848 convert_datetime2str(netmaps)
849 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
850 data={'netmaps' : netmaps}
851 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +0200852 except bottle.HTTPError:
853 raise
tiernof97fd272016-07-11 14:32:37 +0200854 except (nfvo.NfvoException, db_base_Exception) as e:
855 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
856 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000857 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000858 logger.error("Unexpected exception: ", exc_info=True)
859 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
860
tierno7edb6752016-03-21 17:37:52 +0100861
862@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
863def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
864 '''edit a netmap'''
tiernof97fd272016-07-11 14:32:37 +0200865 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100866 #parse input data
867 http_content,_ = format_in( netmap_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200868 r = utils.remove_extra_items(http_content, netmap_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200869 if r:
870 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100871
872 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200873 try:
874 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
tierno7edb6752016-03-21 17:37:52 +0100875 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
tiernod2b560b2017-10-11 10:30:18 +0200876 except bottle.HTTPError:
877 raise
tiernof97fd272016-07-11 14:32:37 +0200878 except (nfvo.NfvoException, db_base_Exception) as e:
879 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
880 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000881 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000882 logger.error("Unexpected exception: ", exc_info=True)
883 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100884
885
886@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
887def http_action_datacenter_id(tenant_id, datacenter_id):
888 '''perform an action over datacenter, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200889 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100890 #parse input data
891 http_content,_ = format_in( datacenter_action_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200892 r = utils.remove_extra_items(http_content, datacenter_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200893 if r:
894 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200895 try:
896 #obtain data, check that only one exist
897 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
898 if 'net-update' in http_content:
899 return http_getnetmap_datacenter_id(datacenter_id)
900 else:
901 return format_out(result)
tiernod2b560b2017-10-11 10:30:18 +0200902 except bottle.HTTPError:
903 raise
tiernof97fd272016-07-11 14:32:37 +0200904 except (nfvo.NfvoException, db_base_Exception) as e:
905 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
906 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000907 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000908 logger.error("Unexpected exception: ", exc_info=True)
909 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100910
911
912@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
913def http_delete_datacenter_id( datacenter_id):
914 '''delete a tenant from database, can use both uuid or name'''
915
tiernof97fd272016-07-11 14:32:37 +0200916 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
917 try:
918 data = nfvo.delete_datacenter(mydb, datacenter_id)
919 return format_out({"result":"datacenter '" + data + "' deleted"})
tiernod2b560b2017-10-11 10:30:18 +0200920 except bottle.HTTPError:
921 raise
tiernof97fd272016-07-11 14:32:37 +0200922 except (nfvo.NfvoException, db_base_Exception) as e:
923 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
924 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000925 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000926 logger.error("Unexpected exception: ", exc_info=True)
927 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
928
tierno7edb6752016-03-21 17:37:52 +0100929
930@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
931def http_associate_datacenters(tenant_id, datacenter_id):
932 '''associate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200933 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100934 #parse input data
gcalvinoc62cfa52017-10-05 18:21:25 +0200935 http_content,_ = format_in(datacenter_associate_schema, confidential_data=True)
tierno42fcc3b2016-07-06 17:20:40 +0200936 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
tiernoefd80c92016-09-16 14:17:46 +0200937 if r:
938 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200939 try:
940 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
941 http_content['datacenter'].get('vim_tenant'),
942 http_content['datacenter'].get('vim_tenant_name'),
943 http_content['datacenter'].get('vim_username'),
tierno8008c3a2016-10-13 15:34:28 +0000944 http_content['datacenter'].get('vim_password'),
945 http_content['datacenter'].get('config')
946 )
tiernof97fd272016-07-11 14:32:37 +0200947 return http_get_datacenter_id(tenant_id, id_)
tiernod2b560b2017-10-11 10:30:18 +0200948 except bottle.HTTPError:
949 raise
tiernof97fd272016-07-11 14:32:37 +0200950 except (nfvo.NfvoException, db_base_Exception) as e:
951 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
952 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000953 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000954 logger.error("Unexpected exception: ", exc_info=True)
955 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
956
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100957@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
958def http_associate_datacenters_edit(tenant_id, datacenter_id):
959 '''associate an existing datacenter to a this tenant. '''
960 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
961 #parse input data
962 http_content,_ = format_in( datacenter_associate_schema )
963 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
964 if r:
965 logger.debug("Remove received extra items %s", str(r))
966 try:
967 id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
968 http_content['datacenter'].get('vim_tenant'),
969 http_content['datacenter'].get('vim_tenant_name'),
970 http_content['datacenter'].get('vim_username'),
971 http_content['datacenter'].get('vim_password'),
972 http_content['datacenter'].get('config')
973 )
974 return http_get_datacenter_id(tenant_id, id_)
tiernod2b560b2017-10-11 10:30:18 +0200975 except bottle.HTTPError:
976 raise
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100977 except (nfvo.NfvoException, db_base_Exception) as e:
978 logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
979 bottle.abort(e.http_code, str(e))
980 except Exception as e:
981 logger.error("Unexpected exception: ", exc_info=True)
982 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100983
984@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
985def http_deassociate_datacenters(tenant_id, datacenter_id):
986 '''deassociate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200987 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
988 try:
989 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
990 return format_out({"result": data})
tiernod2b560b2017-10-11 10:30:18 +0200991 except bottle.HTTPError:
992 raise
tiernof97fd272016-07-11 14:32:37 +0200993 except (nfvo.NfvoException, db_base_Exception) as e:
994 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
995 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000996 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000997 logger.error("Unexpected exception: ", exc_info=True)
998 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
999
Pablo Montes Moreno6aa0b2b2017-05-23 18:33:12 +02001000@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method='POST')
1001def http_post_vim_net_sdn_attach(tenant_id, datacenter_id, network_id):
1002 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1003 http_content, _ = format_in(sdn_external_port_schema)
1004 try:
1005 data = nfvo.vim_net_sdn_attach(mydb, tenant_id, datacenter_id, network_id, http_content)
1006 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001007 except bottle.HTTPError:
1008 raise
Pablo Montes Moreno6aa0b2b2017-05-23 18:33:12 +02001009 except (nfvo.NfvoException, db_base_Exception) as e:
1010 logger.error("http_post_vim_net_sdn_attach error {}: {}".format(e.http_code, str(e)))
1011 bottle.abort(e.http_code, str(e))
1012 except Exception as e:
1013 logger.error("Unexpected exception: ", exc_info=True)
1014 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1015
1016@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method='DELETE')
1017@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method='DELETE')
1018def http_delete_vim_net_sdn_detach(tenant_id, datacenter_id, network_id, port_id=None):
1019 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1020 try:
1021 data = nfvo.vim_net_sdn_detach(mydb, tenant_id, datacenter_id, network_id, port_id)
1022 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001023 except bottle.HTTPError:
1024 raise
Pablo Montes Moreno6aa0b2b2017-05-23 18:33:12 +02001025 except (nfvo.NfvoException, db_base_Exception) as e:
1026 logger.error("http_delete_vim_net_sdn_detach error {}: {}".format(e.http_code, str(e)))
1027 bottle.abort(e.http_code, str(e))
1028 except Exception as e:
1029 logger.error("Unexpected exception: ", exc_info=True)
1030 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001031
tierno7edb6752016-03-21 17:37:52 +01001032@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
1033@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
1034def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
tiernof97fd272016-07-11 14:32:37 +02001035 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1036 try:
1037 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +01001038 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001039 except bottle.HTTPError:
1040 raise
tiernof97fd272016-07-11 14:32:37 +02001041 except (nfvo.NfvoException, db_base_Exception) as e:
1042 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
1043 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001044 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001045 logger.error("Unexpected exception: ", exc_info=True)
1046 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1047
tierno7edb6752016-03-21 17:37:52 +01001048
1049@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
1050def http_del_vim_items(tenant_id, datacenter_id, item, name):
tiernof97fd272016-07-11 14:32:37 +02001051 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1052 try:
1053 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +01001054 return format_out({"result":data})
tiernod2b560b2017-10-11 10:30:18 +02001055 except bottle.HTTPError:
1056 raise
tiernof97fd272016-07-11 14:32:37 +02001057 except (nfvo.NfvoException, db_base_Exception) as e:
1058 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
1059 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001060 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001061 logger.error("Unexpected exception: ", exc_info=True)
1062 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1063
tierno65a9b0c2016-09-28 14:57:25 +00001064
tierno7edb6752016-03-21 17:37:52 +01001065@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
1066def http_post_vim_items(tenant_id, datacenter_id, item):
tiernof97fd272016-07-11 14:32:37 +02001067 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001068 http_content,_ = format_in( object_schema )
tiernof97fd272016-07-11 14:32:37 +02001069 try:
1070 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
tierno7edb6752016-03-21 17:37:52 +01001071 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001072 except bottle.HTTPError:
1073 raise
tiernof97fd272016-07-11 14:32:37 +02001074 except (nfvo.NfvoException, db_base_Exception) as e:
1075 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
1076 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001077 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001078 logger.error("Unexpected exception: ", exc_info=True)
1079 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1080
tierno7edb6752016-03-21 17:37:52 +01001081
1082@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
1083def http_get_vnfs(tenant_id):
tiernof97fd272016-07-11 14:32:37 +02001084 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1085 try:
1086 if tenant_id != 'any':
1087 #check valid tenant_id
1088 nfvo.check_tenant(mydb, tenant_id)
1089 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
tiernof1ba57e2017-09-07 12:23:19 +02001090 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
tiernof97fd272016-07-11 14:32:37 +02001091 where_or = {}
1092 if tenant_id != "any":
1093 where_or["tenant_id"] = tenant_id
1094 where_or["public"] = True
1095 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 +01001096 #change_keys_http2db(content, http2db_vnf, reverse=True)
tiernof97fd272016-07-11 14:32:37 +02001097 utils.convert_str2boolean(vnfs, ('public',))
1098 convert_datetime2str(vnfs)
1099 data={'vnfs' : vnfs}
tierno7edb6752016-03-21 17:37:52 +01001100 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001101 except bottle.HTTPError:
1102 raise
tiernof97fd272016-07-11 14:32:37 +02001103 except (nfvo.NfvoException, db_base_Exception) as e:
1104 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
1105 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001106 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001107 logger.error("Unexpected exception: ", exc_info=True)
1108 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1109
tierno7edb6752016-03-21 17:37:52 +01001110
1111@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
1112def http_get_vnf_id(tenant_id,vnf_id):
1113 '''get vnf details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001114 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1115 try:
1116 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
1117 utils.convert_str2boolean(vnf, ('public',))
1118 convert_datetime2str(vnf)
1119 return format_out(vnf)
tiernod2b560b2017-10-11 10:30:18 +02001120 except bottle.HTTPError:
1121 raise
tiernof97fd272016-07-11 14:32:37 +02001122 except (nfvo.NfvoException, db_base_Exception) as e:
1123 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
1124 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001125 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001126 logger.error("Unexpected exception: ", exc_info=True)
1127 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1128
tierno7edb6752016-03-21 17:37:52 +01001129
1130@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
1131def http_post_vnfs(tenant_id):
tiernof1ba57e2017-09-07 12:23:19 +02001132 """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
1133 :param tenant_id: tenant that this vnf belongs to
1134 :return:
1135 """
1136 # print "Parsing the YAML file of the VNF"
1137 # parse input data
tiernof97fd272016-07-11 14:32:37 +02001138 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001139 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001140 r = utils.remove_extra_items(http_content, used_schema)
tiernoefd80c92016-09-16 14:17:46 +02001141 if r:
1142 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001143 try:
tierno4319dad2016-09-05 12:11:11 +02001144 if used_schema == vnfd_schema_v01:
garciadeblas9f8456e2016-09-05 05:02:59 +02001145 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
tierno4319dad2016-09-05 12:11:11 +02001146 elif used_schema == vnfd_schema_v02:
garciadeblas9f8456e2016-09-05 05:02:59 +02001147 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
1148 else:
1149 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1150 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tiernof97fd272016-07-11 14:32:37 +02001151 return http_get_vnf_id(tenant_id, vnf_id)
tiernod2b560b2017-10-11 10:30:18 +02001152 except bottle.HTTPError:
1153 raise
tiernof97fd272016-07-11 14:32:37 +02001154 except (nfvo.NfvoException, db_base_Exception) as e:
1155 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1156 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001157 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001158 logger.error("Unexpected exception: ", exc_info=True)
1159 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1160
tiernof1ba57e2017-09-07 12:23:19 +02001161
1162@bottle.route(url_base + '/v3/<tenant_id>/vnfd', method='POST')
1163def http_post_vnfs_v3(tenant_id):
1164 """
1165 Insert one or several VNFs in the catalog, following OSM IM
1166 :param tenant_id: tenant owner of the VNF
1167 :return: The detailed list of inserted VNFs, following the old format
1168 """
1169 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1170 http_content, _ = format_in(None)
1171 try:
1172 vnfd_uuid_list = nfvo.new_vnfd_v3(mydb, tenant_id, http_content)
1173 vnfd_list = []
1174 for vnfd_uuid in vnfd_uuid_list:
1175 vnf = nfvo.get_vnf_id(mydb, tenant_id, vnfd_uuid)
1176 utils.convert_str2boolean(vnf, ('public',))
1177 convert_datetime2str(vnf)
1178 vnfd_list.append(vnf["vnf"])
1179 return format_out({"vnfd": vnfd_list})
tiernod2b560b2017-10-11 10:30:18 +02001180 except bottle.HTTPError:
1181 raise
tiernof1ba57e2017-09-07 12:23:19 +02001182 except (nfvo.NfvoException, db_base_Exception) as e:
1183 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1184 bottle.abort(e.http_code, str(e))
1185 except Exception as e:
1186 logger.error("Unexpected exception: ", exc_info=True)
1187 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1188
tierno7edb6752016-03-21 17:37:52 +01001189@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
tiernof1ba57e2017-09-07 12:23:19 +02001190def http_delete_vnf_id(tenant_id, vnf_id):
tierno7edb6752016-03-21 17:37:52 +01001191 '''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 +02001192 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001193 #check valid tenant_id and deletes the vnf, including images,
tiernof97fd272016-07-11 14:32:37 +02001194 try:
1195 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
tierno7edb6752016-03-21 17:37:52 +01001196 #print json.dumps(data, indent=4)
1197 return format_out({"result":"VNF " + data + " deleted"})
tiernod2b560b2017-10-11 10:30:18 +02001198 except bottle.HTTPError:
1199 raise
tiernof97fd272016-07-11 14:32:37 +02001200 except (nfvo.NfvoException, db_base_Exception) as e:
1201 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
1202 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001203 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001204 logger.error("Unexpected exception: ", exc_info=True)
1205 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1206
tierno7edb6752016-03-21 17:37:52 +01001207
1208#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1209#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1210@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
1211def http_get_hosts(tenant_id, datacenter):
1212 '''get the tidvim host hopology from the vim.'''
tiernof97fd272016-07-11 14:32:37 +02001213 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1214 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1215 try:
1216 if datacenter == 'treeview':
1217 data = nfvo.get_hosts(mydb, tenant_id)
1218 else:
1219 #openmano-gui is using a hardcoded value for the datacenter
1220 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
1221
1222 if result < 0:
tiernoefd80c92016-09-16 14:17:46 +02001223 #print "http_get_hosts error %d %s" % (-result, data)
tiernof97fd272016-07-11 14:32:37 +02001224 bottle.abort(-result, data)
1225 else:
1226 convert_datetime2str(data)
tiernoefd80c92016-09-16 14:17:46 +02001227 #print json.dumps(data, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001228 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001229 except bottle.HTTPError:
1230 raise
tiernof97fd272016-07-11 14:32:37 +02001231 except (nfvo.NfvoException, db_base_Exception) as e:
garciadeblas9f8456e2016-09-05 05:02:59 +02001232 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
tiernof97fd272016-07-11 14:32:37 +02001233 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001234 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001235 logger.error("Unexpected exception: ", exc_info=True)
1236 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001237
1238
1239@bottle.route(url_base + '/<path:path>', method='OPTIONS')
1240def http_options_deploy(path):
1241 '''For some reason GUI web ask for OPTIONS that must be responded'''
1242 #TODO: check correct path, and correct headers request
tiernof97fd272016-07-11 14:32:37 +02001243 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001244 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1245 bottle.response.set_header('Accept','application/yaml,application/json')
1246 bottle.response.set_header('Content-Type','application/yaml,application/json')
1247 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
1248 bottle.response.set_header('Access-Control-Allow-Origin','*')
1249 return
1250
1251@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
1252def http_post_deploy(tenant_id):
1253 '''post topology deploy.'''
tiernof97fd272016-07-11 14:32:37 +02001254 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001255
tierno66aa0372016-07-06 17:31:12 +02001256 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001257 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001258 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001259 #print "http_post_deploy input: ", http_content
tierno7edb6752016-03-21 17:37:52 +01001260
tiernof97fd272016-07-11 14:32:37 +02001261 try:
1262 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1263 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
1264 #print json.dumps(data, indent=4)
1265 return format_out(instance)
tiernod2b560b2017-10-11 10:30:18 +02001266 except bottle.HTTPError:
1267 raise
tiernof97fd272016-07-11 14:32:37 +02001268 except (nfvo.NfvoException, db_base_Exception) as e:
1269 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
1270 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001271 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001272 logger.error("Unexpected exception: ", exc_info=True)
1273 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1274
tierno7edb6752016-03-21 17:37:52 +01001275
1276@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
1277def http_post_verify(tenant_id):
1278 #TODO:
1279# '''post topology verify'''
1280# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
tiernof97fd272016-07-11 14:32:37 +02001281 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001282 return
1283
1284#
1285# SCENARIOS
1286#
1287
1288@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
1289def http_post_scenarios(tenant_id):
1290 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +02001291 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001292 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 +02001293 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001294 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001295 #print "http_post_scenarios input: ", http_content
1296 try:
tierno4319dad2016-09-05 12:11:11 +02001297 if used_schema == nsd_schema_v01:
tiernof97fd272016-07-11 14:32:37 +02001298 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +02001299 elif used_schema == nsd_schema_v02:
tierno5bb59dc2017-02-13 14:53:54 +01001300 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
tierno4319dad2016-09-05 12:11:11 +02001301 elif used_schema == nsd_schema_v03:
tierno5bb59dc2017-02-13 14:53:54 +01001302 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
garciadeblas9f8456e2016-09-05 05:02:59 +02001303 else:
1304 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1305 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tierno7edb6752016-03-21 17:37:52 +01001306 #print json.dumps(data, indent=4)
1307 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001308 return http_get_scenario_id(tenant_id, scenario_id)
tiernod2b560b2017-10-11 10:30:18 +02001309 except bottle.HTTPError:
1310 raise
tiernof97fd272016-07-11 14:32:37 +02001311 except (nfvo.NfvoException, db_base_Exception) as e:
1312 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
1313 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001314 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001315 logger.error("Unexpected exception: ", exc_info=True)
1316 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1317
tiernof1ba57e2017-09-07 12:23:19 +02001318@bottle.route(url_base + '/v3/<tenant_id>/nsd', method='POST')
1319def http_post_nsds_v3(tenant_id):
1320 """
1321 Insert one or several NSDs in the catalog, following OSM IM
1322 :param tenant_id: tenant owner of the NSD
1323 :return: The detailed list of inserted NSDs, following the old format
1324 """
1325 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1326 http_content, _ = format_in(None)
1327 try:
1328 nsd_uuid_list = nfvo.new_nsd_v3(mydb, tenant_id, http_content)
1329 nsd_list = []
1330 for nsd_uuid in nsd_uuid_list:
1331 scenario = mydb.get_scenario(nsd_uuid, tenant_id)
1332 convert_datetime2str(scenario)
1333 nsd_list.append(scenario)
1334 data = {'nsd': nsd_list}
1335 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001336 except bottle.HTTPError:
1337 raise
tiernof1ba57e2017-09-07 12:23:19 +02001338 except (nfvo.NfvoException, db_base_Exception) as e:
1339 logger.error("http_post_nsds_v3 error {}: {}".format(e.http_code, str(e)))
1340 bottle.abort(e.http_code, str(e))
1341 except Exception as e:
1342 logger.error("Unexpected exception: ", exc_info=True)
1343 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1344
tierno7edb6752016-03-21 17:37:52 +01001345
1346@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
1347def http_post_scenario_action(tenant_id, scenario_id):
1348 '''take an action over a scenario'''
tiernof97fd272016-07-11 14:32:37 +02001349 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001350 # parse input data
1351 http_content, _ = format_in(scenario_action_schema)
1352 r = utils.remove_extra_items(http_content, scenario_action_schema)
1353 if r:
1354 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001355 try:
tierno551e5322017-01-19 16:16:26 +01001356 # check valid tenant_id
1357 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001358 if "start" in http_content:
1359 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1360 http_content['start'].get('description',http_content['start']['instance_name']),
1361 http_content['start'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001362 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001363 elif "deploy" in http_content: #Equivalent to start
1364 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1365 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1366 http_content['deploy'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001367 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001368 elif "reserve" in http_content: #Reserve resources
1369 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1370 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1371 http_content['reserve'].get('datacenter'), startvms=False )
tierno7edb6752016-03-21 17:37:52 +01001372 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001373 elif "verify" in http_content: #Equivalent to start and then delete
1374 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1375 http_content['verify'].get('description',http_content['verify']['instance_name']),
1376 http_content['verify'].get('datacenter'), startvms=False )
1377 instance_id = data['uuid']
1378 nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001379 return format_out({"result":"Verify OK"})
tiernod2b560b2017-10-11 10:30:18 +02001380 except bottle.HTTPError:
1381 raise
tiernof97fd272016-07-11 14:32:37 +02001382 except (nfvo.NfvoException, db_base_Exception) as e:
1383 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1384 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001385 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001386 logger.error("Unexpected exception: ", exc_info=True)
1387 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1388
tierno7edb6752016-03-21 17:37:52 +01001389
1390@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1391def http_get_scenarios(tenant_id):
1392 '''get scenarios list'''
tiernof97fd272016-07-11 14:32:37 +02001393 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1394 try:
1395 #check valid tenant_id
1396 if tenant_id != "any":
1397 nfvo.check_tenant(mydb, tenant_id)
1398 #obtain data
tiernof1ba57e2017-09-07 12:23:19 +02001399 s,w,l=filter_query_string(bottle.request.query, None,
1400 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
tiernof97fd272016-07-11 14:32:37 +02001401 where_or={}
1402 if tenant_id != "any":
1403 where_or["tenant_id"] = tenant_id
1404 where_or["public"] = True
1405 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1406 convert_datetime2str(scenarios)
1407 utils.convert_str2boolean(scenarios, ('public',) )
1408 data={'scenarios':scenarios}
tierno7edb6752016-03-21 17:37:52 +01001409 #print json.dumps(scenarios, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001410 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001411 except bottle.HTTPError:
1412 raise
tiernof97fd272016-07-11 14:32:37 +02001413 except (nfvo.NfvoException, db_base_Exception) as e:
1414 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1415 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001416 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001417 logger.error("Unexpected exception: ", exc_info=True)
1418 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1419
tierno7edb6752016-03-21 17:37:52 +01001420
1421@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1422def http_get_scenario_id(tenant_id, scenario_id):
1423 '''get scenario details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001424 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 #obtain data
1430 scenario = mydb.get_scenario(scenario_id, tenant_id)
1431 convert_datetime2str(scenario)
1432 data={'scenario' : scenario}
tierno7edb6752016-03-21 17:37:52 +01001433 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001434 except bottle.HTTPError:
1435 raise
tiernof97fd272016-07-11 14:32:37 +02001436 except (nfvo.NfvoException, db_base_Exception) as e:
1437 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1438 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001439 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001440 logger.error("Unexpected exception: ", exc_info=True)
1441 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1442
tierno7edb6752016-03-21 17:37:52 +01001443
1444@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1445def http_delete_scenario_id(tenant_id, scenario_id):
1446 '''delete a scenario from database, can use both uuid or name'''
tierno664691a2017-01-31 12:43:46 +01001447 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tiernof97fd272016-07-11 14:32:37 +02001448 try:
1449 #check valid tenant_id
1450 if tenant_id != "any":
tierno664691a2017-01-31 12:43:46 +01001451 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001452 #obtain data
1453 data = mydb.delete_scenario(scenario_id, tenant_id)
tierno7edb6752016-03-21 17:37:52 +01001454 #print json.dumps(data, indent=4)
1455 return format_out({"result":"scenario " + data + " deleted"})
tiernod2b560b2017-10-11 10:30:18 +02001456 except bottle.HTTPError:
1457 raise
tiernof97fd272016-07-11 14:32:37 +02001458 except (nfvo.NfvoException, db_base_Exception) as e:
1459 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1460 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001461 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001462 logger.error("Unexpected exception: ", exc_info=True)
1463 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001464
1465
1466@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1467def http_put_scenario_id(tenant_id, scenario_id):
1468 '''edit an existing scenario id'''
tiernof97fd272016-07-11 14:32:37 +02001469 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001470 http_content,_ = format_in( scenario_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +02001471 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
tierno7edb6752016-03-21 17:37:52 +01001472 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001473 #print "http_put_scenario_id input: ", http_content
1474 try:
1475 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001476 #print json.dumps(data, indent=4)
1477 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001478 return http_get_scenario_id(tenant_id, scenario_id)
tiernod2b560b2017-10-11 10:30:18 +02001479 except bottle.HTTPError:
1480 raise
tiernof97fd272016-07-11 14:32:37 +02001481 except (nfvo.NfvoException, db_base_Exception) as e:
1482 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1483 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001484 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001485 logger.error("Unexpected exception: ", exc_info=True)
1486 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001487
1488@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1489def http_post_instances(tenant_id):
garciadeblasedca7b32016-09-29 14:01:52 +00001490 '''create an instance-scenario'''
tiernof97fd272016-07-11 14:32:37 +02001491 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001492 # parse input data
1493 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1494 r = utils.remove_extra_items(http_content, used_schema)
1495 if r is not None:
1496 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001497 try:
1498 #check valid tenant_id
1499 if tenant_id != "any":
1500 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001501 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
tierno7edb6752016-03-21 17:37:52 +01001502 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001503 except bottle.HTTPError:
1504 raise
tiernof97fd272016-07-11 14:32:37 +02001505 except (nfvo.NfvoException, db_base_Exception) as e:
1506 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1507 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001508 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001509 logger.error("Unexpected exception: ", exc_info=True)
1510 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001511
1512#
1513# INSTANCES
1514#
1515@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1516def http_get_instances(tenant_id):
1517 '''get instance list'''
tiernof97fd272016-07-11 14:32:37 +02001518 try:
1519 #check valid tenant_id
1520 if tenant_id != "any":
1521 nfvo.check_tenant(mydb, tenant_id)
1522 #obtain data
1523 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1524 if tenant_id != "any":
1525 w['tenant_id'] = tenant_id
1526 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1527 convert_datetime2str(instances)
1528 utils.convert_str2boolean(instances, ('public',) )
1529 data={'instances':instances}
1530 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001531 except bottle.HTTPError:
1532 raise
tiernof97fd272016-07-11 14:32:37 +02001533 except (nfvo.NfvoException, db_base_Exception) as e:
1534 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1535 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001536 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001537 logger.error("Unexpected exception: ", exc_info=True)
1538 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1539
tierno7edb6752016-03-21 17:37:52 +01001540
1541@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1542def http_get_instance_id(tenant_id, instance_id):
1543 '''get instances details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001544 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1545 try:
1546 #check valid tenant_id
1547 if tenant_id != "any":
1548 nfvo.check_tenant(mydb, tenant_id)
1549 if tenant_id == "any":
1550 tenant_id = None
1551 #obtain data (first time is only to check that the instance exists)
1552 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1553 try:
1554 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1555 except (nfvo.NfvoException, db_base_Exception) as e:
1556 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
tiernof5755962017-07-13 15:44:34 +02001557 # obtain data with results upated
tiernof97fd272016-07-11 14:32:37 +02001558 instance = mydb.get_instance_scenario(instance_id, tenant_id)
tiernof5755962017-07-13 15:44:34 +02001559 # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
1560 for vnf in instance.get("vnfs", ()):
1561 for vm in vnf.get("vms", ()):
1562 for iface in vm.get("interfaces", ()):
1563 if iface.get("ip_address"):
1564 index = iface["ip_address"].find(";")
1565 if index >= 0:
1566 iface["ip_address"] = iface["ip_address"][:index]
tiernof97fd272016-07-11 14:32:37 +02001567 convert_datetime2str(instance)
tiernof5755962017-07-13 15:44:34 +02001568 # print json.dumps(instance, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001569 return format_out(instance)
tiernod2b560b2017-10-11 10:30:18 +02001570 except bottle.HTTPError:
1571 raise
tiernof97fd272016-07-11 14:32:37 +02001572 except (nfvo.NfvoException, db_base_Exception) as e:
1573 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1574 bottle.abort(e.http_code, str(e))
tierno8e995ce2016-09-22 08:13:00 +00001575 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001576 logger.error("Unexpected exception: ", exc_info=True)
1577 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1578
tierno7edb6752016-03-21 17:37:52 +01001579
1580@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1581def http_delete_instance_id(tenant_id, instance_id):
1582 '''delete instance from VIM and from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001583 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1584 try:
1585 #check valid tenant_id
1586 if tenant_id != "any":
1587 nfvo.check_tenant(mydb, tenant_id)
1588 if tenant_id == "any":
1589 tenant_id = None
1590 #obtain data
1591 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001592 return format_out({"result":message})
tiernod2b560b2017-10-11 10:30:18 +02001593 except bottle.HTTPError:
1594 raise
tiernof97fd272016-07-11 14:32:37 +02001595 except (nfvo.NfvoException, db_base_Exception) as e:
1596 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1597 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001598 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001599 logger.error("Unexpected exception: ", exc_info=True)
1600 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1601
tierno7edb6752016-03-21 17:37:52 +01001602
1603@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1604def http_post_instance_scenario_action(tenant_id, instance_id):
tierno868220c2017-09-26 00:11:05 +02001605 """
1606 take an action over a scenario instance
1607 :param tenant_id: tenant where user belongs to
1608 :param instance_id: instance indentity
1609 :return:
1610 """
tiernof97fd272016-07-11 14:32:37 +02001611 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001612 # parse input data
1613 http_content, _ = format_in(instance_scenario_action_schema)
1614 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1615 if r:
1616 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001617 try:
1618 #check valid tenant_id
1619 if tenant_id != "any":
1620 nfvo.check_tenant(mydb, tenant_id)
1621
tiernof97fd272016-07-11 14:32:37 +02001622 #print "http_post_instance_scenario_action input: ", http_content
1623 #obtain data
1624 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1625 instance_id = instance["uuid"]
1626
1627 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001628 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001629 except bottle.HTTPError:
1630 raise
tiernof97fd272016-07-11 14:32:37 +02001631 except (nfvo.NfvoException, db_base_Exception) as e:
1632 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1633 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001634 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001635 logger.error("Unexpected exception: ", exc_info=True)
1636 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001637
1638
tierno868220c2017-09-26 00:11:05 +02001639@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='GET')
1640@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action/<action_id>', method='GET')
1641def http_get_instance_scenario_action(tenant_id, instance_id, action_id=None):
1642 """
1643 List the actions done over an instance, or the action details
1644 :param tenant_id: tenant where user belongs to. Can be "any" to ignore
1645 :param instance_id: instance id, can be "any" to get actions of all instances
1646 :return:
1647 """
1648 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1649 try:
1650 # check valid tenant_id
1651 if tenant_id != "any":
1652 nfvo.check_tenant(mydb, tenant_id)
1653 data = nfvo.instance_action_get(mydb, tenant_id, instance_id, action_id)
1654 return format_out(data)
tiernod2b560b2017-10-11 10:30:18 +02001655 except bottle.HTTPError:
1656 raise
tierno868220c2017-09-26 00:11:05 +02001657 except (nfvo.NfvoException, db_base_Exception) as e:
1658 logger.error("http_get_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1659 bottle.abort(e.http_code, str(e))
1660 except Exception as e:
1661 logger.error("Unexpected exception: ", exc_info=True)
1662 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1663
gcalvinoc62cfa52017-10-05 18:21:25 +02001664def remove_clear_passwd(data):
1665 """
1666 Removes clear passwords from the data received
1667 :param data: data with clear password
1668 :return: data without the password information
1669 """
1670
1671 passw = ['password: ', 'passwd: ']
1672
1673 for pattern in passw:
1674 init = data.find(pattern)
1675 while init != -1:
1676 end = data.find('\n', init)
1677 data = data[:init] + '{}******'.format(pattern) + data[end:]
1678 init += 1
1679 init = data.find(pattern, init)
1680 return data
tierno868220c2017-09-26 00:11:05 +02001681
tierno7edb6752016-03-21 17:37:52 +01001682@bottle.error(400)
1683@bottle.error(401)
1684@bottle.error(404)
1685@bottle.error(403)
1686@bottle.error(405)
1687@bottle.error(406)
1688@bottle.error(409)
1689@bottle.error(503)
1690@bottle.error(500)
1691def error400(error):
1692 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1693 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1694 return format_out(e)
1695