blob: 94544f6b00316d0555103ec867695882c905e834 [file] [log] [blame]
tierno7edb6752016-03-21 17:37:52 +01001# -*- coding: utf-8 -*-
2
3##
4# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5# This file is part of openmano
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact with: nfvlabs@tid.es
22##
23
24'''
25HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
26and will use the nfvo.py module to run the appropriate method.
27Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
28'''
29__author__="Alfonso Tierno, Gerardo Garcia"
30__date__ ="$17-sep-2014 09:07:15$"
31
32import bottle
33import yaml
34import json
35import threading
36import time
tiernof97fd272016-07-11 14:32:37 +020037import logging
tierno7edb6752016-03-21 17:37:52 +010038
39from jsonschema import validate as js_v, exceptions as js_e
40from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
garciadeblas9f8456e2016-09-05 05:02:59 +020041 nsd_schema_v01, nsd_schema_v02, nsd_schema_v03, scenario_edit_schema, \
garciadeblas0c317ee2016-08-29 12:33:06 +020042 scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
tierno7edb6752016-03-21 17:37:52 +010043 tenant_schema, tenant_edit_schema,\
44 datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
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
tierno7edb6752016-03-21 17:37:52 +010048import nfvo
tierno42fcc3b2016-07-06 17:20:40 +020049import utils
tiernof97fd272016-07-11 14:32:37 +020050from db_base import db_base_Exception
51from 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:
70 for k in var.keys():
71 if var[k] is None: del var[k]
72 elif type(var[k]) is dict or type(var[k]) is list or type(var[k]) is tuple:
73 if delete_nulls(var[k]): del var[k]
74 if len(var) == 0: return True
75 elif type(var) is list or type(var) is tuple:
76 for k in var:
77 if type(k) is dict: delete_nulls(k)
78 if len(var) == 0: return True
79 return False
80
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:
86 for k,v in var.items():
87 if type(v) is float and k in ("created_at", "modified_at"):
88 var[k] = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(v) )
89 elif type(v) is dict or type(v) is list or type(v) is tuple:
90 convert_datetime2str(v)
91 if len(var) == 0: return True
92 elif type(var) is list or type(var) is tuple:
93 for v in var:
94 convert_datetime2str(v)
95
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:
169 for k,v in http_db.items():
170 if v in data: data[k]=data.pop(v)
171 else:
172 for k,v in http_db.items():
173 if k in data: data[v]=data.pop(k)
174
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
186def format_in(default_schema, version_fields=None, version_dict_schema=None):
187 ''' Parse the content of HTTP request against a json_schema
188 Parameters
189 default_schema: The schema to be parsed by default if no version field is found in the client data
190 version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
191 version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
192 It can contain a None as key, and this is apply if the client data version does not match any key
193 Return:
194 user_data, used_schema: if the data is successfully decoded and matches the schema
195 launch a bottle abort if fails
196 '''
197 #print "HEADERS :" + str(bottle.request.headers.items())
198 try:
199 error_text = "Invalid header format "
200 format_type = bottle.request.headers.get('Content-Type', 'application/json')
201 if 'application/json' in format_type:
202 error_text = "Invalid json format "
203 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
204 client_data = json.load(bottle.request.body)
205 #client_data = bottle.request.json()
206 elif 'application/yaml' in format_type:
207 error_text = "Invalid yaml format "
208 client_data = yaml.load(bottle.request.body)
209 elif 'application/xml' in format_type:
210 bottle.abort(501, "Content-Type: application/xml not supported yet.")
211 else:
tiernoefd80c92016-09-16 14:17:46 +0200212 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
tierno7edb6752016-03-21 17:37:52 +0100213 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
214 return
215 #if client_data == None:
216 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
217 # return
218
tiernoefd80c92016-09-16 14:17:46 +0200219 logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True) )
tierno7edb6752016-03-21 17:37:52 +0100220 #look for the client provider version
221 error_text = "Invalid content "
222 client_version = None
223 used_schema = None
224 if version_fields != None:
225 client_version = client_data
226 for field in version_fields:
227 if field in client_version:
228 client_version = client_version[field]
229 else:
230 client_version=None
231 break
232 if client_version==None:
233 used_schema=default_schema
234 elif version_dict_schema!=None:
235 if client_version in version_dict_schema:
236 used_schema = version_dict_schema[client_version]
237 elif None in version_dict_schema:
238 used_schema = version_dict_schema[None]
239 if used_schema==None:
240 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
241
242 js_v(client_data, used_schema)
243 return client_data, used_schema
244 except (ValueError, yaml.YAMLError) as exc:
245 error_text += str(exc)
tiernoefd80c92016-09-16 14:17:46 +0200246 logger.error(error_text)
tierno7edb6752016-03-21 17:37:52 +0100247 bottle.abort(HTTP_Bad_Request, error_text)
248 except js_e.ValidationError as exc:
tiernoefd80c92016-09-16 14:17:46 +0200249 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
tierno7edb6752016-03-21 17:37:52 +0100250 error_pos = ""
251 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
252 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
253 #except:
254 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
255 # raise
256
257def filter_query_string(qs, http2db, allowed):
258 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
259 Attributes:
260 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
261 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
262 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
263 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
264 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
265 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
266 limit: limit dictated by user with the query string 'limit'. 100 by default
267 abort if not permited, using bottel.abort
268 '''
269 where={}
270 limit=100
271 select=[]
tiernof97fd272016-07-11 14:32:37 +0200272 #if type(qs) is not bottle.FormsDict:
273 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
274 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
275 for k in qs:
276 if k=='field':
277 select += qs.getall(k)
278 for v in select:
279 if v not in allowed:
280 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
281 elif k=='limit':
282 try:
283 limit=int(qs[k])
284 except:
285 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
286 else:
287 if k not in allowed:
288 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
289 if qs[k]!="null": where[k]=qs[k]
290 else: where[k]=None
tierno7edb6752016-03-21 17:37:52 +0100291 if len(select)==0: select += allowed
292 #change from http api to database naming
293 for i in range(0,len(select)):
294 k=select[i]
295 if http2db and k in http2db:
296 select[i] = http2db[k]
297 if http2db:
298 change_keys_http2db(where, http2db)
tiernof97fd272016-07-11 14:32:37 +0200299 #print "filter_query_string", select,where,limit
tierno7edb6752016-03-21 17:37:52 +0100300
301 return select,where,limit
302
303@bottle.hook('after_request')
304def enable_cors():
305 '''Don't know yet if really needed. Keep it just in case'''
306 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
307
tierno6ddeded2017-05-16 15:40:26 +0200308@bottle.route(url_base + '/version', method='GET')
309def http_get_version():
310 return nfvo.get_version()
tierno7edb6752016-03-21 17:37:52 +0100311#
312# VNFs
313#
314
315@bottle.route(url_base + '/tenants', method='GET')
316def http_get_tenants():
tiernof97fd272016-07-11 14:32:37 +0200317 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100318 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
319 ('uuid','name','description','created_at') )
tiernof97fd272016-07-11 14:32:37 +0200320 try:
321 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100322 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200323 convert_datetime2str(tenants)
324 data={'tenants' : tenants}
tierno7edb6752016-03-21 17:37:52 +0100325 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200326 except db_base_Exception as e:
327 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
328 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000329 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000330 logger.error("Unexpected exception: ", exc_info=True)
331 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000332
tierno7edb6752016-03-21 17:37:52 +0100333
334@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
335def http_get_tenant_id(tenant_id):
336 '''get tenant details, can use both uuid or name'''
337 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200338 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
339 try:
340 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
341 #change_keys_http2db(content, http2db_tenant, reverse=True)
342 convert_datetime2str(tenant)
343 data={'tenant' : tenant}
344 return format_out(data)
345 except db_base_Exception as e:
346 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
347 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000348 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000349 logger.error("Unexpected exception: ", exc_info=True)
350 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000351
tierno7edb6752016-03-21 17:37:52 +0100352
353@bottle.route(url_base + '/tenants', method='POST')
354def http_post_tenants():
355 '''insert a tenant into the catalogue. '''
356 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200357 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100358 http_content,_ = format_in( tenant_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200359 r = utils.remove_extra_items(http_content, tenant_schema)
tiernoefd80c92016-09-16 14:17:46 +0200360 if r:
361 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200362 try:
363 data = nfvo.new_tenant(mydb, http_content['tenant'])
tierno7edb6752016-03-21 17:37:52 +0100364 return http_get_tenant_id(data)
tiernof97fd272016-07-11 14:32:37 +0200365 except (nfvo.NfvoException, db_base_Exception) as e:
366 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
367 bottle.abort(e.http_code, str(e))
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/<tenant_id>', method='PUT')
374def http_edit_tenant_id(tenant_id):
375 '''edit tenant details, can use both uuid or name'''
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_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200379 r = utils.remove_extra_items(http_content, tenant_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200380 if r:
381 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100382
383 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200384 try:
385 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
386 #edit data
387 tenant_id = tenant['uuid']
388 where={'uuid': tenant['uuid']}
389 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
390 return http_get_tenant_id(tenant_id)
391 except db_base_Exception as e:
392 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
393 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000394 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000395 logger.error("Unexpected exception: ", exc_info=True)
396 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000397
tierno7edb6752016-03-21 17:37:52 +0100398
399@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
400def http_delete_tenant_id(tenant_id):
401 '''delete a tenant from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200402 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
403 try:
404 data = nfvo.delete_tenant(mydb, tenant_id)
tierno7edb6752016-03-21 17:37:52 +0100405 return format_out({"result":"tenant " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200406 except db_base_Exception as e:
407 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
408 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000409 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000410 logger.error("Unexpected exception: ", exc_info=True)
411 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000412
tierno7edb6752016-03-21 17:37:52 +0100413
414@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
415def http_get_datacenters(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200416 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
417 try:
418 if tenant_id != 'any':
419 #check valid tenant_id
420 nfvo.check_tenant(mydb, tenant_id)
421 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
422 ('uuid','name','vim_url','type','created_at') )
423 if tenant_id != 'any':
424 where_['nfvo_tenant_id'] = tenant_id
425 if 'created_at' in select_:
426 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
427 if 'created_at' in where_:
428 where_['d.created_at'] = where_.pop('created_at')
429 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
430 SELECT=select_,WHERE=where_,LIMIT=limit_)
431 else:
432 datacenters = mydb.get_rows(FROM='datacenters',
433 SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100434 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200435 convert_datetime2str(datacenters)
436 data={'datacenters' : datacenters}
tierno7edb6752016-03-21 17:37:52 +0100437 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200438 except (nfvo.NfvoException, db_base_Exception) as e:
439 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
440 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000441 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000442 logger.error("Unexpected exception: ", exc_info=True)
443 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000444
tierno7edb6752016-03-21 17:37:52 +0100445
446@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
447def http_get_datacenter_id(tenant_id, datacenter_id):
448 '''get datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200449 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
450 try:
451 if tenant_id != 'any':
452 #check valid tenant_id
453 nfvo.check_tenant(mydb, tenant_id)
454 #obtain data
455 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
456 where_={}
457 where_[what] = datacenter_id
tierno8008c3a2016-10-13 15:34:28 +0000458 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 +0200459 if tenant_id != 'any':
460 select_.append("datacenter_tenant_id")
461 where_['td.nfvo_tenant_id']= tenant_id
462 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
463 else:
464 from_='datacenters as d'
465 datacenters = mydb.get_rows(
466 SELECT=select_,
467 FROM=from_,
468 WHERE=where_)
469
470 if len(datacenters)==0:
471 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
472 elif len(datacenters)>1:
473 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
474 datacenter = datacenters[0]
475 if tenant_id != 'any':
476 #get vim tenant info
477 vim_tenants = mydb.get_rows(
tierno8008c3a2016-10-13 15:34:28 +0000478 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
tiernof97fd272016-07-11 14:32:37 +0200479 FROM="datacenter_tenants",
480 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
481 ORDER_BY=("created", ) )
482 del datacenter["datacenter_tenant_id"]
483 datacenter["vim_tenants"] = vim_tenants
tierno8008c3a2016-10-13 15:34:28 +0000484 for vim_tenant in vim_tenants:
485 if vim_tenant["passwd"]:
486 vim_tenant["passwd"] = "******"
487 if vim_tenant['config'] != None:
488 try:
489 config_dict = yaml.load(vim_tenant['config'])
490 vim_tenant['config'] = config_dict
venkatamahesh6ecca182017-01-27 23:04:40 +0530491 except Exception as e:
tierno8008c3a2016-10-13 15:34:28 +0000492 logger.error("Exception '%s' while trying to load config information", str(e))
493
tiernof97fd272016-07-11 14:32:37 +0200494 if datacenter['config'] != None:
495 try:
496 config_dict = yaml.load(datacenter['config'])
497 datacenter['config'] = config_dict
venkatamahesh6ecca182017-01-27 23:04:40 +0530498 except Exception as e:
tiernoefd80c92016-09-16 14:17:46 +0200499 logger.error("Exception '%s' while trying to load config information", str(e))
tiernof97fd272016-07-11 14:32:37 +0200500 #change_keys_http2db(content, http2db_datacenter, reverse=True)
501 convert_datetime2str(datacenter)
502 data={'datacenter' : datacenter}
503 return format_out(data)
504 except (nfvo.NfvoException, db_base_Exception) as e:
505 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
506 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000507 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000508 logger.error("Unexpected exception: ", exc_info=True)
509 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
510
tierno7edb6752016-03-21 17:37:52 +0100511
512@bottle.route(url_base + '/datacenters', method='POST')
513def http_post_datacenters():
garciadeblasc27b0462017-03-22 18:57:47 +0100514 '''insert a datacenter into the catalogue. '''
tierno7edb6752016-03-21 17:37:52 +0100515 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200516 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100517 http_content,_ = format_in( datacenter_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200518 r = utils.remove_extra_items(http_content, datacenter_schema)
tiernoefd80c92016-09-16 14:17:46 +0200519 if r:
520 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200521 try:
522 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100523 return http_get_datacenter_id('any', data)
tiernof97fd272016-07-11 14:32:37 +0200524 except (nfvo.NfvoException, db_base_Exception) as e:
525 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
526 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000527 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000528 logger.error("Unexpected exception: ", exc_info=True)
529 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
530
tierno7edb6752016-03-21 17:37:52 +0100531
532@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
533def http_edit_datacenter_id(datacenter_id_name):
534 '''edit datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200535 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100536 #parse input data
537 http_content,_ = format_in( datacenter_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200538 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200539 if r:
540 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100541
tiernof97fd272016-07-11 14:32:37 +0200542 try:
543 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100544 return http_get_datacenter_id('any', datacenter_id)
tiernof97fd272016-07-11 14:32:37 +0200545 except (nfvo.NfvoException, db_base_Exception) as e:
546 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
547 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000548 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000549 logger.error("Unexpected exception: ", exc_info=True)
550 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
551
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100552@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
553def http_post_sdn_controller(tenant_id):
554 '''insert a sdn controller into the catalogue. '''
555 #parse input data
556 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
557 http_content,_ = format_in( sdn_controller_schema )
558 try:
559 logger.debug("tenant_id: "+tenant_id)
560 #logger.debug("content: {}".format(http_content['sdn_controller']))
561
562 data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
563 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
564 except (nfvo.NfvoException, db_base_Exception) as e:
565 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
566 bottle.abort(e.http_code, str(e))
567 except Exception as e:
568 logger.error("Unexpected exception: ", exc_info=True)
569 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
570
571@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='PUT')
572def http_put_sdn_controller_update(tenant_id, controller_id):
573 '''Update sdn controller'''
574 #parse input data
575 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
576 http_content,_ = format_in( sdn_controller_edit_schema )
577# r = utils.remove_extra_items(http_content, datacenter_schema)
578# if r:
579# logger.debug("Remove received extra items %s", str(r))
580 try:
581 #logger.debug("tenant_id: "+tenant_id)
582 logger.debug("content: {}".format(http_content['sdn_controller']))
583
584 data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
585 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
586
587 except (nfvo.NfvoException, db_base_Exception) as e:
588 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
589 bottle.abort(e.http_code, str(e))
590 except Exception as e:
591 logger.error("Unexpected exception: ", exc_info=True)
592 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
593
594@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
595def http_get_sdn_controller(tenant_id):
596 '''get sdn controllers list, can use both uuid or name'''
597 try:
598 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
599
600 data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
601 return format_out(data)
602 except (nfvo.NfvoException, db_base_Exception) as e:
603 logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
604 bottle.abort(e.http_code, str(e))
605 except Exception as e:
606 logger.error("Unexpected exception: ", exc_info=True)
607 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
608
609@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
610def http_get_sdn_controller_id(tenant_id, controller_id):
611 '''get sdn controller details, can use both uuid or name'''
612 try:
613 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
614 data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
615 return format_out({"sdn_controllers": data})
616 except (nfvo.NfvoException, db_base_Exception) as e:
617 logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
618 bottle.abort(e.http_code, str(e))
619 except Exception as e:
620 logger.error("Unexpected exception: ", exc_info=True)
621 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
622
623@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
624def http_delete_sdn_controller_id(tenant_id, controller_id):
625 '''delete sdn controller, can use both uuid or name'''
626 try:
627 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
628 data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
629 return format_out(data)
630 except (nfvo.NfvoException, db_base_Exception) as e:
631 logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
632 bottle.abort(e.http_code, str(e))
633 except Exception as e:
634 logger.error("Unexpected exception: ", exc_info=True)
635 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
636
637@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
638def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
639 '''Set the sdn port mapping for a datacenter. '''
640 #parse input data
641 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
642 http_content, _ = format_in(sdn_port_mapping_schema)
643# r = utils.remove_extra_items(http_content, datacenter_schema)
644# if r:
645# logger.debug("Remove received extra items %s", str(r))
646 try:
647 data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
648 return format_out({"sdn_port_mapping": data})
649 except (nfvo.NfvoException, db_base_Exception) as e:
650 logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
651 bottle.abort(e.http_code, str(e))
652 except Exception as e:
653 logger.error("Unexpected exception: ", exc_info=True)
654 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
655
656@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
657def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
658 '''get datacenter sdn mapping details, can use both uuid or name'''
659 try:
660 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
661
662 data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
663 return format_out({"sdn_port_mapping": data})
664 except (nfvo.NfvoException, db_base_Exception) as e:
665 logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
666 bottle.abort(e.http_code, str(e))
667 except Exception as e:
668 logger.error("Unexpected exception: ", exc_info=True)
669 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
670
671@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
672def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
673 '''clean datacenter sdn mapping, can use both uuid or name'''
674 try:
675 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
676 data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
677 return format_out({"result": data})
678 except (nfvo.NfvoException, db_base_Exception) as e:
679 logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
680 bottle.abort(e.http_code, str(e))
681 except Exception as e:
682 logger.error("Unexpected exception: ", exc_info=True)
683 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100684
685@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
686@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
687@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
688def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
689 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200690 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100691 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200692 try:
693 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
694 where_= {"datacenter_id":datacenter_dict['uuid']}
695 if netmap_id:
696 if utils.check_valid_uuid(netmap_id):
697 where_["uuid"] = netmap_id
698 else:
699 where_["name"] = netmap_id
700 netmaps =mydb.get_rows(FROM='datacenter_nets',
701 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
702 WHERE=where_ )
703 convert_datetime2str(netmaps)
704 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
705 if netmap_id and len(netmaps)==1:
706 data={'netmap' : netmaps[0]}
707 elif netmap_id and len(netmaps)==0:
708 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
709 return
tierno7edb6752016-03-21 17:37:52 +0100710 else:
tiernof97fd272016-07-11 14:32:37 +0200711 data={'netmaps' : netmaps}
712 return format_out(data)
713 except (nfvo.NfvoException, db_base_Exception) as e:
714 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
715 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000716 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000717 logger.error("Unexpected exception: ", exc_info=True)
718 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
719
tierno7edb6752016-03-21 17:37:52 +0100720
721@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
722@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
723def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
724 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200725 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100726 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200727 try:
728 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
729 where_= {"datacenter_id":datacenter_dict['uuid']}
730 if netmap_id:
731 if utils.check_valid_uuid(netmap_id):
732 where_["uuid"] = netmap_id
733 else:
734 where_["name"] = netmap_id
735 #change_keys_http2db(content, http2db_tenant, reverse=True)
736 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
737 if deleted == 0 and netmap_id :
738 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
739 if netmap_id:
740 return format_out({"result": "netmap %s deleted" % netmap_id})
tierno7edb6752016-03-21 17:37:52 +0100741 else:
tiernof97fd272016-07-11 14:32:37 +0200742 return format_out({"result": "%d netmap deleted" % deleted})
743 except (nfvo.NfvoException, db_base_Exception) as e:
744 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
745 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000746 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000747 logger.error("Unexpected exception: ", exc_info=True)
748 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100749
750
751@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
752def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
tiernof97fd272016-07-11 14:32:37 +0200753 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
754 try:
755 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
756 convert_datetime2str(netmaps)
757 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
758 data={'netmaps' : netmaps}
759 return format_out(data)
760 except (nfvo.NfvoException, db_base_Exception) as e:
761 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
762 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000763 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000764 logger.error("Unexpected exception: ", exc_info=True)
765 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
766
tierno7edb6752016-03-21 17:37:52 +0100767
768@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
769def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
770 '''creates a new netmap'''
tiernof97fd272016-07-11 14:32:37 +0200771 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100772 #parse input data
773 http_content,_ = format_in( netmap_new_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200774 r = utils.remove_extra_items(http_content, netmap_new_schema)
tiernoefd80c92016-09-16 14:17:46 +0200775 if r:
776 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200777 try:
778 #obtain data, check that only one exist
779 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
780 convert_datetime2str(netmaps)
781 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
782 data={'netmaps' : netmaps}
783 return format_out(data)
784 except (nfvo.NfvoException, db_base_Exception) as e:
785 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
786 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000787 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000788 logger.error("Unexpected exception: ", exc_info=True)
789 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
790
tierno7edb6752016-03-21 17:37:52 +0100791
792@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
793def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
794 '''edit a netmap'''
tiernof97fd272016-07-11 14:32:37 +0200795 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100796 #parse input data
797 http_content,_ = format_in( netmap_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200798 r = utils.remove_extra_items(http_content, netmap_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200799 if r:
800 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100801
802 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200803 try:
804 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
tierno7edb6752016-03-21 17:37:52 +0100805 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
tiernof97fd272016-07-11 14:32:37 +0200806 except (nfvo.NfvoException, db_base_Exception) as e:
807 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
808 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000809 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000810 logger.error("Unexpected exception: ", exc_info=True)
811 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100812
813
814@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
815def http_action_datacenter_id(tenant_id, datacenter_id):
816 '''perform an action over datacenter, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200817 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100818 #parse input data
819 http_content,_ = format_in( datacenter_action_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200820 r = utils.remove_extra_items(http_content, datacenter_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200821 if r:
822 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200823 try:
824 #obtain data, check that only one exist
825 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
826 if 'net-update' in http_content:
827 return http_getnetmap_datacenter_id(datacenter_id)
828 else:
829 return format_out(result)
830 except (nfvo.NfvoException, db_base_Exception) as e:
831 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
832 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000833 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000834 logger.error("Unexpected exception: ", exc_info=True)
835 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100836
837
838@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
839def http_delete_datacenter_id( datacenter_id):
840 '''delete a tenant from database, can use both uuid or name'''
841
tiernof97fd272016-07-11 14:32:37 +0200842 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
843 try:
844 data = nfvo.delete_datacenter(mydb, datacenter_id)
845 return format_out({"result":"datacenter '" + data + "' deleted"})
846 except (nfvo.NfvoException, db_base_Exception) as e:
847 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
848 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000849 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000850 logger.error("Unexpected exception: ", exc_info=True)
851 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
852
tierno7edb6752016-03-21 17:37:52 +0100853
854@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
855def http_associate_datacenters(tenant_id, datacenter_id):
856 '''associate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200857 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100858 #parse input data
859 http_content,_ = format_in( datacenter_associate_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200860 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
tiernoefd80c92016-09-16 14:17:46 +0200861 if r:
862 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200863 try:
864 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
865 http_content['datacenter'].get('vim_tenant'),
866 http_content['datacenter'].get('vim_tenant_name'),
867 http_content['datacenter'].get('vim_username'),
tierno8008c3a2016-10-13 15:34:28 +0000868 http_content['datacenter'].get('vim_password'),
869 http_content['datacenter'].get('config')
870 )
tiernof97fd272016-07-11 14:32:37 +0200871 return http_get_datacenter_id(tenant_id, id_)
872 except (nfvo.NfvoException, db_base_Exception) as e:
873 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
874 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000875 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000876 logger.error("Unexpected exception: ", exc_info=True)
877 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
878
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100879@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
880def http_associate_datacenters_edit(tenant_id, datacenter_id):
881 '''associate an existing datacenter to a this tenant. '''
882 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
883 #parse input data
884 http_content,_ = format_in( datacenter_associate_schema )
885 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
886 if r:
887 logger.debug("Remove received extra items %s", str(r))
888 try:
889 id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
890 http_content['datacenter'].get('vim_tenant'),
891 http_content['datacenter'].get('vim_tenant_name'),
892 http_content['datacenter'].get('vim_username'),
893 http_content['datacenter'].get('vim_password'),
894 http_content['datacenter'].get('config')
895 )
896 return http_get_datacenter_id(tenant_id, id_)
897 except (nfvo.NfvoException, db_base_Exception) as e:
898 logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
899 bottle.abort(e.http_code, str(e))
900 except Exception as e:
901 logger.error("Unexpected exception: ", exc_info=True)
902 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100903
904@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
905def http_deassociate_datacenters(tenant_id, datacenter_id):
906 '''deassociate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200907 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
908 try:
909 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
910 return format_out({"result": data})
911 except (nfvo.NfvoException, db_base_Exception) as e:
912 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
913 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000914 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000915 logger.error("Unexpected exception: ", exc_info=True)
916 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
917
Pablo Montes Moreno6aa0b2b2017-05-23 18:33:12 +0200918@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method='POST')
919def http_post_vim_net_sdn_attach(tenant_id, datacenter_id, network_id):
920 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
921 http_content, _ = format_in(sdn_external_port_schema)
922 try:
923 data = nfvo.vim_net_sdn_attach(mydb, tenant_id, datacenter_id, network_id, http_content)
924 return format_out(data)
925 except (nfvo.NfvoException, db_base_Exception) as e:
926 logger.error("http_post_vim_net_sdn_attach error {}: {}".format(e.http_code, str(e)))
927 bottle.abort(e.http_code, str(e))
928 except Exception as e:
929 logger.error("Unexpected exception: ", exc_info=True)
930 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
931
932@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method='DELETE')
933@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method='DELETE')
934def http_delete_vim_net_sdn_detach(tenant_id, datacenter_id, network_id, port_id=None):
935 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
936 try:
937 data = nfvo.vim_net_sdn_detach(mydb, tenant_id, datacenter_id, network_id, port_id)
938 return format_out(data)
939 except (nfvo.NfvoException, db_base_Exception) as e:
940 logger.error("http_delete_vim_net_sdn_detach error {}: {}".format(e.http_code, str(e)))
941 bottle.abort(e.http_code, str(e))
942 except Exception as e:
943 logger.error("Unexpected exception: ", exc_info=True)
944 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100945
tierno7edb6752016-03-21 17:37:52 +0100946@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
947@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
948def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
tiernof97fd272016-07-11 14:32:37 +0200949 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
950 try:
951 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100952 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200953 except (nfvo.NfvoException, db_base_Exception) as e:
954 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
955 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000956 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000957 logger.error("Unexpected exception: ", exc_info=True)
958 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
959
tierno7edb6752016-03-21 17:37:52 +0100960
961@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
962def http_del_vim_items(tenant_id, datacenter_id, item, name):
tiernof97fd272016-07-11 14:32:37 +0200963 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
964 try:
965 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100966 return format_out({"result":data})
tiernof97fd272016-07-11 14:32:37 +0200967 except (nfvo.NfvoException, db_base_Exception) as e:
968 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
969 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000970 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000971 logger.error("Unexpected exception: ", exc_info=True)
972 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
973
tierno65a9b0c2016-09-28 14:57:25 +0000974
tierno7edb6752016-03-21 17:37:52 +0100975@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
976def http_post_vim_items(tenant_id, datacenter_id, item):
tiernof97fd272016-07-11 14:32:37 +0200977 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100978 http_content,_ = format_in( object_schema )
tiernof97fd272016-07-11 14:32:37 +0200979 try:
980 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
tierno7edb6752016-03-21 17:37:52 +0100981 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200982 except (nfvo.NfvoException, db_base_Exception) as e:
983 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
984 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000985 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000986 logger.error("Unexpected exception: ", exc_info=True)
987 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
988
tierno7edb6752016-03-21 17:37:52 +0100989
990@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
991def http_get_vnfs(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200992 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
993 try:
994 if tenant_id != 'any':
995 #check valid tenant_id
996 nfvo.check_tenant(mydb, tenant_id)
997 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
998 ('uuid','name','description','public', "tenant_id", "created_at") )
999 where_or = {}
1000 if tenant_id != "any":
1001 where_or["tenant_id"] = tenant_id
1002 where_or["public"] = True
1003 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 +01001004 #change_keys_http2db(content, http2db_vnf, reverse=True)
tiernof97fd272016-07-11 14:32:37 +02001005 utils.convert_str2boolean(vnfs, ('public',))
1006 convert_datetime2str(vnfs)
1007 data={'vnfs' : vnfs}
tierno7edb6752016-03-21 17:37:52 +01001008 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001009 except (nfvo.NfvoException, db_base_Exception) as e:
1010 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
1011 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001012 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001013 logger.error("Unexpected exception: ", exc_info=True)
1014 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1015
tierno7edb6752016-03-21 17:37:52 +01001016
1017@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
1018def http_get_vnf_id(tenant_id,vnf_id):
1019 '''get vnf details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001020 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1021 try:
1022 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
1023 utils.convert_str2boolean(vnf, ('public',))
1024 convert_datetime2str(vnf)
1025 return format_out(vnf)
1026 except (nfvo.NfvoException, db_base_Exception) as e:
1027 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
1028 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001029 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001030 logger.error("Unexpected exception: ", exc_info=True)
1031 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1032
tierno7edb6752016-03-21 17:37:52 +01001033
1034@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
1035def http_post_vnfs(tenant_id):
1036 '''insert a vnf into the catalogue. Creates the flavor and images in the VIM, and creates the VNF and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +02001037 #print "Parsing the YAML file of the VNF"
tierno7edb6752016-03-21 17:37:52 +01001038 #parse input data
tiernof97fd272016-07-11 14:32:37 +02001039 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001040 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001041 r = utils.remove_extra_items(http_content, used_schema)
tiernoefd80c92016-09-16 14:17:46 +02001042 if r:
1043 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001044 try:
tierno4319dad2016-09-05 12:11:11 +02001045 if used_schema == vnfd_schema_v01:
garciadeblas9f8456e2016-09-05 05:02:59 +02001046 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
tierno4319dad2016-09-05 12:11:11 +02001047 elif used_schema == vnfd_schema_v02:
garciadeblas9f8456e2016-09-05 05:02:59 +02001048 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
1049 else:
1050 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1051 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tiernof97fd272016-07-11 14:32:37 +02001052 return http_get_vnf_id(tenant_id, vnf_id)
1053 except (nfvo.NfvoException, db_base_Exception) as e:
1054 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1055 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001056 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001057 logger.error("Unexpected exception: ", exc_info=True)
1058 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1059
tierno7edb6752016-03-21 17:37:52 +01001060
1061@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
1062def http_delete_vnf_id(tenant_id,vnf_id):
1063 '''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 +02001064 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001065 #check valid tenant_id and deletes the vnf, including images,
tiernof97fd272016-07-11 14:32:37 +02001066 try:
1067 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
tierno7edb6752016-03-21 17:37:52 +01001068 #print json.dumps(data, indent=4)
1069 return format_out({"result":"VNF " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001070 except (nfvo.NfvoException, db_base_Exception) as e:
1071 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
1072 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001073 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001074 logger.error("Unexpected exception: ", exc_info=True)
1075 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1076
tierno7edb6752016-03-21 17:37:52 +01001077
1078#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1079#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1080@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
1081def http_get_hosts(tenant_id, datacenter):
1082 '''get the tidvim host hopology from the vim.'''
tiernof97fd272016-07-11 14:32:37 +02001083 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1084 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1085 try:
1086 if datacenter == 'treeview':
1087 data = nfvo.get_hosts(mydb, tenant_id)
1088 else:
1089 #openmano-gui is using a hardcoded value for the datacenter
1090 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
1091
1092 if result < 0:
tiernoefd80c92016-09-16 14:17:46 +02001093 #print "http_get_hosts error %d %s" % (-result, data)
tiernof97fd272016-07-11 14:32:37 +02001094 bottle.abort(-result, data)
1095 else:
1096 convert_datetime2str(data)
tiernoefd80c92016-09-16 14:17:46 +02001097 #print json.dumps(data, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001098 return format_out(data)
1099 except (nfvo.NfvoException, db_base_Exception) as e:
garciadeblas9f8456e2016-09-05 05:02:59 +02001100 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
tiernof97fd272016-07-11 14:32:37 +02001101 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001102 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001103 logger.error("Unexpected exception: ", exc_info=True)
1104 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001105
1106
1107@bottle.route(url_base + '/<path:path>', method='OPTIONS')
1108def http_options_deploy(path):
1109 '''For some reason GUI web ask for OPTIONS that must be responded'''
1110 #TODO: check correct path, and correct headers request
tiernof97fd272016-07-11 14:32:37 +02001111 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001112 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1113 bottle.response.set_header('Accept','application/yaml,application/json')
1114 bottle.response.set_header('Content-Type','application/yaml,application/json')
1115 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
1116 bottle.response.set_header('Access-Control-Allow-Origin','*')
1117 return
1118
1119@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
1120def http_post_deploy(tenant_id):
1121 '''post topology deploy.'''
tiernof97fd272016-07-11 14:32:37 +02001122 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001123
tierno66aa0372016-07-06 17:31:12 +02001124 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001125 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001126 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001127 #print "http_post_deploy input: ", http_content
tierno7edb6752016-03-21 17:37:52 +01001128
tiernof97fd272016-07-11 14:32:37 +02001129 try:
1130 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1131 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
1132 #print json.dumps(data, indent=4)
1133 return format_out(instance)
1134 except (nfvo.NfvoException, db_base_Exception) as e:
1135 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
1136 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001137 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001138 logger.error("Unexpected exception: ", exc_info=True)
1139 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1140
tierno7edb6752016-03-21 17:37:52 +01001141
1142@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
1143def http_post_verify(tenant_id):
1144 #TODO:
1145# '''post topology verify'''
1146# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
tiernof97fd272016-07-11 14:32:37 +02001147 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001148 return
1149
1150#
1151# SCENARIOS
1152#
1153
1154@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
1155def http_post_scenarios(tenant_id):
1156 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +02001157 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001158 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 +02001159 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001160 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001161 #print "http_post_scenarios input: ", http_content
1162 try:
tierno4319dad2016-09-05 12:11:11 +02001163 if used_schema == nsd_schema_v01:
tiernof97fd272016-07-11 14:32:37 +02001164 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +02001165 elif used_schema == nsd_schema_v02:
tierno5bb59dc2017-02-13 14:53:54 +01001166 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
tierno4319dad2016-09-05 12:11:11 +02001167 elif used_schema == nsd_schema_v03:
tierno5bb59dc2017-02-13 14:53:54 +01001168 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
garciadeblas9f8456e2016-09-05 05:02:59 +02001169 else:
1170 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1171 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tierno7edb6752016-03-21 17:37:52 +01001172 #print json.dumps(data, indent=4)
1173 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001174 return http_get_scenario_id(tenant_id, scenario_id)
1175 except (nfvo.NfvoException, db_base_Exception) as e:
1176 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
1177 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001178 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001179 logger.error("Unexpected exception: ", exc_info=True)
1180 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1181
tierno7edb6752016-03-21 17:37:52 +01001182
1183@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
1184def http_post_scenario_action(tenant_id, scenario_id):
1185 '''take an action over a scenario'''
tiernof97fd272016-07-11 14:32:37 +02001186 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001187 # parse input data
1188 http_content, _ = format_in(scenario_action_schema)
1189 r = utils.remove_extra_items(http_content, scenario_action_schema)
1190 if r:
1191 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001192 try:
tierno551e5322017-01-19 16:16:26 +01001193 # check valid tenant_id
1194 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001195 if "start" in http_content:
1196 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1197 http_content['start'].get('description',http_content['start']['instance_name']),
1198 http_content['start'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001199 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001200 elif "deploy" in http_content: #Equivalent to start
1201 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1202 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1203 http_content['deploy'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001204 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001205 elif "reserve" in http_content: #Reserve resources
1206 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1207 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1208 http_content['reserve'].get('datacenter'), startvms=False )
tierno7edb6752016-03-21 17:37:52 +01001209 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001210 elif "verify" in http_content: #Equivalent to start and then delete
1211 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1212 http_content['verify'].get('description',http_content['verify']['instance_name']),
1213 http_content['verify'].get('datacenter'), startvms=False )
1214 instance_id = data['uuid']
1215 nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001216 return format_out({"result":"Verify OK"})
tiernof97fd272016-07-11 14:32:37 +02001217 except (nfvo.NfvoException, db_base_Exception) as e:
1218 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1219 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001220 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001221 logger.error("Unexpected exception: ", exc_info=True)
1222 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1223
tierno7edb6752016-03-21 17:37:52 +01001224
1225@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1226def http_get_scenarios(tenant_id):
1227 '''get scenarios list'''
tiernof97fd272016-07-11 14:32:37 +02001228 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1229 try:
1230 #check valid tenant_id
1231 if tenant_id != "any":
1232 nfvo.check_tenant(mydb, tenant_id)
1233 #obtain data
1234 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
1235 where_or={}
1236 if tenant_id != "any":
1237 where_or["tenant_id"] = tenant_id
1238 where_or["public"] = True
1239 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1240 convert_datetime2str(scenarios)
1241 utils.convert_str2boolean(scenarios, ('public',) )
1242 data={'scenarios':scenarios}
tierno7edb6752016-03-21 17:37:52 +01001243 #print json.dumps(scenarios, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001244 return format_out(data)
1245 except (nfvo.NfvoException, db_base_Exception) as e:
1246 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1247 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001248 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001249 logger.error("Unexpected exception: ", exc_info=True)
1250 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1251
tierno7edb6752016-03-21 17:37:52 +01001252
1253@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1254def http_get_scenario_id(tenant_id, scenario_id):
1255 '''get scenario details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001256 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1257 try:
1258 #check valid tenant_id
1259 if tenant_id != "any":
1260 nfvo.check_tenant(mydb, tenant_id)
1261 #obtain data
1262 scenario = mydb.get_scenario(scenario_id, tenant_id)
1263 convert_datetime2str(scenario)
1264 data={'scenario' : scenario}
tierno7edb6752016-03-21 17:37:52 +01001265 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001266 except (nfvo.NfvoException, db_base_Exception) as e:
1267 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1268 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001269 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001270 logger.error("Unexpected exception: ", exc_info=True)
1271 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1272
tierno7edb6752016-03-21 17:37:52 +01001273
1274@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1275def http_delete_scenario_id(tenant_id, scenario_id):
1276 '''delete a scenario from database, can use both uuid or name'''
tierno664691a2017-01-31 12:43:46 +01001277 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tiernof97fd272016-07-11 14:32:37 +02001278 try:
1279 #check valid tenant_id
1280 if tenant_id != "any":
tierno664691a2017-01-31 12:43:46 +01001281 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001282 #obtain data
1283 data = mydb.delete_scenario(scenario_id, tenant_id)
tierno7edb6752016-03-21 17:37:52 +01001284 #print json.dumps(data, indent=4)
1285 return format_out({"result":"scenario " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001286 except (nfvo.NfvoException, db_base_Exception) as e:
1287 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1288 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001289 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001290 logger.error("Unexpected exception: ", exc_info=True)
1291 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001292
1293
1294@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1295def http_put_scenario_id(tenant_id, scenario_id):
1296 '''edit an existing scenario id'''
tiernof97fd272016-07-11 14:32:37 +02001297 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001298 http_content,_ = format_in( scenario_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +02001299 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
tierno7edb6752016-03-21 17:37:52 +01001300 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001301 #print "http_put_scenario_id input: ", http_content
1302 try:
1303 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001304 #print json.dumps(data, indent=4)
1305 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001306 return http_get_scenario_id(tenant_id, scenario_id)
1307 except (nfvo.NfvoException, db_base_Exception) as e:
1308 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1309 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001310 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001311 logger.error("Unexpected exception: ", exc_info=True)
1312 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001313
1314@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1315def http_post_instances(tenant_id):
garciadeblasedca7b32016-09-29 14:01:52 +00001316 '''create an instance-scenario'''
tiernof97fd272016-07-11 14:32:37 +02001317 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001318 # parse input data
1319 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1320 r = utils.remove_extra_items(http_content, used_schema)
1321 if r is not None:
1322 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001323 try:
1324 #check valid tenant_id
1325 if tenant_id != "any":
1326 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001327 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
tierno7edb6752016-03-21 17:37:52 +01001328 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001329 except (nfvo.NfvoException, db_base_Exception) as e:
1330 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1331 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001332 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001333 logger.error("Unexpected exception: ", exc_info=True)
1334 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001335
1336#
1337# INSTANCES
1338#
1339@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1340def http_get_instances(tenant_id):
1341 '''get instance list'''
tiernof97fd272016-07-11 14:32:37 +02001342 try:
1343 #check valid tenant_id
1344 if tenant_id != "any":
1345 nfvo.check_tenant(mydb, tenant_id)
1346 #obtain data
1347 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1348 if tenant_id != "any":
1349 w['tenant_id'] = tenant_id
1350 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1351 convert_datetime2str(instances)
1352 utils.convert_str2boolean(instances, ('public',) )
1353 data={'instances':instances}
1354 return format_out(data)
1355 except (nfvo.NfvoException, db_base_Exception) as e:
1356 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1357 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001358 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001359 logger.error("Unexpected exception: ", exc_info=True)
1360 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1361
tierno7edb6752016-03-21 17:37:52 +01001362
1363@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1364def http_get_instance_id(tenant_id, instance_id):
1365 '''get instances details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001366 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1367 try:
1368 #check valid tenant_id
1369 if tenant_id != "any":
1370 nfvo.check_tenant(mydb, tenant_id)
1371 if tenant_id == "any":
1372 tenant_id = None
1373 #obtain data (first time is only to check that the instance exists)
1374 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1375 try:
1376 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1377 except (nfvo.NfvoException, db_base_Exception) as e:
1378 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
1379 #obtain data with results upated
1380 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1381 convert_datetime2str(instance)
1382 #print json.dumps(instance, indent=4)
1383 return format_out(instance)
1384 except (nfvo.NfvoException, db_base_Exception) as e:
1385 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1386 bottle.abort(e.http_code, str(e))
tierno8e995ce2016-09-22 08:13:00 +00001387 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001388 logger.error("Unexpected exception: ", exc_info=True)
1389 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1390
tierno7edb6752016-03-21 17:37:52 +01001391
1392@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1393def http_delete_instance_id(tenant_id, instance_id):
1394 '''delete instance from VIM and from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001395 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1396 try:
1397 #check valid tenant_id
1398 if tenant_id != "any":
1399 nfvo.check_tenant(mydb, tenant_id)
1400 if tenant_id == "any":
1401 tenant_id = None
1402 #obtain data
1403 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001404 return format_out({"result":message})
tiernof97fd272016-07-11 14:32:37 +02001405 except (nfvo.NfvoException, db_base_Exception) as e:
1406 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1407 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001408 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001409 logger.error("Unexpected exception: ", exc_info=True)
1410 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1411
tierno7edb6752016-03-21 17:37:52 +01001412
1413@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1414def http_post_instance_scenario_action(tenant_id, instance_id):
1415 '''take an action over a scenario instance'''
tiernof97fd272016-07-11 14:32:37 +02001416 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001417 # parse input data
1418 http_content, _ = format_in(instance_scenario_action_schema)
1419 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1420 if r:
1421 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001422 try:
1423 #check valid tenant_id
1424 if tenant_id != "any":
1425 nfvo.check_tenant(mydb, tenant_id)
1426
tiernof97fd272016-07-11 14:32:37 +02001427 #print "http_post_instance_scenario_action input: ", http_content
1428 #obtain data
1429 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1430 instance_id = instance["uuid"]
1431
1432 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001433 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001434 except (nfvo.NfvoException, db_base_Exception) as e:
1435 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1436 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001437 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001438 logger.error("Unexpected exception: ", exc_info=True)
1439 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001440
1441
1442@bottle.error(400)
1443@bottle.error(401)
1444@bottle.error(404)
1445@bottle.error(403)
1446@bottle.error(405)
1447@bottle.error(406)
1448@bottle.error(409)
1449@bottle.error(503)
1450@bottle.error(500)
1451def error400(error):
1452 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1453 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1454 return format_out(e)
1455