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):
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")
220 logger
.debug('IN: %s', yaml
.safe_dump(client_data
, explicit_start
=True, indent
=4, default_flow_style
=False,
221 tags
=False, encoding
='utf-8', allow_unicode
=True) )
222 # look for the client provider version
223 error_text
= "Invalid content "
224 if not default_schema
and not version_fields
:
225 return client_data
, None
226 client_version
= None
228 if version_fields
!= None:
229 client_version
= client_data
230 for field
in version_fields
:
231 if field
in client_version
:
232 client_version
= client_version
[field
]
236 if client_version
== None:
237 used_schema
= default_schema
238 elif version_dict_schema
!= None:
239 if client_version
in version_dict_schema
:
240 used_schema
= version_dict_schema
[client_version
]
241 elif None in version_dict_schema
:
242 used_schema
= version_dict_schema
[None]
243 if used_schema
==None:
244 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version or missing version field")
246 js_v(client_data
, used_schema
)
247 return client_data
, used_schema
248 except (ValueError, yaml
.YAMLError
) as exc
:
249 error_text
+= str(exc
)
250 logger
.error(error_text
)
251 bottle
.abort(HTTP_Bad_Request
, error_text
)
252 except js_e
.ValidationError
as exc
:
253 logger
.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc
.path
), str(exc
.message
))
255 if len(exc
.path
)>0: error_pos
=" at " + ":".join(map(json
.dumps
, exc
.path
))
256 bottle
.abort(HTTP_Bad_Request
, error_text
+ exc
.message
+ error_pos
)
258 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
261 def filter_query_string(qs
, http2db
, allowed
):
262 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
264 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
265 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
266 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
267 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
268 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
269 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
270 limit: limit dictated by user with the query string 'limit'. 100 by default
271 abort if not permited, using bottel.abort
276 #if type(qs) is not bottle.FormsDict:
277 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
278 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
281 select
+= qs
.getall(k
)
284 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field="+v
+"'")
289 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit="+qs
[k
]+"'")
292 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '"+k
+"="+qs
[k
]+"'")
293 if qs
[k
]!="null": where
[k
]=qs
[k
]
295 if len(select
)==0: select
+= allowed
296 #change from http api to database naming
297 for i
in range(0,len(select
)):
299 if http2db
and k
in http2db
:
300 select
[i
] = http2db
[k
]
302 change_keys_http2db(where
, http2db
)
303 #print "filter_query_string", select,where,limit
305 return select
,where
,limit
307 @bottle.hook('after_request')
309 '''Don't know yet if really needed. Keep it just in case'''
310 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
312 @bottle.route(url_base
+ '/version', method
='GET')
313 def http_get_version():
314 return nfvo
.get_version()
319 @bottle.route(url_base
+ '/tenants', method
='GET')
320 def http_get_tenants():
321 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
322 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
323 ('uuid','name','description','created_at') )
325 tenants
= mydb
.get_rows(FROM
='nfvo_tenants', SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
326 #change_keys_http2db(content, http2db_tenant, reverse=True)
327 convert_datetime2str(tenants
)
328 data
={'tenants' : tenants
}
329 return format_out(data
)
330 except db_base_Exception
as e
:
331 logger
.error("http_get_tenants error {}: {}".format(e
.http_code
, str(e
)))
332 bottle
.abort(e
.http_code
, str(e
))
333 except Exception as e
:
334 logger
.error("Unexpected exception: ", exc_info
=True)
335 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
338 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
339 def http_get_tenant_id(tenant_id
):
340 '''get tenant details, can use both uuid or name'''
342 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
344 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
, "tenant")
345 #change_keys_http2db(content, http2db_tenant, reverse=True)
346 convert_datetime2str(tenant
)
347 data
={'tenant' : tenant
}
348 return format_out(data
)
349 except db_base_Exception
as e
:
350 logger
.error("http_get_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
351 bottle
.abort(e
.http_code
, str(e
))
352 except Exception as e
:
353 logger
.error("Unexpected exception: ", exc_info
=True)
354 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
357 @bottle.route(url_base
+ '/tenants', method
='POST')
358 def http_post_tenants():
359 '''insert a tenant into the catalogue. '''
361 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
362 http_content
,_
= format_in( tenant_schema
)
363 r
= utils
.remove_extra_items(http_content
, tenant_schema
)
365 logger
.debug("Remove received extra items %s", str(r
))
367 data
= nfvo
.new_tenant(mydb
, http_content
['tenant'])
368 return http_get_tenant_id(data
)
369 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
370 logger
.error("http_post_tenants error {}: {}".format(e
.http_code
, str(e
)))
371 bottle
.abort(e
.http_code
, str(e
))
372 except Exception as e
:
373 logger
.error("Unexpected exception: ", exc_info
=True)
374 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
377 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
378 def http_edit_tenant_id(tenant_id
):
379 '''edit tenant details, can use both uuid or name'''
381 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
382 http_content
,_
= format_in( tenant_edit_schema
)
383 r
= utils
.remove_extra_items(http_content
, tenant_edit_schema
)
385 logger
.debug("Remove received extra items %s", str(r
))
387 #obtain data, check that only one exist
389 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
)
391 tenant_id
= tenant
['uuid']
392 where
={'uuid': tenant
['uuid']}
393 mydb
.update_rows('nfvo_tenants', http_content
['tenant'], where
)
394 return http_get_tenant_id(tenant_id
)
395 except db_base_Exception
as e
:
396 logger
.error("http_edit_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
397 bottle
.abort(e
.http_code
, str(e
))
398 except Exception as e
:
399 logger
.error("Unexpected exception: ", exc_info
=True)
400 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
403 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
404 def http_delete_tenant_id(tenant_id
):
405 '''delete a tenant from database, can use both uuid or name'''
406 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
408 data
= nfvo
.delete_tenant(mydb
, tenant_id
)
409 return format_out({"result":"tenant " + data
+ " deleted"})
410 except db_base_Exception
as e
:
411 logger
.error("http_delete_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
412 bottle
.abort(e
.http_code
, str(e
))
413 except Exception as e
:
414 logger
.error("Unexpected exception: ", exc_info
=True)
415 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
418 @bottle.route(url_base
+ '/<tenant_id>/datacenters', method
='GET')
419 def http_get_datacenters(tenant_id
):
420 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
422 if tenant_id
!= 'any':
423 #check valid tenant_id
424 nfvo
.check_tenant(mydb
, tenant_id
)
425 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
426 ('uuid','name','vim_url','type','created_at') )
427 if tenant_id
!= 'any':
428 where_
['nfvo_tenant_id'] = tenant_id
429 if 'created_at' in select_
:
430 select_
[ select_
.index('created_at') ] = 'd.created_at as created_at'
431 if 'created_at' in where_
:
432 where_
['d.created_at'] = where_
.pop('created_at')
433 datacenters
= mydb
.get_rows(FROM
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
434 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
436 datacenters
= mydb
.get_rows(FROM
='datacenters',
437 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
438 #change_keys_http2db(content, http2db_tenant, reverse=True)
439 convert_datetime2str(datacenters
)
440 data
={'datacenters' : datacenters
}
441 return format_out(data
)
442 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
443 logger
.error("http_get_datacenters error {}: {}".format(e
.http_code
, str(e
)))
444 bottle
.abort(e
.http_code
, str(e
))
445 except Exception as e
:
446 logger
.error("Unexpected exception: ", exc_info
=True)
447 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
450 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='GET')
451 def http_get_datacenter_id(tenant_id
, datacenter_id
):
452 '''get datacenter details, can use both uuid or name'''
453 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
455 if tenant_id
!= 'any':
456 #check valid tenant_id
457 nfvo
.check_tenant(mydb
, tenant_id
)
459 what
= 'uuid' if utils
.check_valid_uuid(datacenter_id
) else 'name'
461 where_
[what
] = datacenter_id
462 select_
=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
463 if tenant_id
!= 'any':
464 select_
.append("datacenter_tenant_id")
465 where_
['td.nfvo_tenant_id']= tenant_id
466 from_
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
468 from_
='datacenters as d'
469 datacenters
= mydb
.get_rows(
474 if len(datacenters
)==0:
475 bottle
.abort( HTTP_Not_Found
, "No datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
476 elif len(datacenters
)>1:
477 bottle
.abort( HTTP_Bad_Request
, "More than one datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
478 datacenter
= datacenters
[0]
479 if tenant_id
!= 'any':
481 vim_tenants
= mydb
.get_rows(
482 SELECT
=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
483 FROM
="datacenter_tenants",
484 WHERE
={"uuid": datacenters
[0]["datacenter_tenant_id"]},
485 ORDER_BY
=("created", ) )
486 del datacenter
["datacenter_tenant_id"]
487 datacenter
["vim_tenants"] = vim_tenants
488 for vim_tenant
in vim_tenants
:
489 if vim_tenant
["passwd"]:
490 vim_tenant
["passwd"] = "******"
491 if vim_tenant
['config'] != None:
493 config_dict
= yaml
.load(vim_tenant
['config'])
494 vim_tenant
['config'] = config_dict
495 except Exception as e
:
496 logger
.error("Exception '%s' while trying to load config information", str(e
))
498 if datacenter
['config'] != None:
500 config_dict
= yaml
.load(datacenter
['config'])
501 datacenter
['config'] = config_dict
502 except Exception as e
:
503 logger
.error("Exception '%s' while trying to load config information", str(e
))
504 #change_keys_http2db(content, http2db_datacenter, reverse=True)
505 convert_datetime2str(datacenter
)
506 data
={'datacenter' : datacenter
}
507 return format_out(data
)
508 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
509 logger
.error("http_get_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
510 bottle
.abort(e
.http_code
, str(e
))
511 except Exception as e
:
512 logger
.error("Unexpected exception: ", exc_info
=True)
513 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
516 @bottle.route(url_base
+ '/datacenters', method
='POST')
517 def http_post_datacenters():
518 '''insert a datacenter into the catalogue. '''
520 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
521 http_content
,_
= format_in( datacenter_schema
)
522 r
= utils
.remove_extra_items(http_content
, datacenter_schema
)
524 logger
.debug("Remove received extra items %s", str(r
))
526 data
= nfvo
.new_datacenter(mydb
, http_content
['datacenter'])
527 return http_get_datacenter_id('any', data
)
528 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
529 logger
.error("http_post_datacenters error {}: {}".format(e
.http_code
, str(e
)))
530 bottle
.abort(e
.http_code
, str(e
))
531 except Exception as e
:
532 logger
.error("Unexpected exception: ", exc_info
=True)
533 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
536 @bottle.route(url_base
+ '/datacenters/<datacenter_id_name>', method
='PUT')
537 def http_edit_datacenter_id(datacenter_id_name
):
538 '''edit datacenter details, can use both uuid or name'''
539 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
541 http_content
,_
= format_in( datacenter_edit_schema
)
542 r
= utils
.remove_extra_items(http_content
, datacenter_edit_schema
)
544 logger
.debug("Remove received extra items %s", str(r
))
547 datacenter_id
= nfvo
.edit_datacenter(mydb
, datacenter_id_name
, http_content
['datacenter'])
548 return http_get_datacenter_id('any', datacenter_id
)
549 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
550 logger
.error("http_edit_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
551 bottle
.abort(e
.http_code
, str(e
))
552 except Exception as e
:
553 logger
.error("Unexpected exception: ", exc_info
=True)
554 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
556 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='POST')
557 def http_post_sdn_controller(tenant_id
):
558 '''insert a sdn controller into the catalogue. '''
560 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
561 http_content
,_
= format_in( sdn_controller_schema
)
563 logger
.debug("tenant_id: "+tenant_id
)
564 #logger.debug("content: {}".format(http_content['sdn_controller']))
566 data
= nfvo
.sdn_controller_create(mydb
, tenant_id
, http_content
['sdn_controller'])
567 return format_out({"sdn_controller": nfvo
.sdn_controller_list(mydb
, tenant_id
, data
)})
568 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
569 logger
.error("http_post_sdn_controller 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/<controller_id>', method
='PUT')
576 def http_put_sdn_controller_update(tenant_id
, controller_id
):
577 '''Update sdn controller'''
579 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
580 http_content
,_
= format_in( sdn_controller_edit_schema
)
581 # r = utils.remove_extra_items(http_content, datacenter_schema)
583 # logger.debug("Remove received extra items %s", str(r))
585 #logger.debug("tenant_id: "+tenant_id)
586 logger
.debug("content: {}".format(http_content
['sdn_controller']))
588 data
= nfvo
.sdn_controller_update(mydb
, tenant_id
, controller_id
, http_content
['sdn_controller'])
589 return format_out({"sdn_controller": nfvo
.sdn_controller_list(mydb
, tenant_id
, controller_id
)})
591 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
592 logger
.error("http_post_sdn_controller 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
='GET')
599 def http_get_sdn_controller(tenant_id
):
600 '''get sdn controllers list, can use both uuid or name'''
602 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
604 data
= {'sdn_controllers': nfvo
.sdn_controller_list(mydb
, tenant_id
)}
605 return format_out(data
)
606 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
607 logger
.error("http_get_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
608 bottle
.abort(e
.http_code
, str(e
))
609 except Exception as e
:
610 logger
.error("Unexpected exception: ", exc_info
=True)
611 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
613 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='GET')
614 def http_get_sdn_controller_id(tenant_id
, controller_id
):
615 '''get sdn controller details, can use both uuid or name'''
617 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
618 data
= nfvo
.sdn_controller_list(mydb
, tenant_id
, controller_id
)
619 return format_out({"sdn_controllers": data
})
620 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
621 logger
.error("http_get_sdn_controller_id error {}: {}".format(e
.http_code
, str(e
)))
622 bottle
.abort(e
.http_code
, str(e
))
623 except Exception as e
:
624 logger
.error("Unexpected exception: ", exc_info
=True)
625 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
627 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='DELETE')
628 def http_delete_sdn_controller_id(tenant_id
, controller_id
):
629 '''delete sdn controller, can use both uuid or name'''
631 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
632 data
= nfvo
.sdn_controller_delete(mydb
, tenant_id
, controller_id
)
633 return format_out(data
)
634 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
635 logger
.error("http_delete_sdn_controller_id error {}: {}".format(e
.http_code
, str(e
)))
636 bottle
.abort(e
.http_code
, str(e
))
637 except Exception as e
:
638 logger
.error("Unexpected exception: ", exc_info
=True)
639 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
641 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='POST')
642 def http_post_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
643 '''Set the sdn port mapping for a datacenter. '''
645 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
646 http_content
, _
= format_in(sdn_port_mapping_schema
)
647 # r = utils.remove_extra_items(http_content, datacenter_schema)
649 # logger.debug("Remove received extra items %s", str(r))
651 data
= nfvo
.datacenter_sdn_port_mapping_set(mydb
, tenant_id
, datacenter_id
, http_content
['sdn_port_mapping'])
652 return format_out({"sdn_port_mapping": data
})
653 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
654 logger
.error("http_post_datacenter_sdn_port_mapping 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
='GET')
661 def http_get_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
662 '''get datacenter sdn mapping details, can use both uuid or name'''
664 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
666 data
= nfvo
.datacenter_sdn_port_mapping_list(mydb
, tenant_id
, datacenter_id
)
667 return format_out({"sdn_port_mapping": data
})
668 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
669 logger
.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
670 bottle
.abort(e
.http_code
, str(e
))
671 except Exception as e
:
672 logger
.error("Unexpected exception: ", exc_info
=True)
673 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
675 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='DELETE')
676 def http_delete_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
677 '''clean datacenter sdn mapping, can use both uuid or name'''
679 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
680 data
= nfvo
.datacenter_sdn_port_mapping_delete(mydb
, tenant_id
, datacenter_id
)
681 return format_out({"result": data
})
682 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
683 logger
.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
684 bottle
.abort(e
.http_code
, str(e
))
685 except Exception as e
:
686 logger
.error("Unexpected exception: ", exc_info
=True)
687 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
689 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/networks', method
='GET') #deprecated
690 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='GET')
691 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='GET')
692 def http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
693 '''get datacenter networks, can use both uuid or name'''
694 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
697 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
698 where_
= {"datacenter_id":datacenter_dict
['uuid']}
700 if utils
.check_valid_uuid(netmap_id
):
701 where_
["uuid"] = netmap_id
703 where_
["name"] = netmap_id
704 netmaps
=mydb
.get_rows(FROM
='datacenter_nets',
705 SELECT
=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
707 convert_datetime2str(netmaps
)
708 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
709 if netmap_id
and len(netmaps
)==1:
710 data
={'netmap' : netmaps
[0]}
711 elif netmap_id
and len(netmaps
)==0:
712 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
715 data
={'netmaps' : netmaps
}
716 return format_out(data
)
717 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
718 logger
.error("http_getnetwork_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
719 bottle
.abort(e
.http_code
, str(e
))
720 except Exception as e
:
721 logger
.error("Unexpected exception: ", exc_info
=True)
722 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
725 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='DELETE')
726 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='DELETE')
727 def http_delnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
728 '''get datacenter networks, can use both uuid or name'''
729 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
732 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
733 where_
= {"datacenter_id":datacenter_dict
['uuid']}
735 if utils
.check_valid_uuid(netmap_id
):
736 where_
["uuid"] = netmap_id
738 where_
["name"] = netmap_id
739 #change_keys_http2db(content, http2db_tenant, reverse=True)
740 deleted
= mydb
.delete_row(FROM
='datacenter_nets', WHERE
= where_
)
741 if deleted
== 0 and netmap_id
:
742 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
744 return format_out({"result": "netmap %s deleted" % netmap_id
})
746 return format_out({"result": "%d netmap deleted" % deleted
})
747 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
748 logger
.error("http_delnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
749 bottle
.abort(e
.http_code
, str(e
))
750 except Exception as e
:
751 logger
.error("Unexpected exception: ", exc_info
=True)
752 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
755 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method
='POST')
756 def http_uploadnetmap_datacenter_id(tenant_id
, datacenter_id
):
757 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
759 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, None)
760 convert_datetime2str(netmaps
)
761 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
762 data
={'netmaps' : netmaps
}
763 return format_out(data
)
764 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
765 logger
.error("http_uploadnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
766 bottle
.abort(e
.http_code
, str(e
))
767 except Exception as e
:
768 logger
.error("Unexpected exception: ", exc_info
=True)
769 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
772 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='POST')
773 def http_postnetmap_datacenter_id(tenant_id
, datacenter_id
):
774 '''creates a new netmap'''
775 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
777 http_content
,_
= format_in( netmap_new_schema
)
778 r
= utils
.remove_extra_items(http_content
, netmap_new_schema
)
780 logger
.debug("Remove received extra items %s", str(r
))
782 #obtain data, check that only one exist
783 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, http_content
)
784 convert_datetime2str(netmaps
)
785 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
786 data
={'netmaps' : netmaps
}
787 return format_out(data
)
788 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
789 logger
.error("http_postnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
790 bottle
.abort(e
.http_code
, str(e
))
791 except Exception as e
:
792 logger
.error("Unexpected exception: ", exc_info
=True)
793 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
796 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='PUT')
797 def http_putnettmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
):
799 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
801 http_content
,_
= format_in( netmap_edit_schema
)
802 r
= utils
.remove_extra_items(http_content
, netmap_edit_schema
)
804 logger
.debug("Remove received extra items %s", str(r
))
806 #obtain data, check that only one exist
808 nfvo
.datacenter_edit_netmap(mydb
, tenant_id
, datacenter_id
, netmap_id
, http_content
)
809 return http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
)
810 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
811 logger
.error("http_putnettmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
812 bottle
.abort(e
.http_code
, str(e
))
813 except Exception as e
:
814 logger
.error("Unexpected exception: ", exc_info
=True)
815 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
818 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/action', method
='POST')
819 def http_action_datacenter_id(tenant_id
, datacenter_id
):
820 '''perform an action over datacenter, can use both uuid or name'''
821 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
823 http_content
,_
= format_in( datacenter_action_schema
)
824 r
= utils
.remove_extra_items(http_content
, datacenter_action_schema
)
826 logger
.debug("Remove received extra items %s", str(r
))
828 #obtain data, check that only one exist
829 result
= nfvo
.datacenter_action(mydb
, tenant_id
, datacenter_id
, http_content
)
830 if 'net-update' in http_content
:
831 return http_getnetmap_datacenter_id(datacenter_id
)
833 return format_out(result
)
834 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
835 logger
.error("http_action_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
836 bottle
.abort(e
.http_code
, str(e
))
837 except Exception as e
:
838 logger
.error("Unexpected exception: ", exc_info
=True)
839 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
842 @bottle.route(url_base
+ '/datacenters/<datacenter_id>', method
='DELETE')
843 def http_delete_datacenter_id( datacenter_id
):
844 '''delete a tenant from database, can use both uuid or name'''
846 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
848 data
= nfvo
.delete_datacenter(mydb
, datacenter_id
)
849 return format_out({"result":"datacenter '" + data
+ "' deleted"})
850 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
851 logger
.error("http_delete_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
852 bottle
.abort(e
.http_code
, str(e
))
853 except Exception as e
:
854 logger
.error("Unexpected exception: ", exc_info
=True)
855 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
858 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='POST')
859 def http_associate_datacenters(tenant_id
, datacenter_id
):
860 '''associate an existing datacenter to a this tenant. '''
861 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
863 http_content
,_
= format_in( datacenter_associate_schema
)
864 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
866 logger
.debug("Remove received extra items %s", str(r
))
868 id_
= nfvo
.associate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
,
869 http_content
['datacenter'].get('vim_tenant'),
870 http_content
['datacenter'].get('vim_tenant_name'),
871 http_content
['datacenter'].get('vim_username'),
872 http_content
['datacenter'].get('vim_password'),
873 http_content
['datacenter'].get('config')
875 return http_get_datacenter_id(tenant_id
, id_
)
876 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
877 logger
.error("http_associate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
878 bottle
.abort(e
.http_code
, str(e
))
879 except Exception as e
:
880 logger
.error("Unexpected exception: ", exc_info
=True)
881 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
883 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='PUT')
884 def http_associate_datacenters_edit(tenant_id
, datacenter_id
):
885 '''associate an existing datacenter to a this tenant. '''
886 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
888 http_content
,_
= format_in( datacenter_associate_schema
)
889 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
891 logger
.debug("Remove received extra items %s", str(r
))
893 id_
= nfvo
.edit_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
,
894 http_content
['datacenter'].get('vim_tenant'),
895 http_content
['datacenter'].get('vim_tenant_name'),
896 http_content
['datacenter'].get('vim_username'),
897 http_content
['datacenter'].get('vim_password'),
898 http_content
['datacenter'].get('config')
900 return http_get_datacenter_id(tenant_id
, id_
)
901 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
902 logger
.error("http_associate_datacenters_edit error {}: {}".format(e
.http_code
, str(e
)))
903 bottle
.abort(e
.http_code
, str(e
))
904 except Exception as e
:
905 logger
.error("Unexpected exception: ", exc_info
=True)
906 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
908 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='DELETE')
909 def http_deassociate_datacenters(tenant_id
, datacenter_id
):
910 '''deassociate an existing datacenter to a this tenant. '''
911 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
913 data
= nfvo
.deassociate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
)
914 return format_out({"result": data
})
915 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
916 logger
.error("http_deassociate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
917 bottle
.abort(e
.http_code
, str(e
))
918 except Exception as e
:
919 logger
.error("Unexpected exception: ", exc_info
=True)
920 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
922 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method
='POST')
923 def http_post_vim_net_sdn_attach(tenant_id
, datacenter_id
, network_id
):
924 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
925 http_content
, _
= format_in(sdn_external_port_schema
)
927 data
= nfvo
.vim_net_sdn_attach(mydb
, tenant_id
, datacenter_id
, network_id
, http_content
)
928 return format_out(data
)
929 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
930 logger
.error("http_post_vim_net_sdn_attach error {}: {}".format(e
.http_code
, str(e
)))
931 bottle
.abort(e
.http_code
, str(e
))
932 except Exception as e
:
933 logger
.error("Unexpected exception: ", exc_info
=True)
934 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
936 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method
='DELETE')
937 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method
='DELETE')
938 def http_delete_vim_net_sdn_detach(tenant_id
, datacenter_id
, network_id
, port_id
=None):
939 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
941 data
= nfvo
.vim_net_sdn_detach(mydb
, tenant_id
, datacenter_id
, network_id
, port_id
)
942 return format_out(data
)
943 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
944 logger
.error("http_delete_vim_net_sdn_detach error {}: {}".format(e
.http_code
, str(e
)))
945 bottle
.abort(e
.http_code
, str(e
))
946 except Exception as e
:
947 logger
.error("Unexpected exception: ", exc_info
=True)
948 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
950 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='GET')
951 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='GET')
952 def http_get_vim_items(tenant_id
, datacenter_id
, item
, name
=None):
953 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
955 data
= nfvo
.vim_action_get(mydb
, tenant_id
, datacenter_id
, item
, name
)
956 return format_out(data
)
957 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
958 logger
.error("http_get_vim_items error {}: {}".format(e
.http_code
, str(e
)))
959 bottle
.abort(e
.http_code
, str(e
))
960 except Exception as e
:
961 logger
.error("Unexpected exception: ", exc_info
=True)
962 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
965 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='DELETE')
966 def http_del_vim_items(tenant_id
, datacenter_id
, item
, name
):
967 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
969 data
= nfvo
.vim_action_delete(mydb
, tenant_id
, datacenter_id
, item
, name
)
970 return format_out({"result":data
})
971 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
972 logger
.error("http_del_vim_items error {}: {}".format(e
.http_code
, str(e
)))
973 bottle
.abort(e
.http_code
, str(e
))
974 except Exception as e
:
975 logger
.error("Unexpected exception: ", exc_info
=True)
976 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
979 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='POST')
980 def http_post_vim_items(tenant_id
, datacenter_id
, item
):
981 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
982 http_content
,_
= format_in( object_schema
)
984 data
= nfvo
.vim_action_create(mydb
, tenant_id
, datacenter_id
, item
, http_content
)
985 return format_out(data
)
986 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
987 logger
.error("http_post_vim_items error {}: {}".format(e
.http_code
, str(e
)))
988 bottle
.abort(e
.http_code
, str(e
))
989 except Exception as e
:
990 logger
.error("Unexpected exception: ", exc_info
=True)
991 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
994 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='GET')
995 def http_get_vnfs(tenant_id
):
996 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
998 if tenant_id
!= 'any':
999 #check valid tenant_id
1000 nfvo
.check_tenant(mydb
, tenant_id
)
1001 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
1002 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
1004 if tenant_id
!= "any":
1005 where_or
["tenant_id"] = tenant_id
1006 where_or
["public"] = True
1007 vnfs
= mydb
.get_rows(FROM
='vnfs', SELECT
=select_
,WHERE
=where_
,WHERE_OR
=where_or
, WHERE_AND_OR
="AND",LIMIT
=limit_
)
1008 #change_keys_http2db(content, http2db_vnf, reverse=True)
1009 utils
.convert_str2boolean(vnfs
, ('public',))
1010 convert_datetime2str(vnfs
)
1011 data
={'vnfs' : vnfs
}
1012 return format_out(data
)
1013 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1014 logger
.error("http_get_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1015 bottle
.abort(e
.http_code
, str(e
))
1016 except Exception as e
:
1017 logger
.error("Unexpected exception: ", exc_info
=True)
1018 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1021 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='GET')
1022 def http_get_vnf_id(tenant_id
,vnf_id
):
1023 '''get vnf details, can use both uuid or name'''
1024 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1026 vnf
= nfvo
.get_vnf_id(mydb
,tenant_id
,vnf_id
)
1027 utils
.convert_str2boolean(vnf
, ('public',))
1028 convert_datetime2str(vnf
)
1029 return format_out(vnf
)
1030 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1031 logger
.error("http_get_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
1032 bottle
.abort(e
.http_code
, str(e
))
1033 except Exception as e
:
1034 logger
.error("Unexpected exception: ", exc_info
=True)
1035 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1038 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='POST')
1039 def http_post_vnfs(tenant_id
):
1040 """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
1041 :param tenant_id: tenant that this vnf belongs to
1044 # print "Parsing the YAML file of the VNF"
1046 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1047 http_content
, used_schema
= format_in( vnfd_schema_v01
, ("schema_version",), {"0.2": vnfd_schema_v02
})
1048 r
= utils
.remove_extra_items(http_content
, used_schema
)
1050 logger
.debug("Remove received extra items %s", str(r
))
1052 if used_schema
== vnfd_schema_v01
:
1053 vnf_id
= nfvo
.new_vnf(mydb
,tenant_id
,http_content
)
1054 elif used_schema
== vnfd_schema_v02
:
1055 vnf_id
= nfvo
.new_vnf_v02(mydb
,tenant_id
,http_content
)
1057 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1058 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1059 return http_get_vnf_id(tenant_id
, vnf_id
)
1060 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1061 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1062 bottle
.abort(e
.http_code
, str(e
))
1063 except Exception as e
:
1064 logger
.error("Unexpected exception: ", exc_info
=True)
1065 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1068 @bottle.route(url_base
+ '/v3/<tenant_id>/vnfd', method
='POST')
1069 def http_post_vnfs_v3(tenant_id
):
1071 Insert one or several VNFs in the catalog, following OSM IM
1072 :param tenant_id: tenant owner of the VNF
1073 :return: The detailed list of inserted VNFs, following the old format
1075 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1076 http_content
, _
= format_in(None)
1078 vnfd_uuid_list
= nfvo
.new_vnfd_v3(mydb
, tenant_id
, http_content
)
1080 for vnfd_uuid
in vnfd_uuid_list
:
1081 vnf
= nfvo
.get_vnf_id(mydb
, tenant_id
, vnfd_uuid
)
1082 utils
.convert_str2boolean(vnf
, ('public',))
1083 convert_datetime2str(vnf
)
1084 vnfd_list
.append(vnf
["vnf"])
1085 return format_out({"vnfd": vnfd_list
})
1086 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1087 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1088 bottle
.abort(e
.http_code
, str(e
))
1089 except Exception as e
:
1090 logger
.error("Unexpected exception: ", exc_info
=True)
1091 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1093 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='DELETE')
1094 def http_delete_vnf_id(tenant_id
, vnf_id
):
1095 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
1096 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1097 #check valid tenant_id and deletes the vnf, including images,
1099 data
= nfvo
.delete_vnf(mydb
,tenant_id
,vnf_id
)
1100 #print json.dumps(data, indent=4)
1101 return format_out({"result":"VNF " + data
+ " deleted"})
1102 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1103 logger
.error("http_delete_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
1104 bottle
.abort(e
.http_code
, str(e
))
1105 except Exception as e
:
1106 logger
.error("Unexpected exception: ", exc_info
=True)
1107 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1110 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1111 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1112 @bottle.route(url_base
+ '/<tenant_id>/physicalview/<datacenter>', method
='GET')
1113 def http_get_hosts(tenant_id
, datacenter
):
1114 '''get the tidvim host hopology from the vim.'''
1115 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1116 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1118 if datacenter
== 'treeview':
1119 data
= nfvo
.get_hosts(mydb
, tenant_id
)
1121 #openmano-gui is using a hardcoded value for the datacenter
1122 result
, data
= nfvo
.get_hosts_info(mydb
, tenant_id
) #, datacenter)
1125 #print "http_get_hosts error %d %s" % (-result, data)
1126 bottle
.abort(-result
, data
)
1128 convert_datetime2str(data
)
1129 #print json.dumps(data, indent=4)
1130 return format_out(data
)
1131 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1132 logger
.error("http_get_hosts error {}: {}".format(e
.http_code
, str(e
)))
1133 bottle
.abort(e
.http_code
, str(e
))
1134 except Exception as e
:
1135 logger
.error("Unexpected exception: ", exc_info
=True)
1136 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1139 @bottle.route(url_base
+ '/<path:path>', method
='OPTIONS')
1140 def http_options_deploy(path
):
1141 '''For some reason GUI web ask for OPTIONS that must be responded'''
1142 #TODO: check correct path, and correct headers request
1143 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1144 bottle
.response
.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1145 bottle
.response
.set_header('Accept','application/yaml,application/json')
1146 bottle
.response
.set_header('Content-Type','application/yaml,application/json')
1147 bottle
.response
.set_header('Access-Control-Allow-Headers','content-type')
1148 bottle
.response
.set_header('Access-Control-Allow-Origin','*')
1151 @bottle.route(url_base
+ '/<tenant_id>/topology/deploy', method
='POST')
1152 def http_post_deploy(tenant_id
):
1153 '''post topology deploy.'''
1154 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1156 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
})
1157 #r = utils.remove_extra_items(http_content, used_schema)
1158 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
1159 #print "http_post_deploy input: ", http_content
1162 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1163 instance
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['name'], http_content
['name'])
1164 #print json.dumps(data, indent=4)
1165 return format_out(instance
)
1166 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1167 logger
.error("http_post_deploy error {}: {}".format(e
.http_code
, str(e
)))
1168 bottle
.abort(e
.http_code
, str(e
))
1169 except Exception as e
:
1170 logger
.error("Unexpected exception: ", exc_info
=True)
1171 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1174 @bottle.route(url_base
+ '/<tenant_id>/topology/verify', method
='POST')
1175 def http_post_verify(tenant_id
):
1177 # '''post topology verify'''
1178 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
1179 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1186 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='POST')
1187 def http_post_scenarios(tenant_id
):
1188 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
1189 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1190 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
, "0.3": nsd_schema_v03
})
1191 #r = utils.remove_extra_items(http_content, used_schema)
1192 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
1193 #print "http_post_scenarios input: ", http_content
1195 if used_schema
== nsd_schema_v01
:
1196 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1197 elif used_schema
== nsd_schema_v02
:
1198 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.2")
1199 elif used_schema
== nsd_schema_v03
:
1200 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.3")
1202 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1203 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1204 #print json.dumps(data, indent=4)
1205 #return format_out(data)
1206 return http_get_scenario_id(tenant_id
, scenario_id
)
1207 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1208 logger
.error("http_post_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1209 bottle
.abort(e
.http_code
, str(e
))
1210 except Exception as e
:
1211 logger
.error("Unexpected exception: ", exc_info
=True)
1212 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1214 @bottle.route(url_base
+ '/v3/<tenant_id>/nsd', method
='POST')
1215 def http_post_nsds_v3(tenant_id
):
1217 Insert one or several NSDs in the catalog, following OSM IM
1218 :param tenant_id: tenant owner of the NSD
1219 :return: The detailed list of inserted NSDs, following the old format
1221 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1222 http_content
, _
= format_in(None)
1224 nsd_uuid_list
= nfvo
.new_nsd_v3(mydb
, tenant_id
, http_content
)
1226 for nsd_uuid
in nsd_uuid_list
:
1227 scenario
= mydb
.get_scenario(nsd_uuid
, tenant_id
)
1228 convert_datetime2str(scenario
)
1229 nsd_list
.append(scenario
)
1230 data
= {'nsd': nsd_list
}
1231 return format_out(data
)
1232 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1233 logger
.error("http_post_nsds_v3 error {}: {}".format(e
.http_code
, str(e
)))
1234 bottle
.abort(e
.http_code
, str(e
))
1235 except Exception as e
:
1236 logger
.error("Unexpected exception: ", exc_info
=True)
1237 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1240 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>/action', method
='POST')
1241 def http_post_scenario_action(tenant_id
, scenario_id
):
1242 '''take an action over a scenario'''
1243 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1245 http_content
, _
= format_in(scenario_action_schema
)
1246 r
= utils
.remove_extra_items(http_content
, scenario_action_schema
)
1248 logger
.debug("Remove received extra items %s", str(r
))
1250 # check valid tenant_id
1251 nfvo
.check_tenant(mydb
, tenant_id
)
1252 if "start" in http_content
:
1253 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['start']['instance_name'], \
1254 http_content
['start'].get('description',http_content
['start']['instance_name']),
1255 http_content
['start'].get('datacenter') )
1256 return format_out(data
)
1257 elif "deploy" in http_content
: #Equivalent to start
1258 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['deploy']['instance_name'],
1259 http_content
['deploy'].get('description',http_content
['deploy']['instance_name']),
1260 http_content
['deploy'].get('datacenter') )
1261 return format_out(data
)
1262 elif "reserve" in http_content
: #Reserve resources
1263 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['reserve']['instance_name'],
1264 http_content
['reserve'].get('description',http_content
['reserve']['instance_name']),
1265 http_content
['reserve'].get('datacenter'), startvms
=False )
1266 return format_out(data
)
1267 elif "verify" in http_content
: #Equivalent to start and then delete
1268 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['verify']['instance_name'],
1269 http_content
['verify'].get('description',http_content
['verify']['instance_name']),
1270 http_content
['verify'].get('datacenter'), startvms
=False )
1271 instance_id
= data
['uuid']
1272 nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1273 return format_out({"result":"Verify OK"})
1274 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1275 logger
.error("http_post_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1276 bottle
.abort(e
.http_code
, str(e
))
1277 except Exception as e
:
1278 logger
.error("Unexpected exception: ", exc_info
=True)
1279 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1282 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='GET')
1283 def http_get_scenarios(tenant_id
):
1284 '''get scenarios list'''
1285 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1287 #check valid tenant_id
1288 if tenant_id
!= "any":
1289 nfvo
.check_tenant(mydb
, tenant_id
)
1291 s
,w
,l
=filter_query_string(bottle
.request
.query
, None,
1292 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
1294 if tenant_id
!= "any":
1295 where_or
["tenant_id"] = tenant_id
1296 where_or
["public"] = True
1297 scenarios
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, WHERE_OR
=where_or
, WHERE_AND_OR
="AND", LIMIT
=l
, FROM
='scenarios')
1298 convert_datetime2str(scenarios
)
1299 utils
.convert_str2boolean(scenarios
, ('public',) )
1300 data
={'scenarios':scenarios
}
1301 #print json.dumps(scenarios, indent=4)
1302 return format_out(data
)
1303 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1304 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1305 bottle
.abort(e
.http_code
, str(e
))
1306 except Exception as e
:
1307 logger
.error("Unexpected exception: ", exc_info
=True)
1308 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1311 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='GET')
1312 def http_get_scenario_id(tenant_id
, scenario_id
):
1313 '''get scenario details, can use both uuid or name'''
1314 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1316 #check valid tenant_id
1317 if tenant_id
!= "any":
1318 nfvo
.check_tenant(mydb
, tenant_id
)
1320 scenario
= mydb
.get_scenario(scenario_id
, tenant_id
)
1321 convert_datetime2str(scenario
)
1322 data
={'scenario' : scenario
}
1323 return format_out(data
)
1324 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1325 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1326 bottle
.abort(e
.http_code
, str(e
))
1327 except Exception as e
:
1328 logger
.error("Unexpected exception: ", exc_info
=True)
1329 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1332 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='DELETE')
1333 def http_delete_scenario_id(tenant_id
, scenario_id
):
1334 '''delete a scenario from database, can use both uuid or name'''
1335 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1337 #check valid tenant_id
1338 if tenant_id
!= "any":
1339 nfvo
.check_tenant(mydb
, tenant_id
)
1341 data
= mydb
.delete_scenario(scenario_id
, tenant_id
)
1342 #print json.dumps(data, indent=4)
1343 return format_out({"result":"scenario " + data
+ " deleted"})
1344 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1345 logger
.error("http_delete_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1346 bottle
.abort(e
.http_code
, str(e
))
1347 except Exception as e
:
1348 logger
.error("Unexpected exception: ", exc_info
=True)
1349 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1352 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='PUT')
1353 def http_put_scenario_id(tenant_id
, scenario_id
):
1354 '''edit an existing scenario id'''
1355 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1356 http_content
,_
= format_in( scenario_edit_schema
)
1357 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1358 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1359 #print "http_put_scenario_id input: ", http_content
1361 nfvo
.edit_scenario(mydb
, tenant_id
, scenario_id
, http_content
)
1362 #print json.dumps(data, indent=4)
1363 #return format_out(data)
1364 return http_get_scenario_id(tenant_id
, scenario_id
)
1365 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1366 logger
.error("http_put_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1367 bottle
.abort(e
.http_code
, str(e
))
1368 except Exception as e
:
1369 logger
.error("Unexpected exception: ", exc_info
=True)
1370 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1372 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='POST')
1373 def http_post_instances(tenant_id
):
1374 '''create an instance-scenario'''
1375 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1377 http_content
, used_schema
= format_in(instance_scenario_create_schema_v01
)
1378 r
= utils
.remove_extra_items(http_content
, used_schema
)
1380 logger
.warning("http_post_instances: Warning: remove extra items %s", str(r
))
1382 #check valid tenant_id
1383 if tenant_id
!= "any":
1384 nfvo
.check_tenant(mydb
, tenant_id
)
1385 data
= nfvo
.create_instance(mydb
, tenant_id
, http_content
["instance"])
1386 return format_out(data
)
1387 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1388 logger
.error("http_post_instances error {}: {}".format(e
.http_code
, str(e
)))
1389 bottle
.abort(e
.http_code
, str(e
))
1390 except Exception as e
:
1391 logger
.error("Unexpected exception: ", exc_info
=True)
1392 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1397 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='GET')
1398 def http_get_instances(tenant_id
):
1399 '''get instance list'''
1401 #check valid tenant_id
1402 if tenant_id
!= "any":
1403 nfvo
.check_tenant(mydb
, tenant_id
)
1405 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1406 if tenant_id
!= "any":
1407 w
['tenant_id'] = tenant_id
1408 instances
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, LIMIT
=l
, FROM
='instance_scenarios')
1409 convert_datetime2str(instances
)
1410 utils
.convert_str2boolean(instances
, ('public',) )
1411 data
={'instances':instances
}
1412 return format_out(data
)
1413 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1414 logger
.error("http_get_instances 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>/instances/<instance_id>', method
='GET')
1422 def http_get_instance_id(tenant_id
, instance_id
):
1423 '''get instances 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
)
1429 if tenant_id
== "any":
1431 #obtain data (first time is only to check that the instance exists)
1432 instance_dict
= mydb
.get_instance_scenario(instance_id
, tenant_id
, verbose
=True)
1434 nfvo
.refresh_instance(mydb
, tenant_id
, instance_dict
)
1435 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1436 logger
.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e
))
1437 # obtain data with results upated
1438 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1439 # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
1440 for vnf
in instance
.get("vnfs", ()):
1441 for vm
in vnf
.get("vms", ()):
1442 for iface
in vm
.get("interfaces", ()):
1443 if iface
.get("ip_address"):
1444 index
= iface
["ip_address"].find(";")
1446 iface
["ip_address"] = iface
["ip_address"][:index
]
1447 convert_datetime2str(instance
)
1448 # print json.dumps(instance, indent=4)
1449 return format_out(instance
)
1450 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1451 logger
.error("http_get_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1452 bottle
.abort(e
.http_code
, str(e
))
1453 except Exception as e
:
1454 logger
.error("Unexpected exception: ", exc_info
=True)
1455 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1458 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='DELETE')
1459 def http_delete_instance_id(tenant_id
, instance_id
):
1460 '''delete instance from VIM and from database, can use both uuid or name'''
1461 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1463 #check valid tenant_id
1464 if tenant_id
!= "any":
1465 nfvo
.check_tenant(mydb
, tenant_id
)
1466 if tenant_id
== "any":
1469 message
= nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1470 return format_out({"result":message
})
1471 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1472 logger
.error("http_delete_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1473 bottle
.abort(e
.http_code
, str(e
))
1474 except Exception as e
:
1475 logger
.error("Unexpected exception: ", exc_info
=True)
1476 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1479 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='POST')
1480 def http_post_instance_scenario_action(tenant_id
, instance_id
):
1482 take an action over a scenario instance
1483 :param tenant_id: tenant where user belongs to
1484 :param instance_id: instance indentity
1487 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1489 http_content
, _
= format_in(instance_scenario_action_schema
)
1490 r
= utils
.remove_extra_items(http_content
, instance_scenario_action_schema
)
1492 logger
.debug("Remove received extra items %s", str(r
))
1494 #check valid tenant_id
1495 if tenant_id
!= "any":
1496 nfvo
.check_tenant(mydb
, tenant_id
)
1498 #print "http_post_instance_scenario_action input: ", http_content
1500 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1501 instance_id
= instance
["uuid"]
1503 data
= nfvo
.instance_action(mydb
, tenant_id
, instance_id
, http_content
)
1504 return format_out(data
)
1505 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1506 logger
.error("http_post_instance_scenario_action 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
))
1513 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='GET')
1514 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action/<action_id>', method
='GET')
1515 def http_get_instance_scenario_action(tenant_id
, instance_id
, action_id
=None):
1517 List the actions done over an instance, or the action details
1518 :param tenant_id: tenant where user belongs to. Can be "any" to ignore
1519 :param instance_id: instance id, can be "any" to get actions of all instances
1522 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1524 # check valid tenant_id
1525 if tenant_id
!= "any":
1526 nfvo
.check_tenant(mydb
, tenant_id
)
1527 data
= nfvo
.instance_action_get(mydb
, tenant_id
, instance_id
, action_id
)
1528 return format_out(data
)
1529 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1530 logger
.error("http_get_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1531 bottle
.abort(e
.http_code
, str(e
))
1532 except Exception as e
:
1533 logger
.error("Unexpected exception: ", exc_info
=True)
1534 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1546 def error400(error
):
1547 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
1548 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
1549 return format_out(e
)