1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 HTTP server implementing the openmano API. It will answer to POST, PUT, GET methods in the appropriate URLs
26 and will use the nfvo.py module to run the appropriate method.
27 Every YAML/JSON file is checked against a schema in openmano_schemas.py module.
29 __author__
="Alfonso Tierno, Gerardo Garcia"
30 __date__
="$17-sep-2014 09:07:15$"
39 from jsonschema
import validate
as js_v
, exceptions
as js_e
40 from openmano_schemas
import vnfd_schema_v01
, vnfd_schema_v02
, \
41 nsd_schema_v01
, nsd_schema_v02
, nsd_schema_v03
, scenario_edit_schema
, \
42 scenario_action_schema
, instance_scenario_action_schema
, instance_scenario_create_schema_v01
, \
43 tenant_schema
, tenant_edit_schema
,\
44 datacenter_schema
, datacenter_edit_schema
, datacenter_action_schema
, datacenter_associate_schema
,\
45 object_schema
, netmap_new_schema
, netmap_edit_schema
48 from db_base
import db_base_Exception
49 from functools
import wraps
57 HTTP_Bad_Request
= 400
58 HTTP_Unauthorized
= 401
61 HTTP_Method_Not_Allowed
= 405
62 HTTP_Not_Acceptable
= 406
63 HTTP_Service_Unavailable
= 503
64 HTTP_Internal_Server_Error
= 500
66 def delete_nulls(var
):
69 if var
[k
] is None: del var
[k
]
70 elif type(var
[k
]) is dict or type(var
[k
]) is list or type(var
[k
]) is tuple:
71 if delete_nulls(var
[k
]): del var
[k
]
72 if len(var
) == 0: return True
73 elif type(var
) is list or type(var
) is tuple:
75 if type(k
) is dict: delete_nulls(k
)
76 if len(var
) == 0: return True
79 def convert_datetime2str(var
):
80 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
81 It enters recursively in the dict var finding this kind of variables
84 for k
,v
in var
.items():
85 if type(v
) is float and k
in ("created_at", "modified_at"):
86 var
[k
] = time
.strftime("%Y-%m-%dT%H:%M:%S", time
.localtime(v
) )
87 elif type(v
) is dict or type(v
) is list or type(v
) is tuple:
88 convert_datetime2str(v
)
89 if len(var
) == 0: return True
90 elif type(var
) is list or type(var
) is tuple:
92 convert_datetime2str(v
)
94 def log_to_logger(fn
):
96 Wrap a Bottle request so that a log line is emitted after it's handled.
97 (This decorator can be extended to take the desired logger as a param.)
100 def _log_to_logger(*args
, **kwargs
):
101 actual_response
= fn(*args
, **kwargs
)
102 # modify this to log exactly what you need:
103 logger
.info('FROM %s %s %s %s' % (bottle
.request
.remote_addr
,
104 bottle
.request
.method
,
106 bottle
.response
.status
))
107 return actual_response
108 return _log_to_logger
110 class httpserver(threading
.Thread
):
111 def __init__(self
, db
, admin
=False, host
='localhost', port
=9090):
117 logger
= logging
.getLogger('openmano.http')
118 threading
.Thread
.__init
__(self
)
120 self
.port
= port
#Port where the listen service must be started
122 self
.name
= "http_admin"
125 #self.url_preffix = 'http://' + host + ':' + str(port) + url_base
127 #self.first_usable_connection_index = 10
128 #self.next_connection_index = self.first_usable_connection_index #The next connection index to be used
129 #Ensure that when the main program exits the thread will also exit
134 bottle
.install(log_to_logger
)
135 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=False, quiet
=True)
137 def run_bottle(db
, host_
='localhost', port_
=9090):
138 '''used for launching in main thread, so that it can be debugged'''
141 bottle
.run(host
=host_
, port
=port_
, debug
=True) #quiet=True
144 @bottle.route(url_base
+ '/', method
='GET')
147 return 'works' #TODO: to be completed
153 def change_keys_http2db(data
, http_db
, reverse
=False):
154 '''Change keys of dictionary data acording to the key_dict values
155 This allow change from http interface names to database names.
156 When reverse is True, the change is otherwise
158 data: can be a dictionary or a list
159 http_db: is a dictionary with hhtp names as keys and database names as value
160 reverse: by default change is done from http api to database. If True change is done otherwise
161 Return: None, but data is modified'''
162 if type(data
) is tuple or type(data
) is list:
164 change_keys_http2db(d
, http_db
, reverse
)
165 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
167 for k
,v
in http_db
.items():
168 if v
in data
: data
[k
]=data
.pop(v
)
170 for k
,v
in http_db
.items():
171 if k
in data
: data
[v
]=data
.pop(k
)
173 def format_out(data
):
174 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
175 logger
.debug("OUT: " + yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) )
176 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
177 bottle
.response
.content_type
='application/yaml'
178 return yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) #, canonical=True, default_style='"'
179 else: #by default json
180 bottle
.response
.content_type
='application/json'
181 #return data #json no style
182 return json
.dumps(data
, indent
=4) + "\n"
184 def format_in(default_schema
, version_fields
=None, version_dict_schema
=None):
185 ''' Parse the content of HTTP request against a json_schema
187 default_schema: The schema to be parsed by default if no version field is found in the client data
188 version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
189 version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
190 It can contain a None as key, and this is apply if the client data version does not match any key
192 user_data, used_schema: if the data is successfully decoded and matches the schema
193 launch a bottle abort if fails
195 #print "HEADERS :" + str(bottle.request.headers.items())
197 error_text
= "Invalid header format "
198 format_type
= bottle
.request
.headers
.get('Content-Type', 'application/json')
199 if 'application/json' in format_type
:
200 error_text
= "Invalid json format "
201 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
202 client_data
= json
.load(bottle
.request
.body
)
203 #client_data = bottle.request.json()
204 elif 'application/yaml' in format_type
:
205 error_text
= "Invalid yaml format "
206 client_data
= yaml
.load(bottle
.request
.body
)
207 elif 'application/xml' in format_type
:
208 bottle
.abort(501, "Content-Type: application/xml not supported yet.")
210 logger
.warning('Content-Type ' + str(format_type
) + ' not supported.')
211 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
213 #if client_data == None:
214 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
217 logger
.debug('IN: %s', yaml
.safe_dump(client_data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) )
218 #look for the client provider version
219 error_text
= "Invalid content "
220 client_version
= None
222 if version_fields
!= None:
223 client_version
= client_data
224 for field
in version_fields
:
225 if field
in client_version
:
226 client_version
= client_version
[field
]
230 if client_version
==None:
231 used_schema
=default_schema
232 elif version_dict_schema
!=None:
233 if client_version
in version_dict_schema
:
234 used_schema
= version_dict_schema
[client_version
]
235 elif None in version_dict_schema
:
236 used_schema
= version_dict_schema
[None]
237 if used_schema
==None:
238 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version or missing version field")
240 js_v(client_data
, used_schema
)
241 return client_data
, used_schema
242 except (ValueError, yaml
.YAMLError
) as exc
:
243 error_text
+= str(exc
)
244 logger
.error(error_text
)
245 bottle
.abort(HTTP_Bad_Request
, error_text
)
246 except js_e
.ValidationError
as exc
:
247 logger
.error("validate_in error, jsonschema exception at '%s' '%s' ", str(exc
.path
), str(exc
.message
))
249 if len(exc
.path
)>0: error_pos
=" at " + ":".join(map(json
.dumps
, exc
.path
))
250 bottle
.abort(HTTP_Bad_Request
, error_text
+ exc
.message
+ error_pos
)
252 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
255 def filter_query_string(qs
, http2db
, allowed
):
256 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
258 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
259 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
260 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
261 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
262 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
263 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
264 limit: limit dictated by user with the query string 'limit'. 100 by default
265 abort if not permited, using bottel.abort
270 #if type(qs) is not bottle.FormsDict:
271 # bottle.abort(HTTP_Internal_Server_Error, '!!!!!!!!!!!!!!invalid query string not a dictionary')
272 # #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
275 select
+= qs
.getall(k
)
278 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field="+v
+"'")
283 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit="+qs
[k
]+"'")
286 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '"+k
+"="+qs
[k
]+"'")
287 if qs
[k
]!="null": where
[k
]=qs
[k
]
289 if len(select
)==0: select
+= allowed
290 #change from http api to database naming
291 for i
in range(0,len(select
)):
293 if http2db
and k
in http2db
:
294 select
[i
] = http2db
[k
]
296 change_keys_http2db(where
, http2db
)
297 #print "filter_query_string", select,where,limit
299 return select
,where
,limit
301 @bottle.hook('after_request')
303 '''Don't know yet if really needed. Keep it just in case'''
304 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
310 @bottle.route(url_base
+ '/tenants', method
='GET')
311 def http_get_tenants():
312 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
313 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
314 ('uuid','name','description','created_at') )
316 tenants
= mydb
.get_rows(FROM
='nfvo_tenants', SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
317 #change_keys_http2db(content, http2db_tenant, reverse=True)
318 convert_datetime2str(tenants
)
319 data
={'tenants' : tenants
}
320 return format_out(data
)
321 except db_base_Exception
as e
:
322 logger
.error("http_get_tenants error {}: {}".format(e
.http_code
, str(e
)))
323 bottle
.abort(e
.http_code
, str(e
))
324 except Exception as e
:
325 logger
.error("Unexpected exception %s", str(e
))
326 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
329 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
330 def http_get_tenant_id(tenant_id
):
331 '''get tenant details, can use both uuid or name'''
333 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
335 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
, "tenant")
336 #change_keys_http2db(content, http2db_tenant, reverse=True)
337 convert_datetime2str(tenant
)
338 data
={'tenant' : tenant
}
339 return format_out(data
)
340 except db_base_Exception
as e
:
341 logger
.error("http_get_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
342 bottle
.abort(e
.http_code
, str(e
))
343 except Exception as e
:
344 logger
.error("Unexpected exception %s", str(e
))
345 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
348 @bottle.route(url_base
+ '/tenants', method
='POST')
349 def http_post_tenants():
350 '''insert a tenant into the catalogue. '''
352 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
353 http_content
,_
= format_in( tenant_schema
)
354 r
= utils
.remove_extra_items(http_content
, tenant_schema
)
356 logger
.debug("Remove received extra items %s", str(r
))
358 data
= nfvo
.new_tenant(mydb
, http_content
['tenant'])
359 return http_get_tenant_id(data
)
360 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
361 logger
.error("http_post_tenants error {}: {}".format(e
.http_code
, str(e
)))
362 bottle
.abort(e
.http_code
, str(e
))
363 except Exception as e
:
364 logger
.error("Unexpected exception %s", str(e
))
365 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
368 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
369 def http_edit_tenant_id(tenant_id
):
370 '''edit tenant details, can use both uuid or name'''
372 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
373 http_content
,_
= format_in( tenant_edit_schema
)
374 r
= utils
.remove_extra_items(http_content
, tenant_edit_schema
)
376 logger
.debug("Remove received extra items %s", str(r
))
378 #obtain data, check that only one exist
380 tenant
= mydb
.get_table_by_uuid_name('nfvo_tenants', tenant_id
)
382 tenant_id
= tenant
['uuid']
383 where
={'uuid': tenant
['uuid']}
384 mydb
.update_rows('nfvo_tenants', http_content
['tenant'], where
)
385 return http_get_tenant_id(tenant_id
)
386 except db_base_Exception
as e
:
387 logger
.error("http_edit_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
388 bottle
.abort(e
.http_code
, str(e
))
389 except Exception as e
:
390 logger
.error("Unexpected exception %s", str(e
))
391 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
394 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
395 def http_delete_tenant_id(tenant_id
):
396 '''delete a tenant from database, can use both uuid or name'''
397 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
399 data
= nfvo
.delete_tenant(mydb
, tenant_id
)
400 return format_out({"result":"tenant " + data
+ " deleted"})
401 except db_base_Exception
as e
:
402 logger
.error("http_delete_tenant_id error {}: {}".format(e
.http_code
, str(e
)))
403 bottle
.abort(e
.http_code
, str(e
))
404 except Exception as e
:
405 logger
.error("Unexpected exception %s", str(e
))
406 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
409 @bottle.route(url_base
+ '/<tenant_id>/datacenters', method
='GET')
410 def http_get_datacenters(tenant_id
):
411 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
413 if tenant_id
!= 'any':
414 #check valid tenant_id
415 nfvo
.check_tenant(mydb
, tenant_id
)
416 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
417 ('uuid','name','vim_url','type','created_at') )
418 if tenant_id
!= 'any':
419 where_
['nfvo_tenant_id'] = tenant_id
420 if 'created_at' in select_
:
421 select_
[ select_
.index('created_at') ] = 'd.created_at as created_at'
422 if 'created_at' in where_
:
423 where_
['d.created_at'] = where_
.pop('created_at')
424 datacenters
= mydb
.get_rows(FROM
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id',
425 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
427 datacenters
= mydb
.get_rows(FROM
='datacenters',
428 SELECT
=select_
,WHERE
=where_
,LIMIT
=limit_
)
429 #change_keys_http2db(content, http2db_tenant, reverse=True)
430 convert_datetime2str(datacenters
)
431 data
={'datacenters' : datacenters
}
432 return format_out(data
)
433 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
434 logger
.error("http_get_datacenters error {}: {}".format(e
.http_code
, str(e
)))
435 bottle
.abort(e
.http_code
, str(e
))
436 except Exception as e
:
437 logger
.error("Unexpected exception %s", str(e
))
438 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
441 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='GET')
442 def http_get_datacenter_id(tenant_id
, datacenter_id
):
443 '''get datacenter details, can use both uuid or name'''
444 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
446 if tenant_id
!= 'any':
447 #check valid tenant_id
448 nfvo
.check_tenant(mydb
, tenant_id
)
450 what
= 'uuid' if utils
.check_valid_uuid(datacenter_id
) else 'name'
452 where_
[what
] = datacenter_id
453 select_
=['uuid', 'name','vim_url', 'vim_url_admin', 'type', 'config', 'description', 'd.created_at as created_at']
454 if tenant_id
!= 'any':
455 select_
.append("datacenter_tenant_id")
456 where_
['td.nfvo_tenant_id']= tenant_id
457 from_
='datacenters as d join tenants_datacenters as td on d.uuid=td.datacenter_id'
459 from_
='datacenters as d'
460 datacenters
= mydb
.get_rows(
465 if len(datacenters
)==0:
466 bottle
.abort( HTTP_Not_Found
, "No datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
467 elif len(datacenters
)>1:
468 bottle
.abort( HTTP_Bad_Request
, "More than one datacenter found for tenant with {} '{}'".format(what
, datacenter_id
) )
469 datacenter
= datacenters
[0]
470 if tenant_id
!= 'any':
472 vim_tenants
= mydb
.get_rows(
473 SELECT
=("vim_tenant_name", "vim_tenant_id", "user"),
474 FROM
="datacenter_tenants",
475 WHERE
={"uuid": datacenters
[0]["datacenter_tenant_id"]},
476 ORDER_BY
=("created", ) )
477 del datacenter
["datacenter_tenant_id"]
478 datacenter
["vim_tenants"] = vim_tenants
480 if datacenter
['config'] != None:
482 config_dict
= yaml
.load(datacenter
['config'])
483 datacenter
['config'] = config_dict
485 logger
.error("Exception '%s' while trying to load config information", str(e
))
486 #change_keys_http2db(content, http2db_datacenter, reverse=True)
487 convert_datetime2str(datacenter
)
488 data
={'datacenter' : datacenter
}
489 return format_out(data
)
490 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
491 logger
.error("http_get_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
492 bottle
.abort(e
.http_code
, str(e
))
493 except Exception as e
:
494 logger
.error("Unexpected exception %s", str(e
))
495 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
497 @bottle.route(url_base
+ '/datacenters', method
='POST')
498 def http_post_datacenters():
499 '''insert a tenant into the catalogue. '''
501 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
502 http_content
,_
= format_in( datacenter_schema
)
503 r
= utils
.remove_extra_items(http_content
, datacenter_schema
)
505 logger
.debug("Remove received extra items %s", str(r
))
507 data
= nfvo
.new_datacenter(mydb
, http_content
['datacenter'])
508 return http_get_datacenter_id('any', data
)
509 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
510 logger
.error("http_post_datacenters error {}: {}".format(e
.http_code
, str(e
)))
511 bottle
.abort(e
.http_code
, str(e
))
512 except Exception as e
:
513 logger
.error("Unexpected exception %s", str(e
))
514 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
516 @bottle.route(url_base
+ '/datacenters/<datacenter_id_name>', method
='PUT')
517 def http_edit_datacenter_id(datacenter_id_name
):
518 '''edit datacenter details, can use both uuid or name'''
519 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
521 http_content
,_
= format_in( datacenter_edit_schema
)
522 r
= utils
.remove_extra_items(http_content
, datacenter_edit_schema
)
524 logger
.debug("Remove received extra items %s", str(r
))
527 datacenter_id
= nfvo
.edit_datacenter(mydb
, datacenter_id_name
, http_content
['datacenter'])
528 return http_get_datacenter_id('any', datacenter_id
)
529 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
530 logger
.error("http_edit_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
531 bottle
.abort(e
.http_code
, str(e
))
532 except Exception as e
:
533 logger
.error("Unexpected exception %s", str(e
))
534 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
536 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/networks', method
='GET') #deprecated
537 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='GET')
538 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='GET')
539 def http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
540 '''get datacenter networks, can use both uuid or name'''
541 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
544 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
545 where_
= {"datacenter_id":datacenter_dict
['uuid']}
547 if utils
.check_valid_uuid(netmap_id
):
548 where_
["uuid"] = netmap_id
550 where_
["name"] = netmap_id
551 netmaps
=mydb
.get_rows(FROM
='datacenter_nets',
552 SELECT
=('name','vim_net_id as vim_id', 'uuid', 'type','multipoint','shared','description', 'created_at'),
554 convert_datetime2str(netmaps
)
555 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
556 if netmap_id
and len(netmaps
)==1:
557 data
={'netmap' : netmaps
[0]}
558 elif netmap_id
and len(netmaps
)==0:
559 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
562 data
={'netmaps' : netmaps
}
563 return format_out(data
)
564 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
565 logger
.error("http_getnetwork_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
566 bottle
.abort(e
.http_code
, str(e
))
567 except Exception as e
:
568 logger
.error("Unexpected exception %s", str(e
))
569 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
571 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='DELETE')
572 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='DELETE')
573 def http_delnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
=None):
574 '''get datacenter networks, can use both uuid or name'''
575 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
578 datacenter_dict
= mydb
.get_table_by_uuid_name('datacenters', datacenter_id
, "datacenter")
579 where_
= {"datacenter_id":datacenter_dict
['uuid']}
581 if utils
.check_valid_uuid(netmap_id
):
582 where_
["uuid"] = netmap_id
584 where_
["name"] = netmap_id
585 #change_keys_http2db(content, http2db_tenant, reverse=True)
586 deleted
= mydb
.delete_row(FROM
='datacenter_nets', WHERE
= where_
)
587 if deleted
== 0 and netmap_id
:
588 bottle
.abort(HTTP_Not_Found
, "No netmap found with " + " and ".join(map(lambda x
: str(x
[0])+": "+str(x
[1]), where_
.iteritems())) )
590 return format_out({"result": "netmap %s deleted" % netmap_id
})
592 return format_out({"result": "%d netmap deleted" % deleted
})
593 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
594 logger
.error("http_delnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
595 bottle
.abort(e
.http_code
, str(e
))
596 except Exception as e
:
597 logger
.error("Unexpected exception %s", str(e
))
598 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
601 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/upload', method
='POST')
602 def http_uploadnetmap_datacenter_id(tenant_id
, datacenter_id
):
603 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
605 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, None)
606 convert_datetime2str(netmaps
)
607 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
608 data
={'netmaps' : netmaps
}
609 return format_out(data
)
610 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
611 logger
.error("http_uploadnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
612 bottle
.abort(e
.http_code
, str(e
))
613 except Exception as e
:
614 logger
.error("Unexpected exception %s", str(e
))
615 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
617 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method
='POST')
618 def http_postnetmap_datacenter_id(tenant_id
, datacenter_id
):
619 '''creates a new netmap'''
620 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
622 http_content
,_
= format_in( netmap_new_schema
)
623 r
= utils
.remove_extra_items(http_content
, netmap_new_schema
)
625 logger
.debug("Remove received extra items %s", str(r
))
627 #obtain data, check that only one exist
628 netmaps
= nfvo
.datacenter_new_netmap(mydb
, tenant_id
, datacenter_id
, http_content
)
629 convert_datetime2str(netmaps
)
630 utils
.convert_str2boolean(netmaps
, ('shared', 'multipoint') )
631 data
={'netmaps' : netmaps
}
632 return format_out(data
)
633 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
634 logger
.error("http_postnetmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
635 bottle
.abort(e
.http_code
, str(e
))
636 except Exception as e
:
637 logger
.error("Unexpected exception %s", str(e
))
638 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
640 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/netmaps/<netmap_id>', method
='PUT')
641 def http_putnettmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
):
643 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
645 http_content
,_
= format_in( netmap_edit_schema
)
646 r
= utils
.remove_extra_items(http_content
, netmap_edit_schema
)
648 logger
.debug("Remove received extra items %s", str(r
))
650 #obtain data, check that only one exist
652 nfvo
.datacenter_edit_netmap(mydb
, tenant_id
, datacenter_id
, netmap_id
, http_content
)
653 return http_getnetmap_datacenter_id(tenant_id
, datacenter_id
, netmap_id
)
654 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
655 logger
.error("http_putnettmap_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
656 bottle
.abort(e
.http_code
, str(e
))
657 except Exception as e
:
658 logger
.error("Unexpected exception %s", str(e
))
659 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
662 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>/action', method
='POST')
663 def http_action_datacenter_id(tenant_id
, datacenter_id
):
664 '''perform an action over datacenter, can use both uuid or name'''
665 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
667 http_content
,_
= format_in( datacenter_action_schema
)
668 r
= utils
.remove_extra_items(http_content
, datacenter_action_schema
)
670 logger
.debug("Remove received extra items %s", str(r
))
672 #obtain data, check that only one exist
673 result
= nfvo
.datacenter_action(mydb
, tenant_id
, datacenter_id
, http_content
)
674 if 'net-update' in http_content
:
675 return http_getnetmap_datacenter_id(datacenter_id
)
677 return format_out(result
)
678 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
679 logger
.error("http_action_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
680 bottle
.abort(e
.http_code
, str(e
))
681 except Exception as e
:
682 logger
.error("Unexpected exception %s", str(e
))
683 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
686 @bottle.route(url_base
+ '/datacenters/<datacenter_id>', method
='DELETE')
687 def http_delete_datacenter_id( datacenter_id
):
688 '''delete a tenant from database, can use both uuid or name'''
690 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
692 data
= nfvo
.delete_datacenter(mydb
, datacenter_id
)
693 return format_out({"result":"datacenter '" + data
+ "' deleted"})
694 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
695 logger
.error("http_delete_datacenter_id error {}: {}".format(e
.http_code
, str(e
)))
696 bottle
.abort(e
.http_code
, str(e
))
697 except Exception as e
:
698 logger
.error("Unexpected exception %s", str(e
))
699 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
701 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='POST')
702 def http_associate_datacenters(tenant_id
, datacenter_id
):
703 '''associate an existing datacenter to a this tenant. '''
704 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
706 http_content
,_
= format_in( datacenter_associate_schema
)
707 r
= utils
.remove_extra_items(http_content
, datacenter_associate_schema
)
709 logger
.debug("Remove received extra items %s", str(r
))
711 id_
= nfvo
.associate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
,
712 http_content
['datacenter'].get('vim_tenant'),
713 http_content
['datacenter'].get('vim_tenant_name'),
714 http_content
['datacenter'].get('vim_username'),
715 http_content
['datacenter'].get('vim_password')
717 return http_get_datacenter_id(tenant_id
, id_
)
718 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
719 logger
.error("http_associate_datacenters 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 %s", str(e
))
723 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
725 @bottle.route(url_base
+ '/<tenant_id>/datacenters/<datacenter_id>', method
='DELETE')
726 def http_deassociate_datacenters(tenant_id
, datacenter_id
):
727 '''deassociate an existing datacenter to a this tenant. '''
728 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
730 data
= nfvo
.deassociate_datacenter_to_tenant(mydb
, tenant_id
, datacenter_id
)
731 return format_out({"result": data
})
732 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
733 logger
.error("http_deassociate_datacenters error {}: {}".format(e
.http_code
, str(e
)))
734 bottle
.abort(e
.http_code
, str(e
))
735 except Exception as e
:
736 logger
.error("Unexpected exception %s", str(e
))
737 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
739 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='GET')
740 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='GET')
741 def http_get_vim_items(tenant_id
, datacenter_id
, item
, name
=None):
742 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
744 data
= nfvo
.vim_action_get(mydb
, tenant_id
, datacenter_id
, item
, name
)
745 return format_out(data
)
746 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
747 logger
.error("http_get_vim_items error {}: {}".format(e
.http_code
, str(e
)))
748 bottle
.abort(e
.http_code
, str(e
))
749 except Exception as e
:
750 logger
.error("Unexpected exception %s", str(e
))
751 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
753 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method
='DELETE')
754 def http_del_vim_items(tenant_id
, datacenter_id
, item
, name
):
755 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
757 data
= nfvo
.vim_action_delete(mydb
, tenant_id
, datacenter_id
, item
, name
)
758 return format_out({"result":data
})
759 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
760 logger
.error("http_del_vim_items error {}: {}".format(e
.http_code
, str(e
)))
761 bottle
.abort(e
.http_code
, str(e
))
762 except Exception as e
:
763 logger
.error("Unexpected exception %s", str(e
))
764 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
766 @bottle.route(url_base
+ '/<tenant_id>/vim/<datacenter_id>/<item>', method
='POST')
767 def http_post_vim_items(tenant_id
, datacenter_id
, item
):
768 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
769 http_content
,_
= format_in( object_schema
)
771 data
= nfvo
.vim_action_create(mydb
, tenant_id
, datacenter_id
, item
, http_content
)
772 return format_out(data
)
773 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
774 logger
.error("http_post_vim_items error {}: {}".format(e
.http_code
, str(e
)))
775 bottle
.abort(e
.http_code
, str(e
))
776 except Exception as e
:
777 logger
.error("Unexpected exception %s", str(e
))
778 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
780 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='GET')
781 def http_get_vnfs(tenant_id
):
782 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
784 if tenant_id
!= 'any':
785 #check valid tenant_id
786 nfvo
.check_tenant(mydb
, tenant_id
)
787 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, None,
788 ('uuid','name','description','public', "tenant_id", "created_at") )
790 if tenant_id
!= "any":
791 where_or
["tenant_id"] = tenant_id
792 where_or
["public"] = True
793 vnfs
= mydb
.get_rows(FROM
='vnfs', SELECT
=select_
,WHERE
=where_
,WHERE_OR
=where_or
, WHERE_AND_OR
="AND",LIMIT
=limit_
)
794 #change_keys_http2db(content, http2db_vnf, reverse=True)
795 utils
.convert_str2boolean(vnfs
, ('public',))
796 convert_datetime2str(vnfs
)
798 return format_out(data
)
799 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
800 logger
.error("http_get_vnfs error {}: {}".format(e
.http_code
, str(e
)))
801 bottle
.abort(e
.http_code
, str(e
))
802 except Exception as e
:
803 logger
.error("Unexpected exception %s", str(e
))
804 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
806 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='GET')
807 def http_get_vnf_id(tenant_id
,vnf_id
):
808 '''get vnf details, can use both uuid or name'''
809 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
811 vnf
= nfvo
.get_vnf_id(mydb
,tenant_id
,vnf_id
)
812 utils
.convert_str2boolean(vnf
, ('public',))
813 convert_datetime2str(vnf
)
814 return format_out(vnf
)
815 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
816 logger
.error("http_get_vnf_id error {}: {}".format(e
.http_code
, str(e
)))
817 bottle
.abort(e
.http_code
, str(e
))
818 except Exception as e
:
819 logger
.error("Unexpected exception %s", str(e
))
820 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
822 @bottle.route(url_base
+ '/<tenant_id>/vnfs', method
='POST')
823 def http_post_vnfs(tenant_id
):
824 '''insert a vnf into the catalogue. Creates the flavor and images in the VIM, and creates the VNF and its internal structure in the OPENMANO DB'''
825 #print "Parsing the YAML file of the VNF"
827 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
828 http_content
, used_schema
= format_in( vnfd_schema_v01
, ("schema_version",), {"0.2": vnfd_schema_v02
})
829 r
= utils
.remove_extra_items(http_content
, used_schema
)
831 logger
.debug("Remove received extra items %s", str(r
))
833 if used_schema
== vnfd_schema_v01
:
834 vnf_id
= nfvo
.new_vnf(mydb
,tenant_id
,http_content
)
835 elif used_schema
== vnfd_schema_v02
:
836 vnf_id
= nfvo
.new_vnf_v02(mydb
,tenant_id
,http_content
)
838 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
839 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
840 return http_get_vnf_id(tenant_id
, vnf_id
)
841 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
842 logger
.error("http_post_vnfs error {}: {}".format(e
.http_code
, str(e
)))
843 bottle
.abort(e
.http_code
, str(e
))
844 except Exception as e
:
845 logger
.error("Unexpected exception %s", str(e
))
846 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
848 @bottle.route(url_base
+ '/<tenant_id>/vnfs/<vnf_id>', method
='DELETE')
849 def http_delete_vnf_id(tenant_id
,vnf_id
):
850 '''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
851 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
852 #check valid tenant_id and deletes the vnf, including images,
854 data
= nfvo
.delete_vnf(mydb
,tenant_id
,vnf_id
)
855 #print json.dumps(data, indent=4)
856 return format_out({"result":"VNF " + data
+ " deleted"})
857 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
858 logger
.error("http_delete_vnf_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 %s", str(e
))
862 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
864 #@bottle.route(url_base + '/<tenant_id>/hosts/topology', method='GET')
865 #@bottle.route(url_base + '/<tenant_id>/physicalview/Madrid-Alcantara', method='GET')
866 @bottle.route(url_base
+ '/<tenant_id>/physicalview/<datacenter>', method
='GET')
867 def http_get_hosts(tenant_id
, datacenter
):
868 '''get the tidvim host hopology from the vim.'''
869 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
870 #print "http_get_hosts received by tenant " + tenant_id + ' datacenter ' + datacenter
872 if datacenter
== 'treeview':
873 data
= nfvo
.get_hosts(mydb
, tenant_id
)
875 #openmano-gui is using a hardcoded value for the datacenter
876 result
, data
= nfvo
.get_hosts_info(mydb
, tenant_id
) #, datacenter)
879 #print "http_get_hosts error %d %s" % (-result, data)
880 bottle
.abort(-result
, data
)
882 convert_datetime2str(data
)
883 #print json.dumps(data, indent=4)
884 return format_out(data
)
885 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
886 logger
.error("http_get_hosts error {}: {}".format(e
.http_code
, str(e
)))
887 bottle
.abort(e
.http_code
, str(e
))
888 except Exception as e
:
889 logger
.error("Unexpected exception %s", str(e
))
890 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
893 @bottle.route(url_base
+ '/<path:path>', method
='OPTIONS')
894 def http_options_deploy(path
):
895 '''For some reason GUI web ask for OPTIONS that must be responded'''
896 #TODO: check correct path, and correct headers request
897 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
898 bottle
.response
.set_header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
899 bottle
.response
.set_header('Accept','application/yaml,application/json')
900 bottle
.response
.set_header('Content-Type','application/yaml,application/json')
901 bottle
.response
.set_header('Access-Control-Allow-Headers','content-type')
902 bottle
.response
.set_header('Access-Control-Allow-Origin','*')
905 @bottle.route(url_base
+ '/<tenant_id>/topology/deploy', method
='POST')
906 def http_post_deploy(tenant_id
):
907 '''post topology deploy.'''
908 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
910 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
})
911 #r = utils.remove_extra_items(http_content, used_schema)
912 #if r is not None: print "http_post_deploy: Warning: remove extra items ", r
913 #print "http_post_deploy input: ", http_content
916 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
917 instance
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['name'], http_content
['name'])
918 #print json.dumps(data, indent=4)
919 return format_out(instance
)
920 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
921 logger
.error("http_post_deploy error {}: {}".format(e
.http_code
, str(e
)))
922 bottle
.abort(e
.http_code
, str(e
))
923 except Exception as e
:
924 logger
.error("Unexpected exception %s", str(e
))
925 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
927 @bottle.route(url_base
+ '/<tenant_id>/topology/verify', method
='POST')
928 def http_post_verify(tenant_id
):
930 # '''post topology verify'''
931 # print "http_post_verify by tenant " + tenant_id + ' datacenter ' + datacenter
932 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
939 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='POST')
940 def http_post_scenarios(tenant_id
):
941 '''add a scenario into the catalogue. Creates the scenario and its internal structure in the OPENMANO DB'''
942 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
943 http_content
, used_schema
= format_in( nsd_schema_v01
, ("schema_version",), {2: nsd_schema_v02
, "0.3": nsd_schema_v03
})
944 #r = utils.remove_extra_items(http_content, used_schema)
945 #if r is not None: print "http_post_scenarios: Warning: remove extra items ", r
946 #print "http_post_scenarios input: ", http_content
948 if used_schema
== nsd_schema_v01
:
949 scenario_id
= nfvo
.new_scenario(mydb
, tenant_id
, http_content
)
950 elif used_schema
== nsd_schema_v02
:
951 scenario_id
= nfvo
.new_scenario_v02(mydb
, tenant_id
, http_content
)
952 elif used_schema
== nsd_schema_v03
:
953 scenario_id
= nfvo
.new_scenario_v03(mydb
, tenant_id
, http_content
)
955 logger
.warning('Unexpected schema_version: %s', http_content
.get("schema_version"))
956 bottle
.abort(HTTP_Bad_Request
, "Invalid schema version")
957 #print json.dumps(data, indent=4)
958 #return format_out(data)
959 return http_get_scenario_id(tenant_id
, scenario_id
)
960 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
961 logger
.error("http_post_scenarios error {}: {}".format(e
.http_code
, str(e
)))
962 bottle
.abort(e
.http_code
, str(e
))
963 except Exception as e
:
964 logger
.error("Unexpected exception %s", str(e
))
965 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
967 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>/action', method
='POST')
968 def http_post_scenario_action(tenant_id
, scenario_id
):
969 '''take an action over a scenario'''
970 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
971 #check valid tenant_id
973 nfvo
.check_tenant(mydb
, tenant_id
)
975 http_content
,_
= format_in( scenario_action_schema
)
976 r
= utils
.remove_extra_items(http_content
, scenario_action_schema
)
978 logger
.debug("Remove received extra items %s", str(r
))
979 if "start" in http_content
:
980 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['start']['instance_name'], \
981 http_content
['start'].get('description',http_content
['start']['instance_name']),
982 http_content
['start'].get('datacenter') )
983 return format_out(data
)
984 elif "deploy" in http_content
: #Equivalent to start
985 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['deploy']['instance_name'],
986 http_content
['deploy'].get('description',http_content
['deploy']['instance_name']),
987 http_content
['deploy'].get('datacenter') )
988 return format_out(data
)
989 elif "reserve" in http_content
: #Reserve resources
990 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['reserve']['instance_name'],
991 http_content
['reserve'].get('description',http_content
['reserve']['instance_name']),
992 http_content
['reserve'].get('datacenter'), startvms
=False )
993 return format_out(data
)
994 elif "verify" in http_content
: #Equivalent to start and then delete
995 data
= nfvo
.start_scenario(mydb
, tenant_id
, scenario_id
, http_content
['verify']['instance_name'],
996 http_content
['verify'].get('description',http_content
['verify']['instance_name']),
997 http_content
['verify'].get('datacenter'), startvms
=False )
998 instance_id
= data
['uuid']
999 nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1000 return format_out({"result":"Verify OK"})
1001 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1002 logger
.error("http_post_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1003 bottle
.abort(e
.http_code
, str(e
))
1004 except Exception as e
:
1005 logger
.error("Unexpected exception %s", str(e
))
1006 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1008 @bottle.route(url_base
+ '/<tenant_id>/scenarios', method
='GET')
1009 def http_get_scenarios(tenant_id
):
1010 '''get scenarios list'''
1011 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1013 #check valid tenant_id
1014 if tenant_id
!= "any":
1015 nfvo
.check_tenant(mydb
, tenant_id
)
1017 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
1019 if tenant_id
!= "any":
1020 where_or
["tenant_id"] = tenant_id
1021 where_or
["public"] = True
1022 scenarios
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, WHERE_OR
=where_or
, WHERE_AND_OR
="AND", LIMIT
=l
, FROM
='scenarios')
1023 convert_datetime2str(scenarios
)
1024 utils
.convert_str2boolean(scenarios
, ('public',) )
1025 data
={'scenarios':scenarios
}
1026 #print json.dumps(scenarios, indent=4)
1027 return format_out(data
)
1028 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1029 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1030 bottle
.abort(e
.http_code
, str(e
))
1031 except Exception as e
:
1032 logger
.error("Unexpected exception %s", str(e
))
1033 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1035 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='GET')
1036 def http_get_scenario_id(tenant_id
, scenario_id
):
1037 '''get scenario details, can use both uuid or name'''
1038 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1040 #check valid tenant_id
1041 if tenant_id
!= "any":
1042 nfvo
.check_tenant(mydb
, tenant_id
)
1044 scenario
= mydb
.get_scenario(scenario_id
, tenant_id
)
1045 convert_datetime2str(scenario
)
1046 data
={'scenario' : scenario
}
1047 return format_out(data
)
1048 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1049 logger
.error("http_get_scenarios error {}: {}".format(e
.http_code
, str(e
)))
1050 bottle
.abort(e
.http_code
, str(e
))
1051 except Exception as e
:
1052 logger
.error("Unexpected exception %s", str(e
))
1053 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1055 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='DELETE')
1056 def http_delete_scenario_id(tenant_id
, scenario_id
):
1057 '''delete a scenario from database, can use both uuid or name'''
1059 #check valid tenant_id
1060 if tenant_id
!= "any":
1061 nfvo
.check_tenant(mydb
, tenant_id
)
1063 data
= mydb
.delete_scenario(scenario_id
, tenant_id
)
1064 #print json.dumps(data, indent=4)
1065 return format_out({"result":"scenario " + data
+ " deleted"})
1066 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1067 logger
.error("http_delete_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1068 bottle
.abort(e
.http_code
, str(e
))
1069 except Exception as e
:
1070 logger
.error("Unexpected exception %s", str(e
))
1071 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1074 @bottle.route(url_base
+ '/<tenant_id>/scenarios/<scenario_id>', method
='PUT')
1075 def http_put_scenario_id(tenant_id
, scenario_id
):
1076 '''edit an existing scenario id'''
1077 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1078 http_content
,_
= format_in( scenario_edit_schema
)
1079 #r = utils.remove_extra_items(http_content, scenario_edit_schema)
1080 #if r is not None: print "http_put_scenario_id: Warning: remove extra items ", r
1081 #print "http_put_scenario_id input: ", http_content
1083 nfvo
.edit_scenario(mydb
, tenant_id
, scenario_id
, http_content
)
1084 #print json.dumps(data, indent=4)
1085 #return format_out(data)
1086 return http_get_scenario_id(tenant_id
, scenario_id
)
1087 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1088 logger
.error("http_put_scenario_id error {}: {}".format(e
.http_code
, str(e
)))
1089 bottle
.abort(e
.http_code
, str(e
))
1090 except Exception as e
:
1091 logger
.error("Unexpected exception %s", str(e
))
1092 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1094 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='POST')
1095 def http_post_instances(tenant_id
):
1096 '''take an action over a scenario'''
1097 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1099 #check valid tenant_id
1100 if tenant_id
!= "any":
1101 nfvo
.check_tenant(mydb
, tenant_id
)
1103 http_content
,used_schema
= format_in( instance_scenario_create_schema_v01
)
1104 r
= utils
.remove_extra_items(http_content
, used_schema
)
1106 logger
.warning("http_post_instances: Warning: remove extra items %s", str(r
))
1107 data
= nfvo
.create_instance(mydb
, tenant_id
, http_content
["instance"])
1108 return format_out(data
)
1109 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1110 logger
.error("http_post_instances error {}: {}".format(e
.http_code
, str(e
)))
1111 bottle
.abort(e
.http_code
, str(e
))
1112 except Exception as e
:
1113 logger
.error("Unexpected exception %s", str(e
))
1114 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1119 @bottle.route(url_base
+ '/<tenant_id>/instances', method
='GET')
1120 def http_get_instances(tenant_id
):
1121 '''get instance list'''
1123 #check valid tenant_id
1124 if tenant_id
!= "any":
1125 nfvo
.check_tenant(mydb
, tenant_id
)
1127 s
,w
,l
=filter_query_string(bottle
.request
.query
, None, ('uuid', 'name', 'scenario_id', 'tenant_id', 'description', 'created_at'))
1128 if tenant_id
!= "any":
1129 w
['tenant_id'] = tenant_id
1130 instances
= mydb
.get_rows(SELECT
=s
, WHERE
=w
, LIMIT
=l
, FROM
='instance_scenarios')
1131 convert_datetime2str(instances
)
1132 utils
.convert_str2boolean(instances
, ('public',) )
1133 data
={'instances':instances
}
1134 return format_out(data
)
1135 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1136 logger
.error("http_get_instances error {}: {}".format(e
.http_code
, str(e
)))
1137 bottle
.abort(e
.http_code
, str(e
))
1138 except Exception as e
:
1139 logger
.error("Unexpected exception %s", str(e
))
1140 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1142 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='GET')
1143 def http_get_instance_id(tenant_id
, instance_id
):
1144 '''get instances details, can use both uuid or name'''
1145 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1147 #check valid tenant_id
1148 if tenant_id
!= "any":
1149 nfvo
.check_tenant(mydb
, tenant_id
)
1150 if tenant_id
== "any":
1152 #obtain data (first time is only to check that the instance exists)
1153 instance_dict
= mydb
.get_instance_scenario(instance_id
, tenant_id
, verbose
=True)
1155 nfvo
.refresh_instance(mydb
, tenant_id
, instance_dict
)
1156 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1157 logger
.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e
))
1158 #obtain data with results upated
1159 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1160 convert_datetime2str(instance
)
1161 #print json.dumps(instance, indent=4)
1162 return format_out(instance
)
1163 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1164 logger
.error("http_get_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1165 bottle
.abort(e
.http_code
, str(e
))
1166 except Exception as e
:
1167 logger
.error("Unexpected exception %s", str(e
))
1168 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1170 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>', method
='DELETE')
1171 def http_delete_instance_id(tenant_id
, instance_id
):
1172 '''delete instance from VIM and from database, can use both uuid or name'''
1173 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1175 #check valid tenant_id
1176 if tenant_id
!= "any":
1177 nfvo
.check_tenant(mydb
, tenant_id
)
1178 if tenant_id
== "any":
1181 message
= nfvo
.delete_instance(mydb
, tenant_id
,instance_id
)
1182 return format_out({"result":message
})
1183 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1184 logger
.error("http_delete_instance_id error {}: {}".format(e
.http_code
, str(e
)))
1185 bottle
.abort(e
.http_code
, str(e
))
1186 except Exception as e
:
1187 logger
.error("Unexpected exception %s", str(e
))
1188 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1190 @bottle.route(url_base
+ '/<tenant_id>/instances/<instance_id>/action', method
='POST')
1191 def http_post_instance_scenario_action(tenant_id
, instance_id
):
1192 '''take an action over a scenario instance'''
1193 logger
.debug('FROM %s %s %s', bottle
.request
.remote_addr
, bottle
.request
.method
, bottle
.request
.url
)
1195 #check valid tenant_id
1196 if tenant_id
!= "any":
1197 nfvo
.check_tenant(mydb
, tenant_id
)
1200 http_content
,_
= format_in( instance_scenario_action_schema
)
1201 r
= utils
.remove_extra_items(http_content
, instance_scenario_action_schema
)
1203 logger
.debug("Remove received extra items %s", str(r
))
1204 #print "http_post_instance_scenario_action input: ", http_content
1206 instance
= mydb
.get_instance_scenario(instance_id
, tenant_id
)
1207 instance_id
= instance
["uuid"]
1209 data
= nfvo
.instance_action(mydb
, tenant_id
, instance_id
, http_content
)
1210 return format_out(data
)
1211 except (nfvo
.NfvoException
, db_base_Exception
) as e
:
1212 logger
.error("http_post_instance_scenario_action error {}: {}".format(e
.http_code
, str(e
)))
1213 bottle
.abort(e
.http_code
, str(e
))
1214 except Exception as e
:
1215 logger
.error("Unexpected exception %s", str(e
))
1216 bottle
.abort(HTTP_Internal_Server_Error
, str(e
))
1228 def error400(error
):
1229 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
1230 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
1231 return format_out(e
)