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 bottle
.HTTPError
:
335 except db_base_Exception
as e
:
336 logger
.error("http_get_tenants error {}: {}".format(e
.http_code
, str(e
)))
337 bottle
.abort(e
.http_code
, str(e
))
338 except Exception as e
:
339 logger
.error("Unexpected exception: ", exc_info
=True)
340 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
343 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
344 def http_get_tenant_id(tenant_id
):
345 '''get tenant details, can use both uuid or name'''
347 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
349 from_
= 'nfvo_tenants'
350 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, None,
351 ('uuid', 'name', 'description', 'created_at'))
352 what
= 'uuid' if utils
.check_valid_uuid(tenant_id
) else 'name'
353 where_
[what
] = tenant_id
354 tenants
= mydb
.get_rows(FROM
=from_
, SELECT
=select_
,WHERE
=where_
)
355 #change_keys_http2db(content, http2db_tenant, reverse=True)
356 if len(tenants
) == 0:
357 bottle
.abort(HTTP_Not_Found
, "No tenant found with {}='{}'".format(what
, tenant_id
))
358 elif len(tenants
) > 1:
359 bottle
.abort(HTTP_Bad_Request
, "More than one tenant found with {}='{}'".format(what
, tenant_id
))
360 convert_datetime2str(tenants
[0])
361 data
= {'tenant': tenants
[0]}
362 return format_out(data
)
363 except bottle
.HTTPError
:
365 except db_base_Exception
as e
:
366 logger
.error("http_get_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
367 bottle
.abort(e
.http_code
, str(e
))
368 except Exception as e
:
369 logger
.error("Unexpected exception: ", exc_info
=True)
370 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
373 @bottle.route(url_base
+ '/tenants', method
='POST')
374 def http_post_tenants():
375 '''insert a tenant into the catalogue. '''
377 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
378 http_content
,_
= format_in( tenant_schema
)
379 r
= utils
.remove_extra_items(http_content
, tenant_schema
)
381 logger
.debug("Remove received extra items %s", str(r
))
383 data
= nfvo
.new_tenant(mydb
, http_content
['tenant'])
384 return http_get_tenant_id(data
)
385 except bottle
.HTTPError
:
387 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
388 logger
.error("http_post_tenants error {}: {}".format(e
.http_code
, str(e
)))
389 bottle
.abort(e
.http_code
, str(e
))
390 except Exception as e
:
391 logger
.error("Unexpected exception: ", exc_info
=True)
392 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
395 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
396 def http_edit_tenant_id(tenant_id
):
397 '''edit tenant details, can use both uuid or name'''
399 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
400 http_content
,_
= format_in( tenant_edit_schema
)
401 r
= utils
.remove_extra_items(http_content
, tenant_edit_schema
)
403 logger
.debug("Remove received extra items %s", str(r
))
405 #obtain data, check that only one exist
407 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
)
409 tenant_id
= tenant
['uuid']
410 where
={'uuid': tenant
['uuid']}
411 mydb
.update_rows('nfvo_tenants', http_content
['tenant'], where
)
412 return http_get_tenant_id(tenant_id
)
413 except bottle
.HTTPError
:
415 except db_base_Exception
as e
:
416 logger
.error("http_edit_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
417 bottle
.abort(e
.http_code
, str(e
))
418 except Exception as e
:
419 logger
.error("Unexpected exception: ", exc_info
=True)
420 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
423 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
424 def http_delete_tenant_id(tenant_id
):
425 '''delete a tenant from database, can use both uuid or name'''
426 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
428 data
= nfvo
.delete_tenant(mydb
, tenant_id
)
429 return format_out({"result":"tenant " + data
+ " deleted"})
430 except bottle
.HTTPError
:
432 except db_base_Exception
as e
:
433 logger
.error("http_delete_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
434 bottle
.abort(e
.http_code
, str(e
))
435 except Exception as e
:
436 logger
.error("Unexpected exception: ", exc_info
=True)
437 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
440 @bottle.route(url_base
+ '/<tenant_id>/datacenters', method
='GET')
441 def http_get_datacenters(tenant_id
):
442 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
444 if tenant_id
!= 'any':
445 #check valid tenant_id
446 nfvo
.check_tenant(mydb
, tenant_id
)
447 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
448 ('uuid','name','vim_url','type','created_at') )
449 if tenant_id
!= 'any':
450 where_
['nfvo_tenant_id'] = tenant_id
451 if 'created_at' in select_
:
452 select_
[ select_
.index('created_at') ] = 'd.created_at as created_at'
453 if 'created_at' in where_
:
454 where_
['d.created_at'] = where_
.pop('created_at')
455 datacenters
= mydb
.get_rows(FROM
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
456 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
458 datacenters
= mydb
.get_rows(FROM
='datacenters',
459 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
460 #change_keys_http2db(content, http2db_tenant, reverse=True)
461 convert_datetime2str(datacenters
)
462 data
={'datacenters' : datacenters
}
463 return format_out(data
)
464 except bottle
.HTTPError
:
466 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
467 logger
.error("http_get_datacenters error {}: {}".format(e
.http_code
, str(e
)))
468 bottle
.abort(e
.http_code
, str(e
))
469 except Exception as e
:
470 logger
.error("Unexpected exception: ", exc_info
=True)
471 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
474 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='GET')
475 def http_get_datacenter_id(tenant_id
, datacenter_id
):
476 '''get datacenter details, can use both uuid or name'''
477 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
479 if tenant_id
!= 'any':
480 #check valid tenant_id
481 nfvo
.check_tenant(mydb
, tenant_id
)
483 what
= 'uuid' if utils
.check_valid_uuid(datacenter_id
) else 'name'
485 where_
[what
] = datacenter_id
486 select_
=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
487 if tenant_id
!= 'any':
488 select_
.append("datacenter_tenant_id")
489 where_
['td.nfvo_tenant_id']= tenant_id
490 from_
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
492 from_
='datacenters as d'
493 datacenters
= mydb
.get_rows(
498 if len(datacenters
)==0:
499 bottle
.abort( HTTP_Not_Found
, "No datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
500 elif len(datacenters
)>1:
501 bottle
.abort( HTTP_Bad_Request
, "More than one datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
502 datacenter
= datacenters
[0]
503 if tenant_id
!= 'any':
505 vim_tenants
= mydb
.get_rows(
506 SELECT
=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
507 FROM
="datacenter_tenants",
508 WHERE
={"uuid": datacenters
[0]["datacenter_tenant_id"]},
509 ORDER_BY
=("created", ) )
510 del datacenter
["datacenter_tenant_id"]
511 datacenter
["vim_tenants"] = vim_tenants
512 for vim_tenant
in vim_tenants
:
513 if vim_tenant
["passwd"]:
514 vim_tenant
["passwd"] = "******"
515 if vim_tenant
['config'] != None:
517 config_dict
= yaml
.load(vim_tenant
['config'])
518 vim_tenant
['config'] = config_dict
519 if vim_tenant
['config'].get('admin_password'):
520 vim_tenant
['config']['admin_password'] = "******"
521 if vim_tenant
['config'].get('vcenter_password'):
522 vim_tenant
['config']['vcenter_password'] = "******"
523 if vim_tenant
['config'].get('nsx_password'):
524 vim_tenant
['config']['nsx_password'] = "******"
525 except Exception as e
:
526 logger
.error("Exception '%s' while trying to load config information", str(e
))
528 if datacenter
['config'] != None:
530 config_dict
= yaml
.load(datacenter
['config'])
531 datacenter
['config'] = config_dict
532 if datacenter
['config'].get('admin_password'):
533 datacenter
['config']['admin_password'] = "******"
534 if datacenter
['config'].get('vcenter_password'):
535 datacenter
['config']['vcenter_password'] = "******"
536 if datacenter
['config'].get('nsx_password'):
537 datacenter
['config']['nsx_password'] = "******"
538 except Exception as e
:
539 logger
.error("Exception '%s' while trying to load config information", str(e
))
540 #change_keys_http2db(content, http2db_datacenter, reverse=True)
541 convert_datetime2str(datacenter
)
542 data
={'datacenter' : datacenter
}
543 return format_out(data
)
544 except bottle
.HTTPError
:
546 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
547 logger
.error("http_get_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
548 bottle
.abort(e
.http_code
, str(e
))
549 except Exception as e
:
550 logger
.error("Unexpected exception: ", exc_info
=True)
551 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
554 @bottle.route(url_base
+ '/datacenters', method
='POST')
555 def http_post_datacenters():
556 '''insert a datacenter into the catalogue. '''
558 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
559 http_content
,_
= format_in(datacenter_schema
, confidential_data
=True)
560 r
= utils
.remove_extra_items(http_content
, datacenter_schema
)
562 logger
.debug("Remove received extra items %s", str(r
))
564 data
= nfvo
.new_datacenter(mydb
, http_content
['datacenter'])
565 return http_get_datacenter_id('any', data
)
566 except bottle
.HTTPError
:
568 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
569 logger
.error("http_post_datacenters 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
))
576 @bottle.route(url_base
+ '/datacenters/<datacenter_id_name>', method
='PUT')
577 def http_edit_datacenter_id(datacenter_id_name
):
578 '''edit datacenter details, can use both uuid or name'''
579 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
581 http_content
,_
= format_in( datacenter_edit_schema
)
582 r
= utils
.remove_extra_items(http_content
, datacenter_edit_schema
)
584 logger
.debug("Remove received extra items %s", str(r
))
587 datacenter_id
= nfvo
.edit_datacenter(mydb
, datacenter_id_name
, http_content
['datacenter'])
588 return http_get_datacenter_id('any', datacenter_id
)
589 except bottle
.HTTPError
:
591 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
592 logger
.error("http_edit_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
593 bottle
.abort(e
.http_code
, str(e
))
594 except Exception as e
:
595 logger
.error("Unexpected exception: ", exc_info
=True)
596 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
598 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='POST')
599 def http_post_sdn_controller(tenant_id
):
600 '''insert a sdn controller into the catalogue. '''
602 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
603 http_content
,_
= format_in( sdn_controller_schema
)
605 logger
.debug("tenant_id: "+tenant_id
)
606 #logger.debug("content: {}".format(http_content['sdn_controller']))
608 data
= nfvo
.sdn_controller_create(mydb
, tenant_id
, http_content
['sdn_controller'])
609 return format_out({"sdn_controller": nfvo
.sdn_controller_list(mydb
, tenant_id
, data
)})
610 except bottle
.HTTPError
:
612 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
613 logger
.error("http_post_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
614 bottle
.abort(e
.http_code
, str(e
))
615 except Exception as e
:
616 logger
.error("Unexpected exception: ", exc_info
=True)
617 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
619 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='PUT')
620 def http_put_sdn_controller_update(tenant_id
, controller_id
):
621 '''Update sdn controller'''
623 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
624 http_content
,_
= format_in( sdn_controller_edit_schema
)
625 # r = utils.remove_extra_items(http_content, datacenter_schema)
627 # logger.debug("Remove received extra items %s", str(r))
629 #logger.debug("tenant_id: "+tenant_id)
630 logger
.debug("content: {}".format(http_content
['sdn_controller']))
632 data
= nfvo
.sdn_controller_update(mydb
, tenant_id
, controller_id
, http_content
['sdn_controller'])
633 return format_out({"sdn_controller": nfvo
.sdn_controller_list(mydb
, tenant_id
, controller_id
)})
635 except bottle
.HTTPError
:
637 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
638 logger
.error("http_post_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
639 bottle
.abort(e
.http_code
, str(e
))
640 except Exception as e
:
641 logger
.error("Unexpected exception: ", exc_info
=True)
642 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
644 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='GET')
645 def http_get_sdn_controller(tenant_id
):
646 '''get sdn controllers list, can use both uuid or name'''
648 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
650 data
= {'sdn_controllers': nfvo
.sdn_controller_list(mydb
, tenant_id
)}
651 return format_out(data
)
652 except bottle
.HTTPError
:
654 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
655 logger
.error("http_get_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
656 bottle
.abort(e
.http_code
, str(e
))
657 except Exception as e
:
658 logger
.error("Unexpected exception: ", exc_info
=True)
659 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
661 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='GET')
662 def http_get_sdn_controller_id(tenant_id
, controller_id
):
663 '''get sdn controller details, can use both uuid or name'''
665 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
666 data
= nfvo
.sdn_controller_list(mydb
, tenant_id
, controller_id
)
667 return format_out({"sdn_controllers": data
})
668 except bottle
.HTTPError
:
670 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
671 logger
.error("http_get_sdn_controller_id error {}: {}".format(e
.http_code
, str(e
)))
672 bottle
.abort(e
.http_code
, str(e
))
673 except Exception as e
:
674 logger
.error("Unexpected exception: ", exc_info
=True)
675 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
677 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='DELETE')
678 def http_delete_sdn_controller_id(tenant_id
, controller_id
):
679 '''delete sdn controller, can use both uuid or name'''
681 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
682 data
= nfvo
.sdn_controller_delete(mydb
, tenant_id
, controller_id
)
683 return format_out(data
)
684 except bottle
.HTTPError
:
686 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
687 logger
.error("http_delete_sdn_controller_id error {}: {}".format(e
.http_code
, str(e
)))
688 bottle
.abort(e
.http_code
, str(e
))
689 except Exception as e
:
690 logger
.error("Unexpected exception: ", exc_info
=True)
691 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
693 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='POST')
694 def http_post_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
695 '''Set the sdn port mapping for a datacenter. '''
697 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
698 http_content
, _
= format_in(sdn_port_mapping_schema
)
699 # r = utils.remove_extra_items(http_content, datacenter_schema)
701 # logger.debug("Remove received extra items %s", str(r))
703 data
= nfvo
.datacenter_sdn_port_mapping_set(mydb
, tenant_id
, datacenter_id
, http_content
['sdn_port_mapping'])
704 return format_out({"sdn_port_mapping": data
})
705 except bottle
.HTTPError
:
707 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
708 logger
.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
709 bottle
.abort(e
.http_code
, str(e
))
710 except Exception as e
:
711 logger
.error("Unexpected exception: ", exc_info
=True)
712 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
714 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='GET')
715 def http_get_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
716 '''get datacenter sdn mapping details, can use both uuid or name'''
718 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
720 data
= nfvo
.datacenter_sdn_port_mapping_list(mydb
, tenant_id
, datacenter_id
)
721 return format_out({"sdn_port_mapping": data
})
722 except bottle
.HTTPError
:
724 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
725 logger
.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
726 bottle
.abort(e
.http_code
, str(e
))
727 except Exception as e
:
728 logger
.error("Unexpected exception: ", exc_info
=True)
729 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
731 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='DELETE')
732 def http_delete_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
733 '''clean datacenter sdn mapping, can use both uuid or name'''
735 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
736 data
= nfvo
.datacenter_sdn_port_mapping_delete(mydb
, tenant_id
, datacenter_id
)
737 return format_out({"result": data
})
738 except bottle
.HTTPError
:
740 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
741 logger
.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
742 bottle
.abort(e
.http_code
, str(e
))
743 except Exception as e
:
744 logger
.error("Unexpected exception: ", exc_info
=True)
745 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
747 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/networks', method
='GET') #deprecated
748 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='GET')
749 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='GET')
750 def http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
751 '''get datacenter networks, can use both uuid or name'''
752 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
755 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
756 where_
= {"datacenter_id":datacenter_dict
['uuid']}
758 if utils
.check_valid_uuid(netmap_id
):
759 where_
["uuid"] = netmap_id
761 where_
["name"] = netmap_id
762 netmaps
=mydb
.get_rows(FROM
='datacenter_nets',
763 SELECT
=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
765 convert_datetime2str(netmaps
)
766 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
767 if netmap_id
and len(netmaps
)==1:
768 data
={'netmap' : netmaps
[0]}
769 elif netmap_id
and len(netmaps
)==0:
770 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
773 data
={'netmaps' : netmaps
}
774 return format_out(data
)
775 except bottle
.HTTPError
:
777 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
778 logger
.error("http_getnetwork_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
779 bottle
.abort(e
.http_code
, str(e
))
780 except Exception as e
:
781 logger
.error("Unexpected exception: ", exc_info
=True)
782 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
785 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='DELETE')
786 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='DELETE')
787 def http_delnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
788 '''get datacenter networks, can use both uuid or name'''
789 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
792 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
793 where_
= {"datacenter_id":datacenter_dict
['uuid']}
795 if utils
.check_valid_uuid(netmap_id
):
796 where_
["uuid"] = netmap_id
798 where_
["name"] = netmap_id
799 #change_keys_http2db(content, http2db_tenant, reverse=True)
800 deleted
= mydb
.delete_row(FROM
='datacenter_nets', WHERE
= where_
)
801 if deleted
== 0 and netmap_id
:
802 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
804 return format_out({"result": "netmap %s deleted" % netmap_id
})
806 return format_out({"result": "%d netmap deleted" % deleted
})
807 except bottle
.HTTPError
:
809 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
810 logger
.error("http_delnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
811 bottle
.abort(e
.http_code
, str(e
))
812 except Exception as e
:
813 logger
.error("Unexpected exception: ", exc_info
=True)
814 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
817 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method
='POST')
818 def http_uploadnetmap_datacenter_id(tenant_id
, datacenter_id
):
819 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
821 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, None)
822 convert_datetime2str(netmaps
)
823 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
824 data
={'netmaps' : netmaps
}
825 return format_out(data
)
826 except bottle
.HTTPError
:
828 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
829 logger
.error("http_uploadnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
830 bottle
.abort(e
.http_code
, str(e
))
831 except Exception as e
:
832 logger
.error("Unexpected exception: ", exc_info
=True)
833 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
836 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='POST')
837 def http_postnetmap_datacenter_id(tenant_id
, datacenter_id
):
838 '''creates a new netmap'''
839 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
841 http_content
,_
= format_in( netmap_new_schema
)
842 r
= utils
.remove_extra_items(http_content
, netmap_new_schema
)
844 logger
.debug("Remove received extra items %s", str(r
))
846 #obtain data, check that only one exist
847 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, http_content
)
848 convert_datetime2str(netmaps
)
849 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
850 data
={'netmaps' : netmaps
}
851 return format_out(data
)
852 except bottle
.HTTPError
:
854 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
855 logger
.error("http_postnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
856 bottle
.abort(e
.http_code
, str(e
))
857 except Exception as e
:
858 logger
.error("Unexpected exception: ", exc_info
=True)
859 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
862 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='PUT')
863 def http_putnettmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
):
865 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
867 http_content
,_
= format_in( netmap_edit_schema
)
868 r
= utils
.remove_extra_items(http_content
, netmap_edit_schema
)
870 logger
.debug("Remove received extra items %s", str(r
))
872 #obtain data, check that only one exist
874 nfvo
.datacenter_edit_netmap(mydb
, tenant_id
, datacenter_id
, netmap_id
, http_content
)
875 return http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
)
876 except bottle
.HTTPError
:
878 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
879 logger
.error("http_putnettmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
880 bottle
.abort(e
.http_code
, str(e
))
881 except Exception as e
:
882 logger
.error("Unexpected exception: ", exc_info
=True)
883 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
886 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/action', method
='POST')
887 def http_action_datacenter_id(tenant_id
, datacenter_id
):
888 '''perform an action over datacenter, can use both uuid or name'''
889 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
891 http_content
,_
= format_in( datacenter_action_schema
)
892 r
= utils
.remove_extra_items(http_content
, datacenter_action_schema
)
894 logger
.debug("Remove received extra items %s", str(r
))
896 #obtain data, check that only one exist
897 result
= nfvo
.datacenter_action(mydb
, tenant_id
, datacenter_id
, http_content
)
898 if 'net-update' in http_content
:
899 return http_getnetmap_datacenter_id(datacenter_id
)
901 return format_out(result
)
902 except bottle
.HTTPError
:
904 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
905 logger
.error("http_action_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
906 bottle
.abort(e
.http_code
, str(e
))
907 except Exception as e
:
908 logger
.error("Unexpected exception: ", exc_info
=True)
909 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
912 @bottle.route(url_base
+ '/datacenters/<datacenter_id>', method
='DELETE')
913 def http_delete_datacenter_id( datacenter_id
):
914 '''delete a tenant from database, can use both uuid or name'''
916 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
918 data
= nfvo
.delete_datacenter(mydb
, datacenter_id
)
919 return format_out({"result":"datacenter '" + data
+ "' deleted"})
920 except bottle
.HTTPError
:
922 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
923 logger
.error("http_delete_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
924 bottle
.abort(e
.http_code
, str(e
))
925 except Exception as e
:
926 logger
.error("Unexpected exception: ", exc_info
=True)
927 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
930 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='POST')
931 def http_associate_datacenters(tenant_id
, datacenter_id
):
932 '''associate an existing datacenter to a this tenant. '''
933 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
935 http_content
,_
= format_in(datacenter_associate_schema
, confidential_data
=True)
936 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
938 logger
.debug("Remove received extra items %s", str(r
))
940 id_
= nfvo
.associate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
,
941 http_content
['datacenter'].get('vim_tenant'),
942 http_content
['datacenter'].get('vim_tenant_name'),
943 http_content
['datacenter'].get('vim_username'),
944 http_content
['datacenter'].get('vim_password'),
945 http_content
['datacenter'].get('config')
947 return http_get_datacenter_id(tenant_id
, id_
)
948 except bottle
.HTTPError
:
950 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
951 logger
.error("http_associate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
952 bottle
.abort(e
.http_code
, str(e
))
953 except Exception as e
:
954 logger
.error("Unexpected exception: ", exc_info
=True)
955 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
957 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='PUT')
958 def http_associate_datacenters_edit(tenant_id
, datacenter_id
):
959 '''associate an existing datacenter to a this tenant. '''
960 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
962 http_content
,_
= format_in( datacenter_associate_schema
)
963 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
965 logger
.debug("Remove received extra items %s", str(r
))
967 id_
= nfvo
.edit_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
,
968 http_content
['datacenter'].get('vim_tenant'),
969 http_content
['datacenter'].get('vim_tenant_name'),
970 http_content
['datacenter'].get('vim_username'),
971 http_content
['datacenter'].get('vim_password'),
972 http_content
['datacenter'].get('config')
974 return http_get_datacenter_id(tenant_id
, id_
)
975 except bottle
.HTTPError
:
977 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
978 logger
.error("http_associate_datacenters_edit error {}: {}".format(e
.http_code
, str(e
)))
979 bottle
.abort(e
.http_code
, str(e
))
980 except Exception as e
:
981 logger
.error("Unexpected exception: ", exc_info
=True)
982 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
984 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='DELETE')
985 def http_deassociate_datacenters(tenant_id
, datacenter_id
):
986 '''deassociate an existing datacenter to a this tenant. '''
987 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
989 data
= nfvo
.deassociate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
)
990 return format_out({"result": data
})
991 except bottle
.HTTPError
:
993 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
994 logger
.error("http_deassociate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
995 bottle
.abort(e
.http_code
, str(e
))
996 except Exception as e
:
997 logger
.error("Unexpected exception: ", exc_info
=True)
998 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1000 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method
='POST')
1001 def http_post_vim_net_sdn_attach(tenant_id
, datacenter_id
, network_id
):
1002 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1003 http_content
, _
= format_in(sdn_external_port_schema
)
1005 data
= nfvo
.vim_net_sdn_attach(mydb
, tenant_id
, datacenter_id
, network_id
, http_content
)
1006 return format_out(data
)
1007 except bottle
.HTTPError
:
1009 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1010 logger
.error("http_post_vim_net_sdn_attach error {}: {}".format(e
.http_code
, str(e
)))
1011 bottle
.abort(e
.http_code
, str(e
))
1012 except Exception as e
:
1013 logger
.error("Unexpected exception: ", exc_info
=True)
1014 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1016 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method
='DELETE')
1017 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method
='DELETE')
1018 def http_delete_vim_net_sdn_detach(tenant_id
, datacenter_id
, network_id
, port_id
=None):
1019 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1021 data
= nfvo
.vim_net_sdn_detach(mydb
, tenant_id
, datacenter_id
, network_id
, port_id
)
1022 return format_out(data
)
1023 except bottle
.HTTPError
:
1025 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1026 logger
.error("http_delete_vim_net_sdn_detach error {}: {}".format(e
.http_code
, str(e
)))
1027 bottle
.abort(e
.http_code
, str(e
))
1028 except Exception as e
:
1029 logger
.error("Unexpected exception: ", exc_info
=True)
1030 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1032 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='GET')
1033 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='GET')
1034 def http_get_vim_items(tenant_id
, datacenter_id
, item
, name
=None):
1035 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1037 data
= nfvo
.vim_action_get(mydb
, tenant_id
, datacenter_id
, item
, name
)
1038 return format_out(data
)
1039 except bottle
.HTTPError
:
1041 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1042 logger
.error("http_get_vim_items error {}: {}".format(e
.http_code
, str(e
)))
1043 bottle
.abort(e
.http_code
, str(e
))
1044 except Exception as e
:
1045 logger
.error("Unexpected exception: ", exc_info
=True)
1046 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1049 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='DELETE')
1050 def http_del_vim_items(tenant_id
, datacenter_id
, item
, name
):
1051 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1053 data
= nfvo
.vim_action_delete(mydb
, tenant_id
, datacenter_id
, item
, name
)
1054 return format_out({"result":data
})
1055 except bottle
.HTTPError
:
1057 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1058 logger
.error("http_del_vim_items error {}: {}".format(e
.http_code
, str(e
)))
1059 bottle
.abort(e
.http_code
, str(e
))
1060 except Exception as e
:
1061 logger
.error("Unexpected exception: ", exc_info
=True)
1062 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1065 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='POST')
1066 def http_post_vim_items(tenant_id
, datacenter_id
, item
):
1067 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1068 http_content
,_
= format_in( object_schema
)
1070 data
= nfvo
.vim_action_create(mydb
, tenant_id
, datacenter_id
, item
, http_content
)
1071 return format_out(data
)
1072 except bottle
.HTTPError
:
1074 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1075 logger
.error("http_post_vim_items error {}: {}".format(e
.http_code
, str(e
)))
1076 bottle
.abort(e
.http_code
, str(e
))
1077 except Exception as e
:
1078 logger
.error("Unexpected exception: ", exc_info
=True)
1079 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1082 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='GET')
1083 def http_get_vnfs(tenant_id
):
1084 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1086 if tenant_id
!= 'any':
1087 #check valid tenant_id
1088 nfvo
.check_tenant(mydb
, tenant_id
)
1089 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
1090 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
1092 if tenant_id
!= "any":
1093 where_or
["tenant_id"] = tenant_id
1094 where_or
["public"] = True
1095 vnfs
= mydb
.get_rows(FROM
='vnfs', SELECT
=select_
,WHERE
=where_
,WHERE_OR
=where_or
, WHERE_AND_OR
="AND",LIMIT
=limit_
)
1096 #change_keys_http2db(content, http2db_vnf, reverse=True)
1097 utils
.convert_str2boolean(vnfs
, ('public',))
1098 convert_datetime2str(vnfs
)
1099 data
={'vnfs' : vnfs
}
1100 return format_out(data
)
1101 except bottle
.HTTPError
:
1103 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1104 logger
.error("http_get_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1105 bottle
.abort(e
.http_code
, str(e
))
1106 except Exception as e
:
1107 logger
.error("Unexpected exception: ", exc_info
=True)
1108 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1111 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='GET')
1112 def http_get_vnf_id(tenant_id
,vnf_id
):
1113 '''get vnf details, can use both uuid or name'''
1114 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1116 vnf
= nfvo
.get_vnf_id(mydb
,tenant_id
,vnf_id
)
1117 utils
.convert_str2boolean(vnf
, ('public',))
1118 convert_datetime2str(vnf
)
1119 return format_out(vnf
)
1120 except bottle
.HTTPError
:
1122 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1123 logger
.error("http_get_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
1124 bottle
.abort(e
.http_code
, str(e
))
1125 except Exception as e
:
1126 logger
.error("Unexpected exception: ", exc_info
=True)
1127 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1130 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='POST')
1131 def http_post_vnfs(tenant_id
):
1132 """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
1133 :param tenant_id: tenant that this vnf belongs to
1136 # print "Parsing the YAML file of the VNF"
1138 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1139 http_content
, used_schema
= format_in( vnfd_schema_v01
, ("schema_version",), {"0.2": vnfd_schema_v02
})
1140 r
= utils
.remove_extra_items(http_content
, used_schema
)
1142 logger
.debug("Remove received extra items %s", str(r
))
1144 if used_schema
== vnfd_schema_v01
:
1145 vnf_id
= nfvo
.new_vnf(mydb
,tenant_id
,http_content
)
1146 elif used_schema
== vnfd_schema_v02
:
1147 vnf_id
= nfvo
.new_vnf_v02(mydb
,tenant_id
,http_content
)
1149 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1150 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1151 return http_get_vnf_id(tenant_id
, vnf_id
)
1152 except bottle
.HTTPError
:
1154 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1155 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1156 bottle
.abort(e
.http_code
, str(e
))
1157 except Exception as e
:
1158 logger
.error("Unexpected exception: ", exc_info
=True)
1159 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1162 @bottle.route(url_base
+ '/v3/<tenant_id>/vnfd', method
='POST')
1163 def http_post_vnfs_v3(tenant_id
):
1165 Insert one or several VNFs in the catalog, following OSM IM
1166 :param tenant_id: tenant owner of the VNF
1167 :return: The detailed list of inserted VNFs, following the old format
1169 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1170 http_content
, _
= format_in(None)
1172 vnfd_uuid_list
= nfvo
.new_vnfd_v3(mydb
, tenant_id
, http_content
)
1174 for vnfd_uuid
in vnfd_uuid_list
:
1175 vnf
= nfvo
.get_vnf_id(mydb
, tenant_id
, vnfd_uuid
)
1176 utils
.convert_str2boolean(vnf
, ('public',))
1177 convert_datetime2str(vnf
)
1178 vnfd_list
.append(vnf
["vnf"])
1179 return format_out({"vnfd": vnfd_list
})
1180 except bottle
.HTTPError
:
1182 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1183 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1184 bottle
.abort(e
.http_code
, str(e
))
1185 except Exception as e
:
1186 logger
.error("Unexpected exception: ", exc_info
=True)
1187 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1189 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='DELETE')
1190 def http_delete_vnf_id(tenant_id
, vnf_id
):
1191 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
1192 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1193 #check valid tenant_id and deletes the vnf, including images,
1195 data
= nfvo
.delete_vnf(mydb
,tenant_id
,vnf_id
)
1196 #print json.dumps(data, indent=4)
1197 return format_out({"result":"VNF " + data
+ " deleted"})
1198 except bottle
.HTTPError
:
1200 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1201 logger
.error("http_delete_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
1202 bottle
.abort(e
.http_code
, str(e
))
1203 except Exception as e
:
1204 logger
.error("Unexpected exception: ", exc_info
=True)
1205 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1208 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1209 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1210 @bottle.route(url_base
+ '/<tenant_id>/physicalview/<datacenter>', method
='GET')
1211 def http_get_hosts(tenant_id
, datacenter
):
1212 '''get the tidvim host hopology from the vim.'''
1213 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1214 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1216 if datacenter
== 'treeview':
1217 data
= nfvo
.get_hosts(mydb
, tenant_id
)
1219 #openmano-gui is using a hardcoded value for the datacenter
1220 result
, data
= nfvo
.get_hosts_info(mydb
, tenant_id
) #, datacenter)
1223 #print "http_get_hosts error %d %s" % (-result, data)
1224 bottle
.abort(-result
, data
)
1226 convert_datetime2str(data
)
1227 #print json.dumps(data, indent=4)
1228 return format_out(data
)
1229 except bottle
.HTTPError
:
1231 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1232 logger
.error("http_get_hosts error {}: {}".format(e
.http_code
, str(e
)))
1233 bottle
.abort(e
.http_code
, str(e
))
1234 except Exception as e
:
1235 logger
.error("Unexpected exception: ", exc_info
=True)
1236 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1239 @bottle.route(url_base
+ '/<path:path>', method
='OPTIONS')
1240 def http_options_deploy(path
):
1241 '''For some reason GUI web ask for OPTIONS that must be responded'''
1242 #TODO: check correct path, and correct headers request
1243 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1244 bottle
.response
.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1245 bottle
.response
.set_header('Accept','application/yaml,application/json')
1246 bottle
.response
.set_header('Content-Type','application/yaml,application/json')
1247 bottle
.response
.set_header('Access-Control-Allow-Headers','content-type')
1248 bottle
.response
.set_header('Access-Control-Allow-Origin','*')
1251 @bottle.route(url_base
+ '/<tenant_id>/topology/deploy', method
='POST')
1252 def http_post_deploy(tenant_id
):
1253 '''post topology deploy.'''
1254 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1256 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
})
1257 #r = utils.remove_extra_items(http_content, used_schema)
1258 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
1259 #print "http_post_deploy input: ", http_content
1262 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1263 instance
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['name'], http_content
['name'])
1264 #print json.dumps(data, indent=4)
1265 return format_out(instance
)
1266 except bottle
.HTTPError
:
1268 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1269 logger
.error("http_post_deploy error {}: {}".format(e
.http_code
, str(e
)))
1270 bottle
.abort(e
.http_code
, str(e
))
1271 except Exception as e
:
1272 logger
.error("Unexpected exception: ", exc_info
=True)
1273 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1276 @bottle.route(url_base
+ '/<tenant_id>/topology/verify', method
='POST')
1277 def http_post_verify(tenant_id
):
1279 # '''post topology verify'''
1280 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
1281 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1288 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='POST')
1289 def http_post_scenarios(tenant_id
):
1290 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
1291 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1292 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
, "0.3": nsd_schema_v03
})
1293 #r = utils.remove_extra_items(http_content, used_schema)
1294 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
1295 #print "http_post_scenarios input: ", http_content
1297 if used_schema
== nsd_schema_v01
:
1298 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1299 elif used_schema
== nsd_schema_v02
:
1300 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.2")
1301 elif used_schema
== nsd_schema_v03
:
1302 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.3")
1304 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1305 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1306 #print json.dumps(data, indent=4)
1307 #return format_out(data)
1308 return http_get_scenario_id(tenant_id
, scenario_id
)
1309 except bottle
.HTTPError
:
1311 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1312 logger
.error("http_post_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1313 bottle
.abort(e
.http_code
, str(e
))
1314 except Exception as e
:
1315 logger
.error("Unexpected exception: ", exc_info
=True)
1316 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1318 @bottle.route(url_base
+ '/v3/<tenant_id>/nsd', method
='POST')
1319 def http_post_nsds_v3(tenant_id
):
1321 Insert one or several NSDs in the catalog, following OSM IM
1322 :param tenant_id: tenant owner of the NSD
1323 :return: The detailed list of inserted NSDs, following the old format
1325 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1326 http_content
, _
= format_in(None)
1328 nsd_uuid_list
= nfvo
.new_nsd_v3(mydb
, tenant_id
, http_content
)
1330 for nsd_uuid
in nsd_uuid_list
:
1331 scenario
= mydb
.get_scenario(nsd_uuid
, tenant_id
)
1332 convert_datetime2str(scenario
)
1333 nsd_list
.append(scenario
)
1334 data
= {'nsd': nsd_list
}
1335 return format_out(data
)
1336 except bottle
.HTTPError
:
1338 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1339 logger
.error("http_post_nsds_v3 error {}: {}".format(e
.http_code
, str(e
)))
1340 bottle
.abort(e
.http_code
, str(e
))
1341 except Exception as e
:
1342 logger
.error("Unexpected exception: ", exc_info
=True)
1343 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1346 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>/action', method
='POST')
1347 def http_post_scenario_action(tenant_id
, scenario_id
):
1348 '''take an action over a scenario'''
1349 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1351 http_content
, _
= format_in(scenario_action_schema
)
1352 r
= utils
.remove_extra_items(http_content
, scenario_action_schema
)
1354 logger
.debug("Remove received extra items %s", str(r
))
1356 # check valid tenant_id
1357 nfvo
.check_tenant(mydb
, tenant_id
)
1358 if "start" in http_content
:
1359 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['start']['instance_name'], \
1360 http_content
['start'].get('description',http_content
['start']['instance_name']),
1361 http_content
['start'].get('datacenter') )
1362 return format_out(data
)
1363 elif "deploy" in http_content
: #Equivalent to start
1364 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['deploy']['instance_name'],
1365 http_content
['deploy'].get('description',http_content
['deploy']['instance_name']),
1366 http_content
['deploy'].get('datacenter') )
1367 return format_out(data
)
1368 elif "reserve" in http_content
: #Reserve resources
1369 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['reserve']['instance_name'],
1370 http_content
['reserve'].get('description',http_content
['reserve']['instance_name']),
1371 http_content
['reserve'].get('datacenter'), startvms
=False )
1372 return format_out(data
)
1373 elif "verify" in http_content
: #Equivalent to start and then delete
1374 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['verify']['instance_name'],
1375 http_content
['verify'].get('description',http_content
['verify']['instance_name']),
1376 http_content
['verify'].get('datacenter'), startvms
=False )
1377 instance_id
= data
['uuid']
1378 nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1379 return format_out({"result":"Verify OK"})
1380 except bottle
.HTTPError
:
1382 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1383 logger
.error("http_post_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1384 bottle
.abort(e
.http_code
, str(e
))
1385 except Exception as e
:
1386 logger
.error("Unexpected exception: ", exc_info
=True)
1387 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1390 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='GET')
1391 def http_get_scenarios(tenant_id
):
1392 '''get scenarios list'''
1393 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1395 #check valid tenant_id
1396 if tenant_id
!= "any":
1397 nfvo
.check_tenant(mydb
, tenant_id
)
1399 s
,w
,l
=filter_query_string(bottle
.request
.query
, None,
1400 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
1402 if tenant_id
!= "any":
1403 where_or
["tenant_id"] = tenant_id
1404 where_or
["public"] = True
1405 scenarios
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, WHERE_OR
=where_or
, WHERE_AND_OR
="AND", LIMIT
=l
, FROM
='scenarios')
1406 convert_datetime2str(scenarios
)
1407 utils
.convert_str2boolean(scenarios
, ('public',) )
1408 data
={'scenarios':scenarios
}
1409 #print json.dumps(scenarios, indent=4)
1410 return format_out(data
)
1411 except bottle
.HTTPError
:
1413 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1414 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1415 bottle
.abort(e
.http_code
, str(e
))
1416 except Exception as e
:
1417 logger
.error("Unexpected exception: ", exc_info
=True)
1418 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1421 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='GET')
1422 def http_get_scenario_id(tenant_id
, scenario_id
):
1423 '''get scenario details, can use both uuid or name'''
1424 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1426 #check valid tenant_id
1427 if tenant_id
!= "any":
1428 nfvo
.check_tenant(mydb
, tenant_id
)
1430 scenario
= mydb
.get_scenario(scenario_id
, tenant_id
)
1431 convert_datetime2str(scenario
)
1432 data
={'scenario' : scenario
}
1433 return format_out(data
)
1434 except bottle
.HTTPError
:
1436 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1437 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1438 bottle
.abort(e
.http_code
, str(e
))
1439 except Exception as e
:
1440 logger
.error("Unexpected exception: ", exc_info
=True)
1441 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1444 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='DELETE')
1445 def http_delete_scenario_id(tenant_id
, scenario_id
):
1446 '''delete a scenario from database, can use both uuid or name'''
1447 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1449 #check valid tenant_id
1450 if tenant_id
!= "any":
1451 nfvo
.check_tenant(mydb
, tenant_id
)
1453 data
= mydb
.delete_scenario(scenario_id
, tenant_id
)
1454 #print json.dumps(data, indent=4)
1455 return format_out({"result":"scenario " + data
+ " deleted"})
1456 except bottle
.HTTPError
:
1458 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1459 logger
.error("http_delete_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1460 bottle
.abort(e
.http_code
, str(e
))
1461 except Exception as e
:
1462 logger
.error("Unexpected exception: ", exc_info
=True)
1463 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1466 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='PUT')
1467 def http_put_scenario_id(tenant_id
, scenario_id
):
1468 '''edit an existing scenario id'''
1469 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1470 http_content
,_
= format_in( scenario_edit_schema
)
1471 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1472 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1473 #print "http_put_scenario_id input: ", http_content
1475 nfvo
.edit_scenario(mydb
, tenant_id
, scenario_id
, http_content
)
1476 #print json.dumps(data, indent=4)
1477 #return format_out(data)
1478 return http_get_scenario_id(tenant_id
, scenario_id
)
1479 except bottle
.HTTPError
:
1481 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1482 logger
.error("http_put_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1483 bottle
.abort(e
.http_code
, str(e
))
1484 except Exception as e
:
1485 logger
.error("Unexpected exception: ", exc_info
=True)
1486 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1488 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='POST')
1489 def http_post_instances(tenant_id
):
1490 '''create an instance-scenario'''
1491 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1493 http_content
, used_schema
= format_in(instance_scenario_create_schema_v01
)
1494 r
= utils
.remove_extra_items(http_content
, used_schema
)
1496 logger
.warning("http_post_instances: Warning: remove extra items %s", str(r
))
1498 #check valid tenant_id
1499 if tenant_id
!= "any":
1500 nfvo
.check_tenant(mydb
, tenant_id
)
1501 data
= nfvo
.create_instance(mydb
, tenant_id
, http_content
["instance"])
1502 return format_out(data
)
1503 except bottle
.HTTPError
:
1505 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1506 logger
.error("http_post_instances error {}: {}".format(e
.http_code
, str(e
)))
1507 bottle
.abort(e
.http_code
, str(e
))
1508 except Exception as e
:
1509 logger
.error("Unexpected exception: ", exc_info
=True)
1510 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1515 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='GET')
1516 def http_get_instances(tenant_id
):
1517 '''get instance list'''
1519 #check valid tenant_id
1520 if tenant_id
!= "any":
1521 nfvo
.check_tenant(mydb
, tenant_id
)
1523 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1524 if tenant_id
!= "any":
1525 w
['tenant_id'] = tenant_id
1526 instances
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, LIMIT
=l
, FROM
='instance_scenarios')
1527 convert_datetime2str(instances
)
1528 utils
.convert_str2boolean(instances
, ('public',) )
1529 data
={'instances':instances
}
1530 return format_out(data
)
1531 except bottle
.HTTPError
:
1533 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1534 logger
.error("http_get_instances error {}: {}".format(e
.http_code
, str(e
)))
1535 bottle
.abort(e
.http_code
, str(e
))
1536 except Exception as e
:
1537 logger
.error("Unexpected exception: ", exc_info
=True)
1538 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1541 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='GET')
1542 def http_get_instance_id(tenant_id
, instance_id
):
1543 '''get instances details, can use both uuid or name'''
1544 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1546 #check valid tenant_id
1547 if tenant_id
!= "any":
1548 nfvo
.check_tenant(mydb
, tenant_id
)
1549 if tenant_id
== "any":
1551 #obtain data (first time is only to check that the instance exists)
1552 instance_dict
= mydb
.get_instance_scenario(instance_id
, tenant_id
, verbose
=True)
1554 nfvo
.refresh_instance(mydb
, tenant_id
, instance_dict
)
1555 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1556 logger
.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e
))
1557 # obtain data with results upated
1558 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1559 # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
1560 for vnf
in instance
.get("vnfs", ()):
1561 for vm
in vnf
.get("vms", ()):
1562 for iface
in vm
.get("interfaces", ()):
1563 if iface
.get("ip_address"):
1564 index
= iface
["ip_address"].find(";")
1566 iface
["ip_address"] = iface
["ip_address"][:index
]
1567 convert_datetime2str(instance
)
1568 # print json.dumps(instance, indent=4)
1569 return format_out(instance
)
1570 except bottle
.HTTPError
:
1572 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1573 logger
.error("http_get_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1574 bottle
.abort(e
.http_code
, str(e
))
1575 except Exception as e
:
1576 logger
.error("Unexpected exception: ", exc_info
=True)
1577 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1580 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='DELETE')
1581 def http_delete_instance_id(tenant_id
, instance_id
):
1582 '''delete instance from VIM and from database, can use both uuid or name'''
1583 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1585 #check valid tenant_id
1586 if tenant_id
!= "any":
1587 nfvo
.check_tenant(mydb
, tenant_id
)
1588 if tenant_id
== "any":
1591 message
= nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1592 return format_out({"result":message
})
1593 except bottle
.HTTPError
:
1595 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1596 logger
.error("http_delete_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1597 bottle
.abort(e
.http_code
, str(e
))
1598 except Exception as e
:
1599 logger
.error("Unexpected exception: ", exc_info
=True)
1600 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1603 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='POST')
1604 def http_post_instance_scenario_action(tenant_id
, instance_id
):
1606 take an action over a scenario instance
1607 :param tenant_id: tenant where user belongs to
1608 :param instance_id: instance indentity
1611 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1613 http_content
, _
= format_in(instance_scenario_action_schema
)
1614 r
= utils
.remove_extra_items(http_content
, instance_scenario_action_schema
)
1616 logger
.debug("Remove received extra items %s", str(r
))
1618 #check valid tenant_id
1619 if tenant_id
!= "any":
1620 nfvo
.check_tenant(mydb
, tenant_id
)
1622 #print "http_post_instance_scenario_action input: ", http_content
1624 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1625 instance_id
= instance
["uuid"]
1627 data
= nfvo
.instance_action(mydb
, tenant_id
, instance_id
, http_content
)
1628 return format_out(data
)
1629 except bottle
.HTTPError
:
1631 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1632 logger
.error("http_post_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1633 bottle
.abort(e
.http_code
, str(e
))
1634 except Exception as e
:
1635 logger
.error("Unexpected exception: ", exc_info
=True)
1636 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1639 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='GET')
1640 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action/<action_id>', method
='GET')
1641 def http_get_instance_scenario_action(tenant_id
, instance_id
, action_id
=None):
1643 List the actions done over an instance, or the action details
1644 :param tenant_id: tenant where user belongs to. Can be "any" to ignore
1645 :param instance_id: instance id, can be "any" to get actions of all instances
1648 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1650 # check valid tenant_id
1651 if tenant_id
!= "any":
1652 nfvo
.check_tenant(mydb
, tenant_id
)
1653 data
= nfvo
.instance_action_get(mydb
, tenant_id
, instance_id
, action_id
)
1654 return format_out(data
)
1655 except bottle
.HTTPError
:
1657 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1658 logger
.error("http_get_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1659 bottle
.abort(e
.http_code
, str(e
))
1660 except Exception as e
:
1661 logger
.error("Unexpected exception: ", exc_info
=True)
1662 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1664 def remove_clear_passwd(data
):
1666 Removes clear passwords from the data received
1667 :param data: data with clear password
1668 :return: data without the password information
1671 passw
= ['password: ', 'passwd: ']
1673 for pattern
in passw
:
1674 init
= data
.find(pattern
)
1676 end
= data
.find('\n', init
)
1677 data
= data
[:init
] + '{}******'.format(pattern
) + data
[end
:]
1679 init
= data
.find(pattern
, init
)
1691 def error400(error
):
1692 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
1693 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
1694 return format_out(e
)