1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
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
12 # http://www.apache.org/licenses/LICENSE-2.0
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
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
26 and will use the nfvo.py module to run the appropriate method.
27 Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
29 __author__
="Alfonso Tierno, Gerardo Garcia"
30 __date__
="$17-sep-2014 09:07:15$"
39 from jsonschema
import validate
as js_v
, exceptions
as js_e
40 from openmano_schemas
import vnfd_schema_v01
, vnfd_schema_v02
, \
41 nsd_schema_v01
, nsd_schema_v02
, nsd_schema_v03
, scenario_edit_schema
, \
42 scenario_action_schema
, instance_scenario_action_schema
, instance_scenario_create_schema_v01
, \
43 tenant_schema
, tenant_edit_schema
,\
44 datacenter_schema
, datacenter_edit_schema
, datacenter_action_schema
, datacenter_associate_schema
,\
45 object_schema
, netmap_new_schema
, netmap_edit_schema
48 from db_base
import db_base_Exception
49 from functools
import wraps
57 HTTP_Bad_Request
= 400
58 HTTP_Unauthorized
= 401
61 HTTP_Method_Not_Allowed
= 405
62 HTTP_Not_Acceptable
= 406
63 HTTP_Service_Unavailable
= 503
64 HTTP_Internal_Server_Error
= 500
66 def delete_nulls(var
):
69 if var
[k
] is None: del var
[k
]
70 elif type(var
[k
]) is dict or type(var
[k
]) is list or type(var
[k
]) is tuple:
71 if delete_nulls(var
[k
]): del var
[k
]
72 if len(var
) == 0: return True
73 elif type(var
) is list or type(var
) is tuple:
75 if type(k
) is dict: delete_nulls(k
)
76 if len(var
) == 0: return True
79 def convert_datetime2str(var
):
80 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
81 It enters recursively in the dict var finding this kind of variables
84 for k
,v
in var
.items():
85 if type(v
) is float and k
in ("created_at", "modified_at"):
86 var
[k
] = time
.strftime("%Y-%m-%dT%H:%M:%S", time
.localtime(v
) )
87 elif type(v
) is dict or type(v
) is list or type(v
) is tuple:
88 convert_datetime2str(v
)
89 if len(var
) == 0: return True
90 elif type(var
) is list or type(var
) is tuple:
92 convert_datetime2str(v
)
94 def log_to_logger(fn
):
96 Wrap a Bottle request so that a log line is emitted after it's handled.
97 (This decorator can be extended to take the desired logger as a param.)
100 def _log_to_logger(*args
, **kwargs
):
101 actual_response
= fn(*args
, **kwargs
)
102 # modify this to log exactly what you need:
103 logger
.info('FROM %s %s %s %s' % (bottle
.request
.remote_addr
,
104 bottle
.request
.method
,
106 bottle
.response
.status
))
107 return actual_response
108 return _log_to_logger
110 class httpserver(threading
.Thread
):
111 def __init__(self
, db
, admin
=False, host
='localhost', port
=9090):
117 logger
= logging
.getLogger('openmano.http')
118 threading
.Thread
.__init
__(self
)
120 self
.port
= port
#Port where the listen service must be started
122 self
.name
= "http_admin"
125 #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
127 #self.first_usable_connection_index = 10
128 #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
129 #Ensure that when the main program exits the thread will also exit
134 bottle
.install(log_to_logger
)
135 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=False, quiet
=True)
137 def run_bottle(db
, host_
='localhost', port_
=9090):
138 '''used for launching in main thread, so that it can be debugged'''
141 bottle
.run(host
=host_
, port
=port_
, debug
=True) #quiet=True
144 @bottle.route(url_base
+ '/', method
='GET')
147 return 'works' #TODO: to be completed
153 def change_keys_http2db(data
, http_db
, reverse
=False):
154 '''Change keys of dictionary data acording to the key_dict values
155 This allow change from http interface names to database names.
156 When reverse is True, the change is otherwise
158 data: can be a dictionary or a list
159 http_db: is a dictionary with hhtp names as keys and database names as value
160 reverse: by default change is done from http api to database. If True change is done otherwise
161 Return: None, but data is modified'''
162 if type(data
) is tuple or type(data
) is list:
164 change_keys_http2db(d
, http_db
, reverse
)
165 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
167 for k
,v
in http_db
.items():
168 if v
in data
: data
[k
]=data
.pop(v
)
170 for k
,v
in http_db
.items():
171 if k
in data
: data
[v
]=data
.pop(k
)
173 def format_out(data
):
174 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
175 logger
.debug(yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) )
176 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
177 bottle
.response
.content_type
='application/yaml'
178 return yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) #, canonical=True, default_style='"'
179 else: #by default json
180 bottle
.response
.content_type
='application/json'
181 #return data #json no style
182 return json
.dumps(data
, indent
=4) + "\n"
184 def format_in(default_schema
, version_fields
=None, version_dict_schema
=None):
185 ''' Parse the content of HTTP request against a json_schema
187 default_schema: The schema to be parsed by default if no version field is found in the client data
188 version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
189 version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
190 It can contain a None as key, and this is apply if the client data version does not match any key
192 user_data, used_schema: if the data is successfully decoded and matches the schema
193 launch a bottle abort if fails
195 #print "HEADERS :" + str(bottle.request.headers.items())
197 error_text
= "Invalid header format "
198 format_type
= bottle
.request
.headers
.get('Content-Type', 'application/json')
199 if 'application/json' in format_type
:
200 error_text
= "Invalid json format "
201 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
202 client_data
= json
.load(bottle
.request
.body
)
203 #client_data = bottle.request.json()
204 elif 'application/yaml' in format_type
:
205 error_text
= "Invalid yaml format "
206 client_data
= yaml
.load(bottle
.request
.body
)
207 elif 'application/xml' in format_type
:
208 bottle
.abort(501, "Content-Type: application/xml not supported yet.")
210 print 'Content-Type ' + str(format_type
) + ' not supported.'
211 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
213 #if client_data == None:
214 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
217 #logger.debug('client-data: %s', client_data)
218 #look for the client provider version
219 error_text
= "Invalid content "
220 client_version
= None
222 if version_fields
!= None:
223 client_version
= client_data
224 for field
in version_fields
:
225 if field
in client_version
:
226 client_version
= client_version
[field
]
230 if client_version
==None:
231 used_schema
=default_schema
232 elif version_dict_schema
!=None:
233 if client_version
in version_dict_schema
:
234 used_schema
= version_dict_schema
[client_version
]
235 elif None in version_dict_schema
:
236 used_schema
= version_dict_schema
[None]
237 if used_schema
==None:
238 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version or missing version field")
240 js_v(client_data
, used_schema
)
241 return client_data
, used_schema
242 except (ValueError, yaml
.YAMLError
) as exc
:
243 error_text
+= str(exc
)
245 bottle
.abort(HTTP_Bad_Request
, error_text
)
246 except js_e
.ValidationError
as exc
:
247 print "validate_in error, jsonschema exception ", exc
.message
, "at", exc
.path
249 if len(exc
.path
)>0: error_pos
=" at " + ":".join(map(json
.dumps
, exc
.path
))
250 bottle
.abort(HTTP_Bad_Request
, error_text
+ exc
.message
+ error_pos
)
252 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
255 def filter_query_string(qs
, http2db
, allowed
):
256 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
258 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
259 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
260 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
261 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
262 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
263 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
264 limit: limit dictated by user with the query string 'limit'. 100 by default
265 abort if not permited, using bottel.abort
270 #if type(qs) is not bottle.FormsDict:
271 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
272 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
275 select
+= qs
.getall(k
)
278 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field="+v
+"'")
283 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit="+qs
[k
]+"'")
286 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '"+k
+"="+qs
[k
]+"'")
287 if qs
[k
]!="null": where
[k
]=qs
[k
]
289 if len(select
)==0: select
+= allowed
290 #change from http api to database naming
291 for i
in range(0,len(select
)):
293 if http2db
and k
in http2db
:
294 select
[i
] = http2db
[k
]
296 change_keys_http2db(where
, http2db
)
297 #print "filter_query_string", select,where,limit
299 return select
,where
,limit
301 @bottle.hook('after_request')
303 '''Don't know yet if really needed. Keep it just in case'''
304 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
310 @bottle.route(url_base
+ '/tenants', method
='GET')
311 def http_get_tenants():
312 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
313 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
314 ('uuid','name','description','created_at') )
316 tenants
= mydb
.get_rows(FROM
='nfvo_tenants', SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
317 #change_keys_http2db(content, http2db_tenant, reverse=True)
318 convert_datetime2str(tenants
)
319 data
={'tenants' : tenants
}
320 return format_out(data
)
321 except db_base_Exception
as e
:
322 logger
.error("http_get_tenants error {}: {}".format(e
.http_code
, str(e
)))
323 bottle
.abort(e
.http_code
, str(e
))
325 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
326 def http_get_tenant_id(tenant_id
):
327 '''get tenant details, can use both uuid or name'''
329 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
331 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
, "tenant")
332 #change_keys_http2db(content, http2db_tenant, reverse=True)
333 convert_datetime2str(tenant
)
334 data
={'tenant' : tenant
}
335 return format_out(data
)
336 except db_base_Exception
as e
:
337 logger
.error("http_get_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
338 bottle
.abort(e
.http_code
, str(e
))
340 @bottle.route(url_base
+ '/tenants', method
='POST')
341 def http_post_tenants():
342 '''insert a tenant into the catalogue. '''
344 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
345 http_content
,_
= format_in( tenant_schema
)
346 r
= utils
.remove_extra_items(http_content
, tenant_schema
)
347 if r
is not None: print "http_post_tenants: Warning: remove extra items ", r
349 data
= nfvo
.new_tenant(mydb
, http_content
['tenant'])
350 return http_get_tenant_id(data
)
351 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
352 logger
.error("http_post_tenants error {}: {}".format(e
.http_code
, str(e
)))
353 bottle
.abort(e
.http_code
, str(e
))
355 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
356 def http_edit_tenant_id(tenant_id
):
357 '''edit tenant details, can use both uuid or name'''
359 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
360 http_content
,_
= format_in( tenant_edit_schema
)
361 r
= utils
.remove_extra_items(http_content
, tenant_edit_schema
)
362 if r
is not None: print "http_edit_tenant_id: Warning: remove extra items ", r
364 #obtain data, check that only one exist
366 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
)
368 tenant_id
= tenant
['uuid']
369 where
={'uuid': tenant
['uuid']}
370 mydb
.update_rows('nfvo_tenants', http_content
['tenant'], where
)
371 return http_get_tenant_id(tenant_id
)
372 except db_base_Exception
as e
:
373 logger
.error("http_edit_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
374 bottle
.abort(e
.http_code
, str(e
))
376 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
377 def http_delete_tenant_id(tenant_id
):
378 '''delete a tenant from database, can use both uuid or name'''
379 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
381 data
= nfvo
.delete_tenant(mydb
, tenant_id
)
382 return format_out({"result":"tenant " + data
+ " deleted"})
383 except db_base_Exception
as e
:
384 logger
.error("http_delete_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
385 bottle
.abort(e
.http_code
, str(e
))
388 @bottle.route(url_base
+ '/<tenant_id>/datacenters', method
='GET')
389 def http_get_datacenters(tenant_id
):
390 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
392 if tenant_id
!= 'any':
393 #check valid tenant_id
394 nfvo
.check_tenant(mydb
, tenant_id
)
395 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
396 ('uuid','name','vim_url','type','created_at') )
397 if tenant_id
!= 'any':
398 where_
['nfvo_tenant_id'] = tenant_id
399 if 'created_at' in select_
:
400 select_
[ select_
.index('created_at') ] = 'd.created_at as created_at'
401 if 'created_at' in where_
:
402 where_
['d.created_at'] = where_
.pop('created_at')
403 datacenters
= mydb
.get_rows(FROM
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
404 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
406 datacenters
= mydb
.get_rows(FROM
='datacenters',
407 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
408 #change_keys_http2db(content, http2db_tenant, reverse=True)
409 convert_datetime2str(datacenters
)
410 data
={'datacenters' : datacenters
}
411 return format_out(data
)
412 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
413 logger
.error("http_get_datacenters error {}: {}".format(e
.http_code
, str(e
)))
414 bottle
.abort(e
.http_code
, str(e
))
416 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='GET')
417 def http_get_datacenter_id(tenant_id
, datacenter_id
):
418 '''get datacenter details, can use both uuid or name'''
419 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
421 if tenant_id
!= 'any':
422 #check valid tenant_id
423 nfvo
.check_tenant(mydb
, tenant_id
)
425 what
= 'uuid' if utils
.check_valid_uuid(datacenter_id
) else 'name'
427 where_
[what
] = datacenter_id
428 select_
=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'config', 'description', 'd.created_at as created_at']
429 if tenant_id
!= 'any':
430 select_
.append("datacenter_tenant_id")
431 where_
['td.nfvo_tenant_id']= tenant_id
432 from_
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
434 from_
='datacenters as d'
435 datacenters
= mydb
.get_rows(
440 if len(datacenters
)==0:
441 bottle
.abort( HTTP_Not_Found
, "No datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
442 elif len(datacenters
)>1:
443 bottle
.abort( HTTP_Bad_Request
, "More than one datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
444 datacenter
= datacenters
[0]
445 if tenant_id
!= 'any':
447 vim_tenants
= mydb
.get_rows(
448 SELECT
=("vim_tenant_name", "vim_tenant_id", "user"),
449 FROM
="datacenter_tenants",
450 WHERE
={"uuid": datacenters
[0]["datacenter_tenant_id"]},
451 ORDER_BY
=("created", ) )
452 del datacenter
["datacenter_tenant_id"]
453 datacenter
["vim_tenants"] = vim_tenants
455 if datacenter
['config'] != None:
457 config_dict
= yaml
.load(datacenter
['config'])
458 datacenter
['config'] = config_dict
460 print "Exception '%s' while trying to load config information" % str(e
)
461 #change_keys_http2db(content, http2db_datacenter, reverse=True)
462 convert_datetime2str(datacenter
)
463 data
={'datacenter' : datacenter
}
464 return format_out(data
)
465 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
466 logger
.error("http_get_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
467 bottle
.abort(e
.http_code
, str(e
))
469 @bottle.route(url_base
+ '/datacenters', method
='POST')
470 def http_post_datacenters():
471 '''insert a tenant into the catalogue. '''
473 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
474 http_content
,_
= format_in( datacenter_schema
)
475 r
= utils
.remove_extra_items(http_content
, datacenter_schema
)
476 if r
is not None: print "http_post_datacenters: Warning: remove extra items ", r
478 data
= nfvo
.new_datacenter(mydb
, http_content
['datacenter'])
479 return http_get_datacenter_id('any', data
)
480 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
481 logger
.error("http_post_datacenters error {}: {}".format(e
.http_code
, str(e
)))
482 bottle
.abort(e
.http_code
, str(e
))
484 @bottle.route(url_base
+ '/datacenters/<datacenter_id_name>', method
='PUT')
485 def http_edit_datacenter_id(datacenter_id_name
):
486 '''edit datacenter details, can use both uuid or name'''
487 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
489 http_content
,_
= format_in( datacenter_edit_schema
)
490 r
= utils
.remove_extra_items(http_content
, datacenter_edit_schema
)
491 if r
is not None: print "http_edit_datacenter_id: Warning: remove extra items ", r
494 datacenter_id
= nfvo
.edit_datacenter(mydb
, datacenter_id_name
, http_content
['datacenter'])
495 return http_get_datacenter_id('any', datacenter_id
)
496 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
497 logger
.error("http_edit_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
498 bottle
.abort(e
.http_code
, str(e
))
500 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/networks', method
='GET') #deprecated
501 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='GET')
502 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='GET')
503 def http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
504 '''get datacenter networks, can use both uuid or name'''
505 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
508 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
509 where_
= {"datacenter_id":datacenter_dict
['uuid']}
511 if utils
.check_valid_uuid(netmap_id
):
512 where_
["uuid"] = netmap_id
514 where_
["name"] = netmap_id
515 netmaps
=mydb
.get_rows(FROM
='datacenter_nets',
516 SELECT
=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
518 convert_datetime2str(netmaps
)
519 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
520 if netmap_id
and len(netmaps
)==1:
521 data
={'netmap' : netmaps
[0]}
522 elif netmap_id
and len(netmaps
)==0:
523 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
526 data
={'netmaps' : netmaps
}
527 return format_out(data
)
528 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
529 logger
.error("http_getnetwork_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
530 bottle
.abort(e
.http_code
, str(e
))
532 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='DELETE')
533 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='DELETE')
534 def http_delnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
535 '''get datacenter networks, can use both uuid or name'''
536 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
539 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
540 where_
= {"datacenter_id":datacenter_dict
['uuid']}
542 if utils
.check_valid_uuid(netmap_id
):
543 where_
["uuid"] = netmap_id
545 where_
["name"] = netmap_id
546 #change_keys_http2db(content, http2db_tenant, reverse=True)
547 deleted
= mydb
.delete_row(FROM
='datacenter_nets', WHERE
= where_
)
548 if deleted
== 0 and netmap_id
:
549 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
551 return format_out({"result": "netmap %s deleted" % netmap_id
})
553 return format_out({"result": "%d netmap deleted" % deleted
})
554 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
555 logger
.error("http_delnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
556 bottle
.abort(e
.http_code
, str(e
))
559 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method
='POST')
560 def http_uploadnetmap_datacenter_id(tenant_id
, datacenter_id
):
561 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
563 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, None)
564 convert_datetime2str(netmaps
)
565 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
566 data
={'netmaps' : netmaps
}
567 return format_out(data
)
568 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
569 logger
.error("http_uploadnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
570 bottle
.abort(e
.http_code
, str(e
))
572 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='POST')
573 def http_postnetmap_datacenter_id(tenant_id
, datacenter_id
):
574 '''creates a new netmap'''
575 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
577 http_content
,_
= format_in( netmap_new_schema
)
578 r
= utils
.remove_extra_items(http_content
, netmap_new_schema
)
579 if r
is not None: print "http_postnetmap_datacenter_id: Warning: remove extra items ", r
582 #obtain data, check that only one exist
583 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, http_content
)
584 convert_datetime2str(netmaps
)
585 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
586 data
={'netmaps' : netmaps
}
587 return format_out(data
)
588 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
589 logger
.error("http_postnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
590 bottle
.abort(e
.http_code
, str(e
))
592 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='PUT')
593 def http_putnettmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
):
595 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
597 http_content
,_
= format_in( netmap_edit_schema
)
598 r
= utils
.remove_extra_items(http_content
, netmap_edit_schema
)
599 if r
is not None: print "http_putnettmap_datacenter_id: Warning: remove extra items ", r
601 #obtain data, check that only one exist
603 nfvo
.datacenter_edit_netmap(mydb
, tenant_id
, datacenter_id
, netmap_id
, http_content
)
604 return http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
)
605 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
606 logger
.error("http_putnettmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
607 bottle
.abort(e
.http_code
, str(e
))
610 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/action', method
='POST')
611 def http_action_datacenter_id(tenant_id
, datacenter_id
):
612 '''perform an action over datacenter, can use both uuid or name'''
613 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
615 http_content
,_
= format_in( datacenter_action_schema
)
616 r
= utils
.remove_extra_items(http_content
, datacenter_action_schema
)
617 if r
is not None: print "http_action_datacenter_id: Warning: remove extra items ", r
620 #obtain data, check that only one exist
621 result
= nfvo
.datacenter_action(mydb
, tenant_id
, datacenter_id
, http_content
)
622 if 'net-update' in http_content
:
623 return http_getnetmap_datacenter_id(datacenter_id
)
625 return format_out(result
)
626 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
627 logger
.error("http_action_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
628 bottle
.abort(e
.http_code
, str(e
))
631 @bottle.route(url_base
+ '/datacenters/<datacenter_id>', method
='DELETE')
632 def http_delete_datacenter_id( datacenter_id
):
633 '''delete a tenant from database, can use both uuid or name'''
635 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
637 data
= nfvo
.delete_datacenter(mydb
, datacenter_id
)
638 return format_out({"result":"datacenter '" + data
+ "' deleted"})
639 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
640 logger
.error("http_delete_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
641 bottle
.abort(e
.http_code
, str(e
))
643 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='POST')
644 def http_associate_datacenters(tenant_id
, datacenter_id
):
645 '''associate an existing datacenter to a this tenant. '''
646 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
648 http_content
,_
= format_in( datacenter_associate_schema
)
649 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
650 if r
!= None: print "http_associate_datacenters: Warning: remove extra items ", r
652 id_
= nfvo
.associate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
,
653 http_content
['datacenter'].get('vim_tenant'),
654 http_content
['datacenter'].get('vim_tenant_name'),
655 http_content
['datacenter'].get('vim_username'),
656 http_content
['datacenter'].get('vim_password')
658 return http_get_datacenter_id(tenant_id
, id_
)
659 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
660 logger
.error("http_associate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
661 bottle
.abort(e
.http_code
, str(e
))
663 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='DELETE')
664 def http_deassociate_datacenters(tenant_id
, datacenter_id
):
665 '''deassociate an existing datacenter to a this tenant. '''
666 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
668 data
= nfvo
.deassociate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
)
669 return format_out({"result": data
})
670 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
671 logger
.error("http_deassociate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
672 bottle
.abort(e
.http_code
, str(e
))
674 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='GET')
675 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='GET')
676 def http_get_vim_items(tenant_id
, datacenter_id
, item
, name
=None):
677 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
679 data
= nfvo
.vim_action_get(mydb
, tenant_id
, datacenter_id
, item
, name
)
680 return format_out(data
)
681 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
682 logger
.error("http_get_vim_items error {}: {}".format(e
.http_code
, str(e
)))
683 bottle
.abort(e
.http_code
, str(e
))
685 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='DELETE')
686 def http_del_vim_items(tenant_id
, datacenter_id
, item
, name
):
687 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
689 data
= nfvo
.vim_action_delete(mydb
, tenant_id
, datacenter_id
, item
, name
)
690 return format_out({"result":data
})
691 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
692 logger
.error("http_del_vim_items error {}: {}".format(e
.http_code
, str(e
)))
693 bottle
.abort(e
.http_code
, str(e
))
694 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='POST')
695 def http_post_vim_items(tenant_id
, datacenter_id
, item
):
696 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
697 http_content
,_
= format_in( object_schema
)
699 data
= nfvo
.vim_action_create(mydb
, tenant_id
, datacenter_id
, item
, http_content
)
700 return format_out(data
)
701 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
702 logger
.error("http_post_vim_items error {}: {}".format(e
.http_code
, str(e
)))
703 bottle
.abort(e
.http_code
, str(e
))
705 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='GET')
706 def http_get_vnfs(tenant_id
):
707 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
709 if tenant_id
!= 'any':
710 #check valid tenant_id
711 nfvo
.check_tenant(mydb
, tenant_id
)
712 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
713 ('uuid','name','description','public', "tenant_id", "created_at") )
715 if tenant_id
!= "any":
716 where_or
["tenant_id"] = tenant_id
717 where_or
["public"] = True
718 vnfs
= mydb
.get_rows(FROM
='vnfs', SELECT
=select_
,WHERE
=where_
,WHERE_OR
=where_or
, WHERE_AND_OR
="AND",LIMIT
=limit_
)
719 #change_keys_http2db(content, http2db_vnf, reverse=True)
720 utils
.convert_str2boolean(vnfs
, ('public',))
721 convert_datetime2str(vnfs
)
723 return format_out(data
)
724 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
725 logger
.error("http_get_vnfs error {}: {}".format(e
.http_code
, str(e
)))
726 bottle
.abort(e
.http_code
, str(e
))
728 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='GET')
729 def http_get_vnf_id(tenant_id
,vnf_id
):
730 '''get vnf details, can use both uuid or name'''
731 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
733 vnf
= nfvo
.get_vnf_id(mydb
,tenant_id
,vnf_id
)
734 utils
.convert_str2boolean(vnf
, ('public',))
735 convert_datetime2str(vnf
)
736 return format_out(vnf
)
737 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
738 logger
.error("http_get_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
739 bottle
.abort(e
.http_code
, str(e
))
741 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='POST')
742 def http_post_vnfs(tenant_id
):
743 '''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'''
744 #print "Parsing the YAML file of the VNF"
746 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
747 http_content
, used_schema
= format_in( vnfd_schema_v01
, ("schema_version",), {"0.2": vnfd_schema_v02
})
748 r
= utils
.remove_extra_items(http_content
, used_schema
)
749 if r
is not None: print "http_post_vnfs: Warning: remove extra items ", r
751 if used_schema
== vnfd_schema_v01
:
752 vnf_id
= nfvo
.new_vnf(mydb
,tenant_id
,http_content
)
753 elif used_schema
== vnfd_schema_v02
:
754 vnf_id
= nfvo
.new_vnf_v02(mydb
,tenant_id
,http_content
)
756 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
757 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
758 return http_get_vnf_id(tenant_id
, vnf_id
)
759 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
760 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
761 bottle
.abort(e
.http_code
, str(e
))
763 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='DELETE')
764 def http_delete_vnf_id(tenant_id
,vnf_id
):
765 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
766 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
767 #check valid tenant_id and deletes the vnf, including images,
769 data
= nfvo
.delete_vnf(mydb
,tenant_id
,vnf_id
)
770 #print json.dumps(data, indent=4)
771 return format_out({"result":"VNF " + data
+ " deleted"})
772 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
773 logger
.error("http_delete_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
774 bottle
.abort(e
.http_code
, str(e
))
776 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
777 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
778 @bottle.route(url_base
+ '/<tenant_id>/physicalview/<datacenter>', method
='GET')
779 def http_get_hosts(tenant_id
, datacenter
):
780 '''get the tidvim host hopology from the vim.'''
781 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
782 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
784 if datacenter
== 'treeview':
785 data
= nfvo
.get_hosts(mydb
, tenant_id
)
787 #openmano-gui is using a hardcoded value for the datacenter
788 result
, data
= nfvo
.get_hosts_info(mydb
, tenant_id
) #, datacenter)
791 print "http_get_hosts error %d %s" % (-result
, data
)
792 bottle
.abort(-result
, data
)
794 convert_datetime2str(data
)
795 print json
.dumps(data
, indent
=4)
796 return format_out(data
)
797 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
798 logger
.error("http_get_hosts error {}: {}".format(e
.http_code
, str(e
)))
799 bottle
.abort(e
.http_code
, str(e
))
802 @bottle.route(url_base
+ '/<path:path>', method
='OPTIONS')
803 def http_options_deploy(path
):
804 '''For some reason GUI web ask for OPTIONS that must be responded'''
805 #TODO: check correct path, and correct headers request
806 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
807 bottle
.response
.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
808 bottle
.response
.set_header('Accept','application/yaml,application/json')
809 bottle
.response
.set_header('Content-Type','application/yaml,application/json')
810 bottle
.response
.set_header('Access-Control-Allow-Headers','content-type')
811 bottle
.response
.set_header('Access-Control-Allow-Origin','*')
814 @bottle.route(url_base
+ '/<tenant_id>/topology/deploy', method
='POST')
815 def http_post_deploy(tenant_id
):
816 '''post topology deploy.'''
817 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
819 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
})
820 #r = utils.remove_extra_items(http_content, used_schema)
821 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
822 #print "http_post_deploy input: ", http_content
825 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
826 instance
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['name'], http_content
['name'])
827 #print json.dumps(data, indent=4)
828 return format_out(instance
)
829 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
830 logger
.error("http_post_deploy error {}: {}".format(e
.http_code
, str(e
)))
831 bottle
.abort(e
.http_code
, str(e
))
833 @bottle.route(url_base
+ '/<tenant_id>/topology/verify', method
='POST')
834 def http_post_verify(tenant_id
):
836 # '''post topology verify'''
837 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
838 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
845 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='POST')
846 def http_post_scenarios(tenant_id
):
847 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
848 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
849 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
, "0.3": nsd_schema_v03
})
850 #r = utils.remove_extra_items(http_content, used_schema)
851 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
852 #print "http_post_scenarios input: ", http_content
854 if used_schema
== nsd_schema_v01
:
855 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
856 elif used_schema
== nsd_schema_v02
:
857 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
)
858 elif used_schema
== nsd_schema_v03
:
859 scenario_id
= nfvo
.new_scenario_v03(mydb
, tenant_id
, http_content
)
861 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
862 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
863 #print json.dumps(data, indent=4)
864 #return format_out(data)
865 return http_get_scenario_id(tenant_id
, scenario_id
)
866 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
867 logger
.error("http_post_scenarios error {}: {}".format(e
.http_code
, str(e
)))
868 bottle
.abort(e
.http_code
, str(e
))
870 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>/action', method
='POST')
871 def http_post_scenario_action(tenant_id
, scenario_id
):
872 '''take an action over a scenario'''
873 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
874 #check valid tenant_id
876 nfvo
.check_tenant(mydb
, tenant_id
)
878 http_content
,_
= format_in( scenario_action_schema
)
879 r
= utils
.remove_extra_items(http_content
, scenario_action_schema
)
880 if r
is not None: print "http_post_scenario_action: Warning: remove extra items ", r
881 if "start" in http_content
:
882 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['start']['instance_name'], \
883 http_content
['start'].get('description',http_content
['start']['instance_name']),
884 http_content
['start'].get('datacenter') )
885 return format_out(data
)
886 elif "deploy" in http_content
: #Equivalent to start
887 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['deploy']['instance_name'],
888 http_content
['deploy'].get('description',http_content
['deploy']['instance_name']),
889 http_content
['deploy'].get('datacenter') )
890 return format_out(data
)
891 elif "reserve" in http_content
: #Reserve resources
892 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['reserve']['instance_name'],
893 http_content
['reserve'].get('description',http_content
['reserve']['instance_name']),
894 http_content
['reserve'].get('datacenter'), startvms
=False )
895 return format_out(data
)
896 elif "verify" in http_content
: #Equivalent to start and then delete
897 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['verify']['instance_name'],
898 http_content
['verify'].get('description',http_content
['verify']['instance_name']),
899 http_content
['verify'].get('datacenter'), startvms
=False )
900 instance_id
= data
['uuid']
901 nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
902 return format_out({"result":"Verify OK"})
903 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
904 logger
.error("http_post_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
905 bottle
.abort(e
.http_code
, str(e
))
907 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='GET')
908 def http_get_scenarios(tenant_id
):
909 '''get scenarios list'''
910 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
912 #check valid tenant_id
913 if tenant_id
!= "any":
914 nfvo
.check_tenant(mydb
, tenant_id
)
916 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
918 if tenant_id
!= "any":
919 where_or
["tenant_id"] = tenant_id
920 where_or
["public"] = True
921 scenarios
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, WHERE_OR
=where_or
, WHERE_AND_OR
="AND", LIMIT
=l
, FROM
='scenarios')
922 convert_datetime2str(scenarios
)
923 utils
.convert_str2boolean(scenarios
, ('public',) )
924 data
={'scenarios':scenarios
}
925 #print json.dumps(scenarios, indent=4)
926 return format_out(data
)
927 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
928 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
929 bottle
.abort(e
.http_code
, str(e
))
931 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='GET')
932 def http_get_scenario_id(tenant_id
, scenario_id
):
933 '''get scenario details, can use both uuid or name'''
934 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
936 #check valid tenant_id
937 if tenant_id
!= "any":
938 nfvo
.check_tenant(mydb
, tenant_id
)
940 scenario
= mydb
.get_scenario(scenario_id
, tenant_id
)
941 convert_datetime2str(scenario
)
942 data
={'scenario' : scenario
}
943 return format_out(data
)
944 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
945 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
946 bottle
.abort(e
.http_code
, str(e
))
948 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='DELETE')
949 def http_delete_scenario_id(tenant_id
, scenario_id
):
950 '''delete a scenario from database, can use both uuid or name'''
952 #check valid tenant_id
953 if tenant_id
!= "any":
954 nfvo
.check_tenant(mydb
, tenant_id
)
956 data
= mydb
.delete_scenario(scenario_id
, tenant_id
)
957 #print json.dumps(data, indent=4)
958 return format_out({"result":"scenario " + data
+ " deleted"})
959 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
960 logger
.error("http_delete_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
961 bottle
.abort(e
.http_code
, str(e
))
964 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='PUT')
965 def http_put_scenario_id(tenant_id
, scenario_id
):
966 '''edit an existing scenario id'''
967 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
968 http_content
,_
= format_in( scenario_edit_schema
)
969 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
970 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
971 #print "http_put_scenario_id input: ", http_content
973 nfvo
.edit_scenario(mydb
, tenant_id
, scenario_id
, http_content
)
974 #print json.dumps(data, indent=4)
975 #return format_out(data)
976 return http_get_scenario_id(tenant_id
, scenario_id
)
977 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
978 logger
.error("http_put_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
979 bottle
.abort(e
.http_code
, str(e
))
981 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='POST')
982 def http_post_instances(tenant_id
):
983 '''take an action over a scenario'''
984 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
986 #check valid tenant_id
987 if tenant_id
!= "any":
988 nfvo
.check_tenant(mydb
, tenant_id
)
990 http_content
,used_schema
= format_in( instance_scenario_create_schema_v01
)
991 r
= utils
.remove_extra_items(http_content
, used_schema
)
993 logger
.warning("http_post_instances: Warning: remove extra items %s", str(r
))
994 data
= nfvo
.create_instance(mydb
, tenant_id
, http_content
["instance"])
995 return format_out(data
)
996 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
997 logger
.error("http_post_instances error {}: {}".format(e
.http_code
, str(e
)))
998 bottle
.abort(e
.http_code
, str(e
))
1003 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='GET')
1004 def http_get_instances(tenant_id
):
1005 '''get instance list'''
1007 #check valid tenant_id
1008 if tenant_id
!= "any":
1009 nfvo
.check_tenant(mydb
, tenant_id
)
1011 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1012 if tenant_id
!= "any":
1013 w
['tenant_id'] = tenant_id
1014 instances
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, LIMIT
=l
, FROM
='instance_scenarios')
1015 convert_datetime2str(instances
)
1016 utils
.convert_str2boolean(instances
, ('public',) )
1017 data
={'instances':instances
}
1018 return format_out(data
)
1019 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1020 logger
.error("http_get_instances error {}: {}".format(e
.http_code
, str(e
)))
1021 bottle
.abort(e
.http_code
, str(e
))
1023 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='GET')
1024 def http_get_instance_id(tenant_id
, instance_id
):
1025 '''get instances details, can use both uuid or name'''
1026 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1028 #check valid tenant_id
1029 if tenant_id
!= "any":
1030 nfvo
.check_tenant(mydb
, tenant_id
)
1031 if tenant_id
== "any":
1033 #obtain data (first time is only to check that the instance exists)
1034 instance_dict
= mydb
.get_instance_scenario(instance_id
, tenant_id
, verbose
=True)
1036 nfvo
.refresh_instance(mydb
, tenant_id
, instance_dict
)
1037 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1038 logger
.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e
))
1039 #obtain data with results upated
1040 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1041 convert_datetime2str(instance
)
1042 #print json.dumps(instance, indent=4)
1043 return format_out(instance
)
1044 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1045 logger
.error("http_get_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1046 bottle
.abort(e
.http_code
, str(e
))
1048 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='DELETE')
1049 def http_delete_instance_id(tenant_id
, instance_id
):
1050 '''delete instance from VIM and from database, can use both uuid or name'''
1051 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1053 #check valid tenant_id
1054 if tenant_id
!= "any":
1055 nfvo
.check_tenant(mydb
, tenant_id
)
1056 if tenant_id
== "any":
1059 message
= nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1060 return format_out({"result":message
})
1061 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1062 logger
.error("http_delete_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1063 bottle
.abort(e
.http_code
, str(e
))
1065 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='POST')
1066 def http_post_instance_scenario_action(tenant_id
, instance_id
):
1067 '''take an action over a scenario instance'''
1068 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1070 #check valid tenant_id
1071 if tenant_id
!= "any":
1072 nfvo
.check_tenant(mydb
, tenant_id
)
1075 http_content
,_
= format_in( instance_scenario_action_schema
)
1076 r
= utils
.remove_extra_items(http_content
, instance_scenario_action_schema
)
1077 if r
is not None: print "http_post_instance_scenario_action: Warning: remove extra items ", r
1078 #print "http_post_instance_scenario_action input: ", http_content
1080 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1081 instance_id
= instance
["uuid"]
1083 data
= nfvo
.instance_action(mydb
, tenant_id
, instance_id
, http_content
)
1084 return format_out(data
)
1085 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1086 logger
.error("http_post_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1087 bottle
.abort(e
.http_code
, str(e
))
1099 def error400(error
):
1100 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
1101 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
1102 return format_out(e
)