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
, sdn_controller_schema
, sdn_controller_edit_schema
, \
46 sdn_port_mapping_schema
, sdn_external_port_schema
50 from db_base
import db_base_Exception
51 from functools
import wraps
59 HTTP_Bad_Request
= 400
60 HTTP_Unauthorized
= 401
63 HTTP_Method_Not_Allowed
= 405
64 HTTP_Not_Acceptable
= 406
65 HTTP_Service_Unavailable
= 503
66 HTTP_Internal_Server_Error
= 500
68 def delete_nulls(var
):
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:
77 if type(k
) is dict: delete_nulls(k
)
78 if len(var
) == 0: return True
81 def 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
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:
94 convert_datetime2str(v
)
96 def log_to_logger(fn
):
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.)
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
,
108 bottle
.response
.status
))
109 return actual_response
110 return _log_to_logger
112 class httpserver(threading
.Thread
):
113 def __init__(self
, db
, admin
=False, host
='localhost', port
=9090):
119 logger
= logging
.getLogger('openmano.http')
120 threading
.Thread
.__init
__(self
)
122 self
.port
= port
#Port where the listen service must be started
124 self
.name
= "http_admin"
127 #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
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
136 bottle
.install(log_to_logger
)
137 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=False, quiet
=True)
139 def run_bottle(db
, host_
='localhost', port_
=9090):
140 '''used for launching in main thread, so that it can be debugged'''
143 bottle
.run(host
=host_
, port
=port_
, debug
=True) #quiet=True
146 @bottle.route(url_base
+ '/', method
='GET')
149 return 'works' #TODO: to be completed
155 def 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
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:
166 change_keys_http2db(d
, http_db
, reverse
)
167 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
169 for k
,v
in http_db
.items():
170 if v
in data
: data
[k
]=data
.pop(v
)
172 for k
,v
in http_db
.items():
173 if k
in data
: data
[v
]=data
.pop(k
)
175 def format_out(data
):
176 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
177 logger
.debug("OUT: " + yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) )
178 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
179 bottle
.response
.content_type
='application/yaml'
180 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"
186 def format_in(default_schema
, version_fields
=None, version_dict_schema
=None, confidential_data
=False):
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
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
198 #print "HEADERS :" + str(bottle.request.headers.items())
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.")
213 logger
.warning('Content-Type ' + str(format_type
) + ' not supported.')
214 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
216 # if client_data == None:
217 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
219 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)))
223 logger
.debug('IN: %s', yaml
.safe_dump(client_data
, explicit_start
=True, indent
=4, default_flow_style
=False,
224 tags
=False, encoding
='utf-8', allow_unicode
=True) )
225 # look for the client provider version
226 error_text
= "Invalid content "
227 if not default_schema
and not version_fields
:
228 return client_data
, None
229 client_version
= 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
]
239 if client_version
== None:
240 used_schema
= default_schema
241 elif version_dict_schema
!= None:
242 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")
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
)
253 logger
.error(error_text
)
254 bottle
.abort(HTTP_Bad_Request
, error_text
)
255 except js_e
.ValidationError
as exc
:
256 logger
.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc
.path
), str(exc
.message
))
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
)
261 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
264 def filter_query_string(qs
, http2db
, allowed
):
265 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
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
279 #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")
284 select
+= qs
.getall(k
)
287 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field="+v
+"'")
292 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit="+qs
[k
]+"'")
295 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '"+k
+"="+qs
[k
]+"'")
296 if qs
[k
]!="null": where
[k
]=qs
[k
]
298 if len(select
)==0: select
+= allowed
299 #change from http api to database naming
300 for i
in range(0,len(select
)):
302 if http2db
and k
in http2db
:
303 select
[i
] = http2db
[k
]
305 change_keys_http2db(where
, http2db
)
306 #print "filter_query_string", select,where,limit
308 return select
,where
,limit
310 @bottle.hook('after_request')
312 '''Don't know yet if really needed. Keep it just in case'''
313 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
315 @bottle.route(url_base
+ '/version', method
='GET')
316 def http_get_version():
317 return nfvo
.get_version()
322 @bottle.route(url_base
+ '/tenants', method
='GET')
323 def http_get_tenants():
324 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
325 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
326 ('uuid','name','description','created_at') )
328 tenants
= mydb
.get_rows(FROM
='nfvo_tenants', SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
329 #change_keys_http2db(content, http2db_tenant, reverse=True)
330 convert_datetime2str(tenants
)
331 data
={'tenants' : tenants
}
332 return format_out(data
)
333 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
))
336 except Exception as e
:
337 logger
.error("Unexpected exception: ", exc_info
=True)
338 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
341 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
342 def http_get_tenant_id(tenant_id
):
343 '''get tenant details, can use both uuid or name'''
345 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
347 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 tenants
= mydb
.get_rows(FROM
=from_
, SELECT
=select_
,WHERE
=where_
)
352 #change_keys_http2db(content, http2db_tenant, reverse=True)
353 convert_datetime2str(tenants
)
354 data
= {'tenant' : tenants
[0]}
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
))
359 except Exception as e
:
360 logger
.error("Unexpected exception: ", exc_info
=True)
361 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
364 @bottle.route(url_base
+ '/tenants', method
='POST')
365 def http_post_tenants():
366 '''insert a tenant into the catalogue. '''
368 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
369 http_content
,_
= format_in( tenant_schema
)
370 r
= utils
.remove_extra_items(http_content
, tenant_schema
)
372 logger
.debug("Remove received extra items %s", str(r
))
374 data
= nfvo
.new_tenant(mydb
, http_content
['tenant'])
375 return http_get_tenant_id(data
)
376 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
))
379 except Exception as e
:
380 logger
.error("Unexpected exception: ", exc_info
=True)
381 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
384 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
385 def http_edit_tenant_id(tenant_id
):
386 '''edit tenant details, can use both uuid or name'''
388 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
389 http_content
,_
= format_in( tenant_edit_schema
)
390 r
= utils
.remove_extra_items(http_content
, tenant_edit_schema
)
392 logger
.debug("Remove received extra items %s", str(r
))
394 #obtain data, check that only one exist
396 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
)
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
))
405 except Exception as e
:
406 logger
.error("Unexpected exception: ", exc_info
=True)
407 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
410 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
411 def http_delete_tenant_id(tenant_id
):
412 '''delete a tenant from database, can use both uuid or name'''
413 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
415 data
= nfvo
.delete_tenant(mydb
, tenant_id
)
416 return format_out({"result":"tenant " + data
+ " deleted"})
417 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
))
420 except Exception as e
:
421 logger
.error("Unexpected exception: ", exc_info
=True)
422 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
425 @bottle.route(url_base
+ '/<tenant_id>/datacenters', method
='GET')
426 def http_get_datacenters(tenant_id
):
427 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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_
)
443 datacenters
= mydb
.get_rows(FROM
='datacenters',
444 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
445 #change_keys_http2db(content, http2db_tenant, reverse=True)
446 convert_datetime2str(datacenters
)
447 data
={'datacenters' : datacenters
}
448 return format_out(data
)
449 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
))
452 except Exception as e
:
453 logger
.error("Unexpected exception: ", exc_info
=True)
454 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
457 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='GET')
458 def http_get_datacenter_id(tenant_id
, datacenter_id
):
459 '''get datacenter details, can use both uuid or name'''
460 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
462 if tenant_id
!= 'any':
463 #check valid tenant_id
464 nfvo
.check_tenant(mydb
, tenant_id
)
466 what
= 'uuid' if utils
.check_valid_uuid(datacenter_id
) else 'name'
468 where_
[what
] = datacenter_id
469 select_
=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
470 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'
475 from_
='datacenters as d'
476 datacenters
= mydb
.get_rows(
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':
488 vim_tenants
= mydb
.get_rows(
489 SELECT
=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
490 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
495 for vim_tenant
in vim_tenants
:
496 if vim_tenant
["passwd"]:
497 vim_tenant
["passwd"] = "******"
498 if vim_tenant
['config'] != None:
500 config_dict
= yaml
.load(vim_tenant
['config'])
501 vim_tenant
['config'] = config_dict
502 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'] = "******"
508 except Exception as e
:
509 logger
.error("Exception '%s' while trying to load config information", str(e
))
511 if datacenter
['config'] != None:
513 config_dict
= yaml
.load(datacenter
['config'])
514 datacenter
['config'] = config_dict
515 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'] = "******"
521 except Exception as e
:
522 logger
.error("Exception '%s' while trying to load config information", str(e
))
523 #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
))
530 except Exception as e
:
531 logger
.error("Unexpected exception: ", exc_info
=True)
532 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
535 @bottle.route(url_base
+ '/datacenters', method
='POST')
536 def http_post_datacenters():
537 '''insert a datacenter into the catalogue. '''
539 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
540 http_content
,_
= format_in(datacenter_schema
, confidential_data
=True)
541 r
= utils
.remove_extra_items(http_content
, datacenter_schema
)
543 logger
.debug("Remove received extra items %s", str(r
))
545 data
= nfvo
.new_datacenter(mydb
, http_content
['datacenter'])
546 return http_get_datacenter_id('any', data
)
547 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
))
550 except Exception as e
:
551 logger
.error("Unexpected exception: ", exc_info
=True)
552 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
555 @bottle.route(url_base
+ '/datacenters/<datacenter_id_name>', method
='PUT')
556 def http_edit_datacenter_id(datacenter_id_name
):
557 '''edit datacenter details, can use both uuid or name'''
558 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
560 http_content
,_
= format_in( datacenter_edit_schema
)
561 r
= utils
.remove_extra_items(http_content
, datacenter_edit_schema
)
563 logger
.debug("Remove received extra items %s", str(r
))
566 datacenter_id
= nfvo
.edit_datacenter(mydb
, datacenter_id_name
, http_content
['datacenter'])
567 return http_get_datacenter_id('any', datacenter_id
)
568 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
))
571 except Exception as e
:
572 logger
.error("Unexpected exception: ", exc_info
=True)
573 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
575 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='POST')
576 def http_post_sdn_controller(tenant_id
):
577 '''insert a sdn controller into the catalogue. '''
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
)
582 logger
.debug("tenant_id: "+tenant_id
)
583 #logger.debug("content: {}".format(http_content['sdn_controller']))
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
))
594 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='PUT')
595 def http_put_sdn_controller_update(tenant_id
, controller_id
):
596 '''Update sdn controller'''
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)
602 # logger.debug("Remove received extra items %s", str(r))
604 #logger.debug("tenant_id: "+tenant_id)
605 logger
.debug("content: {}".format(http_content
['sdn_controller']))
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
)})
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
))
617 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='GET')
618 def http_get_sdn_controller(tenant_id
):
619 '''get sdn controllers list, can use both uuid or name'''
621 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
632 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='GET')
633 def http_get_sdn_controller_id(tenant_id
, controller_id
):
634 '''get sdn controller details, can use both uuid or name'''
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
))
646 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='DELETE')
647 def http_delete_sdn_controller_id(tenant_id
, controller_id
):
648 '''delete sdn controller, can use both uuid or name'''
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
))
660 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='POST')
661 def http_post_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
662 '''Set the sdn port mapping for a datacenter. '''
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)
668 # logger.debug("Remove received extra items %s", str(r))
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
))
679 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='GET')
680 def http_get_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
681 '''get datacenter sdn mapping details, can use both uuid or name'''
683 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
694 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='DELETE')
695 def http_delete_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
696 '''clean datacenter sdn mapping, can use both uuid or name'''
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
))
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')
711 def http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
712 '''get datacenter networks, can use both uuid or name'''
713 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
716 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
717 where_
= {"datacenter_id":datacenter_dict
['uuid']}
719 if utils
.check_valid_uuid(netmap_id
):
720 where_
["uuid"] = netmap_id
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'),
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())) )
734 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
))
739 except Exception as e
:
740 logger
.error("Unexpected exception: ", exc_info
=True)
741 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
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')
746 def http_delnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
747 '''get datacenter networks, can use both uuid or name'''
748 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
751 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
752 where_
= {"datacenter_id":datacenter_dict
['uuid']}
754 if utils
.check_valid_uuid(netmap_id
):
755 where_
["uuid"] = netmap_id
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())) )
763 return format_out({"result": "netmap %s deleted" % netmap_id
})
765 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
))
769 except Exception as e
:
770 logger
.error("Unexpected exception: ", exc_info
=True)
771 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
774 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method
='POST')
775 def http_uploadnetmap_datacenter_id(tenant_id
, datacenter_id
):
776 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
786 except Exception as e
:
787 logger
.error("Unexpected exception: ", exc_info
=True)
788 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
791 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='POST')
792 def http_postnetmap_datacenter_id(tenant_id
, datacenter_id
):
793 '''creates a new netmap'''
794 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
796 http_content
,_
= format_in( netmap_new_schema
)
797 r
= utils
.remove_extra_items(http_content
, netmap_new_schema
)
799 logger
.debug("Remove received extra items %s", str(r
))
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
))
810 except Exception as e
:
811 logger
.error("Unexpected exception: ", exc_info
=True)
812 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
815 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='PUT')
816 def http_putnettmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
):
818 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
820 http_content
,_
= format_in( netmap_edit_schema
)
821 r
= utils
.remove_extra_items(http_content
, netmap_edit_schema
)
823 logger
.debug("Remove received extra items %s", str(r
))
825 #obtain data, check that only one exist
827 nfvo
.datacenter_edit_netmap(mydb
, tenant_id
, datacenter_id
, netmap_id
, http_content
)
828 return http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
)
829 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
))
832 except Exception as e
:
833 logger
.error("Unexpected exception: ", exc_info
=True)
834 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
837 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/action', method
='POST')
838 def http_action_datacenter_id(tenant_id
, datacenter_id
):
839 '''perform an action over datacenter, can use both uuid or name'''
840 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
842 http_content
,_
= format_in( datacenter_action_schema
)
843 r
= utils
.remove_extra_items(http_content
, datacenter_action_schema
)
845 logger
.debug("Remove received extra items %s", str(r
))
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
)
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
))
856 except Exception as e
:
857 logger
.error("Unexpected exception: ", exc_info
=True)
858 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
861 @bottle.route(url_base
+ '/datacenters/<datacenter_id>', method
='DELETE')
862 def http_delete_datacenter_id( datacenter_id
):
863 '''delete a tenant from database, can use both uuid or name'''
865 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
872 except Exception as e
:
873 logger
.error("Unexpected exception: ", exc_info
=True)
874 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
877 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='POST')
878 def http_associate_datacenters(tenant_id
, datacenter_id
):
879 '''associate an existing datacenter to a this tenant. '''
880 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
882 http_content
,_
= format_in(datacenter_associate_schema
, confidential_data
=True)
883 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
885 logger
.debug("Remove received extra items %s", str(r
))
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'),
891 http_content
['datacenter'].get('vim_password'),
892 http_content
['datacenter'].get('config')
894 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
))
898 except Exception as e
:
899 logger
.error("Unexpected exception: ", exc_info
=True)
900 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
902 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='PUT')
903 def 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
)
907 http_content
,_
= format_in( datacenter_associate_schema
)
908 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
910 logger
.debug("Remove received extra items %s", str(r
))
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')
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
))
927 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='DELETE')
928 def http_deassociate_datacenters(tenant_id
, datacenter_id
):
929 '''deassociate an existing datacenter to a this tenant. '''
930 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
937 except Exception as e
:
938 logger
.error("Unexpected exception: ", exc_info
=True)
939 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
941 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method
='POST')
942 def 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
)
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
))
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')
957 def 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
)
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
))
969 @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')
971 def http_get_vim_items(tenant_id
, datacenter_id
, item
, name
=None):
972 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
974 data
= nfvo
.vim_action_get(mydb
, tenant_id
, datacenter_id
, item
, name
)
975 return format_out(data
)
976 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
))
979 except Exception as e
:
980 logger
.error("Unexpected exception: ", exc_info
=True)
981 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
984 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='DELETE')
985 def http_del_vim_items(tenant_id
, datacenter_id
, item
, name
):
986 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
988 data
= nfvo
.vim_action_delete(mydb
, tenant_id
, datacenter_id
, item
, name
)
989 return format_out({"result":data
})
990 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
))
993 except Exception as e
:
994 logger
.error("Unexpected exception: ", exc_info
=True)
995 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
998 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='POST')
999 def http_post_vim_items(tenant_id
, datacenter_id
, item
):
1000 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1001 http_content
,_
= format_in( object_schema
)
1003 data
= nfvo
.vim_action_create(mydb
, tenant_id
, datacenter_id
, item
, http_content
)
1004 return format_out(data
)
1005 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
))
1008 except Exception as e
:
1009 logger
.error("Unexpected exception: ", exc_info
=True)
1010 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1013 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='GET')
1014 def http_get_vnfs(tenant_id
):
1015 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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,
1021 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
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_
)
1027 #change_keys_http2db(content, http2db_vnf, reverse=True)
1028 utils
.convert_str2boolean(vnfs
, ('public',))
1029 convert_datetime2str(vnfs
)
1030 data
={'vnfs' : vnfs
}
1031 return format_out(data
)
1032 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
))
1035 except Exception as e
:
1036 logger
.error("Unexpected exception: ", exc_info
=True)
1037 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1040 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='GET')
1041 def http_get_vnf_id(tenant_id
,vnf_id
):
1042 '''get vnf details, can use both uuid or name'''
1043 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
1052 except Exception as e
:
1053 logger
.error("Unexpected exception: ", exc_info
=True)
1054 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1057 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='POST')
1058 def http_post_vnfs(tenant_id
):
1059 """ 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
1063 # print "Parsing the YAML file of the VNF"
1065 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1066 http_content
, used_schema
= format_in( vnfd_schema_v01
, ("schema_version",), {"0.2": vnfd_schema_v02
})
1067 r
= utils
.remove_extra_items(http_content
, used_schema
)
1069 logger
.debug("Remove received extra items %s", str(r
))
1071 if used_schema
== vnfd_schema_v01
:
1072 vnf_id
= nfvo
.new_vnf(mydb
,tenant_id
,http_content
)
1073 elif used_schema
== vnfd_schema_v02
:
1074 vnf_id
= nfvo
.new_vnf_v02(mydb
,tenant_id
,http_content
)
1076 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1077 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1078 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
))
1082 except Exception as e
:
1083 logger
.error("Unexpected exception: ", exc_info
=True)
1084 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1087 @bottle.route(url_base
+ '/v3/<tenant_id>/vnfd', method
='POST')
1088 def http_post_vnfs_v3(tenant_id
):
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
1094 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1095 http_content
, _
= format_in(None)
1097 vnfd_uuid_list
= nfvo
.new_vnfd_v3(mydb
, tenant_id
, http_content
)
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
))
1112 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='DELETE')
1113 def http_delete_vnf_id(tenant_id
, vnf_id
):
1114 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
1115 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1116 #check valid tenant_id and deletes the vnf, including images,
1118 data
= nfvo
.delete_vnf(mydb
,tenant_id
,vnf_id
)
1119 #print json.dumps(data, indent=4)
1120 return format_out({"result":"VNF " + data
+ " deleted"})
1121 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
))
1124 except Exception as e
:
1125 logger
.error("Unexpected exception: ", exc_info
=True)
1126 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
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')
1132 def http_get_hosts(tenant_id
, datacenter
):
1133 '''get the tidvim host hopology from the vim.'''
1134 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
1137 if datacenter
== 'treeview':
1138 data
= nfvo
.get_hosts(mydb
, tenant_id
)
1140 #openmano-gui is using a hardcoded value for the datacenter
1141 result
, data
= nfvo
.get_hosts_info(mydb
, tenant_id
) #, datacenter)
1144 #print "http_get_hosts error %d %s" % (-result, data)
1145 bottle
.abort(-result
, data
)
1147 convert_datetime2str(data
)
1148 #print json.dumps(data, indent=4)
1149 return format_out(data
)
1150 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1151 logger
.error("http_get_hosts error {}: {}".format(e
.http_code
, str(e
)))
1152 bottle
.abort(e
.http_code
, str(e
))
1153 except Exception as e
:
1154 logger
.error("Unexpected exception: ", exc_info
=True)
1155 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1158 @bottle.route(url_base
+ '/<path:path>', method
='OPTIONS')
1159 def 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
1162 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1163 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','*')
1170 @bottle.route(url_base
+ '/<tenant_id>/topology/deploy', method
='POST')
1171 def http_post_deploy(tenant_id
):
1172 '''post topology deploy.'''
1173 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1175 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
})
1176 #r = utils.remove_extra_items(http_content, used_schema)
1177 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
1178 #print "http_post_deploy input: ", http_content
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
))
1188 except Exception as e
:
1189 logger
.error("Unexpected exception: ", exc_info
=True)
1190 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1193 @bottle.route(url_base
+ '/<tenant_id>/topology/verify', method
='POST')
1194 def http_post_verify(tenant_id
):
1196 # '''post topology verify'''
1197 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
1198 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1205 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='POST')
1206 def http_post_scenarios(tenant_id
):
1207 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
1208 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1209 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
, "0.3": nsd_schema_v03
})
1210 #r = utils.remove_extra_items(http_content, used_schema)
1211 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
1212 #print "http_post_scenarios input: ", http_content
1214 if used_schema
== nsd_schema_v01
:
1215 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1216 elif used_schema
== nsd_schema_v02
:
1217 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.2")
1218 elif used_schema
== nsd_schema_v03
:
1219 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.3")
1221 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1222 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1223 #print json.dumps(data, indent=4)
1224 #return format_out(data)
1225 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
))
1229 except Exception as e
:
1230 logger
.error("Unexpected exception: ", exc_info
=True)
1231 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1233 @bottle.route(url_base
+ '/v3/<tenant_id>/nsd', method
='POST')
1234 def http_post_nsds_v3(tenant_id
):
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
1240 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1241 http_content
, _
= format_in(None)
1243 nsd_uuid_list
= nfvo
.new_nsd_v3(mydb
, tenant_id
, http_content
)
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
))
1259 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>/action', method
='POST')
1260 def http_post_scenario_action(tenant_id
, scenario_id
):
1261 '''take an action over a scenario'''
1262 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1264 http_content
, _
= format_in(scenario_action_schema
)
1265 r
= utils
.remove_extra_items(http_content
, scenario_action_schema
)
1267 logger
.debug("Remove received extra items %s", str(r
))
1269 # check valid tenant_id
1270 nfvo
.check_tenant(mydb
, tenant_id
)
1271 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') )
1275 return format_out(data
)
1276 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') )
1280 return format_out(data
)
1281 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 )
1285 return format_out(data
)
1286 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
)
1292 return format_out({"result":"Verify OK"})
1293 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
))
1296 except Exception as e
:
1297 logger
.error("Unexpected exception: ", exc_info
=True)
1298 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1301 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='GET')
1302 def http_get_scenarios(tenant_id
):
1303 '''get scenarios list'''
1304 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1306 #check valid tenant_id
1307 if tenant_id
!= "any":
1308 nfvo
.check_tenant(mydb
, tenant_id
)
1310 s
,w
,l
=filter_query_string(bottle
.request
.query
, None,
1311 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
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
}
1320 #print json.dumps(scenarios, indent=4)
1321 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
))
1325 except Exception as e
:
1326 logger
.error("Unexpected exception: ", exc_info
=True)
1327 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1330 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='GET')
1331 def http_get_scenario_id(tenant_id
, scenario_id
):
1332 '''get scenario details, can use both uuid or name'''
1333 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1335 #check valid tenant_id
1336 if tenant_id
!= "any":
1337 nfvo
.check_tenant(mydb
, tenant_id
)
1339 scenario
= mydb
.get_scenario(scenario_id
, tenant_id
)
1340 convert_datetime2str(scenario
)
1341 data
={'scenario' : scenario
}
1342 return format_out(data
)
1343 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
))
1346 except Exception as e
:
1347 logger
.error("Unexpected exception: ", exc_info
=True)
1348 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1351 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='DELETE')
1352 def http_delete_scenario_id(tenant_id
, scenario_id
):
1353 '''delete a scenario from database, can use both uuid or name'''
1354 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1356 #check valid tenant_id
1357 if tenant_id
!= "any":
1358 nfvo
.check_tenant(mydb
, tenant_id
)
1360 data
= mydb
.delete_scenario(scenario_id
, tenant_id
)
1361 #print json.dumps(data, indent=4)
1362 return format_out({"result":"scenario " + data
+ " deleted"})
1363 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
))
1366 except Exception as e
:
1367 logger
.error("Unexpected exception: ", exc_info
=True)
1368 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1371 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='PUT')
1372 def http_put_scenario_id(tenant_id
, scenario_id
):
1373 '''edit an existing scenario id'''
1374 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1375 http_content
,_
= format_in( scenario_edit_schema
)
1376 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1377 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1378 #print "http_put_scenario_id input: ", http_content
1380 nfvo
.edit_scenario(mydb
, tenant_id
, scenario_id
, http_content
)
1381 #print json.dumps(data, indent=4)
1382 #return format_out(data)
1383 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
))
1387 except Exception as e
:
1388 logger
.error("Unexpected exception: ", exc_info
=True)
1389 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1391 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='POST')
1392 def http_post_instances(tenant_id
):
1393 '''create an instance-scenario'''
1394 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1396 http_content
, used_schema
= format_in(instance_scenario_create_schema_v01
)
1397 r
= utils
.remove_extra_items(http_content
, used_schema
)
1399 logger
.warning("http_post_instances: Warning: remove extra items %s", str(r
))
1401 #check valid tenant_id
1402 if tenant_id
!= "any":
1403 nfvo
.check_tenant(mydb
, tenant_id
)
1404 data
= nfvo
.create_instance(mydb
, tenant_id
, http_content
["instance"])
1405 return format_out(data
)
1406 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
))
1409 except Exception as e
:
1410 logger
.error("Unexpected exception: ", exc_info
=True)
1411 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1416 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='GET')
1417 def http_get_instances(tenant_id
):
1418 '''get instance list'''
1420 #check valid tenant_id
1421 if tenant_id
!= "any":
1422 nfvo
.check_tenant(mydb
, tenant_id
)
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
))
1435 except Exception as e
:
1436 logger
.error("Unexpected exception: ", exc_info
=True)
1437 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1440 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='GET')
1441 def http_get_instance_id(tenant_id
, instance_id
):
1442 '''get instances details, can use both uuid or name'''
1443 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1445 #check valid tenant_id
1446 if tenant_id
!= "any":
1447 nfvo
.check_tenant(mydb
, tenant_id
)
1448 if tenant_id
== "any":
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)
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
))
1456 # obtain data with results upated
1457 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1458 # 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(";")
1465 iface
["ip_address"] = iface
["ip_address"][:index
]
1466 convert_datetime2str(instance
)
1467 # print json.dumps(instance, indent=4)
1468 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
))
1472 except Exception as e
:
1473 logger
.error("Unexpected exception: ", exc_info
=True)
1474 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1477 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='DELETE')
1478 def http_delete_instance_id(tenant_id
, instance_id
):
1479 '''delete instance from VIM and from database, can use both uuid or name'''
1480 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1482 #check valid tenant_id
1483 if tenant_id
!= "any":
1484 nfvo
.check_tenant(mydb
, tenant_id
)
1485 if tenant_id
== "any":
1488 message
= nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1489 return format_out({"result":message
})
1490 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
))
1493 except Exception as e
:
1494 logger
.error("Unexpected exception: ", exc_info
=True)
1495 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1498 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='POST')
1499 def http_post_instance_scenario_action(tenant_id
, instance_id
):
1501 take an action over a scenario instance
1502 :param tenant_id: tenant where user belongs to
1503 :param instance_id: instance indentity
1506 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1508 http_content
, _
= format_in(instance_scenario_action_schema
)
1509 r
= utils
.remove_extra_items(http_content
, instance_scenario_action_schema
)
1511 logger
.debug("Remove received extra items %s", str(r
))
1513 #check valid tenant_id
1514 if tenant_id
!= "any":
1515 nfvo
.check_tenant(mydb
, tenant_id
)
1517 #print "http_post_instance_scenario_action input: ", http_content
1519 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1520 instance_id
= instance
["uuid"]
1522 data
= nfvo
.instance_action(mydb
, tenant_id
, instance_id
, http_content
)
1523 return format_out(data
)
1524 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
))
1527 except Exception as e
:
1528 logger
.error("Unexpected exception: ", exc_info
=True)
1529 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1532 @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')
1534 def http_get_instance_scenario_action(tenant_id
, instance_id
, action_id
=None):
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
1541 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
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
))
1555 def remove_clear_passwd(data
):
1557 Removes clear passwords from the data received
1558 :param data: data with clear password
1559 :return: data without the password information
1562 passw
= ['password: ', 'passwd: ']
1564 for pattern
in passw
:
1565 init
= data
.find(pattern
)
1567 end
= data
.find('\n', init
)
1568 data
= data
[:init
] + '{}******'.format(pattern
) + data
[end
:]
1570 init
= data
.find(pattern
, init
)
1582 def 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
)