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>/vim_accounts', method
='GET')
475 @bottle.route(url_base
+ '/<tenant_id>/vim_accounts/<vim_account_id>', method
='GET')
476 def http_get_vim_account(tenant_id
, vim_account_id
=None):
477 '''get vim_account list/details, '''
478 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
480 select_
= ('uuid', 'name', 'dt.datacenter_id as vim_id', 'vim_tenant_name', 'vim_tenant_id', 'user', 'config',
481 'dt.created_at as created_at', 'passwd')
482 where_
= {'nfvo_tenant_id': tenant_id
}
484 where_
['dt.uuid'] = vim_account_id
485 from_
= 'tenants_datacenters as td join datacenter_tenants as dt on dt.uuid=td.datacenter_tenant_id'
486 vim_accounts
= mydb
.get_rows(SELECT
=select_
, FROM
=from_
, WHERE
=where_
)
488 if len(vim_accounts
) == 0 and vim_account_id
:
489 bottle
.abort(HTTP_Not_Found
, "No vim_account found for tenant {} and id '{}'".format(tenant_id
,
491 for vim_account
in vim_accounts
:
492 if vim_account
["passwd"]:
493 vim_account
["passwd"] = "******"
494 if vim_account
['config'] != None:
496 config_dict
= yaml
.load(vim_account
['config'])
497 vim_account
['config'] = config_dict
498 if vim_account
['config'].get('admin_password'):
499 vim_account
['config']['admin_password'] = "******"
500 if vim_account
['config'].get('vcenter_password'):
501 vim_account
['config']['vcenter_password'] = "******"
502 if vim_account
['config'].get('nsx_password'):
503 vim_account
['config']['nsx_password'] = "******"
504 except Exception as e
:
505 logger
.error("Exception '%s' while trying to load config information", str(e
))
506 # change_keys_http2db(content, http2db_datacenter, reverse=True)
507 #convert_datetime2str(vim_account)
509 return format_out({"datacenter": vim_accounts
[0]})
511 return format_out({"datacenters": vim_accounts
})
512 except bottle
.HTTPError
:
514 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
515 logger
.error("http_get_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
516 bottle
.abort(e
.http_code
, str(e
))
517 except Exception as e
:
518 logger
.error("Unexpected exception: ", exc_info
=True)
519 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
522 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='GET')
523 def http_get_datacenter_id(tenant_id
, datacenter_id
):
524 '''get datacenter details, can use both uuid or name'''
525 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
527 if tenant_id
!= 'any':
528 #check valid tenant_id
529 nfvo
.check_tenant(mydb
, tenant_id
)
531 what
= 'uuid' if utils
.check_valid_uuid(datacenter_id
) else 'name'
533 where_
[what
] = datacenter_id
534 select_
=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'd.config as config', 'description', 'd.created_at as created_at']
535 if tenant_id
!= 'any':
536 select_
.append("datacenter_tenant_id")
537 where_
['td.nfvo_tenant_id']= tenant_id
538 from_
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
540 from_
='datacenters as d'
541 datacenters
= mydb
.get_rows(
546 if len(datacenters
)==0:
547 bottle
.abort( HTTP_Not_Found
, "No datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
548 elif len(datacenters
)>1:
549 bottle
.abort( HTTP_Bad_Request
, "More than one datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
550 datacenter
= datacenters
[0]
551 if tenant_id
!= 'any':
553 vim_tenants
= mydb
.get_rows(
554 SELECT
=("vim_tenant_name", "vim_tenant_id", "user", "passwd", "config"),
555 FROM
="datacenter_tenants",
556 WHERE
={"uuid": datacenters
[0]["datacenter_tenant_id"]},
557 ORDER_BY
=("created", ) )
558 del datacenter
["datacenter_tenant_id"]
559 datacenter
["vim_tenants"] = vim_tenants
560 for vim_tenant
in vim_tenants
:
561 if vim_tenant
["passwd"]:
562 vim_tenant
["passwd"] = "******"
563 if vim_tenant
['config'] != None:
565 config_dict
= yaml
.load(vim_tenant
['config'])
566 vim_tenant
['config'] = config_dict
567 if vim_tenant
['config'].get('admin_password'):
568 vim_tenant
['config']['admin_password'] = "******"
569 if vim_tenant
['config'].get('vcenter_password'):
570 vim_tenant
['config']['vcenter_password'] = "******"
571 if vim_tenant
['config'].get('nsx_password'):
572 vim_tenant
['config']['nsx_password'] = "******"
573 except Exception as e
:
574 logger
.error("Exception '%s' while trying to load config information", str(e
))
576 if datacenter
['config'] != None:
578 config_dict
= yaml
.load(datacenter
['config'])
579 datacenter
['config'] = config_dict
580 if datacenter
['config'].get('admin_password'):
581 datacenter
['config']['admin_password'] = "******"
582 if datacenter
['config'].get('vcenter_password'):
583 datacenter
['config']['vcenter_password'] = "******"
584 if datacenter
['config'].get('nsx_password'):
585 datacenter
['config']['nsx_password'] = "******"
586 except Exception as e
:
587 logger
.error("Exception '%s' while trying to load config information", str(e
))
588 #change_keys_http2db(content, http2db_datacenter, reverse=True)
589 convert_datetime2str(datacenter
)
590 data
={'datacenter' : datacenter
}
591 return format_out(data
)
592 except bottle
.HTTPError
:
594 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
595 logger
.error("http_get_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
596 bottle
.abort(e
.http_code
, str(e
))
597 except Exception as e
:
598 logger
.error("Unexpected exception: ", exc_info
=True)
599 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
602 @bottle.route(url_base
+ '/datacenters', method
='POST')
603 def http_post_datacenters():
604 '''insert a datacenter into the catalogue. '''
606 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
607 http_content
,_
= format_in(datacenter_schema
, confidential_data
=True)
608 r
= utils
.remove_extra_items(http_content
, datacenter_schema
)
610 logger
.debug("Remove received extra items %s", str(r
))
612 data
= nfvo
.new_datacenter(mydb
, http_content
['datacenter'])
613 return http_get_datacenter_id('any', data
)
614 except bottle
.HTTPError
:
616 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
617 logger
.error("http_post_datacenters error {}: {}".format(e
.http_code
, str(e
)))
618 bottle
.abort(e
.http_code
, str(e
))
619 except Exception as e
:
620 logger
.error("Unexpected exception: ", exc_info
=True)
621 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
624 @bottle.route(url_base
+ '/datacenters/<datacenter_id_name>', method
='PUT')
625 def http_edit_datacenter_id(datacenter_id_name
):
626 '''edit datacenter details, can use both uuid or name'''
627 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
629 http_content
,_
= format_in( datacenter_edit_schema
)
630 r
= utils
.remove_extra_items(http_content
, datacenter_edit_schema
)
632 logger
.debug("Remove received extra items %s", str(r
))
635 datacenter_id
= nfvo
.edit_datacenter(mydb
, datacenter_id_name
, http_content
['datacenter'])
636 return http_get_datacenter_id('any', datacenter_id
)
637 except bottle
.HTTPError
:
639 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
640 logger
.error("http_edit_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
641 bottle
.abort(e
.http_code
, str(e
))
642 except Exception as e
:
643 logger
.error("Unexpected exception: ", exc_info
=True)
644 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
646 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='POST')
647 def http_post_sdn_controller(tenant_id
):
648 '''insert a sdn controller into the catalogue. '''
650 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
651 http_content
,_
= format_in( sdn_controller_schema
)
653 logger
.debug("tenant_id: "+tenant_id
)
654 #logger.debug("content: {}".format(http_content['sdn_controller']))
656 data
= nfvo
.sdn_controller_create(mydb
, tenant_id
, http_content
['sdn_controller'])
657 return format_out({"sdn_controller": nfvo
.sdn_controller_list(mydb
, tenant_id
, data
)})
658 except bottle
.HTTPError
:
660 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
661 logger
.error("http_post_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
662 bottle
.abort(e
.http_code
, str(e
))
663 except Exception as e
:
664 logger
.error("Unexpected exception: ", exc_info
=True)
665 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
667 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='PUT')
668 def http_put_sdn_controller_update(tenant_id
, controller_id
):
669 '''Update sdn controller'''
671 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
672 http_content
,_
= format_in( sdn_controller_edit_schema
)
673 # r = utils.remove_extra_items(http_content, datacenter_schema)
675 # logger.debug("Remove received extra items %s", str(r))
677 #logger.debug("tenant_id: "+tenant_id)
678 logger
.debug("content: {}".format(http_content
['sdn_controller']))
680 data
= nfvo
.sdn_controller_update(mydb
, tenant_id
, controller_id
, http_content
['sdn_controller'])
681 return format_out({"sdn_controller": nfvo
.sdn_controller_list(mydb
, tenant_id
, controller_id
)})
683 except bottle
.HTTPError
:
685 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
686 logger
.error("http_post_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
687 bottle
.abort(e
.http_code
, str(e
))
688 except Exception as e
:
689 logger
.error("Unexpected exception: ", exc_info
=True)
690 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
692 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers', method
='GET')
693 def http_get_sdn_controller(tenant_id
):
694 '''get sdn controllers list, can use both uuid or name'''
696 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
698 data
= {'sdn_controllers': nfvo
.sdn_controller_list(mydb
, tenant_id
)}
699 return format_out(data
)
700 except bottle
.HTTPError
:
702 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
703 logger
.error("http_get_sdn_controller error {}: {}".format(e
.http_code
, str(e
)))
704 bottle
.abort(e
.http_code
, str(e
))
705 except Exception as e
:
706 logger
.error("Unexpected exception: ", exc_info
=True)
707 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
709 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='GET')
710 def http_get_sdn_controller_id(tenant_id
, controller_id
):
711 '''get sdn controller details, can use both uuid or name'''
713 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
714 data
= nfvo
.sdn_controller_list(mydb
, tenant_id
, controller_id
)
715 return format_out({"sdn_controllers": data
})
716 except bottle
.HTTPError
:
718 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
719 logger
.error("http_get_sdn_controller_id error {}: {}".format(e
.http_code
, str(e
)))
720 bottle
.abort(e
.http_code
, str(e
))
721 except Exception as e
:
722 logger
.error("Unexpected exception: ", exc_info
=True)
723 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
725 @bottle.route(url_base
+ '/<tenant_id>/sdn_controllers/<controller_id>', method
='DELETE')
726 def http_delete_sdn_controller_id(tenant_id
, controller_id
):
727 '''delete sdn controller, can use both uuid or name'''
729 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
730 data
= nfvo
.sdn_controller_delete(mydb
, tenant_id
, controller_id
)
731 return format_out(data
)
732 except bottle
.HTTPError
:
734 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
735 logger
.error("http_delete_sdn_controller_id error {}: {}".format(e
.http_code
, str(e
)))
736 bottle
.abort(e
.http_code
, str(e
))
737 except Exception as e
:
738 logger
.error("Unexpected exception: ", exc_info
=True)
739 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
741 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='POST')
742 def http_post_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
743 '''Set the sdn port mapping for a datacenter. '''
745 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
746 http_content
, _
= format_in(sdn_port_mapping_schema
)
747 # r = utils.remove_extra_items(http_content, datacenter_schema)
749 # logger.debug("Remove received extra items %s", str(r))
751 data
= nfvo
.datacenter_sdn_port_mapping_set(mydb
, tenant_id
, datacenter_id
, http_content
['sdn_port_mapping'])
752 return format_out({"sdn_port_mapping": data
})
753 except bottle
.HTTPError
:
755 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
756 logger
.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
757 bottle
.abort(e
.http_code
, str(e
))
758 except Exception as e
:
759 logger
.error("Unexpected exception: ", exc_info
=True)
760 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
762 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='GET')
763 def http_get_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
764 '''get datacenter sdn mapping details, can use both uuid or name'''
766 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
768 data
= nfvo
.datacenter_sdn_port_mapping_list(mydb
, tenant_id
, datacenter_id
)
769 return format_out({"sdn_port_mapping": data
})
770 except bottle
.HTTPError
:
772 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
773 logger
.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e
.http_code
, str(e
)))
774 bottle
.abort(e
.http_code
, str(e
))
775 except Exception as e
:
776 logger
.error("Unexpected exception: ", exc_info
=True)
777 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
779 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method
='DELETE')
780 def http_delete_datacenter_sdn_port_mapping(tenant_id
, datacenter_id
):
781 '''clean datacenter sdn mapping, can use both uuid or name'''
783 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
784 data
= nfvo
.datacenter_sdn_port_mapping_delete(mydb
, tenant_id
, datacenter_id
)
785 return format_out({"result": data
})
786 except bottle
.HTTPError
:
788 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
789 logger
.error("http_delete_datacenter_sdn_port_mapping 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
))
795 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/networks', method
='GET') #deprecated
796 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='GET')
797 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='GET')
798 def http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
799 '''get datacenter networks, can use both uuid or name'''
800 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
803 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
804 where_
= {"datacenter_id":datacenter_dict
['uuid']}
806 if utils
.check_valid_uuid(netmap_id
):
807 where_
["uuid"] = netmap_id
809 where_
["name"] = netmap_id
810 netmaps
=mydb
.get_rows(FROM
='datacenter_nets',
811 SELECT
=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
813 convert_datetime2str(netmaps
)
814 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
815 if netmap_id
and len(netmaps
)==1:
816 data
={'netmap' : netmaps
[0]}
817 elif netmap_id
and len(netmaps
)==0:
818 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
821 data
={'netmaps' : netmaps
}
822 return format_out(data
)
823 except bottle
.HTTPError
:
825 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
826 logger
.error("http_getnetwork_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
827 bottle
.abort(e
.http_code
, str(e
))
828 except Exception as e
:
829 logger
.error("Unexpected exception: ", exc_info
=True)
830 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
833 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='DELETE')
834 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='DELETE')
835 def http_delnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
836 '''get datacenter networks, can use both uuid or name'''
837 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
840 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
841 where_
= {"datacenter_id":datacenter_dict
['uuid']}
843 if utils
.check_valid_uuid(netmap_id
):
844 where_
["uuid"] = netmap_id
846 where_
["name"] = netmap_id
847 #change_keys_http2db(content, http2db_tenant, reverse=True)
848 deleted
= mydb
.delete_row(FROM
='datacenter_nets', WHERE
= where_
)
849 if deleted
== 0 and netmap_id
:
850 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
852 return format_out({"result": "netmap %s deleted" % netmap_id
})
854 return format_out({"result": "%d netmap deleted" % deleted
})
855 except bottle
.HTTPError
:
857 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
858 logger
.error("http_delnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
859 bottle
.abort(e
.http_code
, str(e
))
860 except Exception as e
:
861 logger
.error("Unexpected exception: ", exc_info
=True)
862 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
865 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method
='POST')
866 def http_uploadnetmap_datacenter_id(tenant_id
, datacenter_id
):
867 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
869 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, None)
870 convert_datetime2str(netmaps
)
871 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
872 data
={'netmaps' : netmaps
}
873 return format_out(data
)
874 except bottle
.HTTPError
:
876 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
877 logger
.error("http_uploadnetmap_datacenter_id 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
))
884 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='POST')
885 def http_postnetmap_datacenter_id(tenant_id
, datacenter_id
):
886 '''creates a new netmap'''
887 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
889 http_content
,_
= format_in( netmap_new_schema
)
890 r
= utils
.remove_extra_items(http_content
, netmap_new_schema
)
892 logger
.debug("Remove received extra items %s", str(r
))
894 #obtain data, check that only one exist
895 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, http_content
)
896 convert_datetime2str(netmaps
)
897 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
898 data
={'netmaps' : netmaps
}
899 return format_out(data
)
900 except bottle
.HTTPError
:
902 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
903 logger
.error("http_postnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
904 bottle
.abort(e
.http_code
, str(e
))
905 except Exception as e
:
906 logger
.error("Unexpected exception: ", exc_info
=True)
907 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
910 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='PUT')
911 def http_putnettmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
):
913 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
915 http_content
,_
= format_in( netmap_edit_schema
)
916 r
= utils
.remove_extra_items(http_content
, netmap_edit_schema
)
918 logger
.debug("Remove received extra items %s", str(r
))
920 #obtain data, check that only one exist
922 nfvo
.datacenter_edit_netmap(mydb
, tenant_id
, datacenter_id
, netmap_id
, http_content
)
923 return http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
)
924 except bottle
.HTTPError
:
926 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
927 logger
.error("http_putnettmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
928 bottle
.abort(e
.http_code
, str(e
))
929 except Exception as e
:
930 logger
.error("Unexpected exception: ", exc_info
=True)
931 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
934 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/action', method
='POST')
935 def http_action_datacenter_id(tenant_id
, datacenter_id
):
936 '''perform an action over datacenter, can use both uuid or name'''
937 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
939 http_content
,_
= format_in( datacenter_action_schema
)
940 r
= utils
.remove_extra_items(http_content
, datacenter_action_schema
)
942 logger
.debug("Remove received extra items %s", str(r
))
944 #obtain data, check that only one exist
945 result
= nfvo
.datacenter_action(mydb
, tenant_id
, datacenter_id
, http_content
)
946 if 'net-update' in http_content
:
947 return http_getnetmap_datacenter_id(datacenter_id
)
949 return format_out(result
)
950 except bottle
.HTTPError
:
952 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
953 logger
.error("http_action_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
954 bottle
.abort(e
.http_code
, str(e
))
955 except Exception as e
:
956 logger
.error("Unexpected exception: ", exc_info
=True)
957 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
960 @bottle.route(url_base
+ '/datacenters/<datacenter_id>', method
='DELETE')
961 def http_delete_datacenter_id( datacenter_id
):
962 '''delete a tenant from database, can use both uuid or name'''
964 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
966 data
= nfvo
.delete_datacenter(mydb
, datacenter_id
)
967 return format_out({"result":"datacenter '" + data
+ "' deleted"})
968 except bottle
.HTTPError
:
970 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
971 logger
.error("http_delete_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
972 bottle
.abort(e
.http_code
, str(e
))
973 except Exception as e
:
974 logger
.error("Unexpected exception: ", exc_info
=True)
975 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
978 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='POST')
979 @bottle.route(url_base
+ '/<tenant_id>/vim_accounts', method
='POST')
980 def http_associate_datacenters(tenant_id
, datacenter_id
=None):
981 '''associate an existing datacenter to a this tenant. '''
982 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
984 http_content
,_
= format_in(datacenter_associate_schema
, confidential_data
=True)
985 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
987 logger
.debug("Remove received extra items %s", str(r
))
989 vim_account_id
= nfvo
.create_vim_account(mydb
, tenant_id
, datacenter_id
,
990 **http_content
['datacenter'])
991 return http_get_vim_account(tenant_id
, vim_account_id
)
992 except bottle
.HTTPError
:
994 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
995 logger
.error("http_associate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
996 bottle
.abort(e
.http_code
, str(e
))
997 except Exception as e
:
998 logger
.error("Unexpected exception: ", exc_info
=True)
999 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1001 @bottle.route(url_base
+ '/<tenant_id>/vim_accounts/<vim_account_id>', method
='PUT')
1002 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='PUT')
1003 def http_vim_account_edit(tenant_id
, vim_account_id
=None, datacenter_id
=None):
1004 '''associate an existing datacenter to a this tenant. '''
1005 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1007 http_content
,_
= format_in(datacenter_associate_schema
)
1008 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
1010 logger
.debug("Remove received extra items %s", str(r
))
1012 vim_account_id
= nfvo
.edit_vim_account(mydb
, tenant_id
, vim_account_id
, datacenter_id
=datacenter_id
,
1013 **http_content
['datacenter'])
1014 return http_get_vim_account(tenant_id
, vim_account_id
)
1015 except bottle
.HTTPError
:
1017 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1018 logger
.error("http_vim_account_edit error {}: {}".format(e
.http_code
, str(e
)))
1019 bottle
.abort(e
.http_code
, str(e
))
1020 except Exception as e
:
1021 logger
.error("Unexpected exception: ", exc_info
=True)
1022 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1025 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='DELETE')
1026 @bottle.route(url_base
+ '/<tenant_id>/vim_accounts/<vim_account_id>', method
='DELETE')
1027 def http_deassociate_datacenters(tenant_id
, datacenter_id
=None, vim_account_id
=None):
1028 '''deassociate an existing datacenter to a this tenant. '''
1029 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1031 data
= nfvo
.delete_vim_account(mydb
, tenant_id
, vim_account_id
, datacenter_id
)
1032 return format_out({"result": data
})
1033 except bottle
.HTTPError
:
1035 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1036 logger
.error("http_deassociate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
1037 bottle
.abort(e
.http_code
, str(e
))
1038 except Exception as e
:
1039 logger
.error("Unexpected exception: ", exc_info
=True)
1040 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1042 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method
='POST')
1043 def http_post_vim_net_sdn_attach(tenant_id
, datacenter_id
, network_id
):
1044 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1045 http_content
, _
= format_in(sdn_external_port_schema
)
1047 data
= nfvo
.vim_net_sdn_attach(mydb
, tenant_id
, datacenter_id
, network_id
, http_content
)
1048 return format_out(data
)
1049 except bottle
.HTTPError
:
1051 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1052 logger
.error("http_post_vim_net_sdn_attach error {}: {}".format(e
.http_code
, str(e
)))
1053 bottle
.abort(e
.http_code
, str(e
))
1054 except Exception as e
:
1055 logger
.error("Unexpected exception: ", exc_info
=True)
1056 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1058 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method
='DELETE')
1059 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method
='DELETE')
1060 def http_delete_vim_net_sdn_detach(tenant_id
, datacenter_id
, network_id
, port_id
=None):
1061 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1063 data
= nfvo
.vim_net_sdn_detach(mydb
, tenant_id
, datacenter_id
, network_id
, port_id
)
1064 return format_out(data
)
1065 except bottle
.HTTPError
:
1067 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1068 logger
.error("http_delete_vim_net_sdn_detach error {}: {}".format(e
.http_code
, str(e
)))
1069 bottle
.abort(e
.http_code
, str(e
))
1070 except Exception as e
:
1071 logger
.error("Unexpected exception: ", exc_info
=True)
1072 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1074 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='GET')
1075 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='GET')
1076 def http_get_vim_items(tenant_id
, datacenter_id
, item
, name
=None):
1077 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1079 data
= nfvo
.vim_action_get(mydb
, tenant_id
, datacenter_id
, item
, name
)
1080 return format_out(data
)
1081 except bottle
.HTTPError
:
1083 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1084 logger
.error("http_get_vim_items error {}: {}".format(e
.http_code
, str(e
)))
1085 bottle
.abort(e
.http_code
, str(e
))
1086 except Exception as e
:
1087 logger
.error("Unexpected exception: ", exc_info
=True)
1088 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1091 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='DELETE')
1092 def http_del_vim_items(tenant_id
, datacenter_id
, item
, name
):
1093 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1095 data
= nfvo
.vim_action_delete(mydb
, tenant_id
, datacenter_id
, item
, name
)
1096 return format_out({"result":data
})
1097 except bottle
.HTTPError
:
1099 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1100 logger
.error("http_del_vim_items error {}: {}".format(e
.http_code
, str(e
)))
1101 bottle
.abort(e
.http_code
, str(e
))
1102 except Exception as e
:
1103 logger
.error("Unexpected exception: ", exc_info
=True)
1104 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1107 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='POST')
1108 def http_post_vim_items(tenant_id
, datacenter_id
, item
):
1109 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1110 http_content
,_
= format_in( object_schema
)
1112 data
= nfvo
.vim_action_create(mydb
, tenant_id
, datacenter_id
, item
, http_content
)
1113 return format_out(data
)
1114 except bottle
.HTTPError
:
1116 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1117 logger
.error("http_post_vim_items error {}: {}".format(e
.http_code
, str(e
)))
1118 bottle
.abort(e
.http_code
, str(e
))
1119 except Exception as e
:
1120 logger
.error("Unexpected exception: ", exc_info
=True)
1121 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1124 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='GET')
1125 def http_get_vnfs(tenant_id
):
1126 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1128 if tenant_id
!= 'any':
1129 #check valid tenant_id
1130 nfvo
.check_tenant(mydb
, tenant_id
)
1131 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
1132 ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
1133 if tenant_id
!= "any":
1134 where_
["OR"]={"tenant_id": tenant_id
, "public": True}
1135 vnfs
= mydb
.get_rows(FROM
='vnfs', SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1136 # change_keys_http2db(content, http2db_vnf, reverse=True)
1137 utils
.convert_str2boolean(vnfs
, ('public',))
1138 convert_datetime2str(vnfs
)
1140 return format_out(data
)
1141 except bottle
.HTTPError
:
1143 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1144 logger
.error("http_get_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1145 bottle
.abort(e
.http_code
, str(e
))
1146 except Exception as e
:
1147 logger
.error("Unexpected exception: ", exc_info
=True)
1148 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1151 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='GET')
1152 def http_get_vnf_id(tenant_id
,vnf_id
):
1153 '''get vnf details, can use both uuid or name'''
1154 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1156 vnf
= nfvo
.get_vnf_id(mydb
,tenant_id
,vnf_id
)
1157 utils
.convert_str2boolean(vnf
, ('public',))
1158 convert_datetime2str(vnf
)
1159 return format_out(vnf
)
1160 except bottle
.HTTPError
:
1162 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1163 logger
.error("http_get_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
1164 bottle
.abort(e
.http_code
, str(e
))
1165 except Exception as e
:
1166 logger
.error("Unexpected exception: ", exc_info
=True)
1167 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1170 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='POST')
1171 def http_post_vnfs(tenant_id
):
1172 """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
1173 :param tenant_id: tenant that this vnf belongs to
1176 # print "Parsing the YAML file of the VNF"
1178 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1179 http_content
, used_schema
= format_in( vnfd_schema_v01
, ("schema_version",), {"0.2": vnfd_schema_v02
})
1180 r
= utils
.remove_extra_items(http_content
, used_schema
)
1182 logger
.debug("Remove received extra items %s", str(r
))
1184 if used_schema
== vnfd_schema_v01
:
1185 vnf_id
= nfvo
.new_vnf(mydb
,tenant_id
,http_content
)
1186 elif used_schema
== vnfd_schema_v02
:
1187 vnf_id
= nfvo
.new_vnf_v02(mydb
,tenant_id
,http_content
)
1189 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1190 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1191 return http_get_vnf_id(tenant_id
, vnf_id
)
1192 except bottle
.HTTPError
:
1194 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1195 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1196 bottle
.abort(e
.http_code
, str(e
))
1197 except Exception as e
:
1198 logger
.error("Unexpected exception: ", exc_info
=True)
1199 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1202 @bottle.route(url_base
+ '/v3/<tenant_id>/vnfd', method
='POST')
1203 def http_post_vnfs_v3(tenant_id
):
1205 Insert one or several VNFs in the catalog, following OSM IM
1206 :param tenant_id: tenant owner of the VNF
1207 :return: The detailed list of inserted VNFs, following the old format
1209 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1210 http_content
, _
= format_in(None)
1212 vnfd_uuid_list
= nfvo
.new_vnfd_v3(mydb
, tenant_id
, http_content
)
1214 for vnfd_uuid
in vnfd_uuid_list
:
1215 vnf
= nfvo
.get_vnf_id(mydb
, tenant_id
, vnfd_uuid
)
1216 utils
.convert_str2boolean(vnf
, ('public',))
1217 convert_datetime2str(vnf
)
1218 vnfd_list
.append(vnf
["vnf"])
1219 return format_out({"vnfd": vnfd_list
})
1220 except bottle
.HTTPError
:
1222 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1223 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
1224 bottle
.abort(e
.http_code
, str(e
))
1225 except Exception as e
:
1226 logger
.error("Unexpected exception: ", exc_info
=True)
1227 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1229 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='DELETE')
1230 def http_delete_vnf_id(tenant_id
, vnf_id
):
1231 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
1232 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1233 #check valid tenant_id and deletes the vnf, including images,
1235 data
= nfvo
.delete_vnf(mydb
,tenant_id
,vnf_id
)
1236 #print json.dumps(data, indent=4)
1237 return format_out({"result":"VNF " + data
+ " deleted"})
1238 except bottle
.HTTPError
:
1240 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1241 logger
.error("http_delete_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
1242 bottle
.abort(e
.http_code
, str(e
))
1243 except Exception as e
:
1244 logger
.error("Unexpected exception: ", exc_info
=True)
1245 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1248 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
1249 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
1250 @bottle.route(url_base
+ '/<tenant_id>/physicalview/<datacenter>', method
='GET')
1251 def http_get_hosts(tenant_id
, datacenter
):
1252 '''get the tidvim host hopology from the vim.'''
1253 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1254 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
1256 if datacenter
== 'treeview':
1257 data
= nfvo
.get_hosts(mydb
, tenant_id
)
1259 #openmano-gui is using a hardcoded value for the datacenter
1260 result
, data
= nfvo
.get_hosts_info(mydb
, tenant_id
) #, datacenter)
1263 #print "http_get_hosts error %d %s" % (-result, data)
1264 bottle
.abort(-result
, data
)
1266 convert_datetime2str(data
)
1267 #print json.dumps(data, indent=4)
1268 return format_out(data
)
1269 except bottle
.HTTPError
:
1271 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1272 logger
.error("http_get_hosts error {}: {}".format(e
.http_code
, str(e
)))
1273 bottle
.abort(e
.http_code
, str(e
))
1274 except Exception as e
:
1275 logger
.error("Unexpected exception: ", exc_info
=True)
1276 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1279 @bottle.route(url_base
+ '/<path:path>', method
='OPTIONS')
1280 def http_options_deploy(path
):
1281 '''For some reason GUI web ask for OPTIONS that must be responded'''
1282 #TODO: check correct path, and correct headers request
1283 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1284 bottle
.response
.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
1285 bottle
.response
.set_header('Accept','application/yaml,application/json')
1286 bottle
.response
.set_header('Content-Type','application/yaml,application/json')
1287 bottle
.response
.set_header('Access-Control-Allow-Headers','content-type')
1288 bottle
.response
.set_header('Access-Control-Allow-Origin','*')
1291 @bottle.route(url_base
+ '/<tenant_id>/topology/deploy', method
='POST')
1292 def http_post_deploy(tenant_id
):
1293 '''post topology deploy.'''
1294 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1296 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
})
1297 #r = utils.remove_extra_items(http_content, used_schema)
1298 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
1299 #print "http_post_deploy input: ", http_content
1302 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1303 instance
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['name'], http_content
['name'])
1304 #print json.dumps(data, indent=4)
1305 return format_out(instance
)
1306 except bottle
.HTTPError
:
1308 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1309 logger
.error("http_post_deploy error {}: {}".format(e
.http_code
, str(e
)))
1310 bottle
.abort(e
.http_code
, str(e
))
1311 except Exception as e
:
1312 logger
.error("Unexpected exception: ", exc_info
=True)
1313 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1316 @bottle.route(url_base
+ '/<tenant_id>/topology/verify', method
='POST')
1317 def http_post_verify(tenant_id
):
1319 # '''post topology verify'''
1320 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
1321 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1328 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='POST')
1329 def http_post_scenarios(tenant_id
):
1330 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
1331 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1332 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
, "0.3": nsd_schema_v03
})
1333 #r = utils.remove_extra_items(http_content, used_schema)
1334 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
1335 #print "http_post_scenarios input: ", http_content
1337 if used_schema
== nsd_schema_v01
:
1338 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
1339 elif used_schema
== nsd_schema_v02
:
1340 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.2")
1341 elif used_schema
== nsd_schema_v03
:
1342 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
, "0.3")
1344 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
1345 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
1346 #print json.dumps(data, indent=4)
1347 #return format_out(data)
1348 return http_get_scenario_id(tenant_id
, scenario_id
)
1349 except bottle
.HTTPError
:
1351 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1352 logger
.error("http_post_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1353 bottle
.abort(e
.http_code
, str(e
))
1354 except Exception as e
:
1355 logger
.error("Unexpected exception: ", exc_info
=True)
1356 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1358 @bottle.route(url_base
+ '/v3/<tenant_id>/nsd', method
='POST')
1359 def http_post_nsds_v3(tenant_id
):
1361 Insert one or several NSDs in the catalog, following OSM IM
1362 :param tenant_id: tenant owner of the NSD
1363 :return: The detailed list of inserted NSDs, following the old format
1365 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1366 http_content
, _
= format_in(None)
1368 nsd_uuid_list
= nfvo
.new_nsd_v3(mydb
, tenant_id
, http_content
)
1370 for nsd_uuid
in nsd_uuid_list
:
1371 scenario
= mydb
.get_scenario(nsd_uuid
, tenant_id
)
1372 convert_datetime2str(scenario
)
1373 nsd_list
.append(scenario
)
1374 data
= {'nsd': nsd_list
}
1375 return format_out(data
)
1376 except bottle
.HTTPError
:
1378 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1379 logger
.error("http_post_nsds_v3 error {}: {}".format(e
.http_code
, str(e
)))
1380 bottle
.abort(e
.http_code
, str(e
))
1381 except Exception as e
:
1382 logger
.error("Unexpected exception: ", exc_info
=True)
1383 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1386 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>/action', method
='POST')
1387 def http_post_scenario_action(tenant_id
, scenario_id
):
1388 '''take an action over a scenario'''
1389 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1391 http_content
, _
= format_in(scenario_action_schema
)
1392 r
= utils
.remove_extra_items(http_content
, scenario_action_schema
)
1394 logger
.debug("Remove received extra items %s", str(r
))
1396 # check valid tenant_id
1397 nfvo
.check_tenant(mydb
, tenant_id
)
1398 if "start" in http_content
:
1399 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['start']['instance_name'], \
1400 http_content
['start'].get('description',http_content
['start']['instance_name']),
1401 http_content
['start'].get('datacenter') )
1402 return format_out(data
)
1403 elif "deploy" in http_content
: #Equivalent to start
1404 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['deploy']['instance_name'],
1405 http_content
['deploy'].get('description',http_content
['deploy']['instance_name']),
1406 http_content
['deploy'].get('datacenter') )
1407 return format_out(data
)
1408 elif "reserve" in http_content
: #Reserve resources
1409 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['reserve']['instance_name'],
1410 http_content
['reserve'].get('description',http_content
['reserve']['instance_name']),
1411 http_content
['reserve'].get('datacenter'), startvms
=False )
1412 return format_out(data
)
1413 elif "verify" in http_content
: #Equivalent to start and then delete
1414 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['verify']['instance_name'],
1415 http_content
['verify'].get('description',http_content
['verify']['instance_name']),
1416 http_content
['verify'].get('datacenter'), startvms
=False )
1417 instance_id
= data
['uuid']
1418 nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1419 return format_out({"result":"Verify OK"})
1420 except bottle
.HTTPError
:
1422 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1423 logger
.error("http_post_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1424 bottle
.abort(e
.http_code
, str(e
))
1425 except Exception as e
:
1426 logger
.error("Unexpected exception: ", exc_info
=True)
1427 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1430 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='GET')
1431 def http_get_scenarios(tenant_id
):
1432 '''get scenarios list'''
1433 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1435 #check valid tenant_id
1436 if tenant_id
!= "any":
1437 nfvo
.check_tenant(mydb
, tenant_id
)
1439 s
,w
,l
=filter_query_string(bottle
.request
.query
, None,
1440 ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
1441 if tenant_id
!= "any":
1442 w
["OR"] = {"tenant_id": tenant_id
, "public": True}
1443 scenarios
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, LIMIT
=l
, FROM
='scenarios')
1444 convert_datetime2str(scenarios
)
1445 utils
.convert_str2boolean(scenarios
, ('public',) )
1446 data
={'scenarios':scenarios
}
1447 #print json.dumps(scenarios, indent=4)
1448 return format_out(data
)
1449 except bottle
.HTTPError
:
1451 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1452 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1453 bottle
.abort(e
.http_code
, str(e
))
1454 except Exception as e
:
1455 logger
.error("Unexpected exception: ", exc_info
=True)
1456 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1459 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='GET')
1460 def http_get_scenario_id(tenant_id
, scenario_id
):
1461 '''get scenario details, can use both uuid or name'''
1462 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1464 #check valid tenant_id
1465 if tenant_id
!= "any":
1466 nfvo
.check_tenant(mydb
, tenant_id
)
1468 scenario
= mydb
.get_scenario(scenario_id
, tenant_id
)
1469 convert_datetime2str(scenario
)
1470 data
={'scenario' : scenario
}
1471 return format_out(data
)
1472 except bottle
.HTTPError
:
1474 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1475 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1476 bottle
.abort(e
.http_code
, str(e
))
1477 except Exception as e
:
1478 logger
.error("Unexpected exception: ", exc_info
=True)
1479 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1482 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='DELETE')
1483 def http_delete_scenario_id(tenant_id
, scenario_id
):
1484 '''delete a scenario from database, can use both uuid or name'''
1485 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1487 #check valid tenant_id
1488 if tenant_id
!= "any":
1489 nfvo
.check_tenant(mydb
, tenant_id
)
1491 data
= mydb
.delete_scenario(scenario_id
, tenant_id
)
1492 #print json.dumps(data, indent=4)
1493 return format_out({"result":"scenario " + data
+ " deleted"})
1494 except bottle
.HTTPError
:
1496 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1497 logger
.error("http_delete_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1498 bottle
.abort(e
.http_code
, str(e
))
1499 except Exception as e
:
1500 logger
.error("Unexpected exception: ", exc_info
=True)
1501 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1504 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='PUT')
1505 def http_put_scenario_id(tenant_id
, scenario_id
):
1506 '''edit an existing scenario id'''
1507 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1508 http_content
,_
= format_in( scenario_edit_schema
)
1509 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1510 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1511 #print "http_put_scenario_id input: ", http_content
1513 nfvo
.edit_scenario(mydb
, tenant_id
, scenario_id
, http_content
)
1514 #print json.dumps(data, indent=4)
1515 #return format_out(data)
1516 return http_get_scenario_id(tenant_id
, scenario_id
)
1517 except bottle
.HTTPError
:
1519 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1520 logger
.error("http_put_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1521 bottle
.abort(e
.http_code
, str(e
))
1522 except Exception as e
:
1523 logger
.error("Unexpected exception: ", exc_info
=True)
1524 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1526 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='POST')
1527 def http_post_instances(tenant_id
):
1528 '''create an instance-scenario'''
1529 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1531 http_content
, used_schema
= format_in(instance_scenario_create_schema_v01
)
1532 r
= utils
.remove_extra_items(http_content
, used_schema
)
1534 logger
.warning("http_post_instances: Warning: remove extra items %s", str(r
))
1536 #check valid tenant_id
1537 if tenant_id
!= "any":
1538 nfvo
.check_tenant(mydb
, tenant_id
)
1539 data
= nfvo
.create_instance(mydb
, tenant_id
, http_content
["instance"])
1540 return format_out(data
)
1541 except bottle
.HTTPError
:
1543 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1544 logger
.error("http_post_instances error {}: {}".format(e
.http_code
, str(e
)))
1545 bottle
.abort(e
.http_code
, str(e
))
1546 except Exception as e
:
1547 logger
.error("Unexpected exception: ", exc_info
=True)
1548 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1553 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='GET')
1554 def http_get_instances(tenant_id
):
1555 '''get instance list'''
1557 #check valid tenant_id
1558 if tenant_id
!= "any":
1559 nfvo
.check_tenant(mydb
, tenant_id
)
1561 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1562 if tenant_id
!= "any":
1563 w
['tenant_id'] = tenant_id
1564 instances
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, LIMIT
=l
, FROM
='instance_scenarios')
1565 convert_datetime2str(instances
)
1566 utils
.convert_str2boolean(instances
, ('public',) )
1567 data
={'instances':instances
}
1568 return format_out(data
)
1569 except bottle
.HTTPError
:
1571 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1572 logger
.error("http_get_instances error {}: {}".format(e
.http_code
, str(e
)))
1573 bottle
.abort(e
.http_code
, str(e
))
1574 except Exception as e
:
1575 logger
.error("Unexpected exception: ", exc_info
=True)
1576 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1579 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='GET')
1580 def http_get_instance_id(tenant_id
, instance_id
):
1581 '''get instances details, can use both uuid or name'''
1582 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 instance
= nfvo
.get_instance_id(mydb
, tenant_id
, instance_id
)
1593 # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
1594 for vnf
in instance
.get("vnfs", ()):
1595 for vm
in vnf
.get("vms", ()):
1596 for iface
in vm
.get("interfaces", ()):
1597 if iface
.get("ip_address"):
1598 index
= iface
["ip_address"].find(";")
1600 iface
["ip_address"] = iface
["ip_address"][:index
]
1601 convert_datetime2str(instance
)
1602 # print json.dumps(instance, indent=4)
1603 return format_out(instance
)
1604 except bottle
.HTTPError
:
1606 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1607 logger
.error("http_get_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1608 bottle
.abort(e
.http_code
, str(e
))
1609 except Exception as e
:
1610 logger
.error("Unexpected exception: ", exc_info
=True)
1611 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1614 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='DELETE')
1615 def http_delete_instance_id(tenant_id
, instance_id
):
1616 '''delete instance from VIM and from database, can use both uuid or name'''
1617 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1619 #check valid tenant_id
1620 if tenant_id
!= "any":
1621 nfvo
.check_tenant(mydb
, tenant_id
)
1622 if tenant_id
== "any":
1625 message
= nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1626 return format_out({"result":message
})
1627 except bottle
.HTTPError
:
1629 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1630 logger
.error("http_delete_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1631 bottle
.abort(e
.http_code
, str(e
))
1632 except Exception as e
:
1633 logger
.error("Unexpected exception: ", exc_info
=True)
1634 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1637 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='POST')
1638 def http_post_instance_scenario_action(tenant_id
, instance_id
):
1640 take an action over a scenario instance
1641 :param tenant_id: tenant where user belongs to
1642 :param instance_id: instance indentity
1645 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1647 http_content
, _
= format_in(instance_scenario_action_schema
)
1648 r
= utils
.remove_extra_items(http_content
, instance_scenario_action_schema
)
1650 logger
.debug("Remove received extra items %s", str(r
))
1652 #check valid tenant_id
1653 if tenant_id
!= "any":
1654 nfvo
.check_tenant(mydb
, tenant_id
)
1656 #print "http_post_instance_scenario_action input: ", http_content
1658 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1659 instance_id
= instance
["uuid"]
1661 data
= nfvo
.instance_action(mydb
, tenant_id
, instance_id
, http_content
)
1662 return format_out(data
)
1663 except bottle
.HTTPError
:
1665 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1666 logger
.error("http_post_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1667 bottle
.abort(e
.http_code
, str(e
))
1668 except Exception as e
:
1669 logger
.error("Unexpected exception: ", exc_info
=True)
1670 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1673 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='GET')
1674 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action/<action_id>', method
='GET')
1675 def http_get_instance_scenario_action(tenant_id
, instance_id
, action_id
=None):
1677 List the actions done over an instance, or the action details
1678 :param tenant_id: tenant where user belongs to. Can be "any" to ignore
1679 :param instance_id: instance id, can be "any" to get actions of all instances
1682 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1684 # check valid tenant_id
1685 if tenant_id
!= "any":
1686 nfvo
.check_tenant(mydb
, tenant_id
)
1687 data
= nfvo
.instance_action_get(mydb
, tenant_id
, instance_id
, action_id
)
1688 return format_out(data
)
1689 except bottle
.HTTPError
:
1691 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1692 logger
.error("http_get_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1693 bottle
.abort(e
.http_code
, str(e
))
1694 except Exception as e
:
1695 logger
.error("Unexpected exception: ", exc_info
=True)
1696 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1698 def remove_clear_passwd(data
):
1700 Removes clear passwords from the data received
1701 :param data: data with clear password
1702 :return: data without the password information
1705 passw
= ['password: ', 'passwd: ']
1707 for pattern
in passw
:
1708 init
= data
.find(pattern
)
1710 end
= data
.find('\n', init
)
1711 data
= data
[:init
] + '{}******'.format(pattern
) + data
[end
:]
1713 init
= data
.find(pattern
, init
)
1725 def error400(error
):
1726 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
1727 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
1728 return format_out(e
)