scenario_action_schema, instance_scenario_action_schema, instance_scenario_create_schema_v01, \
tenant_schema, tenant_edit_schema,\
datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
- object_schema, netmap_new_schema, netmap_edit_schema
+ object_schema, netmap_new_schema, netmap_edit_schema, sdn_controller_schema, sdn_controller_edit_schema, \
+ sdn_port_mapping_schema, sdn_external_port_schema
+
import nfvo
import utils
from db_base import db_base_Exception
return json.dumps(data, indent=4) + "\n"
def format_in(default_schema, version_fields=None, version_dict_schema=None):
- ''' Parse the content of HTTP request against a json_schema
- Parameters
- default_schema: The schema to be parsed by default if no version field is found in the client data
- version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to obtain the version
- version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value
- It can contain a None as key, and this is apply if the client data version does not match any key
- Return:
- user_data, used_schema: if the data is successfully decoded and matches the schema
- launch a bottle abort if fails
- '''
+ """
+ Parse the content of HTTP request against a json_schema
+ :param default_schema: The schema to be parsed by default if no version field is found in the client data. In None
+ no validation is done
+ :param version_fields: If provided it contains a tuple or list with the fields to iterate across the client data to
+ obtain the version
+ :param version_dict_schema: It contains a dictionary with the version as key, and json schema to apply as value.
+ It can contain a None as key, and this is apply if the client data version does not match any key
+ :return: user_data, used_schema: if the data is successfully decoded and matches the schema.
+ Launch a bottle abort if fails
+ """
#print "HEADERS :" + str(bottle.request.headers.items())
try:
error_text = "Invalid header format "
logger.warning('Content-Type ' + str(format_type) + ' not supported.')
bottle.abort(HTTP_Not_Acceptable, 'Content-Type ' + str(format_type) + ' not supported.')
return
- #if client_data == None:
+ # if client_data == None:
# bottle.abort(HTTP_Bad_Request, "Content error, empty")
# return
- 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) )
- #look for the client provider version
+ 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) )
+ # look for the client provider version
error_text = "Invalid content "
+ if not default_schema and not version_fields:
+ return client_data, None
client_version = None
used_schema = None
if version_fields != None:
else:
client_version=None
break
- if client_version==None:
- used_schema=default_schema
- elif version_dict_schema!=None:
+ if client_version == None:
+ used_schema = default_schema
+ elif version_dict_schema != None:
if client_version in version_dict_schema:
used_schema = version_dict_schema[client_version]
elif None in version_dict_schema:
'''Don't know yet if really needed. Keep it just in case'''
bottle.response.headers['Access-Control-Allow-Origin'] = '*'
+@bottle.route(url_base + '/version', method='GET')
+def http_get_version():
+ return nfvo.get_version()
#
# VNFs
#
@bottle.route(url_base + '/datacenters', method='POST')
def http_post_datacenters():
- '''insert a tenant into the catalogue. '''
+ '''insert a datacenter into the catalogue. '''
#parse input data
logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
http_content,_ = format_in( datacenter_schema )
logger.error("Unexpected exception: ", exc_info=True)
bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='POST')
+def http_post_sdn_controller(tenant_id):
+ '''insert a sdn controller into the catalogue. '''
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( sdn_controller_schema )
+ try:
+ logger.debug("tenant_id: "+tenant_id)
+ #logger.debug("content: {}".format(http_content['sdn_controller']))
+
+ data = nfvo.sdn_controller_create(mydb, tenant_id, http_content['sdn_controller'])
+ return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, data)})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='PUT')
+def http_put_sdn_controller_update(tenant_id, controller_id):
+ '''Update sdn controller'''
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content,_ = format_in( sdn_controller_edit_schema )
+# r = utils.remove_extra_items(http_content, datacenter_schema)
+# if r:
+# logger.debug("Remove received extra items %s", str(r))
+ try:
+ #logger.debug("tenant_id: "+tenant_id)
+ logger.debug("content: {}".format(http_content['sdn_controller']))
+
+ data = nfvo.sdn_controller_update(mydb, tenant_id, controller_id, http_content['sdn_controller'])
+ return format_out({"sdn_controller": nfvo.sdn_controller_list(mydb, tenant_id, controller_id)})
+
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_sdn_controller error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/sdn_controllers', method='GET')
+def http_get_sdn_controller(tenant_id):
+ '''get sdn controllers list, can use both uuid or name'''
+ try:
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+
+ data = {'sdn_controllers': nfvo.sdn_controller_list(mydb, tenant_id)}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_sdn_controller error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='GET')
+def http_get_sdn_controller_id(tenant_id, controller_id):
+ '''get sdn controller details, can use both uuid or name'''
+ try:
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ data = nfvo.sdn_controller_list(mydb, tenant_id, controller_id)
+ return format_out({"sdn_controllers": data})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/sdn_controllers/<controller_id>', method='DELETE')
+def http_delete_sdn_controller_id(tenant_id, controller_id):
+ '''delete sdn controller, can use both uuid or name'''
+ try:
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ data = nfvo.sdn_controller_delete(mydb, tenant_id, controller_id)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_sdn_controller_id error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='POST')
+def http_post_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
+ '''Set the sdn port mapping for a datacenter. '''
+ #parse input data
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content, _ = format_in(sdn_port_mapping_schema)
+# r = utils.remove_extra_items(http_content, datacenter_schema)
+# if r:
+# logger.debug("Remove received extra items %s", str(r))
+ try:
+ data = nfvo.datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, http_content['sdn_port_mapping'])
+ return format_out({"sdn_port_mapping": data})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='GET')
+def http_get_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
+ '''get datacenter sdn mapping details, can use both uuid or name'''
+ try:
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+
+ data = nfvo.datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id)
+ return format_out({"sdn_port_mapping": data})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/sdn_mapping', method='DELETE')
+def http_delete_datacenter_sdn_port_mapping(tenant_id, datacenter_id):
+ '''clean datacenter sdn mapping, can use both uuid or name'''
+ try:
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ data = nfvo.datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id)
+ return format_out({"result": data})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_datacenter_sdn_port_mapping error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/networks', method='GET') #deprecated
@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>/netmaps', method='GET')
logger.error("Unexpected exception: ", exc_info=True)
bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='PUT')
+def http_associate_datacenters_edit(tenant_id, datacenter_id):
+ '''associate an existing datacenter to a this tenant. '''
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ #parse input data
+ http_content,_ = format_in( datacenter_associate_schema )
+ r = utils.remove_extra_items(http_content, datacenter_associate_schema)
+ if r:
+ logger.debug("Remove received extra items %s", str(r))
+ try:
+ id_ = nfvo.edit_datacenter_to_tenant(mydb, tenant_id, datacenter_id,
+ http_content['datacenter'].get('vim_tenant'),
+ http_content['datacenter'].get('vim_tenant_name'),
+ http_content['datacenter'].get('vim_username'),
+ http_content['datacenter'].get('vim_password'),
+ http_content['datacenter'].get('config')
+ )
+ return http_get_datacenter_id(tenant_id, id_)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_associate_datacenters_edit error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
@bottle.route(url_base + '/<tenant_id>/datacenters/<datacenter_id>', method='DELETE')
def http_deassociate_datacenters(tenant_id, datacenter_id):
logger.error("Unexpected exception: ", exc_info=True)
bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method='POST')
+def http_post_vim_net_sdn_attach(tenant_id, datacenter_id, network_id):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content, _ = format_in(sdn_external_port_schema)
+ try:
+ data = nfvo.vim_net_sdn_attach(mydb, tenant_id, datacenter_id, network_id, http_content)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_vim_net_sdn_attach error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method='DELETE')
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method='DELETE')
+def http_delete_vim_net_sdn_detach(tenant_id, datacenter_id, network_id, port_id=None):
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ data = nfvo.vim_net_sdn_detach(mydb, tenant_id, datacenter_id, network_id, port_id)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_delete_vim_net_sdn_detach error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
#check valid tenant_id
nfvo.check_tenant(mydb, tenant_id)
select_,where_,limit_ = filter_query_string(bottle.request.query, None,
- ('uuid','name','description','public', "tenant_id", "created_at") )
+ ('uuid', 'name', 'osm_id', 'description', 'public', "tenant_id", "created_at") )
where_or = {}
if tenant_id != "any":
where_or["tenant_id"] = tenant_id
@bottle.route(url_base + '/<tenant_id>/vnfs', method='POST')
def http_post_vnfs(tenant_id):
- '''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'''
- #print "Parsing the YAML file of the VNF"
- #parse input data
+ """ Insert a vnf into the catalogue. Creates the flavor and images, and fill the tables at database
+ :param tenant_id: tenant that this vnf belongs to
+ :return:
+ """
+ # print "Parsing the YAML file of the VNF"
+ # parse input data
logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
http_content, used_schema = format_in( vnfd_schema_v01, ("schema_version",), {"0.2": vnfd_schema_v02})
r = utils.remove_extra_items(http_content, used_schema)
logger.error("Unexpected exception: ", exc_info=True)
bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
-
+
+@bottle.route(url_base + '/v3/<tenant_id>/vnfd', method='POST')
+def http_post_vnfs_v3(tenant_id):
+ """
+ Insert one or several VNFs in the catalog, following OSM IM
+ :param tenant_id: tenant owner of the VNF
+ :return: The detailed list of inserted VNFs, following the old format
+ """
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content, _ = format_in(None)
+ try:
+ vnfd_uuid_list = nfvo.new_vnfd_v3(mydb, tenant_id, http_content)
+ vnfd_list = []
+ for vnfd_uuid in vnfd_uuid_list:
+ vnf = nfvo.get_vnf_id(mydb, tenant_id, vnfd_uuid)
+ utils.convert_str2boolean(vnf, ('public',))
+ convert_datetime2str(vnf)
+ vnfd_list.append(vnf["vnf"])
+ return format_out({"vnfd": vnfd_list})
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_vnfs error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
@bottle.route(url_base + '/<tenant_id>/vnfs/<vnf_id>', method='DELETE')
-def http_delete_vnf_id(tenant_id,vnf_id):
+def http_delete_vnf_id(tenant_id, vnf_id):
'''delete a vnf from database, and images and flavors in VIM when appropriate, can use both uuid or name'''
logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
#check valid tenant_id and deletes the vnf, including images,
logger.error("Unexpected exception: ", exc_info=True)
bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+@bottle.route(url_base + '/v3/<tenant_id>/nsd', method='POST')
+def http_post_nsds_v3(tenant_id):
+ """
+ Insert one or several NSDs in the catalog, following OSM IM
+ :param tenant_id: tenant owner of the NSD
+ :return: The detailed list of inserted NSDs, following the old format
+ """
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ http_content, _ = format_in(None)
+ try:
+ nsd_uuid_list = nfvo.new_nsd_v3(mydb, tenant_id, http_content)
+ nsd_list = []
+ for nsd_uuid in nsd_uuid_list:
+ scenario = mydb.get_scenario(nsd_uuid, tenant_id)
+ convert_datetime2str(scenario)
+ nsd_list.append(scenario)
+ data = {'nsd': nsd_list}
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_post_nsds_v3 error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
@bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>/action', method='POST')
def http_post_scenario_action(tenant_id, scenario_id):
if tenant_id != "any":
nfvo.check_tenant(mydb, tenant_id)
#obtain data
- s,w,l=filter_query_string(bottle.request.query, None, ('uuid', 'name', 'description', 'tenant_id', 'created_at', 'public'))
+ s,w,l=filter_query_string(bottle.request.query, None,
+ ('uuid', 'name', 'osm_id', 'description', 'tenant_id', 'created_at', 'public'))
where_or={}
if tenant_id != "any":
where_or["tenant_id"] = tenant_id
nfvo.refresh_instance(mydb, tenant_id, instance_dict)
except (nfvo.NfvoException, db_base_Exception) as e:
logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e))
- #obtain data with results upated
+ # obtain data with results upated
instance = mydb.get_instance_scenario(instance_id, tenant_id)
+ # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value
+ for vnf in instance.get("vnfs", ()):
+ for vm in vnf.get("vms", ()):
+ for iface in vm.get("interfaces", ()):
+ if iface.get("ip_address"):
+ index = iface["ip_address"].find(";")
+ if index >= 0:
+ iface["ip_address"] = iface["ip_address"][:index]
convert_datetime2str(instance)
- #print json.dumps(instance, indent=4)
+ # print json.dumps(instance, indent=4)
return format_out(instance)
except (nfvo.NfvoException, db_base_Exception) as e:
logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e)))
@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='POST')
def http_post_instance_scenario_action(tenant_id, instance_id):
- '''take an action over a scenario instance'''
+ """
+ take an action over a scenario instance
+ :param tenant_id: tenant where user belongs to
+ :param instance_id: instance indentity
+ :return:
+ """
logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
# parse input data
http_content, _ = format_in(instance_scenario_action_schema)
bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action', method='GET')
+@bottle.route(url_base + '/<tenant_id>/instances/<instance_id>/action/<action_id>', method='GET')
+def http_get_instance_scenario_action(tenant_id, instance_id, action_id=None):
+ """
+ List the actions done over an instance, or the action details
+ :param tenant_id: tenant where user belongs to. Can be "any" to ignore
+ :param instance_id: instance id, can be "any" to get actions of all instances
+ :return:
+ """
+ logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+ try:
+ # check valid tenant_id
+ if tenant_id != "any":
+ nfvo.check_tenant(mydb, tenant_id)
+ data = nfvo.instance_action_get(mydb, tenant_id, instance_id, action_id)
+ return format_out(data)
+ except (nfvo.NfvoException, db_base_Exception) as e:
+ logger.error("http_get_instance_scenario_action error {}: {}".format(e.http_code, str(e)))
+ bottle.abort(e.http_code, str(e))
+ except Exception as e:
+ logger.error("Unexpected exception: ", exc_info=True)
+ bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+
@bottle.error(400)
@bottle.error(401)
@bottle.error(404)