blob: bb082d5e72ee7d6ca4df11fb53ae119cac60077a [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
gcalvinoc62cfa52017-10-05 18:21:25 +0200186def format_in(default_schema, version_fields=None, version_dict_schema=None, confidential_data=False):
tiernof1ba57e2017-09-07 12:23:19 +0200187 """
188 Parse the content of HTTP request against a json_schema
189 :param default_schema: The schema to be parsed by default if no version field is found in the client data. In None
190 no validation is done
191 :param version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to
192 obtain the version
193 :param version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value.
194 It can contain a None as key, and this is apply if the client data version does not match any key
195 :return: user_data, used_schema: if the data is successfully decoded and matches the schema.
196 Launch a bottle abort if fails
197 """
tierno7edb6752016-03-21 17:37:52 +0100198 #print "HEADERS :" + str(bottle.request.headers.items())
199 try:
200 error_text = "Invalid header format "
201 format_type = bottle.request.headers.get('Content-Type', 'application/json')
202 if 'application/json' in format_type:
203 error_text = "Invalid json format "
204 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
205 client_data = json.load(bottle.request.body)
206 #client_data = bottle.request.json()
207 elif 'application/yaml' in format_type:
208 error_text = "Invalid yaml format "
209 client_data = yaml.load(bottle.request.body)
210 elif 'application/xml' in format_type:
211 bottle.abort(501, "Content-Type: application/xml not supported yet.")
212 else:
tiernoefd80c92016-09-16 14:17:46 +0200213 logger.warning('Content-Type ' + str(format_type) + ' not supported.')
tierno7edb6752016-03-21 17:37:52 +0100214 bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
215 return
tiernof1ba57e2017-09-07 12:23:19 +0200216 # if client_data == None:
tierno7edb6752016-03-21 17:37:52 +0100217 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
218 # return
gcalvinoc62cfa52017-10-05 18:21:25 +0200219 if confidential_data:
220 logger.debug('IN: %s', remove_clear_passwd (yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False,
221 tags=False, encoding='utf-8', allow_unicode=True)))
222 else:
223 logger.debug('IN: %s', yaml.safe_dump(client_data, explicit_start=True, indent=4, default_flow_style=False,
tiernof1ba57e2017-09-07 12:23:19 +0200224 tags=False, encoding='utf-8', allow_unicode=True) )
225 # look for the client provider version
tierno7edb6752016-03-21 17:37:52 +0100226 error_text = "Invalid content "
tiernof1ba57e2017-09-07 12:23:19 +0200227 if not default_schema and not version_fields:
228 return client_data, None
tierno7edb6752016-03-21 17:37:52 +0100229 client_version = None
230 used_schema = None
231 if version_fields != None:
232 client_version = client_data
233 for field in version_fields:
234 if field in client_version:
235 client_version = client_version[field]
236 else:
237 client_version=None
238 break
tiernof1ba57e2017-09-07 12:23:19 +0200239 if client_version == None:
240 used_schema = default_schema
241 elif version_dict_schema != None:
tierno7edb6752016-03-21 17:37:52 +0100242 if client_version in version_dict_schema:
243 used_schema = version_dict_schema[client_version]
244 elif None in version_dict_schema:
245 used_schema = version_dict_schema[None]
246 if used_schema==None:
247 bottle.abort(HTTP_Bad_Request, "Invalid schema version or missing version field")
248
249 js_v(client_data, used_schema)
250 return client_data, used_schema
251 except (ValueError, yaml.YAMLError) as exc:
252 error_text += str(exc)
tiernoefd80c92016-09-16 14:17:46 +0200253 logger.error(error_text)
tierno7edb6752016-03-21 17:37:52 +0100254 bottle.abort(HTTP_Bad_Request, error_text)
255 except js_e.ValidationError as exc:
tiernoefd80c92016-09-16 14:17:46 +0200256 logger.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc.path), str(exc.message))
tierno7edb6752016-03-21 17:37:52 +0100257 error_pos = ""
258 if len(exc.path)>0: error_pos=" at " + ":".join(map(json.dumps, exc.path))
259 bottle.abort(HTTP_Bad_Request, error_text + exc.message + error_pos)
260 #except:
261 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
262 # raise
263
264def filter_query_string(qs, http2db, allowed):
265 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
266 Attributes:
267 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
268 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
269 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
270 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
271 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
272 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
273 limit: limit dictated by user with the query string 'limit'. 100 by default
274 abort if not permited, using bottel.abort
275 '''
276 where={}
277 limit=100
278 select=[]
tiernof97fd272016-07-11 14:32:37 +0200279 #if type(qs) is not bottle.FormsDict:
280 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
281 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
282 for k in qs:
283 if k=='field':
284 select += qs.getall(k)
285 for v in select:
286 if v not in allowed:
287 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
288 elif k=='limit':
289 try:
290 limit=int(qs[k])
291 except:
292 bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
293 else:
294 if k not in allowed:
295 bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
296 if qs[k]!="null": where[k]=qs[k]
297 else: where[k]=None
tierno7edb6752016-03-21 17:37:52 +0100298 if len(select)==0: select += allowed
299 #change from http api to database naming
300 for i in range(0,len(select)):
301 k=select[i]
302 if http2db and k in http2db:
303 select[i] = http2db[k]
304 if http2db:
305 change_keys_http2db(where, http2db)
tiernof97fd272016-07-11 14:32:37 +0200306 #print "filter_query_string", select,where,limit
tierno7edb6752016-03-21 17:37:52 +0100307
308 return select,where,limit
309
310@bottle.hook('after_request')
311def enable_cors():
312 '''Don't know yet if really needed. Keep it just in case'''
313 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
314
tierno6ddeded2017-05-16 15:40:26 +0200315@bottle.route(url_base + '/version', method='GET')
316def http_get_version():
317 return nfvo.get_version()
tierno7edb6752016-03-21 17:37:52 +0100318#
319# VNFs
320#
321
322@bottle.route(url_base + '/tenants', method='GET')
323def http_get_tenants():
tiernof97fd272016-07-11 14:32:37 +0200324 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100325 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
326 ('uuid','name','description','created_at') )
tiernof97fd272016-07-11 14:32:37 +0200327 try:
328 tenants = mydb.get_rows(FROM='nfvo_tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100329 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200330 convert_datetime2str(tenants)
331 data={'tenants' : tenants}
tierno7edb6752016-03-21 17:37:52 +0100332 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200333 except db_base_Exception as e:
334 logger.error("http_get_tenants error {}: {}".format(e.http_code, str(e)))
335 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000336 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000337 logger.error("Unexpected exception: ", exc_info=True)
338 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000339
tierno7edb6752016-03-21 17:37:52 +0100340
341@bottle.route(url_base + '/tenants/<tenant_id>', method='GET')
342def http_get_tenant_id(tenant_id):
343 '''get tenant details, can use both uuid or name'''
344 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200345 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
346 try:
gcalvinoc62cfa52017-10-05 18:21:25 +0200347 from_ = 'nfvo_tenants'
348 select_, where_, limit_ = filter_query_string(bottle.request.query, None,
349 ('uuid', 'name', 'description', 'created_at'))
350 where_['uuid'] = tenant_id
351 tenant = mydb.get_rows(FROM=from_, SELECT=select_,WHERE=where_)
tiernof97fd272016-07-11 14:32:37 +0200352 #change_keys_http2db(content, http2db_tenant, reverse=True)
353 convert_datetime2str(tenant)
354 data={'tenant' : tenant}
355 return format_out(data)
356 except db_base_Exception as e:
357 logger.error("http_get_tenant_id error {}: {}".format(e.http_code, str(e)))
358 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000359 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000360 logger.error("Unexpected exception: ", exc_info=True)
361 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000362
tierno7edb6752016-03-21 17:37:52 +0100363
364@bottle.route(url_base + '/tenants', method='POST')
365def http_post_tenants():
366 '''insert a tenant into the catalogue. '''
367 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200368 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100369 http_content,_ = format_in( tenant_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200370 r = utils.remove_extra_items(http_content, tenant_schema)
tiernoefd80c92016-09-16 14:17:46 +0200371 if r:
372 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200373 try:
374 data = nfvo.new_tenant(mydb, http_content['tenant'])
tierno7edb6752016-03-21 17:37:52 +0100375 return http_get_tenant_id(data)
tiernof97fd272016-07-11 14:32:37 +0200376 except (nfvo.NfvoException, db_base_Exception) as e:
377 logger.error("http_post_tenants error {}: {}".format(e.http_code, str(e)))
378 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000379 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000380 logger.error("Unexpected exception: ", exc_info=True)
381 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000382
tierno7edb6752016-03-21 17:37:52 +0100383
384@bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
385def http_edit_tenant_id(tenant_id):
386 '''edit tenant details, can use both uuid or name'''
387 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200388 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100389 http_content,_ = format_in( tenant_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200390 r = utils.remove_extra_items(http_content, tenant_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200391 if r:
392 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100393
394 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200395 try:
396 tenant = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
397 #edit data
398 tenant_id = tenant['uuid']
399 where={'uuid': tenant['uuid']}
400 mydb.update_rows('nfvo_tenants', http_content['tenant'], where)
401 return http_get_tenant_id(tenant_id)
402 except db_base_Exception as e:
403 logger.error("http_edit_tenant_id error {}: {}".format(e.http_code, str(e)))
404 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000405 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000406 logger.error("Unexpected exception: ", exc_info=True)
407 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000408
tierno7edb6752016-03-21 17:37:52 +0100409
410@bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
411def http_delete_tenant_id(tenant_id):
412 '''delete a tenant from database, can use both uuid or name'''
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 data = nfvo.delete_tenant(mydb, tenant_id)
tierno7edb6752016-03-21 17:37:52 +0100416 return format_out({"result":"tenant " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +0200417 except db_base_Exception as e:
418 logger.error("http_delete_tenant_id error {}: {}".format(e.http_code, str(e)))
419 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000420 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000421 logger.error("Unexpected exception: ", exc_info=True)
422 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000423
tierno7edb6752016-03-21 17:37:52 +0100424
425@bottle.route(url_base + '/<tenant_id>/datacenters', method='GET')
426def http_get_datacenters(tenant_id):
tiernof97fd272016-07-11 14:32:37 +0200427 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
428 try:
429 if tenant_id != 'any':
430 #check valid tenant_id
431 nfvo.check_tenant(mydb, tenant_id)
432 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
433 ('uuid','name','vim_url','type','created_at') )
434 if tenant_id != 'any':
435 where_['nfvo_tenant_id'] = tenant_id
436 if 'created_at' in select_:
437 select_[ select_.index('created_at') ] = 'd.created_at as created_at'
438 if 'created_at' in where_:
439 where_['d.created_at'] = where_.pop('created_at')
440 datacenters = mydb.get_rows(FROM='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
441 SELECT=select_,WHERE=where_,LIMIT=limit_)
442 else:
443 datacenters = mydb.get_rows(FROM='datacenters',
444 SELECT=select_,WHERE=where_,LIMIT=limit_)
tierno7edb6752016-03-21 17:37:52 +0100445 #change_keys_http2db(content, http2db_tenant, reverse=True)
tiernof97fd272016-07-11 14:32:37 +0200446 convert_datetime2str(datacenters)
447 data={'datacenters' : datacenters}
tierno7edb6752016-03-21 17:37:52 +0100448 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200449 except (nfvo.NfvoException, db_base_Exception) as e:
450 logger.error("http_get_datacenters error {}: {}".format(e.http_code, str(e)))
451 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000452 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000453 logger.error("Unexpected exception: ", exc_info=True)
454 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000455
tierno7edb6752016-03-21 17:37:52 +0100456
457@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='GET')
458def http_get_datacenter_id(tenant_id, datacenter_id):
459 '''get datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200460 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
461 try:
462 if tenant_id != 'any':
463 #check valid tenant_id
464 nfvo.check_tenant(mydb, tenant_id)
465 #obtain data
466 what = 'uuid' if utils.check_valid_uuid(datacenter_id) else 'name'
467 where_={}
468 where_[what] = datacenter_id
tierno8008c3a2016-10-13 15:34:28 +0000469 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 +0200470 if tenant_id != 'any':
471 select_.append("datacenter_tenant_id")
472 where_['td.nfvo_tenant_id']= tenant_id
473 from_='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
474 else:
475 from_='datacenters as d'
476 datacenters = mydb.get_rows(
477 SELECT=select_,
478 FROM=from_,
479 WHERE=where_)
480
481 if len(datacenters)==0:
482 bottle.abort( HTTP_Not_Found, "No datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
483 elif len(datacenters)>1:
484 bottle.abort( HTTP_Bad_Request, "More than one datacenter found for tenant with {} '{}'".format(what, datacenter_id) )
485 datacenter = datacenters[0]
486 if tenant_id != 'any':
487 #get vim tenant info
488 vim_tenants = mydb.get_rows(
tierno8008c3a2016-10-13 15:34:28 +0000489 SELECT=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
tiernof97fd272016-07-11 14:32:37 +0200490 FROM="datacenter_tenants",
491 WHERE={"uuid": datacenters[0]["datacenter_tenant_id"]},
492 ORDER_BY=("created", ) )
493 del datacenter["datacenter_tenant_id"]
494 datacenter["vim_tenants"] = vim_tenants
tierno8008c3a2016-10-13 15:34:28 +0000495 for vim_tenant in vim_tenants:
496 if vim_tenant["passwd"]:
497 vim_tenant["passwd"] = "******"
498 if vim_tenant['config'] != None:
499 try:
500 config_dict = yaml.load(vim_tenant['config'])
501 vim_tenant['config'] = config_dict
gcalvinoc62cfa52017-10-05 18:21:25 +0200502 if vim_tenant['config'].get('admin_password'):
503 vim_tenant['config']['admin_password'] = "******"
504 if vim_tenant['config'].get('vcenter_password'):
505 vim_tenant['config']['vcenter_password'] = "******"
506 if vim_tenant['config'].get('nsx_password'):
507 vim_tenant['config']['nsx_password'] = "******"
venkatamahesh6ecca182017-01-27 23:04:40 +0530508 except Exception as e:
tierno8008c3a2016-10-13 15:34:28 +0000509 logger.error("Exception '%s' while trying to load config information", str(e))
510
tiernof97fd272016-07-11 14:32:37 +0200511 if datacenter['config'] != None:
512 try:
513 config_dict = yaml.load(datacenter['config'])
514 datacenter['config'] = config_dict
gcalvinoc62cfa52017-10-05 18:21:25 +0200515 if datacenter['config'].get('admin_password'):
516 datacenter['config']['admin_password'] = "******"
517 if datacenter['config'].get('vcenter_password'):
518 datacenter['config']['vcenter_password'] = "******"
519 if datacenter['config'].get('nsx_password'):
520 datacenter['config']['nsx_password'] = "******"
venkatamahesh6ecca182017-01-27 23:04:40 +0530521 except Exception as e:
tiernoefd80c92016-09-16 14:17:46 +0200522 logger.error("Exception '%s' while trying to load config information", str(e))
tiernof97fd272016-07-11 14:32:37 +0200523 #change_keys_http2db(content, http2db_datacenter, reverse=True)
524 convert_datetime2str(datacenter)
525 data={'datacenter' : datacenter}
526 return format_out(data)
527 except (nfvo.NfvoException, db_base_Exception) as e:
528 logger.error("http_get_datacenter_id error {}: {}".format(e.http_code, str(e)))
529 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000530 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000531 logger.error("Unexpected exception: ", exc_info=True)
532 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
533
tierno7edb6752016-03-21 17:37:52 +0100534
535@bottle.route(url_base + '/datacenters', method='POST')
536def http_post_datacenters():
garciadeblasc27b0462017-03-22 18:57:47 +0100537 '''insert a datacenter into the catalogue. '''
tierno7edb6752016-03-21 17:37:52 +0100538 #parse input data
tiernof97fd272016-07-11 14:32:37 +0200539 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
gcalvinoc62cfa52017-10-05 18:21:25 +0200540 http_content,_ = format_in(datacenter_schema, confidential_data=True)
tierno42fcc3b2016-07-06 17:20:40 +0200541 r = utils.remove_extra_items(http_content, datacenter_schema)
tiernoefd80c92016-09-16 14:17:46 +0200542 if r:
543 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200544 try:
545 data = nfvo.new_datacenter(mydb, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100546 return http_get_datacenter_id('any', data)
tiernof97fd272016-07-11 14:32:37 +0200547 except (nfvo.NfvoException, db_base_Exception) as e:
548 logger.error("http_post_datacenters error {}: {}".format(e.http_code, str(e)))
549 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000550 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000551 logger.error("Unexpected exception: ", exc_info=True)
552 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
553
tierno7edb6752016-03-21 17:37:52 +0100554
555@bottle.route(url_base + '/datacenters/<datacenter_id_name>', method='PUT')
556def http_edit_datacenter_id(datacenter_id_name):
557 '''edit datacenter details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200558 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100559 #parse input data
560 http_content,_ = format_in( datacenter_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200561 r = utils.remove_extra_items(http_content, datacenter_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200562 if r:
563 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100564
tiernof97fd272016-07-11 14:32:37 +0200565 try:
566 datacenter_id = nfvo.edit_datacenter(mydb, datacenter_id_name, http_content['datacenter'])
tierno7edb6752016-03-21 17:37:52 +0100567 return http_get_datacenter_id('any', datacenter_id)
tiernof97fd272016-07-11 14:32:37 +0200568 except (nfvo.NfvoException, db_base_Exception) as e:
569 logger.error("http_edit_datacenter_id error {}: {}".format(e.http_code, str(e)))
570 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000571 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000572 logger.error("Unexpected exception: ", exc_info=True)
573 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
574
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100575@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
576def http_post_sdn_controller(tenant_id):
577 '''insert a sdn controller into the catalogue. '''
578 #parse input data
579 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
580 http_content,_ = format_in( sdn_controller_schema )
581 try:
582 logger.debug("tenant_id: "+tenant_id)
583 #logger.debug("content: {}".format(http_content['sdn_controller']))
584
585 data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
586 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
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/<controller_id>', method='PUT')
595def http_put_sdn_controller_update(tenant_id, controller_id):
596 '''Update sdn controller'''
597 #parse input data
598 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
599 http_content,_ = format_in( sdn_controller_edit_schema )
600# r = utils.remove_extra_items(http_content, datacenter_schema)
601# if r:
602# logger.debug("Remove received extra items %s", str(r))
603 try:
604 #logger.debug("tenant_id: "+tenant_id)
605 logger.debug("content: {}".format(http_content['sdn_controller']))
606
607 data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
608 return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
609
610 except (nfvo.NfvoException, db_base_Exception) as e:
611 logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
612 bottle.abort(e.http_code, str(e))
613 except Exception as e:
614 logger.error("Unexpected exception: ", exc_info=True)
615 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
616
617@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
618def http_get_sdn_controller(tenant_id):
619 '''get sdn controllers list, can use both uuid or name'''
620 try:
621 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
622
623 data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
624 return format_out(data)
625 except (nfvo.NfvoException, db_base_Exception) as e:
626 logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
627 bottle.abort(e.http_code, str(e))
628 except Exception as e:
629 logger.error("Unexpected exception: ", exc_info=True)
630 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
631
632@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
633def http_get_sdn_controller_id(tenant_id, controller_id):
634 '''get sdn controller details, can use both uuid or name'''
635 try:
636 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
637 data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
638 return format_out({"sdn_controllers": data})
639 except (nfvo.NfvoException, db_base_Exception) as e:
640 logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
641 bottle.abort(e.http_code, str(e))
642 except Exception as e:
643 logger.error("Unexpected exception: ", exc_info=True)
644 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
645
646@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
647def http_delete_sdn_controller_id(tenant_id, controller_id):
648 '''delete sdn controller, can use both uuid or name'''
649 try:
650 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
651 data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
652 return format_out(data)
653 except (nfvo.NfvoException, db_base_Exception) as e:
654 logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
655 bottle.abort(e.http_code, str(e))
656 except Exception as e:
657 logger.error("Unexpected exception: ", exc_info=True)
658 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
659
660@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
661def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
662 '''Set the sdn port mapping for a datacenter. '''
663 #parse input data
664 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
665 http_content, _ = format_in(sdn_port_mapping_schema)
666# r = utils.remove_extra_items(http_content, datacenter_schema)
667# if r:
668# logger.debug("Remove received extra items %s", str(r))
669 try:
670 data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
671 return format_out({"sdn_port_mapping": data})
672 except (nfvo.NfvoException, db_base_Exception) as e:
673 logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
674 bottle.abort(e.http_code, str(e))
675 except Exception as e:
676 logger.error("Unexpected exception: ", exc_info=True)
677 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
678
679@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
680def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
681 '''get datacenter sdn mapping details, can use both uuid or name'''
682 try:
683 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
684
685 data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
686 return format_out({"sdn_port_mapping": data})
687 except (nfvo.NfvoException, db_base_Exception) as e:
688 logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
689 bottle.abort(e.http_code, str(e))
690 except Exception as e:
691 logger.error("Unexpected exception: ", exc_info=True)
692 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
693
694@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
695def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
696 '''clean datacenter sdn mapping, can use both uuid or name'''
697 try:
698 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
699 data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
700 return format_out({"result": data})
701 except (nfvo.NfvoException, db_base_Exception) as e:
702 logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
703 bottle.abort(e.http_code, str(e))
704 except Exception as e:
705 logger.error("Unexpected exception: ", exc_info=True)
706 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100707
708@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
709@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
710@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='GET')
711def http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
712 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200713 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100714 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200715 try:
716 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
717 where_= {"datacenter_id":datacenter_dict['uuid']}
718 if netmap_id:
719 if utils.check_valid_uuid(netmap_id):
720 where_["uuid"] = netmap_id
721 else:
722 where_["name"] = netmap_id
723 netmaps =mydb.get_rows(FROM='datacenter_nets',
724 SELECT=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
725 WHERE=where_ )
726 convert_datetime2str(netmaps)
727 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
728 if netmap_id and len(netmaps)==1:
729 data={'netmap' : netmaps[0]}
730 elif netmap_id and len(netmaps)==0:
731 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
732 return
tierno7edb6752016-03-21 17:37:52 +0100733 else:
tiernof97fd272016-07-11 14:32:37 +0200734 data={'netmaps' : netmaps}
735 return format_out(data)
736 except (nfvo.NfvoException, db_base_Exception) as e:
737 logger.error("http_getnetwork_datacenter_id error {}: {}".format(e.http_code, str(e)))
738 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000739 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000740 logger.error("Unexpected exception: ", exc_info=True)
741 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
742
tierno7edb6752016-03-21 17:37:52 +0100743
744@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='DELETE')
745@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='DELETE')
746def http_delnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id=None):
747 '''get datacenter networks, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200748 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100749 #obtain data
tiernof97fd272016-07-11 14:32:37 +0200750 try:
751 datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter_id, "datacenter")
752 where_= {"datacenter_id":datacenter_dict['uuid']}
753 if netmap_id:
754 if utils.check_valid_uuid(netmap_id):
755 where_["uuid"] = netmap_id
756 else:
757 where_["name"] = netmap_id
758 #change_keys_http2db(content, http2db_tenant, reverse=True)
759 deleted = mydb.delete_row(FROM='datacenter_nets', WHERE= where_)
760 if deleted == 0 and netmap_id :
761 bottle.abort(HTTP_Not_Found, "No netmap found with " + " and ".join(map(lambda x: str(x[0])+": "+str(x[1]), where_.iteritems())) )
762 if netmap_id:
763 return format_out({"result": "netmap %s deleted" % netmap_id})
tierno7edb6752016-03-21 17:37:52 +0100764 else:
tiernof97fd272016-07-11 14:32:37 +0200765 return format_out({"result": "%d netmap deleted" % deleted})
766 except (nfvo.NfvoException, db_base_Exception) as e:
767 logger.error("http_delnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
768 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000769 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000770 logger.error("Unexpected exception: ", exc_info=True)
771 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100772
773
774@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method='POST')
775def http_uploadnetmap_datacenter_id(tenant_id, datacenter_id):
tiernof97fd272016-07-11 14:32:37 +0200776 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
777 try:
778 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, None)
779 convert_datetime2str(netmaps)
780 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
781 data={'netmaps' : netmaps}
782 return format_out(data)
783 except (nfvo.NfvoException, db_base_Exception) as e:
784 logger.error("http_uploadnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
785 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000786 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000787 logger.error("Unexpected exception: ", exc_info=True)
788 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
789
tierno7edb6752016-03-21 17:37:52 +0100790
791@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='POST')
792def http_postnetmap_datacenter_id(tenant_id, datacenter_id):
793 '''creates a new netmap'''
tiernof97fd272016-07-11 14:32:37 +0200794 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100795 #parse input data
796 http_content,_ = format_in( netmap_new_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200797 r = utils.remove_extra_items(http_content, netmap_new_schema)
tiernoefd80c92016-09-16 14:17:46 +0200798 if r:
799 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200800 try:
801 #obtain data, check that only one exist
802 netmaps = nfvo.datacenter_new_netmap(mydb, tenant_id, datacenter_id, http_content)
803 convert_datetime2str(netmaps)
804 utils.convert_str2boolean(netmaps, ('shared', 'multipoint') )
805 data={'netmaps' : netmaps}
806 return format_out(data)
807 except (nfvo.NfvoException, db_base_Exception) as e:
808 logger.error("http_postnetmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
809 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000810 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000811 logger.error("Unexpected exception: ", exc_info=True)
812 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
813
tierno7edb6752016-03-21 17:37:52 +0100814
815@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method='PUT')
816def http_putnettmap_datacenter_id(tenant_id, datacenter_id, netmap_id):
817 '''edit a netmap'''
tiernof97fd272016-07-11 14:32:37 +0200818 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100819 #parse input data
820 http_content,_ = format_in( netmap_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200821 r = utils.remove_extra_items(http_content, netmap_edit_schema)
tiernoefd80c92016-09-16 14:17:46 +0200822 if r:
823 logger.debug("Remove received extra items %s", str(r))
tierno7edb6752016-03-21 17:37:52 +0100824
825 #obtain data, check that only one exist
tiernof97fd272016-07-11 14:32:37 +0200826 try:
827 nfvo.datacenter_edit_netmap(mydb, tenant_id, datacenter_id, netmap_id, http_content)
tierno7edb6752016-03-21 17:37:52 +0100828 return http_getnetmap_datacenter_id(tenant_id, datacenter_id, netmap_id)
tiernof97fd272016-07-11 14:32:37 +0200829 except (nfvo.NfvoException, db_base_Exception) as e:
830 logger.error("http_putnettmap_datacenter_id error {}: {}".format(e.http_code, str(e)))
831 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000832 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000833 logger.error("Unexpected exception: ", exc_info=True)
834 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100835
836
837@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/action', method='POST')
838def http_action_datacenter_id(tenant_id, datacenter_id):
839 '''perform an action over datacenter, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +0200840 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100841 #parse input data
842 http_content,_ = format_in( datacenter_action_schema )
tierno42fcc3b2016-07-06 17:20:40 +0200843 r = utils.remove_extra_items(http_content, datacenter_action_schema)
tiernoefd80c92016-09-16 14:17:46 +0200844 if r:
845 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200846 try:
847 #obtain data, check that only one exist
848 result = nfvo.datacenter_action(mydb, tenant_id, datacenter_id, http_content)
849 if 'net-update' in http_content:
850 return http_getnetmap_datacenter_id(datacenter_id)
851 else:
852 return format_out(result)
853 except (nfvo.NfvoException, db_base_Exception) as e:
854 logger.error("http_action_datacenter_id error {}: {}".format(e.http_code, str(e)))
855 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000856 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000857 logger.error("Unexpected exception: ", exc_info=True)
858 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100859
860
861@bottle.route(url_base + '/datacenters/<datacenter_id>', method='DELETE')
862def http_delete_datacenter_id( datacenter_id):
863 '''delete a tenant from database, can use both uuid or name'''
864
tiernof97fd272016-07-11 14:32:37 +0200865 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
866 try:
867 data = nfvo.delete_datacenter(mydb, datacenter_id)
868 return format_out({"result":"datacenter '" + data + "' deleted"})
869 except (nfvo.NfvoException, db_base_Exception) as e:
870 logger.error("http_delete_datacenter_id 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
tierno7edb6752016-03-21 17:37:52 +0100876
877@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='POST')
878def http_associate_datacenters(tenant_id, datacenter_id):
879 '''associate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200880 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +0100881 #parse input data
gcalvinoc62cfa52017-10-05 18:21:25 +0200882 http_content,_ = format_in(datacenter_associate_schema, confidential_data=True)
tierno42fcc3b2016-07-06 17:20:40 +0200883 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
tiernoefd80c92016-09-16 14:17:46 +0200884 if r:
885 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +0200886 try:
887 id_ = nfvo.associate_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
888 http_content['datacenter'].get('vim_tenant'),
889 http_content['datacenter'].get('vim_tenant_name'),
890 http_content['datacenter'].get('vim_username'),
tierno8008c3a2016-10-13 15:34:28 +0000891 http_content['datacenter'].get('vim_password'),
892 http_content['datacenter'].get('config')
893 )
tiernof97fd272016-07-11 14:32:37 +0200894 return http_get_datacenter_id(tenant_id, id_)
895 except (nfvo.NfvoException, db_base_Exception) as e:
896 logger.error("http_associate_datacenters error {}: {}".format(e.http_code, str(e)))
897 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000898 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000899 logger.error("Unexpected exception: ", exc_info=True)
900 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
901
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100902@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
903def http_associate_datacenters_edit(tenant_id, datacenter_id):
904 '''associate an existing datacenter to a this tenant. '''
905 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
906 #parse input data
907 http_content,_ = format_in( datacenter_associate_schema )
908 r = utils.remove_extra_items(http_content, datacenter_associate_schema)
909 if r:
910 logger.debug("Remove received extra items %s", str(r))
911 try:
912 id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
913 http_content['datacenter'].get('vim_tenant'),
914 http_content['datacenter'].get('vim_tenant_name'),
915 http_content['datacenter'].get('vim_username'),
916 http_content['datacenter'].get('vim_password'),
917 http_content['datacenter'].get('config')
918 )
919 return http_get_datacenter_id(tenant_id, id_)
920 except (nfvo.NfvoException, db_base_Exception) as e:
921 logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
922 bottle.abort(e.http_code, str(e))
923 except Exception as e:
924 logger.error("Unexpected exception: ", exc_info=True)
925 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100926
927@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
928def http_deassociate_datacenters(tenant_id, datacenter_id):
929 '''deassociate an existing datacenter to a this tenant. '''
tiernof97fd272016-07-11 14:32:37 +0200930 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
931 try:
932 data = nfvo.deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter_id)
933 return format_out({"result": data})
934 except (nfvo.NfvoException, db_base_Exception) as e:
935 logger.error("http_deassociate_datacenters error {}: {}".format(e.http_code, str(e)))
936 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000937 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000938 logger.error("Unexpected exception: ", exc_info=True)
939 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
940
Pablo Montes Moreno6aa0b2b2017-05-23 18:33:12 +0200941@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method='POST')
942def http_post_vim_net_sdn_attach(tenant_id, datacenter_id, network_id):
943 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
944 http_content, _ = format_in(sdn_external_port_schema)
945 try:
946 data = nfvo.vim_net_sdn_attach(mydb, tenant_id, datacenter_id, network_id, http_content)
947 return format_out(data)
948 except (nfvo.NfvoException, db_base_Exception) as e:
949 logger.error("http_post_vim_net_sdn_attach error {}: {}".format(e.http_code, str(e)))
950 bottle.abort(e.http_code, str(e))
951 except Exception as e:
952 logger.error("Unexpected exception: ", exc_info=True)
953 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
954
955@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method='DELETE')
956@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method='DELETE')
957def http_delete_vim_net_sdn_detach(tenant_id, datacenter_id, network_id, port_id=None):
958 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
959 try:
960 data = nfvo.vim_net_sdn_detach(mydb, tenant_id, datacenter_id, network_id, port_id)
961 return format_out(data)
962 except (nfvo.NfvoException, db_base_Exception) as e:
963 logger.error("http_delete_vim_net_sdn_detach error {}: {}".format(e.http_code, str(e)))
964 bottle.abort(e.http_code, str(e))
965 except Exception as e:
966 logger.error("Unexpected exception: ", exc_info=True)
967 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +0100968
tierno7edb6752016-03-21 17:37:52 +0100969@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
970@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
971def http_get_vim_items(tenant_id, datacenter_id, item, name=None):
tiernof97fd272016-07-11 14:32:37 +0200972 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
973 try:
974 data = nfvo.vim_action_get(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100975 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +0200976 except (nfvo.NfvoException, db_base_Exception) as e:
977 logger.error("http_get_vim_items error {}: {}".format(e.http_code, str(e)))
978 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000979 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000980 logger.error("Unexpected exception: ", exc_info=True)
981 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
982
tierno7edb6752016-03-21 17:37:52 +0100983
984@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='DELETE')
985def http_del_vim_items(tenant_id, datacenter_id, item, name):
tiernof97fd272016-07-11 14:32:37 +0200986 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
987 try:
988 data = nfvo.vim_action_delete(mydb, tenant_id, datacenter_id, item, name)
tierno7edb6752016-03-21 17:37:52 +0100989 return format_out({"result":data})
tiernof97fd272016-07-11 14:32:37 +0200990 except (nfvo.NfvoException, db_base_Exception) as e:
991 logger.error("http_del_vim_items error {}: {}".format(e.http_code, str(e)))
992 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +0000993 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +0000994 logger.error("Unexpected exception: ", exc_info=True)
995 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
996
tierno65a9b0c2016-09-28 14:57:25 +0000997
tierno7edb6752016-03-21 17:37:52 +0100998@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='POST')
999def http_post_vim_items(tenant_id, datacenter_id, item):
tiernof97fd272016-07-11 14:32:37 +02001000 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001001 http_content,_ = format_in( object_schema )
tiernof97fd272016-07-11 14:32:37 +02001002 try:
1003 data = nfvo.vim_action_create(mydb, tenant_id, datacenter_id, item, http_content)
tierno7edb6752016-03-21 17:37:52 +01001004 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001005 except (nfvo.NfvoException, db_base_Exception) as e:
1006 logger.error("http_post_vim_items error {}: {}".format(e.http_code, str(e)))
1007 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001008 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001009 logger.error("Unexpected exception: ", exc_info=True)
1010 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1011
tierno7edb6752016-03-21 17:37:52 +01001012
1013@bottle.route(url_base + '/<tenant_id>/vnfs', method='GET')
1014def http_get_vnfs(tenant_id):
tiernof97fd272016-07-11 14:32:37 +02001015 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1016 try:
1017 if tenant_id != 'any':
1018 #check valid tenant_id
1019 nfvo.check_tenant(mydb, tenant_id)
1020 select_,where_,limit_ = filter_query_string(bottle.request.query, None,
tiernof1ba57e2017-09-07 12:23:19 +02001021 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
tiernof97fd272016-07-11 14:32:37 +02001022 where_or = {}
1023 if tenant_id != "any":
1024 where_or["tenant_id"] = tenant_id
1025 where_or["public"] = True
1026 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 +01001027 #change_keys_http2db(content, http2db_vnf, reverse=True)
tiernof97fd272016-07-11 14:32:37 +02001028 utils.convert_str2boolean(vnfs, ('public',))
1029 convert_datetime2str(vnfs)
1030 data={'vnfs' : vnfs}
tierno7edb6752016-03-21 17:37:52 +01001031 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001032 except (nfvo.NfvoException, db_base_Exception) as e:
1033 logger.error("http_get_vnfs error {}: {}".format(e.http_code, str(e)))
1034 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001035 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001036 logger.error("Unexpected exception: ", exc_info=True)
1037 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1038
tierno7edb6752016-03-21 17:37:52 +01001039
1040@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='GET')
1041def http_get_vnf_id(tenant_id,vnf_id):
1042 '''get vnf details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001043 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1044 try:
1045 vnf = nfvo.get_vnf_id(mydb,tenant_id,vnf_id)
1046 utils.convert_str2boolean(vnf, ('public',))
1047 convert_datetime2str(vnf)
1048 return format_out(vnf)
1049 except (nfvo.NfvoException, db_base_Exception) as e:
1050 logger.error("http_get_vnf_id error {}: {}".format(e.http_code, str(e)))
1051 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001052 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001053 logger.error("Unexpected exception: ", exc_info=True)
1054 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1055
tierno7edb6752016-03-21 17:37:52 +01001056
1057@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
1058def http_post_vnfs(tenant_id):
tiernof1ba57e2017-09-07 12:23:19 +02001059 """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
1060 :param tenant_id: tenant that this vnf belongs to
1061 :return:
1062 """
1063 # print "Parsing the YAML file of the VNF"
1064 # parse input data
tiernof97fd272016-07-11 14:32:37 +02001065 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001066 http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001067 r = utils.remove_extra_items(http_content, used_schema)
tiernoefd80c92016-09-16 14:17:46 +02001068 if r:
1069 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001070 try:
tierno4319dad2016-09-05 12:11:11 +02001071 if used_schema == vnfd_schema_v01:
garciadeblas9f8456e2016-09-05 05:02:59 +02001072 vnf_id = nfvo.new_vnf(mydb,tenant_id,http_content)
tierno4319dad2016-09-05 12:11:11 +02001073 elif used_schema == vnfd_schema_v02:
garciadeblas9f8456e2016-09-05 05:02:59 +02001074 vnf_id = nfvo.new_vnf_v02(mydb,tenant_id,http_content)
1075 else:
1076 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1077 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tiernof97fd272016-07-11 14:32:37 +02001078 return http_get_vnf_id(tenant_id, vnf_id)
1079 except (nfvo.NfvoException, db_base_Exception) as e:
1080 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1081 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001082 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001083 logger.error("Unexpected exception: ", exc_info=True)
1084 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1085
tiernof1ba57e2017-09-07 12:23:19 +02001086
1087@bottle.route(url_base + '/v3/<tenant_id>/vnfd', method='POST')
1088def http_post_vnfs_v3(tenant_id):
1089 """
1090 Insert one or several VNFs in the catalog, following OSM IM
1091 :param tenant_id: tenant owner of the VNF
1092 :return: The detailed list of inserted VNFs, following the old format
1093 """
1094 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1095 http_content, _ = format_in(None)
1096 try:
1097 vnfd_uuid_list = nfvo.new_vnfd_v3(mydb, tenant_id, http_content)
1098 vnfd_list = []
1099 for vnfd_uuid in vnfd_uuid_list:
1100 vnf = nfvo.get_vnf_id(mydb, tenant_id, vnfd_uuid)
1101 utils.convert_str2boolean(vnf, ('public',))
1102 convert_datetime2str(vnf)
1103 vnfd_list.append(vnf["vnf"])
1104 return format_out({"vnfd": vnfd_list})
1105 except (nfvo.NfvoException, db_base_Exception) as e:
1106 logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
1107 bottle.abort(e.http_code, str(e))
1108 except Exception as e:
1109 logger.error("Unexpected exception: ", exc_info=True)
1110 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1111
tierno7edb6752016-03-21 17:37:52 +01001112@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
tiernof1ba57e2017-09-07 12:23:19 +02001113def http_delete_vnf_id(tenant_id, vnf_id):
tierno7edb6752016-03-21 17:37:52 +01001114 '''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 +02001115 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001116 #check valid tenant_id and deletes the vnf, including images,
tiernof97fd272016-07-11 14:32:37 +02001117 try:
1118 data = nfvo.delete_vnf(mydb,tenant_id,vnf_id)
tierno7edb6752016-03-21 17:37:52 +01001119 #print json.dumps(data, indent=4)
1120 return format_out({"result":"VNF " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001121 except (nfvo.NfvoException, db_base_Exception) as e:
1122 logger.error("http_delete_vnf_id error {}: {}".format(e.http_code, str(e)))
1123 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001124 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001125 logger.error("Unexpected exception: ", exc_info=True)
1126 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1127
tierno7edb6752016-03-21 17:37:52 +01001128
1129#@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1130#@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1131@bottle.route(url_base + '/<tenant_id>/physicalview/<datacenter>', method='GET')
1132def http_get_hosts(tenant_id, datacenter):
1133 '''get the tidvim host hopology from the vim.'''
tiernof97fd272016-07-11 14:32:37 +02001134 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1135 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1136 try:
1137 if datacenter == 'treeview':
1138 data = nfvo.get_hosts(mydb, tenant_id)
1139 else:
1140 #openmano-gui is using a hardcoded value for the datacenter
1141 result, data = nfvo.get_hosts_info(mydb, tenant_id) #, datacenter)
1142
1143 if result < 0:
tiernoefd80c92016-09-16 14:17:46 +02001144 #print "http_get_hosts error %d %s" % (-result, data)
tiernof97fd272016-07-11 14:32:37 +02001145 bottle.abort(-result, data)
1146 else:
1147 convert_datetime2str(data)
tiernoefd80c92016-09-16 14:17:46 +02001148 #print json.dumps(data, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001149 return format_out(data)
1150 except (nfvo.NfvoException, db_base_Exception) as e:
garciadeblas9f8456e2016-09-05 05:02:59 +02001151 logger.error("http_get_hosts error {}: {}".format(e.http_code, str(e)))
tiernof97fd272016-07-11 14:32:37 +02001152 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001153 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001154 logger.error("Unexpected exception: ", exc_info=True)
1155 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001156
1157
1158@bottle.route(url_base + '/<path:path>', method='OPTIONS')
1159def http_options_deploy(path):
1160 '''For some reason GUI web ask for OPTIONS that must be responded'''
1161 #TODO: check correct path, and correct headers request
tiernof97fd272016-07-11 14:32:37 +02001162 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001163 bottle.response.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1164 bottle.response.set_header('Accept','application/yaml,application/json')
1165 bottle.response.set_header('Content-Type','application/yaml,application/json')
1166 bottle.response.set_header('Access-Control-Allow-Headers','content-type')
1167 bottle.response.set_header('Access-Control-Allow-Origin','*')
1168 return
1169
1170@bottle.route(url_base + '/<tenant_id>/topology/deploy', method='POST')
1171def http_post_deploy(tenant_id):
1172 '''post topology deploy.'''
tiernof97fd272016-07-11 14:32:37 +02001173 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001174
tierno66aa0372016-07-06 17:31:12 +02001175 http_content, used_schema = format_in( nsd_schema_v01, ("schema_version",), {2: nsd_schema_v02})
tierno42fcc3b2016-07-06 17:20:40 +02001176 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001177 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001178 #print "http_post_deploy input: ", http_content
tierno7edb6752016-03-21 17:37:52 +01001179
tiernof97fd272016-07-11 14:32:37 +02001180 try:
1181 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
1182 instance = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['name'], http_content['name'])
1183 #print json.dumps(data, indent=4)
1184 return format_out(instance)
1185 except (nfvo.NfvoException, db_base_Exception) as e:
1186 logger.error("http_post_deploy error {}: {}".format(e.http_code, str(e)))
1187 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001188 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001189 logger.error("Unexpected exception: ", exc_info=True)
1190 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1191
tierno7edb6752016-03-21 17:37:52 +01001192
1193@bottle.route(url_base + '/<tenant_id>/topology/verify', method='POST')
1194def http_post_verify(tenant_id):
1195 #TODO:
1196# '''post topology verify'''
1197# print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
tiernof97fd272016-07-11 14:32:37 +02001198 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001199 return
1200
1201#
1202# SCENARIOS
1203#
1204
1205@bottle.route(url_base + '/<tenant_id>/scenarios', method='POST')
1206def http_post_scenarios(tenant_id):
1207 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
tiernof97fd272016-07-11 14:32:37 +02001208 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
garciadeblas9f8456e2016-09-05 05:02:59 +02001209 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 +02001210 #r = utils.remove_extra_items(http_content, used_schema)
tierno7edb6752016-03-21 17:37:52 +01001211 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001212 #print "http_post_scenarios input: ", http_content
1213 try:
tierno4319dad2016-09-05 12:11:11 +02001214 if used_schema == nsd_schema_v01:
tiernof97fd272016-07-11 14:32:37 +02001215 scenario_id = nfvo.new_scenario(mydb, tenant_id, http_content)
tierno4319dad2016-09-05 12:11:11 +02001216 elif used_schema == nsd_schema_v02:
tierno5bb59dc2017-02-13 14:53:54 +01001217 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.2")
tierno4319dad2016-09-05 12:11:11 +02001218 elif used_schema == nsd_schema_v03:
tierno5bb59dc2017-02-13 14:53:54 +01001219 scenario_id = nfvo.new_scenario_v02(mydb, tenant_id, http_content, "0.3")
garciadeblas9f8456e2016-09-05 05:02:59 +02001220 else:
1221 logger.warning('Unexpected schema_version: %s', http_content.get("schema_version"))
1222 bottle.abort(HTTP_Bad_Request, "Invalid schema version")
tierno7edb6752016-03-21 17:37:52 +01001223 #print json.dumps(data, indent=4)
1224 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001225 return http_get_scenario_id(tenant_id, scenario_id)
1226 except (nfvo.NfvoException, db_base_Exception) as e:
1227 logger.error("http_post_scenarios error {}: {}".format(e.http_code, str(e)))
1228 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001229 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001230 logger.error("Unexpected exception: ", exc_info=True)
1231 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1232
tiernof1ba57e2017-09-07 12:23:19 +02001233@bottle.route(url_base + '/v3/<tenant_id>/nsd', method='POST')
1234def http_post_nsds_v3(tenant_id):
1235 """
1236 Insert one or several NSDs in the catalog, following OSM IM
1237 :param tenant_id: tenant owner of the NSD
1238 :return: The detailed list of inserted NSDs, following the old format
1239 """
1240 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1241 http_content, _ = format_in(None)
1242 try:
1243 nsd_uuid_list = nfvo.new_nsd_v3(mydb, tenant_id, http_content)
1244 nsd_list = []
1245 for nsd_uuid in nsd_uuid_list:
1246 scenario = mydb.get_scenario(nsd_uuid, tenant_id)
1247 convert_datetime2str(scenario)
1248 nsd_list.append(scenario)
1249 data = {'nsd': nsd_list}
1250 return format_out(data)
1251 except (nfvo.NfvoException, db_base_Exception) as e:
1252 logger.error("http_post_nsds_v3 error {}: {}".format(e.http_code, str(e)))
1253 bottle.abort(e.http_code, str(e))
1254 except Exception as e:
1255 logger.error("Unexpected exception: ", exc_info=True)
1256 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1257
tierno7edb6752016-03-21 17:37:52 +01001258
1259@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
1260def http_post_scenario_action(tenant_id, scenario_id):
1261 '''take an action over a scenario'''
tiernof97fd272016-07-11 14:32:37 +02001262 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001263 # parse input data
1264 http_content, _ = format_in(scenario_action_schema)
1265 r = utils.remove_extra_items(http_content, scenario_action_schema)
1266 if r:
1267 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001268 try:
tierno551e5322017-01-19 16:16:26 +01001269 # check valid tenant_id
1270 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001271 if "start" in http_content:
1272 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
1273 http_content['start'].get('description',http_content['start']['instance_name']),
1274 http_content['start'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001275 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001276 elif "deploy" in http_content: #Equivalent to start
1277 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['deploy']['instance_name'],
1278 http_content['deploy'].get('description',http_content['deploy']['instance_name']),
1279 http_content['deploy'].get('datacenter') )
tierno7edb6752016-03-21 17:37:52 +01001280 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001281 elif "reserve" in http_content: #Reserve resources
1282 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['reserve']['instance_name'],
1283 http_content['reserve'].get('description',http_content['reserve']['instance_name']),
1284 http_content['reserve'].get('datacenter'), startvms=False )
tierno7edb6752016-03-21 17:37:52 +01001285 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001286 elif "verify" in http_content: #Equivalent to start and then delete
1287 data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['verify']['instance_name'],
1288 http_content['verify'].get('description',http_content['verify']['instance_name']),
1289 http_content['verify'].get('datacenter'), startvms=False )
1290 instance_id = data['uuid']
1291 nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001292 return format_out({"result":"Verify OK"})
tiernof97fd272016-07-11 14:32:37 +02001293 except (nfvo.NfvoException, db_base_Exception) as e:
1294 logger.error("http_post_scenario_action error {}: {}".format(e.http_code, str(e)))
1295 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001296 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001297 logger.error("Unexpected exception: ", exc_info=True)
1298 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1299
tierno7edb6752016-03-21 17:37:52 +01001300
1301@bottle.route(url_base + '/<tenant_id>/scenarios', method='GET')
1302def http_get_scenarios(tenant_id):
1303 '''get scenarios list'''
tiernof97fd272016-07-11 14:32:37 +02001304 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1305 try:
1306 #check valid tenant_id
1307 if tenant_id != "any":
1308 nfvo.check_tenant(mydb, tenant_id)
1309 #obtain data
tiernof1ba57e2017-09-07 12:23:19 +02001310 s,w,l=filter_query_string(bottle.request.query, None,
1311 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
tiernof97fd272016-07-11 14:32:37 +02001312 where_or={}
1313 if tenant_id != "any":
1314 where_or["tenant_id"] = tenant_id
1315 where_or["public"] = True
1316 scenarios = mydb.get_rows(SELECT=s, WHERE=w, WHERE_OR=where_or, WHERE_AND_OR="AND", LIMIT=l, FROM='scenarios')
1317 convert_datetime2str(scenarios)
1318 utils.convert_str2boolean(scenarios, ('public',) )
1319 data={'scenarios':scenarios}
tierno7edb6752016-03-21 17:37:52 +01001320 #print json.dumps(scenarios, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001321 return format_out(data)
1322 except (nfvo.NfvoException, db_base_Exception) as e:
1323 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1324 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001325 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001326 logger.error("Unexpected exception: ", exc_info=True)
1327 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1328
tierno7edb6752016-03-21 17:37:52 +01001329
1330@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='GET')
1331def http_get_scenario_id(tenant_id, scenario_id):
1332 '''get scenario details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001333 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1334 try:
1335 #check valid tenant_id
1336 if tenant_id != "any":
1337 nfvo.check_tenant(mydb, tenant_id)
1338 #obtain data
1339 scenario = mydb.get_scenario(scenario_id, tenant_id)
1340 convert_datetime2str(scenario)
1341 data={'scenario' : scenario}
tierno7edb6752016-03-21 17:37:52 +01001342 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001343 except (nfvo.NfvoException, db_base_Exception) as e:
1344 logger.error("http_get_scenarios error {}: {}".format(e.http_code, str(e)))
1345 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001346 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001347 logger.error("Unexpected exception: ", exc_info=True)
1348 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1349
tierno7edb6752016-03-21 17:37:52 +01001350
1351@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
1352def http_delete_scenario_id(tenant_id, scenario_id):
1353 '''delete a scenario from database, can use both uuid or name'''
tierno664691a2017-01-31 12:43:46 +01001354 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tiernof97fd272016-07-11 14:32:37 +02001355 try:
1356 #check valid tenant_id
1357 if tenant_id != "any":
tierno664691a2017-01-31 12:43:46 +01001358 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001359 #obtain data
1360 data = mydb.delete_scenario(scenario_id, tenant_id)
tierno7edb6752016-03-21 17:37:52 +01001361 #print json.dumps(data, indent=4)
1362 return format_out({"result":"scenario " + data + " deleted"})
tiernof97fd272016-07-11 14:32:37 +02001363 except (nfvo.NfvoException, db_base_Exception) as e:
1364 logger.error("http_delete_scenario_id error {}: {}".format(e.http_code, str(e)))
1365 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001366 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001367 logger.error("Unexpected exception: ", exc_info=True)
1368 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001369
1370
1371@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='PUT')
1372def http_put_scenario_id(tenant_id, scenario_id):
1373 '''edit an existing scenario id'''
tiernof97fd272016-07-11 14:32:37 +02001374 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno7edb6752016-03-21 17:37:52 +01001375 http_content,_ = format_in( scenario_edit_schema )
tierno42fcc3b2016-07-06 17:20:40 +02001376 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
tierno7edb6752016-03-21 17:37:52 +01001377 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
tiernof97fd272016-07-11 14:32:37 +02001378 #print "http_put_scenario_id input: ", http_content
1379 try:
1380 nfvo.edit_scenario(mydb, tenant_id, scenario_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001381 #print json.dumps(data, indent=4)
1382 #return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001383 return http_get_scenario_id(tenant_id, scenario_id)
1384 except (nfvo.NfvoException, db_base_Exception) as e:
1385 logger.error("http_put_scenario_id error {}: {}".format(e.http_code, str(e)))
1386 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +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))
tierno7edb6752016-03-21 17:37:52 +01001390
1391@bottle.route(url_base + '/<tenant_id>/instances', method='POST')
1392def http_post_instances(tenant_id):
garciadeblasedca7b32016-09-29 14:01:52 +00001393 '''create an instance-scenario'''
tiernof97fd272016-07-11 14:32:37 +02001394 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001395 # parse input data
1396 http_content, used_schema = format_in(instance_scenario_create_schema_v01)
1397 r = utils.remove_extra_items(http_content, used_schema)
1398 if r is not None:
1399 logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001400 try:
1401 #check valid tenant_id
1402 if tenant_id != "any":
1403 nfvo.check_tenant(mydb, tenant_id)
tiernof97fd272016-07-11 14:32:37 +02001404 data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
tierno7edb6752016-03-21 17:37:52 +01001405 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001406 except (nfvo.NfvoException, db_base_Exception) as e:
1407 logger.error("http_post_instances error {}: {}".format(e.http_code, str(e)))
1408 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001409 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001410 logger.error("Unexpected exception: ", exc_info=True)
1411 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001412
1413#
1414# INSTANCES
1415#
1416@bottle.route(url_base + '/<tenant_id>/instances', method='GET')
1417def http_get_instances(tenant_id):
1418 '''get instance list'''
tiernof97fd272016-07-11 14:32:37 +02001419 try:
1420 #check valid tenant_id
1421 if tenant_id != "any":
1422 nfvo.check_tenant(mydb, tenant_id)
1423 #obtain data
1424 s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1425 if tenant_id != "any":
1426 w['tenant_id'] = tenant_id
1427 instances = mydb.get_rows(SELECT=s, WHERE=w, LIMIT=l, FROM='instance_scenarios')
1428 convert_datetime2str(instances)
1429 utils.convert_str2boolean(instances, ('public',) )
1430 data={'instances':instances}
1431 return format_out(data)
1432 except (nfvo.NfvoException, db_base_Exception) as e:
1433 logger.error("http_get_instances error {}: {}".format(e.http_code, str(e)))
1434 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001435 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001436 logger.error("Unexpected exception: ", exc_info=True)
1437 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1438
tierno7edb6752016-03-21 17:37:52 +01001439
1440@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='GET')
1441def http_get_instance_id(tenant_id, instance_id):
1442 '''get instances details, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001443 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1444 try:
1445 #check valid tenant_id
1446 if tenant_id != "any":
1447 nfvo.check_tenant(mydb, tenant_id)
1448 if tenant_id == "any":
1449 tenant_id = None
1450 #obtain data (first time is only to check that the instance exists)
1451 instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
1452 try:
1453 nfvo.refresh_instance(mydb, tenant_id, instance_dict)
1454 except (nfvo.NfvoException, db_base_Exception) as e:
1455 logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
tiernof5755962017-07-13 15:44:34 +02001456 # obtain data with results upated
tiernof97fd272016-07-11 14:32:37 +02001457 instance = mydb.get_instance_scenario(instance_id, tenant_id)
tiernof5755962017-07-13 15:44:34 +02001458 # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
1459 for vnf in instance.get("vnfs", ()):
1460 for vm in vnf.get("vms", ()):
1461 for iface in vm.get("interfaces", ()):
1462 if iface.get("ip_address"):
1463 index = iface["ip_address"].find(";")
1464 if index >= 0:
1465 iface["ip_address"] = iface["ip_address"][:index]
tiernof97fd272016-07-11 14:32:37 +02001466 convert_datetime2str(instance)
tiernof5755962017-07-13 15:44:34 +02001467 # print json.dumps(instance, indent=4)
tiernof97fd272016-07-11 14:32:37 +02001468 return format_out(instance)
1469 except (nfvo.NfvoException, db_base_Exception) as e:
1470 logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
1471 bottle.abort(e.http_code, str(e))
tierno8e995ce2016-09-22 08:13:00 +00001472 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001473 logger.error("Unexpected exception: ", exc_info=True)
1474 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1475
tierno7edb6752016-03-21 17:37:52 +01001476
1477@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>', method='DELETE')
1478def http_delete_instance_id(tenant_id, instance_id):
1479 '''delete instance from VIM and from database, can use both uuid or name'''
tiernof97fd272016-07-11 14:32:37 +02001480 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1481 try:
1482 #check valid tenant_id
1483 if tenant_id != "any":
1484 nfvo.check_tenant(mydb, tenant_id)
1485 if tenant_id == "any":
1486 tenant_id = None
1487 #obtain data
1488 message = nfvo.delete_instance(mydb, tenant_id,instance_id)
tierno7edb6752016-03-21 17:37:52 +01001489 return format_out({"result":message})
tiernof97fd272016-07-11 14:32:37 +02001490 except (nfvo.NfvoException, db_base_Exception) as e:
1491 logger.error("http_delete_instance_id error {}: {}".format(e.http_code, str(e)))
1492 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001493 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001494 logger.error("Unexpected exception: ", exc_info=True)
1495 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1496
tierno7edb6752016-03-21 17:37:52 +01001497
1498@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
1499def http_post_instance_scenario_action(tenant_id, instance_id):
tierno868220c2017-09-26 00:11:05 +02001500 """
1501 take an action over a scenario instance
1502 :param tenant_id: tenant where user belongs to
1503 :param instance_id: instance indentity
1504 :return:
1505 """
tiernof97fd272016-07-11 14:32:37 +02001506 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
tierno551e5322017-01-19 16:16:26 +01001507 # parse input data
1508 http_content, _ = format_in(instance_scenario_action_schema)
1509 r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
1510 if r:
1511 logger.debug("Remove received extra items %s", str(r))
tiernof97fd272016-07-11 14:32:37 +02001512 try:
1513 #check valid tenant_id
1514 if tenant_id != "any":
1515 nfvo.check_tenant(mydb, tenant_id)
1516
tiernof97fd272016-07-11 14:32:37 +02001517 #print "http_post_instance_scenario_action input: ", http_content
1518 #obtain data
1519 instance = mydb.get_instance_scenario(instance_id, tenant_id)
1520 instance_id = instance["uuid"]
1521
1522 data = nfvo.instance_action(mydb, tenant_id, instance_id, http_content)
tierno7edb6752016-03-21 17:37:52 +01001523 return format_out(data)
tiernof97fd272016-07-11 14:32:37 +02001524 except (nfvo.NfvoException, db_base_Exception) as e:
1525 logger.error("http_post_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1526 bottle.abort(e.http_code, str(e))
tierno65a9b0c2016-09-28 14:57:25 +00001527 except Exception as e:
garciadeblasedca7b32016-09-29 14:01:52 +00001528 logger.error("Unexpected exception: ", exc_info=True)
1529 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
tierno7edb6752016-03-21 17:37:52 +01001530
1531
tierno868220c2017-09-26 00:11:05 +02001532@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='GET')
1533@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action/<action_id>', method='GET')
1534def http_get_instance_scenario_action(tenant_id, instance_id, action_id=None):
1535 """
1536 List the actions done over an instance, or the action details
1537 :param tenant_id: tenant where user belongs to. Can be "any" to ignore
1538 :param instance_id: instance id, can be "any" to get actions of all instances
1539 :return:
1540 """
1541 logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
1542 try:
1543 # check valid tenant_id
1544 if tenant_id != "any":
1545 nfvo.check_tenant(mydb, tenant_id)
1546 data = nfvo.instance_action_get(mydb, tenant_id, instance_id, action_id)
1547 return format_out(data)
1548 except (nfvo.NfvoException, db_base_Exception) as e:
1549 logger.error("http_get_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
1550 bottle.abort(e.http_code, str(e))
1551 except Exception as e:
1552 logger.error("Unexpected exception: ", exc_info=True)
1553 bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
1554
gcalvinoc62cfa52017-10-05 18:21:25 +02001555def remove_clear_passwd(data):
1556 """
1557 Removes clear passwords from the data received
1558 :param data: data with clear password
1559 :return: data without the password information
1560 """
1561
1562 passw = ['password: ', 'passwd: ']
1563
1564 for pattern in passw:
1565 init = data.find(pattern)
1566 while init != -1:
1567 end = data.find('\n', init)
1568 data = data[:init] + '{}******'.format(pattern) + data[end:]
1569 init += 1
1570 init = data.find(pattern, init)
1571 return data
tierno868220c2017-09-26 00:11:05 +02001572
tierno7edb6752016-03-21 17:37:52 +01001573@bottle.error(400)
1574@bottle.error(401)
1575@bottle.error(404)
1576@bottle.error(403)
1577@bottle.error(405)
1578@bottle.error(406)
1579@bottle.error(409)
1580@bottle.error(503)
1581@bottle.error(500)
1582def error400(error):
1583 e={"error":{"code":error.status_code, "type":error.status, "description":error.body}}
1584 bottle.response.headers['Access-Control-Allow-Origin'] = '*'
1585 return format_out(e)
1586