blob: 4841a98bcc0c04de42f309203897fc65eaa4566a [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, \
46 sdn_port_mapping_schema
47
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
308#
309# VNFs
310#
311
312@bottle.route(url_base + '/tenants', method='GET')
313def http_get_tenants():
tiernof97fd272016-07-11 14:32:37 +0200314 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100315 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
316 ('uuid','name','description','created_at') )
tiernof97fd272016-07-11 14:32:37 +0200317 try:
318 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100319 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200320 convert_datetime2str(tenants)
321 data={'tenants' : tenants}
tierno7edb6752016-03-21 17:37:52 +0100322 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200323 except db_base_Exception as e:
324 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
325 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000326 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000327 logger.error("Unexpected exception: ", exc_info=True)
328 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000329
tierno7edb6752016-03-21 17:37:52 +0100330
331@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
332def http_get_tenant_id(tenant_id):
333 '''get tenant details, can use both uuid or name'''
334 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200335 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
336 try:
337 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id, "tenant")
338 #change_keys_http2db(content, http2db_tenant, reverse=True)
339 convert_datetime2str(tenant)
340 data={'tenant' : tenant}
341 return format_out(data)
342 except db_base_Exception as e:
343 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
344 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000345 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000346 logger.error("Unexpected exception: ", exc_info=True)
347 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000348
tierno7edb6752016-03-21 17:37:52 +0100349
350@bottle.route(url_base + '/tenants', method='POST')
351def http_post_tenants():
352 '''insert a tenant into the catalogue. '''
353 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200354 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100355 http_content,_ = format_in( tenant_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200356 r = utils.remove_extra_items(http_content, tenant_schema)
tiernoefd80c92016-09-16 14:17:46 +0200357 if r:
358 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200359 try:
360 data = nfvo.new_tenant(mydb, http_content['tenant'])
tierno7edb6752016-03-21 17:37:52 +0100361 return http_get_tenant_id(data)
tiernof97fd272016-07-11 14:32:37 +0200362 except (nfvo.NfvoException, db_base_Exception) as e:
363 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
364 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000365 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000366 logger.error("Unexpected exception: ", exc_info=True)
367 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000368
tierno7edb6752016-03-21 17:37:52 +0100369
370@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
371def http_edit_tenant_id(tenant_id):
372 '''edit tenant details, can use both uuid or name'''
373 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200374 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100375 http_content,_ = format_in( tenant_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200376 r = utils.remove_extra_items(http_content, tenant_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200377 if r:
378 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100379
380 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200381 try:
382 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
383 #edit data
384 tenant_id = tenant['uuid']
385 where={'uuid': tenant['uuid']}
386 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
387 return http_get_tenant_id(tenant_id)
388 except db_base_Exception as e:
389 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
390 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000391 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000392 logger.error("Unexpected exception: ", exc_info=True)
393 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000394
tierno7edb6752016-03-21 17:37:52 +0100395
396@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
397def http_delete_tenant_id(tenant_id):
398 '''delete a tenant from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200399 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
400 try:
401 data = nfvo.delete_tenant(mydb, tenant_id)
tierno7edb6752016-03-21 17:37:52 +0100402 return format_out({"result":"tenant " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200403 except db_base_Exception as e:
404 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
405 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000406 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000407 logger.error("Unexpected exception: ", exc_info=True)
408 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000409
tierno7edb6752016-03-21 17:37:52 +0100410
411@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
412def http_get_datacenters(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200413 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
414 try:
415 if tenant_id != 'any':
416 #check valid tenant_id
417 nfvo.check_tenant(mydb, tenant_id)
418 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
419 ('uuid','name','vim_url','type','created_at') )
420 if tenant_id != 'any':
421 where_['nfvo_tenant_id'] = tenant_id
422 if 'created_at' in select_:
423 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
424 if 'created_at' in where_:
425 where_['d.created_at'] = where_.pop('created_at')
426 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
427 SELECT=select_,WHERE=where_,LIMIT=limit_)
428 else:
429 datacenters = mydb.get_rows(FROM='datacenters',
430 SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100431 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200432 convert_datetime2str(datacenters)
433 data={'datacenters' : datacenters}
tierno7edb6752016-03-21 17:37:52 +0100434 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200435 except (nfvo.NfvoException, db_base_Exception) as e:
436 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
437 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000438 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000439 logger.error("Unexpected exception: ", exc_info=True)
440 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000441
tierno7edb6752016-03-21 17:37:52 +0100442
443@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
444def http_get_datacenter_id(tenant_id, datacenter_id):
445 '''get datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200446 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
447 try:
448 if tenant_id != 'any':
449 #check valid tenant_id
450 nfvo.check_tenant(mydb, tenant_id)
451 #obtain data
452 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
453 where_={}
454 where_[what] = datacenter_id
tierno8008c3a2016-10-13 15:34:28 +0000455 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 +0200456 if tenant_id != 'any':
457 select_.append("datacenter_tenant_id")
458 where_['td.nfvo_tenant_id']= tenant_id
459 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
460 else:
461 from_='datacenters as d'
462 datacenters = mydb.get_rows(
463 SELECT=select_,
464 FROM=from_,
465 WHERE=where_)
466
467 if len(datacenters)==0:
468 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
469 elif len(datacenters)>1:
470 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
471 datacenter = datacenters[0]
472 if tenant_id != 'any':
473 #get vim tenant info
474 vim_tenants = mydb.get_rows(
tierno8008c3a2016-10-13 15:34:28 +0000475 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
tiernof97fd272016-07-11 14:32:37 +0200476 FROM="datacenter_tenants",
477 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
478 ORDER_BY=("created", ) )
479 del datacenter["datacenter_tenant_id"]
480 datacenter["vim_tenants"] = vim_tenants
tierno8008c3a2016-10-13 15:34:28 +0000481 for vim_tenant in vim_tenants:
482 if vim_tenant["passwd"]:
483 vim_tenant["passwd"] = "******"
484 if vim_tenant['config'] != None:
485 try:
486 config_dict = yaml.load(vim_tenant['config'])
487 vim_tenant['config'] = config_dict
venkatamahesh6ecca182017-01-27 23:04:40 +0530488 except Exception as e:
tierno8008c3a2016-10-13 15:34:28 +0000489 logger.error("Exception '%s' while trying to load config information", str(e))
490
tiernof97fd272016-07-11 14:32:37 +0200491 if datacenter['config'] != None:
492 try:
493 config_dict = yaml.load(datacenter['config'])
494 datacenter['config'] = config_dict
venkatamahesh6ecca182017-01-27 23:04:40 +0530495 except Exception as e:
tiernoefd80c92016-09-16 14:17:46 +0200496 logger.error("Exception '%s' while trying to load config information", str(e))
tiernof97fd272016-07-11 14:32:37 +0200497 #change_keys_http2db(content, http2db_datacenter, reverse=True)
498 convert_datetime2str(datacenter)
499 data={'datacenter' : datacenter}
500 return format_out(data)
501 except (nfvo.NfvoException, db_base_Exception) as e:
502 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
503 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000504 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000505 logger.error("Unexpected exception: ", exc_info=True)
506 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
507
tierno7edb6752016-03-21 17:37:52 +0100508
509@bottle.route(url_base + '/datacenters', method='POST')
510def http_post_datacenters():
garciadeblasc27b0462017-03-22 18:57:47 +0100511 '''insert a datacenter into the catalogue. '''
tierno7edb6752016-03-21 17:37:52 +0100512 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200513 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100514 http_content,_ = format_in( datacenter_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200515 r = utils.remove_extra_items(http_content, datacenter_schema)
tiernoefd80c92016-09-16 14:17:46 +0200516 if r:
517 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200518 try:
519 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100520 return http_get_datacenter_id('any', data)
tiernof97fd272016-07-11 14:32:37 +0200521 except (nfvo.NfvoException, db_base_Exception) as e:
522 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
523 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000524 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000525 logger.error("Unexpected exception: ", exc_info=True)
526 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
527
tierno7edb6752016-03-21 17:37:52 +0100528
529@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
530def http_edit_datacenter_id(datacenter_id_name):
531 '''edit datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200532 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100533 #parse input data
534 http_content,_ = format_in( datacenter_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200535 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200536 if r:
537 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100538
tiernof97fd272016-07-11 14:32:37 +0200539 try:
540 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100541 return http_get_datacenter_id('any', datacenter_id)
tiernof97fd272016-07-11 14:32:37 +0200542 except (nfvo.NfvoException, db_base_Exception) as e:
543 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
544 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000545 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000546 logger.error("Unexpected exception: ", exc_info=True)
547 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
548
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100549@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
550def http_post_sdn_controller(tenant_id):
551 '''insert a sdn controller into the catalogue. '''
552 #parse input data
553 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
554 http_content,_ = format_in( sdn_controller_schema )
555 try:
556 logger.debug("tenant_id: "+tenant_id)
557 #logger.debug("content: {}".format(http_content['sdn_controller']))
558
559 data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
560 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
561 except (nfvo.NfvoException, db_base_Exception) as e:
562 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
563 bottle.abort(e.http_code, str(e))
564 except Exception as e:
565 logger.error("Unexpected exception: ", exc_info=True)
566 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
567
568@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='PUT')
569def http_put_sdn_controller_update(tenant_id, controller_id):
570 '''Update sdn controller'''
571 #parse input data
572 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
573 http_content,_ = format_in( sdn_controller_edit_schema )
574# r = utils.remove_extra_items(http_content, datacenter_schema)
575# if r:
576# logger.debug("Remove received extra items %s", str(r))
577 try:
578 #logger.debug("tenant_id: "+tenant_id)
579 logger.debug("content: {}".format(http_content['sdn_controller']))
580
581 data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
582 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
583
584 except (nfvo.NfvoException, db_base_Exception) as e:
585 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
586 bottle.abort(e.http_code, str(e))
587 except Exception as e:
588 logger.error("Unexpected exception: ", exc_info=True)
589 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
590
591@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
592def http_get_sdn_controller(tenant_id):
593 '''get sdn controllers list, can use both uuid or name'''
594 try:
595 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
596
597 data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
598 return format_out(data)
599 except (nfvo.NfvoException, db_base_Exception) as e:
600 logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
601 bottle.abort(e.http_code, str(e))
602 except Exception as e:
603 logger.error("Unexpected exception: ", exc_info=True)
604 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
605
606@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
607def http_get_sdn_controller_id(tenant_id, controller_id):
608 '''get sdn controller details, can use both uuid or name'''
609 try:
610 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
611 data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
612 return format_out({"sdn_controllers": data})
613 except (nfvo.NfvoException, db_base_Exception) as e:
614 logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
615 bottle.abort(e.http_code, str(e))
616 except Exception as e:
617 logger.error("Unexpected exception: ", exc_info=True)
618 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
619
620@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
621def http_delete_sdn_controller_id(tenant_id, controller_id):
622 '''delete sdn controller, can use both uuid or name'''
623 try:
624 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
625 data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
626 return format_out(data)
627 except (nfvo.NfvoException, db_base_Exception) as e:
628 logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
629 bottle.abort(e.http_code, str(e))
630 except Exception as e:
631 logger.error("Unexpected exception: ", exc_info=True)
632 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
633
634@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
635def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
636 '''Set the sdn port mapping for a datacenter. '''
637 #parse input data
638 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
639 http_content, _ = format_in(sdn_port_mapping_schema)
640# r = utils.remove_extra_items(http_content, datacenter_schema)
641# if r:
642# logger.debug("Remove received extra items %s", str(r))
643 try:
644 data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
645 return format_out({"sdn_port_mapping": data})
646 except (nfvo.NfvoException, db_base_Exception) as e:
647 logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
648 bottle.abort(e.http_code, str(e))
649 except Exception as e:
650 logger.error("Unexpected exception: ", exc_info=True)
651 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
652
653@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
654def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
655 '''get datacenter sdn mapping details, can use both uuid or name'''
656 try:
657 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
658
659 data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
660 return format_out({"sdn_port_mapping": data})
661 except (nfvo.NfvoException, db_base_Exception) as e:
662 logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
663 bottle.abort(e.http_code, str(e))
664 except Exception as e:
665 logger.error("Unexpected exception: ", exc_info=True)
666 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
667
668@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
669def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
670 '''clean datacenter sdn mapping, can use both uuid or name'''
671 try:
672 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
673 data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
674 return format_out({"result": data})
675 except (nfvo.NfvoException, db_base_Exception) as e:
676 logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
677 bottle.abort(e.http_code, str(e))
678 except Exception as e:
679 logger.error("Unexpected exception: ", exc_info=True)
680 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100681
682@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
683@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
684@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
685def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
686 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200687 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100688 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200689 try:
690 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
691 where_= {"datacenter_id":datacenter_dict['uuid']}
692 if netmap_id:
693 if utils.check_valid_uuid(netmap_id):
694 where_["uuid"] = netmap_id
695 else:
696 where_["name"] = netmap_id
697 netmaps =mydb.get_rows(FROM='datacenter_nets',
698 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
699 WHERE=where_ )
700 convert_datetime2str(netmaps)
701 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
702 if netmap_id and len(netmaps)==1:
703 data={'netmap' : netmaps[0]}
704 elif netmap_id and len(netmaps)==0:
705 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
706 return
tierno7edb6752016-03-21 17:37:52 +0100707 else:
tiernof97fd272016-07-11 14:32:37 +0200708 data={'netmaps' : netmaps}
709 return format_out(data)
710 except (nfvo.NfvoException, db_base_Exception) as e:
711 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
712 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000713 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000714 logger.error("Unexpected exception: ", exc_info=True)
715 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
716
tierno7edb6752016-03-21 17:37:52 +0100717
718@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
719@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
720def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
721 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200722 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100723 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200724 try:
725 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
726 where_= {"datacenter_id":datacenter_dict['uuid']}
727 if netmap_id:
728 if utils.check_valid_uuid(netmap_id):
729 where_["uuid"] = netmap_id
730 else:
731 where_["name"] = netmap_id
732 #change_keys_http2db(content, http2db_tenant, reverse=True)
733 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
734 if deleted == 0 and netmap_id :
735 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
736 if netmap_id:
737 return format_out({"result": "netmap %s deleted" % netmap_id})
tierno7edb6752016-03-21 17:37:52 +0100738 else:
tiernof97fd272016-07-11 14:32:37 +0200739 return format_out({"result": "%d netmap deleted" % deleted})
740 except (nfvo.NfvoException, db_base_Exception) as e:
741 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
742 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000743 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000744 logger.error("Unexpected exception: ", exc_info=True)
745 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100746
747
748@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
749def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
tiernof97fd272016-07-11 14:32:37 +0200750 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
751 try:
752 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
753 convert_datetime2str(netmaps)
754 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
755 data={'netmaps' : netmaps}
756 return format_out(data)
757 except (nfvo.NfvoException, db_base_Exception) as e:
758 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
759 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000760 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000761 logger.error("Unexpected exception: ", exc_info=True)
762 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
763
tierno7edb6752016-03-21 17:37:52 +0100764
765@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
766def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
767 '''creates a new netmap'''
tiernof97fd272016-07-11 14:32:37 +0200768 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100769 #parse input data
770 http_content,_ = format_in( netmap_new_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200771 r = utils.remove_extra_items(http_content, netmap_new_schema)
tiernoefd80c92016-09-16 14:17:46 +0200772 if r:
773 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200774 try:
775 #obtain data, check that only one exist
776 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
777 convert_datetime2str(netmaps)
778 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
779 data={'netmaps' : netmaps}
780 return format_out(data)
781 except (nfvo.NfvoException, db_base_Exception) as e:
782 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
783 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000784 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000785 logger.error("Unexpected exception: ", exc_info=True)
786 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
787
tierno7edb6752016-03-21 17:37:52 +0100788
789@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
790def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
791 '''edit a netmap'''
tiernof97fd272016-07-11 14:32:37 +0200792 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100793 #parse input data
794 http_content,_ = format_in( netmap_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200795 r = utils.remove_extra_items(http_content, netmap_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200796 if r:
797 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100798
799 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200800 try:
801 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
tierno7edb6752016-03-21 17:37:52 +0100802 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
tiernof97fd272016-07-11 14:32:37 +0200803 except (nfvo.NfvoException, db_base_Exception) as e:
804 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
805 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000806 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000807 logger.error("Unexpected exception: ", exc_info=True)
808 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100809
810
811@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
812def http_action_datacenter_id(tenant_id, datacenter_id):
813 '''perform an action over datacenter, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200814 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100815 #parse input data
816 http_content,_ = format_in( datacenter_action_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200817 r = utils.remove_extra_items(http_content, datacenter_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200818 if r:
819 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200820 try:
821 #obtain data, check that only one exist
822 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
823 if 'net-update' in http_content:
824 return http_getnetmap_datacenter_id(datacenter_id)
825 else:
826 return format_out(result)
827 except (nfvo.NfvoException, db_base_Exception) as e:
828 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
829 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000830 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000831 logger.error("Unexpected exception: ", exc_info=True)
832 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100833
834
835@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
836def http_delete_datacenter_id( datacenter_id):
837 '''delete a tenant from database, can use both uuid or name'''
838
tiernof97fd272016-07-11 14:32:37 +0200839 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
840 try:
841 data = nfvo.delete_datacenter(mydb, datacenter_id)
842 return format_out({"result":"datacenter '" + data + "' deleted"})
843 except (nfvo.NfvoException, db_base_Exception) as e:
844 logger.error("http_delete_datacenter_id error {}: {}".format(e.http_code, str(e)))
845 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000846 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000847 logger.error("Unexpected exception: ", exc_info=True)
848 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
849
tierno7edb6752016-03-21 17:37:52 +0100850
851@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
852def http_associate_datacenters(tenant_id, datacenter_id):
853 '''associate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200854 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100855 #parse input data
856 http_content,_ = format_in( datacenter_associate_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200857 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
tiernoefd80c92016-09-16 14:17:46 +0200858 if r:
859 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200860 try:
861 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
862 http_content['datacenter'].get('vim_tenant'),
863 http_content['datacenter'].get('vim_tenant_name'),
864 http_content['datacenter'].get('vim_username'),
tierno8008c3a2016-10-13 15:34:28 +0000865 http_content['datacenter'].get('vim_password'),
866 http_content['datacenter'].get('config')
867 )
tiernof97fd272016-07-11 14:32:37 +0200868 return http_get_datacenter_id(tenant_id, id_)
869 except (nfvo.NfvoException, db_base_Exception) as e:
870 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
871 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000872 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000873 logger.error("Unexpected exception: ", exc_info=True)
874 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
875
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100876@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
877def http_associate_datacenters_edit(tenant_id, datacenter_id):
878 '''associate an existing datacenter to a this tenant. '''
879 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
880 #parse input data
881 http_content,_ = format_in( datacenter_associate_schema )
882 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
883 if r:
884 logger.debug("Remove received extra items %s", str(r))
885 try:
886 id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
887 http_content['datacenter'].get('vim_tenant'),
888 http_content['datacenter'].get('vim_tenant_name'),
889 http_content['datacenter'].get('vim_username'),
890 http_content['datacenter'].get('vim_password'),
891 http_content['datacenter'].get('config')
892 )
893 return http_get_datacenter_id(tenant_id, id_)
894 except (nfvo.NfvoException, db_base_Exception) as e:
895 logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
896 bottle.abort(e.http_code, str(e))
897 except Exception as e:
898 logger.error("Unexpected exception: ", exc_info=True)
899 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100900
901@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
902def http_deassociate_datacenters(tenant_id, datacenter_id):
903 '''deassociate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200904 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
905 try:
906 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
907 return format_out({"result": data})
908 except (nfvo.NfvoException, db_base_Exception) as e:
909 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
910 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000911 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000912 logger.error("Unexpected exception: ", exc_info=True)
913 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
914
tierno7edb6752016-03-21 17:37:52 +0100915
tierno7edb6752016-03-21 17:37:52 +0100916@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
917@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
918def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
tiernof97fd272016-07-11 14:32:37 +0200919 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
920 try:
921 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100922 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200923 except (nfvo.NfvoException, db_base_Exception) as e:
924 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
925 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000926 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000927 logger.error("Unexpected exception: ", exc_info=True)
928 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
929
tierno7edb6752016-03-21 17:37:52 +0100930
931@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
932def http_del_vim_items(tenant_id, datacenter_id, item, name):
tiernof97fd272016-07-11 14:32:37 +0200933 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
934 try:
935 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100936 return format_out({"result":data})
tiernof97fd272016-07-11 14:32:37 +0200937 except (nfvo.NfvoException, db_base_Exception) as e:
938 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
939 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000940 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000941 logger.error("Unexpected exception: ", exc_info=True)
942 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
943
tierno65a9b0c2016-09-28 14:57:25 +0000944
tierno7edb6752016-03-21 17:37:52 +0100945@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
946def http_post_vim_items(tenant_id, datacenter_id, item):
tiernof97fd272016-07-11 14:32:37 +0200947 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100948 http_content,_ = format_in( object_schema )
tiernof97fd272016-07-11 14:32:37 +0200949 try:
950 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
tierno7edb6752016-03-21 17:37:52 +0100951 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200952 except (nfvo.NfvoException, db_base_Exception) as e:
953 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
954 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000955 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000956 logger.error("Unexpected exception: ", exc_info=True)
957 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
958
tierno7edb6752016-03-21 17:37:52 +0100959
960@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
961def http_get_vnfs(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200962 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
963 try:
964 if tenant_id != 'any':
965 #check valid tenant_id
966 nfvo.check_tenant(mydb, tenant_id)
967 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
968 ('uuid','name','description','public', "tenant_id", "created_at") )
969 where_or = {}
970 if tenant_id != "any":
971 where_or["tenant_id"] = tenant_id
972 where_or["public"] = True
973 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 +0100974 #change_keys_http2db(content, http2db_vnf, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200975 utils.convert_str2boolean(vnfs, ('public',))
976 convert_datetime2str(vnfs)
977 data={'vnfs' : vnfs}
tierno7edb6752016-03-21 17:37:52 +0100978 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200979 except (nfvo.NfvoException, db_base_Exception) as e:
980 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
981 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000982 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000983 logger.error("Unexpected exception: ", exc_info=True)
984 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
985
tierno7edb6752016-03-21 17:37:52 +0100986
987@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
988def http_get_vnf_id(tenant_id,vnf_id):
989 '''get vnf details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200990 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
991 try:
992 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
993 utils.convert_str2boolean(vnf, ('public',))
994 convert_datetime2str(vnf)
995 return format_out(vnf)
996 except (nfvo.NfvoException, db_base_Exception) as e:
997 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
998 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000999 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001000 logger.error("Unexpected exception: ", exc_info=True)
1001 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1002
tierno7edb6752016-03-21 17:37:52 +01001003
1004@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
1005def http_post_vnfs(tenant_id):
1006 '''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 +02001007 #print "Parsing the YAML file of the VNF"
tierno7edb6752016-03-21 17:37:52 +01001008 #parse input data
tiernof97fd272016-07-11 14:32:37 +02001009 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001010 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001011 r = utils.remove_extra_items(http_content, used_schema)
tiernoefd80c92016-09-16 14:17:46 +02001012 if r:
1013 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001014 try:
tierno4319dad2016-09-05 12:11:11 +02001015 if used_schema == vnfd_schema_v01:
garciadeblas9f8456e2016-09-05 05:02:59 +02001016 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
tierno4319dad2016-09-05 12:11:11 +02001017 elif used_schema == vnfd_schema_v02:
garciadeblas9f8456e2016-09-05 05:02:59 +02001018 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
1019 else:
1020 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1021 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tiernof97fd272016-07-11 14:32:37 +02001022 return http_get_vnf_id(tenant_id, vnf_id)
1023 except (nfvo.NfvoException, db_base_Exception) as e:
1024 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1025 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001026 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001027 logger.error("Unexpected exception: ", exc_info=True)
1028 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1029
tierno7edb6752016-03-21 17:37:52 +01001030
1031@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
1032def http_delete_vnf_id(tenant_id,vnf_id):
1033 '''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 +02001034 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001035 #check valid tenant_id and deletes the vnf, including images,
tiernof97fd272016-07-11 14:32:37 +02001036 try:
1037 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
tierno7edb6752016-03-21 17:37:52 +01001038 #print json.dumps(data, indent=4)
1039 return format_out({"result":"VNF " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001040 except (nfvo.NfvoException, db_base_Exception) as e:
1041 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
1042 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001043 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001044 logger.error("Unexpected exception: ", exc_info=True)
1045 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1046
tierno7edb6752016-03-21 17:37:52 +01001047
1048#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1049#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1050@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
1051def http_get_hosts(tenant_id, datacenter):
1052 '''get the tidvim host hopology from the vim.'''
tiernof97fd272016-07-11 14:32:37 +02001053 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1054 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1055 try:
1056 if datacenter == 'treeview':
1057 data = nfvo.get_hosts(mydb, tenant_id)
1058 else:
1059 #openmano-gui is using a hardcoded value for the datacenter
1060 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
1061
1062 if result < 0:
tiernoefd80c92016-09-16 14:17:46 +02001063 #print "http_get_hosts error %d %s" % (-result, data)
tiernof97fd272016-07-11 14:32:37 +02001064 bottle.abort(-result, data)
1065 else:
1066 convert_datetime2str(data)
tiernoefd80c92016-09-16 14:17:46 +02001067 #print json.dumps(data, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001068 return format_out(data)
1069 except (nfvo.NfvoException, db_base_Exception) as e:
garciadeblas9f8456e2016-09-05 05:02:59 +02001070 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
tiernof97fd272016-07-11 14:32:37 +02001071 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001072 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001073 logger.error("Unexpected exception: ", exc_info=True)
1074 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001075
1076
1077@bottle.route(url_base + '/<path:path>', method='OPTIONS')
1078def http_options_deploy(path):
1079 '''For some reason GUI web ask for OPTIONS that must be responded'''
1080 #TODO: check correct path, and correct headers request
tiernof97fd272016-07-11 14:32:37 +02001081 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001082 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1083 bottle.response.set_header('Accept','application/yaml,application/json')
1084 bottle.response.set_header('Content-Type','application/yaml,application/json')
1085 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
1086 bottle.response.set_header('Access-Control-Allow-Origin','*')
1087 return
1088
1089@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
1090def http_post_deploy(tenant_id):
1091 '''post topology deploy.'''
tiernof97fd272016-07-11 14:32:37 +02001092 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001093
tierno66aa0372016-07-06 17:31:12 +02001094 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001095 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001096 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001097 #print "http_post_deploy input: ", http_content
tierno7edb6752016-03-21 17:37:52 +01001098
tiernof97fd272016-07-11 14:32:37 +02001099 try:
1100 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1101 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
1102 #print json.dumps(data, indent=4)
1103 return format_out(instance)
1104 except (nfvo.NfvoException, db_base_Exception) as e:
1105 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
1106 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001107 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001108 logger.error("Unexpected exception: ", exc_info=True)
1109 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1110
tierno7edb6752016-03-21 17:37:52 +01001111
1112@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
1113def http_post_verify(tenant_id):
1114 #TODO:
1115# '''post topology verify'''
1116# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
tiernof97fd272016-07-11 14:32:37 +02001117 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001118 return
1119
1120#
1121# SCENARIOS
1122#
1123
1124@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
1125def http_post_scenarios(tenant_id):
1126 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +02001127 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001128 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 +02001129 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001130 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001131 #print "http_post_scenarios input: ", http_content
1132 try:
tierno4319dad2016-09-05 12:11:11 +02001133 if used_schema == nsd_schema_v01:
tiernof97fd272016-07-11 14:32:37 +02001134 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +02001135 elif used_schema == nsd_schema_v02:
tierno5bb59dc2017-02-13 14:53:54 +01001136 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
tierno4319dad2016-09-05 12:11:11 +02001137 elif used_schema == nsd_schema_v03:
tierno5bb59dc2017-02-13 14:53:54 +01001138 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
garciadeblas9f8456e2016-09-05 05:02:59 +02001139 else:
1140 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1141 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tierno7edb6752016-03-21 17:37:52 +01001142 #print json.dumps(data, indent=4)
1143 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001144 return http_get_scenario_id(tenant_id, scenario_id)
1145 except (nfvo.NfvoException, db_base_Exception) as e:
1146 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
1147 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001148 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001149 logger.error("Unexpected exception: ", exc_info=True)
1150 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1151
tierno7edb6752016-03-21 17:37:52 +01001152
1153@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
1154def http_post_scenario_action(tenant_id, scenario_id):
1155 '''take an action over a scenario'''
tiernof97fd272016-07-11 14:32:37 +02001156 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001157 # parse input data
1158 http_content, _ = format_in(scenario_action_schema)
1159 r = utils.remove_extra_items(http_content, scenario_action_schema)
1160 if r:
1161 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001162 try:
tierno551e5322017-01-19 16:16:26 +01001163 # check valid tenant_id
1164 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001165 if "start" in http_content:
1166 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1167 http_content['start'].get('description',http_content['start']['instance_name']),
1168 http_content['start'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001169 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001170 elif "deploy" in http_content: #Equivalent to start
1171 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1172 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1173 http_content['deploy'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001174 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001175 elif "reserve" in http_content: #Reserve resources
1176 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1177 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1178 http_content['reserve'].get('datacenter'), startvms=False )
tierno7edb6752016-03-21 17:37:52 +01001179 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001180 elif "verify" in http_content: #Equivalent to start and then delete
1181 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1182 http_content['verify'].get('description',http_content['verify']['instance_name']),
1183 http_content['verify'].get('datacenter'), startvms=False )
1184 instance_id = data['uuid']
1185 nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001186 return format_out({"result":"Verify OK"})
tiernof97fd272016-07-11 14:32:37 +02001187 except (nfvo.NfvoException, db_base_Exception) as e:
1188 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1189 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001190 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001191 logger.error("Unexpected exception: ", exc_info=True)
1192 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1193
tierno7edb6752016-03-21 17:37:52 +01001194
1195@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1196def http_get_scenarios(tenant_id):
1197 '''get scenarios list'''
tiernof97fd272016-07-11 14:32:37 +02001198 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1199 try:
1200 #check valid tenant_id
1201 if tenant_id != "any":
1202 nfvo.check_tenant(mydb, tenant_id)
1203 #obtain data
1204 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
1205 where_or={}
1206 if tenant_id != "any":
1207 where_or["tenant_id"] = tenant_id
1208 where_or["public"] = True
1209 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1210 convert_datetime2str(scenarios)
1211 utils.convert_str2boolean(scenarios, ('public',) )
1212 data={'scenarios':scenarios}
tierno7edb6752016-03-21 17:37:52 +01001213 #print json.dumps(scenarios, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001214 return format_out(data)
1215 except (nfvo.NfvoException, db_base_Exception) as e:
1216 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1217 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001218 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001219 logger.error("Unexpected exception: ", exc_info=True)
1220 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1221
tierno7edb6752016-03-21 17:37:52 +01001222
1223@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1224def http_get_scenario_id(tenant_id, scenario_id):
1225 '''get scenario details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001226 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1227 try:
1228 #check valid tenant_id
1229 if tenant_id != "any":
1230 nfvo.check_tenant(mydb, tenant_id)
1231 #obtain data
1232 scenario = mydb.get_scenario(scenario_id, tenant_id)
1233 convert_datetime2str(scenario)
1234 data={'scenario' : scenario}
tierno7edb6752016-03-21 17:37:52 +01001235 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001236 except (nfvo.NfvoException, db_base_Exception) as e:
1237 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1238 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001239 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001240 logger.error("Unexpected exception: ", exc_info=True)
1241 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1242
tierno7edb6752016-03-21 17:37:52 +01001243
1244@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1245def http_delete_scenario_id(tenant_id, scenario_id):
1246 '''delete a scenario from database, can use both uuid or name'''
tierno664691a2017-01-31 12:43:46 +01001247 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tiernof97fd272016-07-11 14:32:37 +02001248 try:
1249 #check valid tenant_id
1250 if tenant_id != "any":
tierno664691a2017-01-31 12:43:46 +01001251 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001252 #obtain data
1253 data = mydb.delete_scenario(scenario_id, tenant_id)
tierno7edb6752016-03-21 17:37:52 +01001254 #print json.dumps(data, indent=4)
1255 return format_out({"result":"scenario " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001256 except (nfvo.NfvoException, db_base_Exception) as e:
1257 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1258 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001259 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001260 logger.error("Unexpected exception: ", exc_info=True)
1261 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001262
1263
1264@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1265def http_put_scenario_id(tenant_id, scenario_id):
1266 '''edit an existing scenario id'''
tiernof97fd272016-07-11 14:32:37 +02001267 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001268 http_content,_ = format_in( scenario_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +02001269 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
tierno7edb6752016-03-21 17:37:52 +01001270 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001271 #print "http_put_scenario_id input: ", http_content
1272 try:
1273 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001274 #print json.dumps(data, indent=4)
1275 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001276 return http_get_scenario_id(tenant_id, scenario_id)
1277 except (nfvo.NfvoException, db_base_Exception) as e:
1278 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1279 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001280 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001281 logger.error("Unexpected exception: ", exc_info=True)
1282 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001283
1284@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1285def http_post_instances(tenant_id):
garciadeblasedca7b32016-09-29 14:01:52 +00001286 '''create an instance-scenario'''
tiernof97fd272016-07-11 14:32:37 +02001287 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001288 # parse input data
1289 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1290 r = utils.remove_extra_items(http_content, used_schema)
1291 if r is not None:
1292 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001293 try:
1294 #check valid tenant_id
1295 if tenant_id != "any":
1296 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001297 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
tierno7edb6752016-03-21 17:37:52 +01001298 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001299 except (nfvo.NfvoException, db_base_Exception) as e:
1300 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1301 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001302 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001303 logger.error("Unexpected exception: ", exc_info=True)
1304 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001305
1306#
1307# INSTANCES
1308#
1309@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1310def http_get_instances(tenant_id):
1311 '''get instance list'''
tiernof97fd272016-07-11 14:32:37 +02001312 try:
1313 #check valid tenant_id
1314 if tenant_id != "any":
1315 nfvo.check_tenant(mydb, tenant_id)
1316 #obtain data
1317 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1318 if tenant_id != "any":
1319 w['tenant_id'] = tenant_id
1320 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1321 convert_datetime2str(instances)
1322 utils.convert_str2boolean(instances, ('public',) )
1323 data={'instances':instances}
1324 return format_out(data)
1325 except (nfvo.NfvoException, db_base_Exception) as e:
1326 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1327 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001328 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001329 logger.error("Unexpected exception: ", exc_info=True)
1330 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1331
tierno7edb6752016-03-21 17:37:52 +01001332
1333@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1334def http_get_instance_id(tenant_id, instance_id):
1335 '''get instances details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001336 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1337 try:
1338 #check valid tenant_id
1339 if tenant_id != "any":
1340 nfvo.check_tenant(mydb, tenant_id)
1341 if tenant_id == "any":
1342 tenant_id = None
1343 #obtain data (first time is only to check that the instance exists)
1344 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1345 try:
1346 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1347 except (nfvo.NfvoException, db_base_Exception) as e:
1348 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
1349 #obtain data with results upated
1350 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1351 convert_datetime2str(instance)
1352 #print json.dumps(instance, indent=4)
1353 return format_out(instance)
1354 except (nfvo.NfvoException, db_base_Exception) as e:
1355 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1356 bottle.abort(e.http_code, str(e))
tierno8e995ce2016-09-22 08:13:00 +00001357 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001358 logger.error("Unexpected exception: ", exc_info=True)
1359 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1360
tierno7edb6752016-03-21 17:37:52 +01001361
1362@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1363def http_delete_instance_id(tenant_id, instance_id):
1364 '''delete instance from VIM and from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001365 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1366 try:
1367 #check valid tenant_id
1368 if tenant_id != "any":
1369 nfvo.check_tenant(mydb, tenant_id)
1370 if tenant_id == "any":
1371 tenant_id = None
1372 #obtain data
1373 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001374 return format_out({"result":message})
tiernof97fd272016-07-11 14:32:37 +02001375 except (nfvo.NfvoException, db_base_Exception) as e:
1376 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1377 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001378 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001379 logger.error("Unexpected exception: ", exc_info=True)
1380 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1381
tierno7edb6752016-03-21 17:37:52 +01001382
1383@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1384def http_post_instance_scenario_action(tenant_id, instance_id):
1385 '''take an action over a scenario instance'''
tiernof97fd272016-07-11 14:32:37 +02001386 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001387 # parse input data
1388 http_content, _ = format_in(instance_scenario_action_schema)
1389 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1390 if r:
1391 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001392 try:
1393 #check valid tenant_id
1394 if tenant_id != "any":
1395 nfvo.check_tenant(mydb, tenant_id)
1396
tiernof97fd272016-07-11 14:32:37 +02001397 #print "http_post_instance_scenario_action input: ", http_content
1398 #obtain data
1399 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1400 instance_id = instance["uuid"]
1401
1402 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001403 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001404 except (nfvo.NfvoException, db_base_Exception) as e:
1405 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1406 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001407 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001408 logger.error("Unexpected exception: ", exc_info=True)
1409 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001410
1411
1412@bottle.error(400)
1413@bottle.error(401)
1414@bottle.error(404)
1415@bottle.error(403)
1416@bottle.error(405)
1417@bottle.error(406)
1418@bottle.error(409)
1419@bottle.error(503)
1420@bottle.error(500)
1421def error400(error):
1422 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1423 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1424 return format_out(e)
1425