From 6aa0b2b620956618cfdd318af5399239c874243d Mon Sep 17 00:00:00 2001 From: Pablo Montes Moreno Date: Tue, 23 May 2017 18:33:12 +0200 Subject: [PATCH 01/16] External port implementation for SDN assist Change-Id: I8dec60c615ffe473adc96c4cd557af9d8de78e04 Signed-off-by: Pablo Montes Moreno --- database_utils/migrate_mano_db.sh | 23 +++- openmano | 119 +++++++++++++++- openmanod | 6 +- osm_ro/httpserver.py | 29 +++- osm_ro/nfvo.py | 219 +++++++++++++++++++++++++++++- osm_ro/openmano_schemas.py | 14 +- osm_ro/vim_thread.py | 24 +++- 7 files changed, 418 insertions(+), 16 deletions(-) diff --git a/database_utils/migrate_mano_db.sh b/database_utils/migrate_mano_db.sh index 27e7a899..d57194e0 100755 --- a/database_utils/migrate_mano_db.sh +++ b/database_utils/migrate_mano_db.sh @@ -33,7 +33,7 @@ DBPORT="3306" DBNAME="mano_db" QUIET_MODE="" #TODO update it with the last database version -LAST_DB_VERSION=20 +LAST_DB_VERSION=21 # Detect paths MYSQL=$(which mysql) @@ -188,6 +188,7 @@ fi #[ $OPENMANO_VER_NUM -ge 5004 ] && DB_VERSION=18 #0.5.4 => 18 #[ $OPENMANO_VER_NUM -ge 5005 ] && DB_VERSION=19 #0.5.5 => 19 #[ $OPENMANO_VER_NUM -ge 5009 ] && DB_VERSION=20 #0.5.9 => 20 +#[ $OPENMANO_VER_NUM -ge 5015 ] && DB_VERSION=21 #0.5.15 => 21 #TODO ... put next versions here function upgrade_to_1(){ @@ -751,6 +752,26 @@ function downgrade_from_20(){ echo "DELETE FROM schema_version WHERE version_int='20';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 } +function upgrade_to_21(){ + # echo " upgrade database from version 0.20 to version 0.21" + echo " edit 'instance_nets' to allow instance_scenario_id=None" + echo "ALTER TABLE instance_nets MODIFY COLUMN instance_scenario_id varchar(36) NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo " enlarge column 'dns_address' at table 'ip_profiles'" + echo "ALTER TABLE ip_profiles MODIFY dns_address varchar(255) DEFAULT NULL NULL "\ + "comment 'dns ip list separated by semicolon';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo "INSERT INTO schema_version (version_int, version, openmano_ver, comments, date) VALUES (21, '0.21', '0.5.15', 'Edit instance_nets to allow instance_scenario_id=None and enlarge column dns_address at table ip_profiles', '2017-06-02');" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 +} +function downgrade_from_21(){ + # echo " downgrade database from version 0.21 to version 0.20" + echo " edit 'instance_nets' to disallow instance_scenario_id=None" + #Delete all lines with a instance_scenario_id=NULL in order to disable this option + echo "DELETE FROM instance_nets WHERE instance_scenario_id IS NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo "ALTER TABLE instance_nets MODIFY COLUMN instance_scenario_id varchar(36) NOT NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo " shorten column 'dns_address' at table 'ip_profiles'" + echo "ALTER TABLE ip_profiles MODIFY dns_address varchar(64) DEFAULT NULL NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo "DELETE FROM schema_version WHERE version_int='21';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 +} + function upgrade_to_X(){ echo " change 'datacenter_nets'" echo "ALTER TABLE datacenter_nets ADD COLUMN vim_tenant_id VARCHAR(36) NOT NULL AFTER datacenter_id, DROP INDEX name_datacenter_id, ADD UNIQUE INDEX name_datacenter_id (name, datacenter_id, vim_tenant_id);" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 diff --git a/openmano b/openmano index 111df432..6c0d15b2 100755 --- a/openmano +++ b/openmano @@ -141,6 +141,9 @@ def _print_verbose(mano_response, verbose_level=0): if verbose_level >=1: if content.get('created_at'): myoutput += " " + content['created_at'].ljust(20) + if content.get('sdn_attached_ports'): + #myoutput += " " + str(content['sdn_attached_ports']).ljust(20) + myoutput += "\nsdn_attached_ports:\n" + yaml.safe_dump(content['sdn_attached_ports'], indent=4, default_flow_style=False) if verbose_level >=2: new_line='\n' if content.get('type'): @@ -1057,6 +1060,8 @@ def datacenter_sdn_port_mapping_set(args): "No yaml/json has been provided specifying the SDN port mapping") port_mapping = yaml.load(datacenter_sdn_port_mapping_list(args)) + if 'error' in port_mapping: + raise OpenmanoCLIError("openmano client error: {}".format(port_mapping['error']['description'])) if len(port_mapping["sdn_port_mapping"]["ports_mapping"]) > 0: if not args.force: r = raw_input("Datacenter %s already contains a port mapping. Overwrite? (y/N)? " % (datacenter)) @@ -1279,6 +1284,82 @@ def vim_action(args): args.verbose=0 return _print_verbose(mano_response, args.verbose) +def _get_items(item, item_name_id=None, datacenter=None, tenant=None): + URLrequest = "http://%s:%s/openmano" %(mano_host, mano_port) + if tenant: + URLrequest += "/" + tenant + if datacenter: + URLrequest += "/vim/" + datacenter + if item: + URLrequest += "/" + item +"s" + if item_name_id: + URLrequest += "/" + item_name_id + mano_response = requests.get(URLrequest) + logger.debug("openmano response: %s", mano_response.text ) + + return mano_response + +def vim_net_sdn_attach(args): + #Verify the network exists in the vim + tenant = _get_tenant() + datacenter = _get_datacenter(args.datacenter, tenant) + result = _get_items('network', item_name_id=args.vim_net, datacenter=datacenter, tenant=tenant) + content = yaml.load(result.content) + if 'networks' in content: + raise OpenmanoCLIError('More than one network in the vim named ' + args.vim_net + '. Use uuid instead') + if 'error' in content: + raise OpenmanoCLIError(yaml.safe_dump(content)) + network_uuid = content['network']['id'] + + #Make call to attach the dataplane port to the SND network associated to the vim network + headers_req = {'content-type': 'application/yaml'} + payload_req = {'port': args.port} + if args.vlan: + payload_req['vlan'] = int(args.vlan) + if args.mac: + payload_req['mac'] = args.mac + + URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/attach" % (mano_host, mano_port, tenant, datacenter, network_uuid) + logger.debug("openmano request: %s", payload_req) + mano_response = requests.post(URLrequest, headers=headers_req, data=json.dumps(payload_req)) + logger.debug("openmano response: %s", mano_response.text) + result = _print_verbose(mano_response, args.verbose) + + return + + +def vim_net_sdn_detach(args): + if not args.all and not args.id: + print "--all or --id must be used" + return 1 + + # Verify the network exists in the vim + tenant = _get_tenant() + datacenter = _get_datacenter(args.datacenter, tenant) + result = _get_items('network', item_name_id=args.vim_net, datacenter=datacenter, tenant=tenant) + content = yaml.load(result.content) + if 'networks' in content: + raise OpenmanoCLIError('More than one network in the vim named ' + args.vim_net + '. Use uuid instead') + if 'error' in content: + raise OpenmanoCLIError(yaml.safe_dump(content)) + network_uuid = content['network']['id'] + + if not args.force: + r = raw_input("Confirm action' (y/N)? ") + if len(r) == 0 or r[0].lower() != "y": + return 0 + + if args.id: + URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/detach/%s" % ( + mano_host, mano_port, tenant, datacenter, network_uuid, args.id) + else: + URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/detach" % ( + mano_host, mano_port, tenant, datacenter, network_uuid) + mano_response = requests.delete(URLrequest) + logger.debug("openmano response: %s", mano_response.text) + result = _print_verbose(mano_response, args.verbose) + + return def datacenter_net_action(args): if args.action == "net-update": @@ -1798,24 +1879,50 @@ if __name__=="__main__": if item=='netmap-import': datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation") datacenter_action_parser.set_defaults(func=datacenter_netmap_action, action=item) - + + # =======================vim_net_sdn_xxx section======================= + # vim_net_sdn_attach + vim_net_sdn_attach_parser = subparsers.add_parser('vim-net-sdn-attach', + parents=[parent_parser], + help="Specify the port to access to an external network using SDN") + vim_net_sdn_attach_parser.add_argument("vim_net", action="store", + help="Name/id of the network in the vim that will be used to connect to the external network") + vim_net_sdn_attach_parser.add_argument("port", action="store", help="Specifies the port in the dataplane switch to access to the external network") + vim_net_sdn_attach_parser.add_argument("--vlan", action="store", help="Specifies the vlan (if any) to use in the defined port") + vim_net_sdn_attach_parser.add_argument("--mac", action="store", help="Specifies the MAC (if known) of the physical device that will be reachable by this external port") + vim_net_sdn_attach_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") + vim_net_sdn_attach_parser.set_defaults(func=vim_net_sdn_attach) + + # vim_net_sdn_detach + vim_net_sdn_detach_parser = subparsers.add_parser('vim-net-sdn-detach', + parents=[parent_parser], + help="Remove the port information to access to an external network using SDN") + + vim_net_sdn_detach_parser.add_argument("vim_net", action="store", help="Name/id of the vim network") + vim_net_sdn_detach_parser.add_argument("--id", action="store",help="Specify the uuid of the external ports from this network to be detached") + vim_net_sdn_detach_parser.add_argument("--all", action="store_true", help="Detach all external ports from this network") + vim_net_sdn_detach_parser.add_argument("-f", "--force", action="store_true", help="forces clearing without asking") + vim_net_sdn_detach_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") + vim_net_sdn_detach_parser.set_defaults(func=vim_net_sdn_detach) + # ======================= + for item in ("network", "tenant", "image"): if item=="network": - commnad_name = 'vim-net' + command_name = 'vim-net' else: - commnad_name = 'vim-'+item - vim_item_list_parser = subparsers.add_parser(commnad_name + '-list', parents=[parent_parser], help="list the vim " + item + "s") + command_name = 'vim-'+item + vim_item_list_parser = subparsers.add_parser(command_name + '-list', parents=[parent_parser], help="list the vim " + item + "s") vim_item_list_parser.add_argument("name", nargs='?', help="name or uuid of the " + item + "s") vim_item_list_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") vim_item_list_parser.set_defaults(func=vim_action, item=item, action="list") - vim_item_del_parser = subparsers.add_parser(commnad_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s") + vim_item_del_parser = subparsers.add_parser(command_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s") vim_item_del_parser.add_argument("name", help="name or uuid of the " + item + "s") vim_item_del_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") vim_item_del_parser.set_defaults(func=vim_action, item=item, action="delete") if item == "network" or item == "tenant": - vim_item_create_parser = subparsers.add_parser(commnad_name + '-create', parents=[parent_parser], help="create a "+item+" at vim") + vim_item_create_parser = subparsers.add_parser(command_name + '-create', parents=[parent_parser], help="create a "+item+" at vim") vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item ) vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter") diff --git a/openmanod b/openmanod index e8bd2da4..8f8c3f1b 100755 --- a/openmanod +++ b/openmanod @@ -48,9 +48,9 @@ import osm_ro __author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes" __date__ = "$26-aug-2014 11:09:29$" -__version__ = "0.5.14-r523" -version_date = "May 2017" -database_version = 20 #expected database schema version +__version__ = "0.5.15-r524" +version_date = "Jun 2017" +database_version = 21 #expected database schema version global global_config global logger diff --git a/osm_ro/httpserver.py b/osm_ro/httpserver.py index d6dc0c7a..94544f6b 100644 --- a/osm_ro/httpserver.py +++ b/osm_ro/httpserver.py @@ -43,7 +43,7 @@ from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \ tenant_schema, tenant_edit_schema,\ datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\ object_schema, netmap_new_schema, netmap_edit_schema, sdn_controller_schema, sdn_controller_edit_schema, \ - sdn_port_mapping_schema + sdn_port_mapping_schema, sdn_external_port_schema import nfvo import utils @@ -915,6 +915,33 @@ 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 + '//vim//network//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 + '//vim//network//detach', method='DELETE') +@bottle.route(url_base + '//vim//network//detach/', 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 + '//vim//', method='GET') @bottle.route(url_base + '//vim///', method='GET') diff --git a/osm_ro/nfvo.py b/osm_ro/nfvo.py index 3e59285b..93300472 100644 --- a/osm_ro/nfvo.py +++ b/osm_ro/nfvo.py @@ -44,6 +44,7 @@ import nfvo_db from threading import Lock from time import time from lib_osm_openvim import ovim as ovim_module +from lib_osm_openvim.ovim import ovimException global global_config global vimconn_imported @@ -3075,6 +3076,137 @@ def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None): net_list.append(net_nfvo) return net_list +def get_sdn_net_id(mydb, tenant_id, datacenter, network_id): + # obtain all network data + try: + if utils.check_valid_uuid(network_id): + filter_dict = {"id": network_id} + else: + filter_dict = {"name": network_id} + + datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter) + network = myvim.get_network_list(filter_dict=filter_dict) + except vimconn.vimconnException as e: + print "vim_action Not possible to get_%s_list from VIM: %s " % (item, str(e)) + raise NfvoException("Not possible to get_{}_list from VIM: {}".format(item, str(e)), e.http_code) + + # ensure the network is defined + if len(network) == 0: + raise NfvoException("Network {} is not present in the system".format(network_id), + HTTP_Bad_Request) + + # ensure there is only one network with the provided name + if len(network) > 1: + raise NfvoException("Multiple networks present in vim identified by {}".format(network_id), HTTP_Bad_Request) + + # ensure it is a dataplane network + if network[0]['type'] != 'data': + return None + + # ensure we use the id + network_id = network[0]['id'] + + # search in dabase mano_db in table instance nets for the sdn_net_id that corresponds to the vim_net_id==network_id + # and with instance_scenario_id==NULL + #search_dict = {'vim_net_id': network_id, 'instance_scenario_id': None} + search_dict = {'vim_net_id': network_id} + + try: + #sdn_network_id = mydb.get_rows(SELECT=('sdn_net_id',), FROM='instance_nets', WHERE=search_dict)[0]['sdn_net_id'] + result = mydb.get_rows(SELECT=('sdn_net_id',), FROM='instance_nets', WHERE=search_dict) + except db_base_Exception as e: + raise NfvoException("db_base_Exception obtaining SDN network to associated to vim network {}".format( + network_id) + str(e), HTTP_Internal_Server_Error) + + sdn_net_counter = 0 + for net in result: + if net['sdn_net_id'] != None: + sdn_net_counter+=1 + sdn_net_id = net['sdn_net_id'] + + if sdn_net_counter == 0: + return None + elif sdn_net_counter == 1: + return sdn_net_id + else: + raise NfvoException("More than one SDN network is associated to vim network {}".format( + network_id), HTTP_Internal_Server_Error) + +def get_sdn_controller_id(mydb, datacenter): + # Obtain sdn controller id + config = mydb.get_rows(SELECT=('config',), FROM='datacenters', WHERE={'uuid': datacenter})[0].get('config', '{}') + if not config: + return None + + return yaml.load(config).get('sdn-controller') + +def vim_net_sdn_attach(mydb, tenant_id, datacenter, network_id, descriptor): + try: + sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, network_id) + if not sdn_network_id: + raise NfvoException("No SDN network is associated to vim-network {}".format(network_id), HTTP_Internal_Server_Error) + + #Obtain sdn controller id + controller_id = get_sdn_controller_id(mydb, datacenter) + if not controller_id: + raise NfvoException("No SDN controller is set for datacenter {}".format(datacenter), HTTP_Internal_Server_Error) + + #Obtain sdn controller info + sdn_controller = ovim.show_of_controller(controller_id) + + port_data = { + 'name': 'external_port', + 'net_id': sdn_network_id, + 'ofc_id': controller_id, + 'switch_dpid': sdn_controller['dpid'], + 'switch_port': descriptor['port'] + } + + if 'vlan' in descriptor: + port_data['vlan'] = descriptor['vlan'] + if 'mac' in descriptor: + port_data['mac'] = descriptor['mac'] + + result = ovim.new_port(port_data) + except ovimException as e: + raise NfvoException("ovimException attaching SDN network {} to vim network {}".format( + sdn_network_id, network_id) + str(e), HTTP_Internal_Server_Error) + except db_base_Exception as e: + raise NfvoException("db_base_Exception attaching SDN network to vim network {}".format( + network_id) + str(e), HTTP_Internal_Server_Error) + + return 'Port uuid: '+ result + +def vim_net_sdn_detach(mydb, tenant_id, datacenter, network_id, port_id=None): + if port_id: + filter = {'uuid': port_id} + else: + sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, network_id) + if not sdn_network_id: + raise NfvoException("No SDN network is associated to vim-network {}".format(network_id), + HTTP_Internal_Server_Error) + #in case no port_id is specified only ports marked as 'external_port' will be detached + filter = {'name': 'external_port', 'net_id': sdn_network_id} + + try: + port_list = ovim.get_ports(columns={'uuid'}, filter=filter) + except ovimException as e: + raise NfvoException("ovimException obtaining external ports for net {}. ".format(network_id) + str(e), + HTTP_Internal_Server_Error) + + if len(port_list) == 0: + raise NfvoException("No ports attached to the network {} were found with the requested criteria".format(network_id), + HTTP_Bad_Request) + + port_uuid_list = [] + for port in port_list: + try: + port_uuid_list.append(port['uuid']) + ovim.delete_port(port['uuid']) + except ovimException as e: + raise NfvoException("ovimException deleting port {} for net {}. ".format(port['uuid'], network_id) + str(e), HTTP_Internal_Server_Error) + + return 'Detached ports uuid: {}'.format(','.join(port_uuid_list)) def vim_action_get(mydb, tenant_id, datacenter, item, name): #get datacenter info @@ -3089,9 +3221,32 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name): if item=="networks": #filter_dict['tenant_id'] = myvim['tenant_id'] content = myvim.get_network_list(filter_dict=filter_dict) + + if len(content) == 0: + raise NfvoException("Network {} is not present in the system. ".format(name), + HTTP_Bad_Request) + + #Update the networks with the attached ports + for net in content: + sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, net['id']) + if sdn_network_id != None: + try: + #port_list = ovim.get_ports(columns={'uuid', 'switch_port', 'vlan'}, filter={'name': 'external_port', 'net_id': sdn_network_id}) + port_list = ovim.get_ports(columns={'uuid', 'switch_port', 'vlan','name'}, filter={'net_id': sdn_network_id}) + except ovimException as e: + raise NfvoException("ovimException obtaining external ports for net {}. ".format(network_id) + str(e), HTTP_Internal_Server_Error) + #Remove field name and if port name is external_port save it as 'type' + for port in port_list: + if port['name'] == 'external_port': + port['type'] = "External" + del port['name'] + net['sdn_network_id'] = sdn_network_id + net['sdn_attached_ports'] = port_list + elif item=="tenants": content = myvim.get_tenant_list(filter_dict=filter_dict) elif item == "images": + content = myvim.get_image_list(filter_dict=filter_dict) else: raise NfvoException(item + "?", HTTP_Method_Not_Allowed) @@ -3128,6 +3283,36 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name): try: if item=="networks": + # If there is a SDN network associated to the vim-network, proceed to clear the relationship and delete it + sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, item_id) + if sdn_network_id != None: + #Delete any port attachment to this network + try: + port_list = ovim.get_ports(columns={'uuid'}, filter={'net_id': sdn_network_id}) + except ovimException as e: + raise NfvoException( + "ovimException obtaining external ports for net {}. ".format(network_id) + str(e), + HTTP_Internal_Server_Error) + + # By calling one by one all ports to be detached we ensure that not only the external_ports get detached + for port in port_list: + vim_net_sdn_detach(mydb, tenant_id, datacenter, item_id, port['uuid']) + + #Delete from 'instance_nets' the correspondence between the vim-net-id and the sdn-net-id + try: + mydb.delete_row(FROM='instance_nets', WHERE={'instance_scenario_id': None, 'sdn_net_id': sdn_network_id, 'vim_net_id': item_id}) + except db_base_Exception as e: + raise NfvoException("Error deleting correspondence for VIM/SDN dataplane networks{}: ".format(correspondence) + + str(e), HTTP_Internal_Server_Error) + + #Delete the SDN network + try: + ovim.delete_network(sdn_network_id) + except ovimException as e: + logger.error("ovimException deleting SDN network={} ".format(sdn_network_id) + str(e), exc_info=True) + raise NfvoException("ovimException deleting SDN network={} ".format(sdn_network_id) + str(e), + HTTP_Internal_Server_Error) + content = myvim.delete_network(item_id) elif item=="tenants": content = myvim.delete_tenant(item_id) @@ -3157,6 +3342,32 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor): net_ipprofile = net.pop("ip_profile", None) net_vlan = net.pop("vlan", None) content = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net) + + #If the datacenter has a SDN controller defined and the network is of dataplane type, then create the sdn network + if get_sdn_controller_id(mydb, datacenter) != None and (net_type == 'data' or net_type == 'ptp'): + try: + sdn_network = {} + sdn_network['vlan'] = net_vlan + sdn_network['type'] = net_type + sdn_network['name'] = net_name + ovim_content = ovim.new_network(sdn_network) + except ovimException as e: + self.logger.error("ovimException creating SDN network={} ".format( + sdn_network) + str(e), exc_info=True) + raise NfvoException("ovimException creating SDN network={} ".format(sdn_network) + str(e), + HTTP_Internal_Server_Error) + + # Save entry in in dabase mano_db in table instance_nets to stablish a dictionary vim_net_id <->sdn_net_id + # use instance_scenario_id=None to distinguish from real instaces of nets + correspondence = {'instance_scenario_id': None, 'sdn_net_id': ovim_content, 'vim_net_id': content} + #obtain datacenter_tenant_id + correspondence['datacenter_tenant_id'] = mydb.get_rows(SELECT=('uuid',), FROM='datacenter_tenants', WHERE={'datacenter_id': datacenter})[0]['uuid'] + + try: + mydb.new_row('instance_nets', correspondence, add_uuid=True) + except db_base_Exception as e: + raise NfvoException("Error saving correspondence for VIM/SDN dataplane networks{}: ".format(correspondence) + + str(e), HTTP_Internal_Server_Error) elif item=="tenants": tenant = descriptor["tenant"] content = myvim.new_tenant(tenant["name"], tenant.get("description")) @@ -3250,9 +3461,11 @@ def datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id): result["sdn-controller"] = controller_id result["dpid"] = sdn_controller["dpid"] - if result["sdn-controller"] == None or result["dpid"] == None: - raise NfvoException("Not all SDN controller information for datacenter {} could be found: {}".format(datacenter_id, result), - HTTP_Internal_Server_Error) + if result["sdn-controller"] == None: + raise NfvoException("SDN controller is not defined for datacenter {}".format(datacenter_id), HTTP_Bad_Request) + if result["dpid"] == None: + raise NfvoException("It was not possible to determine DPID for SDN controller {}".format(result["sdn-controller"]), + HTTP_Internal_Server_Error) if len(maps) == 0: return result diff --git a/osm_ro/openmano_schemas.py b/osm_ro/openmano_schemas.py index 1dfcffe4..765c05f0 100644 --- a/osm_ro/openmano_schemas.py +++ b/osm_ro/openmano_schemas.py @@ -1104,7 +1104,7 @@ instance_scenario_action_schema = { sdn_controller_properties={ "name": name_schema, - "dpid": {"type":"string", "pattern":"^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){7}$"}, + "dpid": {"type":"string", "pattern":"^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$"}, "ip": ip_schema, "port": port_schema, "type": {"type": "string", "enum": ["opendaylight","floodlight","onos"]}, @@ -1172,4 +1172,16 @@ sdn_port_mapping_schema = { } }, "required": ["sdn_port_mapping"] +} + +sdn_external_port_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "title":"External port ingformation", + "type": "object", + "properties": { + "port": {"type" : "string", "minLength":1, "maxLength":60}, + "vlan": vlan_schema, + "mac": mac_schema + }, + "required": ["port"] } \ No newline at end of file diff --git a/osm_ro/vim_thread.py b/osm_ro/vim_thread.py index 373fe7bf..8a987ac2 100644 --- a/osm_ro/vim_thread.py +++ b/osm_ro/vim_thread.py @@ -161,7 +161,7 @@ class vim_thread(threading.Thread): FROM="instance_interfaces as ii left join instance_nets as ine on " "ii.instance_net_id=ine.uuid left join instance_vms as iv on " "ii.instance_vm_id=iv.uuid", - SELECT=("ii.uuid as iface_id", "ine.uuid as net_id", "iv.uuid as vm_id", "sdn_net_id"), + SELECT=("ii.uuid as iface_id", "ine.uuid as net_id", "iv.uuid as vm_id", "sdn_net_id", "vim_net_id"), WHERE=where_) if len(db_ifaces)>1: self.logger.critical("Refresing interfaces. " @@ -172,6 +172,14 @@ class vim_thread(threading.Thread): continue else: db_iface = db_ifaces[0] + #If there is no sdn_net_id, check if it is because an already created vim network is being used + #in that case, the sdn_net_id will be in that entry of the instance_nets table + if not db_iface.get("sdn_net_id"): + result = self.db.get_rows(SELECT=('sdn_net_id',), FROM='instance_nets', + WHERE={'vim_net_id': db_iface.get("vim_net_id"), 'instance_scenario_id': None, "datacenter_tenant_id": self.datacenter_tenant_id}) + if len(result) == 1: + db_iface["sdn_net_id"] = result[0]['sdn_net_id'] + if db_iface.get("sdn_net_id") and interface.get("compute_node") and interface.get("pci"): sdn_net_id = db_iface["sdn_net_id"] sdn_port_name = sdn_net_id + "." + db_iface["vm_id"] @@ -549,6 +557,20 @@ class vim_thread(threading.Thread): self._remove_refresh("get-net", net_id) result = self.vim.delete_network(net_id) if sdn_net_id: + #Delete any attached port to this sdn network + #At this point, there will be ports associated to this network in case it was manually done using 'openmano vim-net-sdn-attach' + try: + port_list = self.ovim.get_ports(columns={'uuid'}, filter={'name': 'external_port', 'net_id': sdn_net_id}) + except ovimException as e: + raise vimconn.vimconnException( + "ovimException obtaining external ports for net {}. ".format(sdn_net_id) + str(e)) + + for port in port_list: + try: + self.ovim.delete_port(port['uuid']) + except ovimException as e: + raise vimconn.vimconnException( + "ovimException deleting port {} for net {}. ".format(port['uuid'], sdn_net_id) + str(e)) with self.db_lock: self.ovim.delete_network(sdn_net_id) return True, result -- 2.25.1 From bd11fd897768418d2436078faeb10dbadc62a9c2 Mon Sep 17 00:00:00 2001 From: kasar Date: Thu, 1 Jun 2017 23:44:03 -0700 Subject: [PATCH 02/16] Added vimconnetor unit tests for get_network,delete_network,get_flavor Change-Id: Ia2b28dba8948d6191ac9e49c2bcc38537df33e61 Signed-off-by: kasar --- test/test_RO.py | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/test/test_RO.py b/test/test_RO.py index 972907d3..b1e24599 100755 --- a/test/test_RO.py +++ b/test/test_RO.py @@ -42,6 +42,7 @@ import yaml import sys import time from pyvcloud.vcloudair import VCA +import uuid global test_config # used for global variables with the test configuration test_config = {} @@ -714,6 +715,200 @@ class test_vimconn_get_network_list(unittest.TestCase): network_list = test_config["vim_conn"].get_network_list({'name': 'unknown_name'}) self.assertEqual(network_list, []) +class test_vimconn_get_network(unittest.TestCase): + test_index = 1 + network_name = None + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + + @classmethod + def tearDownClass(cls): + test_config["test_number"] += 1 + + def setUp(self): + # creating new network + self.__class__.network_name = _get_random_string(20) + self.__class__.net_type = 'bridge' + network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name, + net_type=self.__class__.net_type) + self.__class__.network_id = network + logger.debug("{}".format(network)) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text+" -> TEST OK") + else: + logger.warning(self.__class__.test_text+" -> TEST NOK") + logger.critical("Traceback error",exc_info=True) + + # Deleting created network + result = test_config["vim_conn"].delete_network(self.__class__.network_id) + if result: + logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id)) + else: + logger.info("Failed to delete network id {}".format(self.__class__.network_id)) + + def test_000_get_network(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + network_info = test_config["vim_conn"].get_network(self.__class__.network_id) + self.assertEqual(network_info.get('status'), 'ACTIVE') + self.assertIn(self.__class__.network_name, network_info.get('name')) + self.assertEqual(network_info.get('type'), self.__class__.net_type) + self.assertEqual(network_info.get('id'), self.__class__.network_id) + + def test_010_get_network_negative(self): + Non_exist_id = str(uuid.uuid4()) + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + network_info = test_config["vim_conn"].get_network(Non_exist_id) + self.assertEqual(network_info, {}) + +class test_vimconn_delete_network(unittest.TestCase): + test_index = 1 + network_name = None + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + + @classmethod + def tearDownClass(cls): + test_config["test_number"] += 1 + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text+" -> TEST OK") + else: + logger.warning(self.__class__.test_text+" -> TEST NOK") + logger.critical("Traceback error",exc_info=True) + + def test_000_delete_network(self): + # Creating network + self.__class__.network_name = _get_random_string(20) + self.__class__.net_type = 'bridge' + network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name, + net_type=self.__class__.net_type) + self.__class__.network_id = network + logger.debug("{}".format(network)) + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + result = test_config["vim_conn"].delete_network(self.__class__.network_id) + if result: + logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id)) + else: + logger.info("Failed to delete network id {}".format(self.__class__.network_id)) + time.sleep(5) + # after deleting network we check in network list + network_list = test_config["vim_conn"].get_network_list({ 'id':self.__class__.network_id }) + self.assertEqual(network_list, []) + + def test_010_delete_network_negative(self): + Non_exist_id = str(uuid.uuid4()) + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + with self.assertRaises(Exception) as context: + test_config["vim_conn"].delete_network(Non_exist_id) + + self.assertEqual((context.exception).http_code, 400) + +class test_vimconn_get_flavor(unittest.TestCase): + test_index = 1 + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + + @classmethod + def tearDownClass(cls): + test_config["test_number"] += 1 + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text+" -> TEST OK") + else: + logger.warning(self.__class__.test_text+" -> TEST NOK") + logger.critical("Traceback error",exc_info=True) + + def test_000_get_flavor(self): + test_directory_content = os.listdir(test_config["test_directory"]) + + for dir_name in test_directory_content: + if dir_name == 'simple_linux': + self.__class__.scenario_test_path = test_config["test_directory"] + '/'+ dir_name + vnfd_files = glob.glob(self.__class__.scenario_test_path+'/vnfd_*.yaml') + break + + for vnfd in vnfd_files: + with open(vnfd, 'r') as stream: + vnf_descriptor = yaml.load(stream) + + vnfc_list = vnf_descriptor['vnf']['VNFC'] + for item in vnfc_list: + if 'ram' in item and 'vcpus' in item and 'disk' in item: + ram = item['ram'] + vcpus = item['vcpus'] + disk = item['disk'] + + flavor_data = {'ram': ram, + 'vcpus': vcpus, + 'disk': disk + } + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + # get flavor by id + result = test_config["vim_conn"].get_flavor(flavor_id) + self.assertEqual(ram, result['ram']) + self.assertEqual(vcpus, result['vcpus']) + self.assertEqual(disk, result['disk']) + + # delete flavor + result = test_config["vim_conn"].delete_flavor(flavor_id) + if result: + logger.info("Flavor id {} sucessfully deleted".format(result)) + else: + logger.info("Failed to delete flavor id {}".format(result)) + + def test_010_get_flavor_negative(self): + Non_exist_flavor_id = str(uuid.uuid4()) + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + with self.assertRaises(Exception) as context: + test_config["vim_conn"].get_flavor(Non_exist_flavor_id) + + self.assertEqual((context.exception).http_code, 404) + ''' IMPORTANT NOTE -- 2.25.1 From b334d445d87c1bd38655f4e285ad928b8546b9a9 Mon Sep 17 00:00:00 2001 From: garciadeblas Date: Fri, 2 Jun 2017 14:32:47 +0200 Subject: [PATCH 03/16] Updated vim_thread to have global timers; REFRESH_ACTIVE changed to 1 minute Change-Id: I923ba189101184fed6d8148835af579c03f51efa Signed-off-by: garciadeblas --- osm_ro/vim_thread.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osm_ro/vim_thread.py b/osm_ro/vim_thread.py index 8a987ac2..67bef44d 100644 --- a/osm_ro/vim_thread.py +++ b/osm_ro/vim_thread.py @@ -46,6 +46,8 @@ def is_task_id(task_id): class vim_thread(threading.Thread): + REFRESH_BUILD = 5 # 5 seconds + REFRESH_ACTIVE = 60 # 1 minute def __init__(self, vimconn, task_lock, name=None, datacenter_name=None, datacenter_tenant_id=None, db=None, db_lock=None, ovim=None): @@ -212,12 +214,12 @@ class vim_thread(threading.Thread): task["vim_info"] = vim_info if task["vim_info"]["status"] == "BUILD": - self._insert_refresh(task, now+5) # 5seconds + self._insert_refresh(task, now + self.REFRESH_BUILD) else: - self._insert_refresh(task, now+300) # 5minutes + self._insert_refresh(task, now + self.REFRESH_ACTIVE) except vimconn.vimconnException as e: self.logger.error("vimconnException Exception when trying to refresh vms " + str(e)) - self._insert_refresh(task, now + 300) # 5minutes + self._insert_refresh(task, now + self.REFRESH_ACTIVE) if net_to_refresh_list: try: @@ -279,12 +281,12 @@ class vim_thread(threading.Thread): task["vim_info"] = vim_info if task["vim_info"]["status"] == "BUILD": - self._insert_refresh(task, now+5) # 5seconds + self._insert_refresh(task, now + self.REFRESH_BUILD) else: - self._insert_refresh(task, now+300) # 5minutes + self._insert_refresh(task, now + self.REFRESH_ACTIVE) except vimconn.vimconnException as e: self.logger.error("vimconnException Exception when trying to refresh nets " + str(e)) - self._insert_refresh(task, now + 300) # 5minutes + self._insert_refresh(task, now + self.REFRESH_ACTIVE) if not items_to_refresh: time.sleep(1) -- 2.25.1 From c3b6d3775a351b9663ebb9bd94a5694d99d647da Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 30 May 2017 17:12:00 +0200 Subject: [PATCH 04/16] (bug 283) fixed error at vnf creation on missing vpci for dataplane interfaces Change-Id: I45541219873336dd7a8f8ec155ad0772404ad51a Signed-off-by: tierno --- osm_ro/nfvo_db.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osm_ro/nfvo_db.py b/osm_ro/nfvo_db.py index ea6d3397..ac392c6d 100644 --- a/osm_ro/nfvo_db.py +++ b/osm_ro/nfvo_db.py @@ -107,7 +107,7 @@ class nfvo_db(db_base.db_base): created_time += 0.00001 db_base._convert_bandwidth(dataiface, logger=self.logger) dataifacesDict[vm['name']][dataiface['name']] = {} - dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci'] + dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface.get('vpci') dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth'] dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface[ 'dedicated'] == "yes" else ( @@ -258,7 +258,7 @@ class nfvo_db(db_base.db_base): created_time += 0.00001 db_base._convert_bandwidth(dataiface, logger=self.logger) ifaceDict = {} - ifaceDict['vpci'] = dataiface['vpci'] + ifaceDict['vpci'] = dataiface.get('vpci') ifaceDict['bw'] = dataiface['bandwidth'] ifaceDict['model'] = "PF" if dataiface['dedicated'] == "yes" else \ ("VF" if dataiface['dedicated'] == "no" else "VFnotShared") -- 2.25.1 From 455612d1570ba8924c7170fa8f5d19729b080625 Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 30 May 2017 16:40:10 +0200 Subject: [PATCH 05/16] (bug 80) allow several dns_address for ip_profile Change-Id: Icded218d71a1fe193321b53ebb4fe522682b8225 Signed-off-by: tierno --- osm_ro/nfvo.py | 27 +++++++++++++++------------ osm_ro/openmano_schemas.py | 5 +++-- osm_ro/vimconn_openstack.py | 4 +--- osm_ro/vimconn_vmware.py | 21 +++++++++++++-------- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/osm_ro/nfvo.py b/osm_ro/nfvo.py index 93300472..d45a3c79 100644 --- a/osm_ro/nfvo.py +++ b/osm_ro/nfvo.py @@ -2041,20 +2041,23 @@ def create_instance(mydb, tenant_id, instance_dict): for scenario_net in scenarioDict['nets']: if net_name == scenario_net["name"]: if 'ip-profile' in net_instance_desc: - ipprofile = net_instance_desc['ip-profile'] - ipprofile['subnet_address'] = ipprofile.pop('subnet-address',None) - ipprofile['ip_version'] = ipprofile.pop('ip-version','IPv4') - ipprofile['gateway_address'] = ipprofile.pop('gateway-address',None) - ipprofile['dns_address'] = ipprofile.pop('dns-address',None) - if 'dhcp' in ipprofile: - ipprofile['dhcp_start_address'] = ipprofile['dhcp'].get('start-address',None) - ipprofile['dhcp_enabled'] = ipprofile['dhcp'].get('enabled',True) - ipprofile['dhcp_count'] = ipprofile['dhcp'].get('count',None) - del ipprofile['dhcp'] + # translate from input format to database format + ipprofile_in = net_instance_desc['ip-profile'] + ipprofile_db = {} + ipprofile_db['subnet_address'] = ipprofile_in.get('subnet-address') + ipprofile_db['ip_version'] = ipprofile_in.get('ip-version', 'IPv4') + ipprofile_db['gateway_address'] = ipprofile_in.get('gateway-address') + ipprofile_db['dns_address'] = ipprofile_in.get('dns-address') + if isinstance(ipprofile_db['dns_address'], (list, tuple)): + ipprofile_db['dns_address'] = ";".join(ipprofile_db['dns_address']) + if 'dhcp' in ipprofile_in: + ipprofile_db['dhcp_start_address'] = ipprofile_in['dhcp'].get('start-address') + ipprofile_db['dhcp_enabled'] = ipprofile_in['dhcp'].get('enabled', True) + ipprofile_db['dhcp_count'] = ipprofile_in['dhcp'].get('count' ) if 'ip_profile' not in scenario_net: - scenario_net['ip_profile'] = ipprofile + scenario_net['ip_profile'] = ipprofile_db else: - update(scenario_net['ip_profile'],ipprofile) + update(scenario_net['ip_profile'], ipprofile_db) for interface in net_instance_desc.get('interfaces', () ): if 'ip_address' in interface: for vnf in scenarioDict['vnfs']: diff --git a/osm_ro/openmano_schemas.py b/osm_ro/openmano_schemas.py index 765c05f0..7d218ec1 100644 --- a/osm_ro/openmano_schemas.py +++ b/osm_ro/openmano_schemas.py @@ -316,10 +316,11 @@ ip_profile_schema = { "$schema": "http://json-schema.org/draft-04/schema#", "type":"object", "properties":{ - "ip-version": {"type":"string", "enum":["IPv4","IPv6"]}, + "ip-version": {"type": "string", "enum": ["IPv4","IPv6"]}, "subnet-address": ip_prefix_schema, "gateway-address": ip_schema, - "dns-address": ip_schema, + "dns-address": {"oneOf": [ip_schema, # for backward compatibility + {"type": "array", "items": ip_schema}]}, "dhcp": dhcp_schema }, } diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index de94fff0..e88d18d7 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -322,9 +322,7 @@ class vimconnector(vimconn.vimconnector): if 'gateway_address' in ip_profile: subnet['gateway_ip'] = ip_profile['gateway_address'] if ip_profile.get('dns_address'): - #TODO: manage dns_address as a list of addresses separated by commas - subnet['dns_nameservers'] = [] - subnet['dns_nameservers'].append(ip_profile['dns_address']) + subnet['dns_nameservers'] = ip_profile['dns_address'].split(";") if 'dhcp_enabled' in ip_profile: subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True if 'dhcp_start_address' in ip_profile: diff --git a/osm_ro/vimconn_vmware.py b/osm_ro/vimconn_vmware.py index 910387d2..43ffba88 100644 --- a/osm_ro/vimconn_vmware.py +++ b/osm_ro/vimconn_vmware.py @@ -2751,6 +2751,11 @@ class vimconnector(vimconn.vimconnector): #Unused in case of Underlay (data/ptp) network interface. fence_mode="bridged" is_inherited='false' + dns_list = dns_address.split(";") + dns1 = dns_list[0] + dns2_text = "" + if len(dns_list) >= 2: + dns2_text = "\n {}\n".format(dns_list[1]) data = """ Openmano created @@ -2759,22 +2764,22 @@ class vimconnector(vimconn.vimconnector): {1:s} {2:s} {3:s} - {4:s} - {5:s} + {4:s}{5:s} + {6:s} - {6:s} - {7:s} + {7:s} + {8:s} - - {9:s} + + {10:s} - {10:s} + {11:s} """.format(escape(network_name), is_inherited, gateway_address, - subnet_address, dns_address, dhcp_enabled, + subnet_address, dns1, dns2_text, dhcp_enabled, dhcp_start_address, dhcp_end_address, available_networks, fence_mode, isshared) -- 2.25.1 From e26fc7a4106017149fd68b67e313d374c50c041b Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 30 May 2017 14:43:03 +0200 Subject: [PATCH 06/16] (bug 116) allow to use closer existing flavors at openstack Change-Id: If14c75b99e6cb8fac040c812a5eb6a15c9d45bd6 Signed-off-by: tierno --- osm_ro/vimconn_openstack.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index e88d18d7..3182dbe8 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -470,11 +470,19 @@ class vimconnector(vimconn.vimconnector): def get_flavor_id_from_data(self, flavor_dict): """Obtain flavor id that match the flavor description Returns the flavor_id or raises a vimconnNotFoundException + flavor_dict: contains the required ram, vcpus, disk + If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus + and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a + vimconnNotFoundException is raised """ + exact_match = False if self.config.get('use_existing_flavors') else True try: self._reload_connection() - numa=None - numas = flavor_dict.get("extended",{}).get("numas") + flavor_candidate_id = None + flavor_candidate_data = (10000, 10000, 10000) + flavor_target = (flavor_dict["ram"], flavor_dict["vcpus"], flavor_dict["disk"]) + # numa=None + numas = flavor_dict.get("extended", {}).get("numas") if numas: #TODO raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemted") @@ -486,14 +494,15 @@ class vimconnector(vimconn.vimconnector): epa = flavor.get_keys() if epa: continue - #TODO - if flavor.ram != flavor_dict["ram"]: - continue - if flavor.vcpus != flavor_dict["vcpus"]: - continue - if flavor.disk != flavor_dict["disk"]: - continue - return flavor.id + # TODO + flavor_data = (flavor.ram, flavor.vcpus, flavor.disk) + if flavor_data == flavor_target: + return flavor.id + elif not exact_match and flavor_target < flavor_data < flavor_candidate_data: + flavor_candidate_id = flavor.id + flavor_candidate_data = flavor_data + if not exact_match and flavor_candidate_id: + return flavor_candidate_id raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict))) except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e: self._format_exception(e) -- 2.25.1 From 518a866ae32d168a794276d85952632bb862d375 Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 30 May 2017 11:35:46 +0200 Subject: [PATCH 07/16] adding region=datacenter to sdn networks Change-Id: Ifc702841fff85c484dc7dabb76c5e2a620b495bf Signed-off-by: tierno --- osm_ro/vim_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osm_ro/vim_thread.py b/osm_ro/vim_thread.py index 67bef44d..9f396a26 100644 --- a/osm_ro/vim_thread.py +++ b/osm_ro/vim_thread.py @@ -412,7 +412,7 @@ class vim_thread(threading.Thread): sdn_net_id = None sdn_controller = self.vim.config.get('sdn-controller') if sdn_controller and (net_type == "data" or net_type == "ptp"): - network = {"name": net_name, "type": net_type} + network = {"name": net_name, "type": net_type, "region": self.vim["config"]["datacenter_id"]} vim_net = self.vim.get_network(net_id) if vim_net.get('encapsulation') != 'vlan': -- 2.25.1 From 714bc90493799217c485d2a77fc4262209aeec78 Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 6 Jun 2017 18:25:15 +0200 Subject: [PATCH 08/16] fixed issue at vimconn_openvim for extra disks Change-Id: Ie43fc8d6ad878b397f9c512ce67baad863d8c56f Signed-off-by: tierno --- osm_ro/vimconn_openvim.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osm_ro/vimconn_openvim.py b/osm_ro/vimconn_openvim.py index e86b54db..e722dc8d 100644 --- a/osm_ro/vimconn_openvim.py +++ b/osm_ro/vimconn_openvim.py @@ -596,6 +596,9 @@ class vimconnector(vimconn.vimconnector): '''Returns the flavor identifier''' try: new_flavor_dict = flavor_data.copy() + for device in new_flavor_dict.get('extended', {}).get('devices', ()): + if 'image name' in device: + del device['image name'] new_flavor_dict["name"] = flavor_data["name"][:64] self._get_my_tenant() payload_req = json.dumps({'flavor': new_flavor_dict}) -- 2.25.1 From f44e9e5d8b652fd2cdba4c0253c958271eea9a2c Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 13 Jun 2017 18:48:30 +0200 Subject: [PATCH 09/16] (bug 267) Fix flavor info provided as dict Change-Id: I1a388177c6fb0025679ec3bc72b9c3e6fd927810 Signed-off-by: tierno --- osm_ro/vimconn_aws.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osm_ro/vimconn_aws.py b/osm_ro/vimconn_aws.py index dbe9acb2..f0eebb66 100644 --- a/osm_ro/vimconn_aws.py +++ b/osm_ro/vimconn_aws.py @@ -117,7 +117,7 @@ class vimconnector(vimconn.vimconnector): except IOError as e: raise vimconn.vimconnException("Error reading file '{}': {}".format(flavor_data[1:], e)) elif isinstance(flavor_data, dict): - self.flavor_data = flavor_data + self.flavor_info = flavor_data self.logger = logging.getLogger('openmano.vim.aws') if log_level: -- 2.25.1 From b4c22392a4cd59e8218c3b071594423cb7efbda2 Mon Sep 17 00:00:00 2001 From: tierno Date: Wed, 14 Jun 2017 12:55:38 +0200 Subject: [PATCH 10/16] added python- openstackclient package at installation useful for debug Change-Id: I6443eeefe8beb961e20d465db99d6c2a78ed3a43 Signed-off-by: tierno --- Dockerfile | 2 +- requirements.txt | 1 + scripts/install-openmano.sh | 7 +++++-- setup.py | 1 + stdeb.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 84144a8c..82c3ca98 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,5 +6,5 @@ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-pip libmysqlclient-dev libssl-dev libffi-dev && \ DEBIAN_FRONTEND=noninteractive pip install --upgrade pip && \ DEBIAN_FRONTEND=noninteractive pip install --upgrade setuptools && \ - DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-boto python-bottle python-jsonschema python-logutils python-cinderclient python-glanceclient python-keystoneclient python-neutronclient python-novaclient python-mysqldb + DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-boto python-bottle python-jsonschema python-logutils python-cinderclient python-glanceclient python-keystoneclient python-neutronclient python-novaclient python-openstackclient python-mysqldb diff --git a/requirements.txt b/requirements.txt index eb924781..0d6c4324 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ paramiko argcomplete requests logutils +python-openstackclient python-novaclient python-keystoneclient python-glanceclient diff --git a/scripts/install-openmano.sh b/scripts/install-openmano.sh index 0323cb4b..624dce35 100755 --- a/scripts/install-openmano.sh +++ b/scripts/install-openmano.sh @@ -249,8 +249,11 @@ then [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-boto" #TODO check if at Centos it exists with this name, or PIP should be used # install openstack client needed for using openstack as a VIM - [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient" - [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient #TODO revise if gcc python-pip is needed + [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient "\ + "python-neutronclient python-cinderclient python-openstackclient" + [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install \ + python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient \ + python-openstackclient #TODO revise if gcc python-pip is needed fi # [[ -z "$NO_PACKAGES" ]] if [[ -z $NOCLONE ]]; then diff --git a/setup.py b/setup.py index 5f1382d9..303a6520 100755 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ _requirements = [ "argcomplete", "requests", "logutils", + "python-openstackclient", "python-novaclient", "python-keystoneclient", "python-glanceclient", diff --git a/stdeb.cfg b/stdeb.cfg index 7fc8a8e3..a0c20ec7 100644 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -2,5 +2,5 @@ Suite: xenial XS-Python-Version: >= 2.7 Maintainer: Gerardo Garcia -Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-novaclient, python-mysqldb +Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-novaclient, python-openstackclient, python-mysqldb -- 2.25.1 From f0508358d1724a1342ca16c098a01242106f1451 Mon Sep 17 00:00:00 2001 From: tierno Date: Mon, 19 Jun 2017 13:37:44 +0200 Subject: [PATCH 11/16] Added a base class at test_RO with common methods to avoid code duplication Change-Id: I563a12732e6637c204dccb6ff3d9d596e91ce55c Signed-off-by: tierno --- test/test_RO.py | 356 ++++++++++++++++++------------------------------ 1 file changed, 129 insertions(+), 227 deletions(-) diff --git a/test/test_RO.py b/test/test_RO.py index b1e24599..0d5e6243 100755 --- a/test/test_RO.py +++ b/test/test_RO.py @@ -23,10 +23,10 @@ ''' Module for testing openmano functionality. It uses openmanoclient.py for invoking openmano ''' -__author__="Pablo Montes, Alfonso Tierno" -__date__ ="$16-Feb-2017 17:08:16$" -__version__="0.0.3" -version_date="May 2017" +__author__ = "Pablo Montes, Alfonso Tierno" +__date__ = "$16-Feb-2017 17:08:16$" +__version__ = "0.0.4" +version_date = "Jun 2017" import logging import os @@ -47,6 +47,26 @@ import uuid global test_config # used for global variables with the test configuration test_config = {} +class test_base(unittest.TestCase): + test_index = 1 + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + + @classmethod + def tearDownClass(cls): + test_config["test_number"] += 1 + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text+" -> TEST OK") + else: + logger.warning(self.__class__.test_text+" -> TEST NOK") + logger.critical("Traceback error",exc_info=True) + def check_instance_scenario_active(uuid): instance = test_config["client"].get_instance(uuid=uuid) @@ -69,30 +89,8 @@ def check_instance_scenario_active(uuid): IMPORTANT NOTE All unittest classes for code based tests must have prefix 'test_' in order to be taken into account for tests ''' -class test_VIM_datacenter_tenant_operations(unittest.TestCase): - test_index = 1 +class test_VIM_datacenter_tenant_operations(test_base): tenant_name = None - test_text = None - - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 - - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) - msg = "" - for line in error_trace: - msg = msg + line - logger.critical("{}".format(msg)) def test_000_create_RO_tenant(self): self.__class__.tenant_name = _get_random_string(20) @@ -121,30 +119,8 @@ class test_VIM_datacenter_tenant_operations(unittest.TestCase): assert('deleted' in tenant.get('result',"")) -class test_VIM_datacenter_operations(unittest.TestCase): - test_index = 1 +class test_VIM_datacenter_operations(test_base): datacenter_name = None - test_text = None - - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 - - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) - msg = "" - for line in error_trace: - msg = msg + line - logger.critical("{}".format(msg)) def test_000_create_datacenter(self): self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index, @@ -203,32 +179,10 @@ class test_VIM_datacenter_operations(unittest.TestCase): assert('deleted' in self.datacenter.get('result',"")) -class test_VIM_network_operations(unittest.TestCase): - test_index = 1 +class test_VIM_network_operations(test_base): vim_network_name = None - test_text = None vim_network_uuid = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 - - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text + " -> TEST OK") - else: - logger.warning(self.__class__.test_text + " -> TEST NOK") - error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) - msg = "" - for line in error_trace: - msg = msg + line - logger.critical("{}".format(msg)) - def test_000_create_VIM_network(self): self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index, inspect.currentframe().f_code.co_name) @@ -265,29 +219,7 @@ class test_VIM_network_operations(unittest.TestCase): assert ('deleted' in network.get('result', "")) -class test_VIM_image_operations(unittest.TestCase): - test_index = 1 - test_text = None - - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 - - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text + " -> TEST OK") - else: - logger.warning(self.__class__.test_text + " -> TEST NOK") - error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) - msg = "" - for line in error_trace: - msg = msg + line - logger.critical("{}".format(msg)) +class test_VIM_image_operations(test_base): def test_000_list_VIM_images(self): self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index, @@ -301,34 +233,16 @@ The following is a non critical test that will fail most of the times. In case of OpenStack datacenter these tests will only success if RO has access to the admin endpoint This test will only be executed in case it is specifically requested by the user ''' -class test_VIM_tenant_operations(unittest.TestCase): - test_index = 1 +class test_VIM_tenant_operations(test_base): vim_tenant_name = None - test_text = None vim_tenant_uuid = None @classmethod def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + test_base.setUpClass(cls) logger.warning("In case of OpenStack datacenter these tests will only success " "if RO has access to the admin endpoint") - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 - - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text + " -> TEST OK") - else: - logger.warning(self.__class__.test_text + " -> TEST NOK") - error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) - msg = "" - for line in error_trace: - msg = msg + line - logger.critical("{}".format(msg)) - def test_000_create_VIM_tenant(self): self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index, inspect.currentframe().f_code.co_name) @@ -364,25 +278,25 @@ class test_VIM_tenant_operations(unittest.TestCase): logger.debug("{}".format(tenant)) assert ('deleted' in tenant.get('result', "")) -class test_vimconn_connect(unittest.TestCase): - test_index = 1 - test_text = None +class test_vimconn_connect(test_base): + # test_index = 1 + # test_text = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + # @classmethod + # def setUpClass(cls): + # logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 + # @classmethod + # def tearDownClass(cls): + # test_config["test_number"] += 1 - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - logger.critical("Traceback error",exc_info=True) + # def tearDown(self): + # exec_info = sys.exc_info() + # if exec_info == (None, None, None): + # logger.info(self.__class__.test_text+" -> TEST OK") + # else: + # logger.warning(self.__class__.test_text+" -> TEST NOK") + # logger.critical("Traceback error",exc_info=True) def test_000_connect(self): self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], @@ -396,26 +310,26 @@ class test_vimconn_connect(unittest.TestCase): self.assertIsInstance(vca_object, VCA) -class test_vimconn_new_network(unittest.TestCase): - test_index = 1 +class test_vimconn_new_network(test_base): + # test_index = 1 network_name = None - test_text = None + # test_text = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + # @classmethod + # def setUpClass(cls): + # logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 + # @classmethod + # def tearDownClass(cls): + # test_config["test_number"] += 1 - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - logger.critical("Traceback error",exc_info=True) + # def tearDown(self): + # exec_info = sys.exc_info() + # if exec_info == (None, None, None): + # logger.info(self.__class__.test_text+" -> TEST OK") + # else: + # logger.warning(self.__class__.test_text+" -> TEST NOK") + # logger.critical("Traceback error",exc_info=True) def test_000_new_network(self): self.__class__.network_name = _get_random_string(20) @@ -570,18 +484,18 @@ class test_vimconn_new_network(unittest.TestCase): else: logger.info("Failed to delete network id {}".format(self.__class__.network_id)) -class test_vimconn_get_network_list(unittest.TestCase): - test_index = 1 +class test_vimconn_get_network_list(test_base): + # test_index = 1 network_name = None - test_text = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + # test_text = None + # @classmethod + # def setUpClass(cls): + # logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 + # @classmethod + # def tearDownClass(cls): + # test_config["test_number"] += 1 def setUp(self): # creating new network @@ -593,12 +507,13 @@ class test_vimconn_get_network_list(unittest.TestCase): logger.debug("{}".format(network)) def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - logger.critical("Traceback error",exc_info=True) + test_base.tearDown(self) + # exec_info = sys.exc_info() + # if exec_info == (None, None, None): + # logger.info(self.__class__.test_text+" -> TEST OK") + # else: + # logger.warning(self.__class__.test_text+" -> TEST NOK") + # logger.critical("Traceback error",exc_info=True) # Deleting created network result = test_config["vim_conn"].delete_network(self.__class__.network_id) @@ -715,18 +630,18 @@ class test_vimconn_get_network_list(unittest.TestCase): network_list = test_config["vim_conn"].get_network_list({'name': 'unknown_name'}) self.assertEqual(network_list, []) -class test_vimconn_get_network(unittest.TestCase): - test_index = 1 +class test_vimconn_get_network(test_base): + # test_index = 1 network_name = None - test_text = None + # test_text = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + # @classmethod + # def setUpClass(cls): + # logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 + # @classmethod + # def tearDownClass(cls): + # test_config["test_number"] += 1 def setUp(self): # creating new network @@ -738,12 +653,13 @@ class test_vimconn_get_network(unittest.TestCase): logger.debug("{}".format(network)) def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - logger.critical("Traceback error",exc_info=True) + test_base.tearDown(self) + # exec_info = sys.exc_info() + # if exec_info == (None, None, None): + # logger.info(self.__class__.test_text+" -> TEST OK") + # else: + # logger.warning(self.__class__.test_text+" -> TEST NOK") + # logger.critical("Traceback error",exc_info=True) # Deleting created network result = test_config["vim_conn"].delete_network(self.__class__.network_id) @@ -774,26 +690,26 @@ class test_vimconn_get_network(unittest.TestCase): network_info = test_config["vim_conn"].get_network(Non_exist_id) self.assertEqual(network_info, {}) -class test_vimconn_delete_network(unittest.TestCase): - test_index = 1 +class test_vimconn_delete_network(test_base): + # test_index = 1 network_name = None - test_text = None + # test_text = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + # @classmethod + # def setUpClass(cls): + # logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 + # @classmethod + # def tearDownClass(cls): + # test_config["test_number"] += 1 - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - logger.critical("Traceback error",exc_info=True) + # def tearDown(self): + # exec_info = sys.exc_info() + # if exec_info == (None, None, None): + # logger.info(self.__class__.test_text+" -> TEST OK") + # else: + # logger.warning(self.__class__.test_text+" -> TEST NOK") + # logger.critical("Traceback error",exc_info=True) def test_000_delete_network(self): # Creating network @@ -832,25 +748,25 @@ class test_vimconn_delete_network(unittest.TestCase): self.assertEqual((context.exception).http_code, 400) -class test_vimconn_get_flavor(unittest.TestCase): - test_index = 1 - test_text = None +class test_vimconn_get_flavor(test_base): + # test_index = 1 + # test_text = None - @classmethod - def setUpClass(cls): - logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) + # @classmethod + # def setUpClass(cls): + # logger.info("{}. {}".format(test_config["test_number"], cls.__name__)) - @classmethod - def tearDownClass(cls): - test_config["test_number"] += 1 + # @classmethod + # def tearDownClass(cls): + # test_config["test_number"] += 1 - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text+" -> TEST OK") - else: - logger.warning(self.__class__.test_text+" -> TEST NOK") - logger.critical("Traceback error",exc_info=True) + # def tearDown(self): + # exec_info = sys.exc_info() + # if exec_info == (None, None, None): + # logger.info(self.__class__.test_text+" -> TEST OK") + # else: + # logger.warning(self.__class__.test_text+" -> TEST NOK") + # logger.critical("Traceback error",exc_info=True) def test_000_get_flavor(self): test_directory_content = os.listdir(test_config["test_directory"]) @@ -915,9 +831,8 @@ IMPORTANT NOTE The following unittest class does not have the 'test_' on purpose. This test is the one used for the scenario based tests. ''' -class descriptor_based_scenario_test(unittest.TestCase): +class descriptor_based_scenario_test(test_base): test_index = 0 - test_text = None scenario_test_path = None scenario_uuid = None instance_scenario_uuid = None @@ -934,19 +849,6 @@ class descriptor_based_scenario_test(unittest.TestCase): def tearDownClass(cls): test_config["test_number"] += 1 - def tearDown(self): - exec_info = sys.exc_info() - if exec_info == (None, None, None): - logger.info(self.__class__.test_text + " -> TEST OK") - else: - logger.warning(self.__class__.test_text + " -> TEST NOK") - error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) - msg = "" - for line in error_trace: - msg = msg + line - logger.critical("{}".format(msg)) - - def test_000_load_scenario(self): self.__class__.test_text = "{}.{}. TEST {} {}".format(test_config["test_number"], self.__class__.test_index, inspect.currentframe().f_code.co_name, @@ -1223,9 +1125,9 @@ def test_deploy(args): test_directory_content = os.listdir(test_config["test_directory"]) # If only want to obtain a tests list print it and exit if args.list_tests: - msg = "he 'deploy' set tests are:\n\t" + ', '.join(sorted(test_directory_content)) + msg = "the 'deploy' set tests are:\n\t" + ', '.join(sorted(test_directory_content)) print(msg) - logger.info(msg) + # logger.info(msg) sys.exit(0) descriptor_based_tests = [] -- 2.25.1 From d1da1d6da1f25b2a01876b88cbc928f93e43f934 Mon Sep 17 00:00:00 2001 From: Mike Marchetti Date: Tue, 20 Jun 2017 20:33:20 -0400 Subject: [PATCH 12/16] remove specific node label Change-Id: I96f9c60c4b06179270cbb820dc67a2c37188a3ae Signed-off-by: Mike Marchetti --- Jenkinsfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0964de55..13a85d03 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,5 @@ pipeline { - agent { - label "pipeline" - } + agent any stages { stage("Build") { agent { -- 2.25.1 From b7490b5bd885a1b2f5d872d7e2c4cd0c126e94e8 Mon Sep 17 00:00:00 2001 From: Pablo Montes Moreno Date: Tue, 20 Jun 2017 10:49:58 +0200 Subject: [PATCH 13/16] minor change to increase log information Change-Id: Icbb252efd959f7accfd44401c8db7fa24f4981d8 Signed-off-by: Pablo Montes Moreno --- osm_ro/vimconn_openstack.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index 3182dbe8..195d7bbc 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -892,6 +892,11 @@ class vimconnector(vimconn.vimconnector): raise vimconn.vimconnException('Timeout creating volumes for instance ' + name, http_code=vimconn.HTTP_Request_Timeout) + self.logger.debug("nova.servers.create({}, {}, {}, nics={}, meta={}, security_groups={}," \ + "availability_zone={}, key_name={}, userdata={}, config_drive={}, " \ + "block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim, + metadata, security_groups, self.config.get('availability_zone'), + self.config.get('keypair'), userdata, config_drive, block_device_mapping)) server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim, meta=metadata, security_groups=security_groups, availability_zone=self.config.get('availability_zone'), -- 2.25.1 From feaaa05aa3e18970bb1eebde4a80ffca9ca6c9a2 Mon Sep 17 00:00:00 2001 From: kasar Date: Thu, 8 Jun 2017 03:46:18 -0700 Subject: [PATCH 14/16] Added validation in new_flavor method and unit tests for new_flavor,delete_flavor,new_image Change-Id: I5747f07f287ad2b4370b3cd586cf0adcf5b9888a Signed-off-by: kasar --- osm_ro/vimconn_vmware.py | 7 ++++ test/test_RO.py | 86 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/osm_ro/vimconn_vmware.py b/osm_ro/vimconn_vmware.py index 43ffba88..f9ff2fef 100644 --- a/osm_ro/vimconn_vmware.py +++ b/osm_ro/vimconn_vmware.py @@ -754,6 +754,13 @@ class vimconnector(vimconn.vimconnector): cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1) disk = flavor_data.get(FLAVOR_DISK_KEY, 1) + if not isinstance(ram, int): + raise vimconn.vimconnException("Non-integer value for ram") + elif not isinstance(cpu, int): + raise vimconn.vimconnException("Non-integer value for cpu") + elif not isinstance(disk, int): + raise vimconn.vimconnException("Non-integer value for disk") + extended_flv = flavor_data.get("extended") if extended_flv: numas=extended_flv.get("numas") diff --git a/test/test_RO.py b/test/test_RO.py index 0d5e6243..c46894e1 100755 --- a/test/test_RO.py +++ b/test/test_RO.py @@ -825,6 +825,90 @@ class test_vimconn_get_flavor(test_base): self.assertEqual((context.exception).http_code, 404) +class test_vimconn_new_flavor(test_base): + flavor_id = None + + def test_000_new_flavor(self): + flavor_data = {'ram': 1024, 'vpcus': 1, 'disk': 10} + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + self.assertEqual(type(flavor_id),str) + self.assertIsInstance(uuid.UUID(flavor_id),uuid.UUID) + + def test_010_delete_flavor(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + # delete flavor + result = test_config["vim_conn"].delete_flavor(self.__class__.flavor_id) + if result: + logger.info("Flavor id {} sucessfully deleted".format(result)) + else: + logger.error("Failed to delete flavor id {}".format(result)) + raise Exception ("Failed to delete created flavor") + + def test_020_new_flavor_negative(self): + Invalid_flavor_data = {'ram': '1024', 'vcpus': 2.0, 'disk': 2.0} + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + with self.assertRaises(Exception) as context: + test_config["vim_conn"].new_flavor(Invalid_flavor_data) + + self.assertEqual((context.exception).http_code, 400) + + def test_030_delete_flavor_negative(self): + Non_exist_flavor_id = str(uuid.uuid4()) + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + with self.assertRaises(Exception) as context: + test_config["vim_conn"].delete_flavor(Non_exist_flavor_id) + + self.assertEqual((context.exception).http_code, 404) + +class test_vimconn_new_image(test_base): + + def test_000_new_image(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + image_path = test_config['image_path'] + if image_path: + image_id = test_config["vim_conn"].new_image({ 'name': 'TestImage', 'location' : image_path }) + self.assertEqual(type(image_id),str) + self.assertIsInstance(uuid.UUID(image_id),uuid.UUID) + else: + self.skipTest("Skipping test as image file not present at RO container") + + def test_010_new_image_negative(self): + Non_exist_image_path = '/temp1/cirros.ovf' + + self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], + self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + + with self.assertRaises(Exception) as context: + test_config["vim_conn"].new_image({ 'name': 'TestImage', 'location' : Non_exist_image_path }) + + self.assertEqual((context.exception).http_code, 400) ''' IMPORTANT NOTE @@ -978,6 +1062,7 @@ def test_vimconnector(args): org_user = config_params.get('user') org_passwd = config_params.get('passwd') vim_url = args.endpoint_url + test_config['image_path'] = args.image_path # vmware connector obj test_config['vim_conn'] = vim.vimconnector(name=org_name, tenant_name=tenant_name, user=org_user,passwd=org_passwd, url=vim_url, config=config_params) @@ -1233,6 +1318,7 @@ if __name__=="__main__": help='Set the vimconnector specific config parameters in dictionary format') mandatory_arguments.add_argument('-u', '--url', dest='endpoint_url',required=True, help="Set the vim connector url or Host IP") # Optional arguments + vimconn_parser.add_argument('-i', '--image-path', dest='image_path', help="Provide image path present at RO container") # TODO add optional arguments for vimconn tests # vimconn_parser.add_argument("-i", '--image-name', dest='image_name', help='')) -- 2.25.1 From b5cef37dbecf2164bd39afa3f4e7161b78ba8ef0 Mon Sep 17 00:00:00 2001 From: tierno Date: Mon, 19 Jun 2017 15:52:22 +0200 Subject: [PATCH 15/16] openstack client for keystone v3. Maintain sessions upon class reload with persistent_info Change-Id: I8528b89d233a6417a840bc339816e618588851f2 Signed-off-by: tierno --- osm_ro/vimconn_openstack.py | 172 +++++++++++------------------------- 1 file changed, 50 insertions(+), 122 deletions(-) diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index 195d7bbc..bdc0bb58 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -36,19 +36,16 @@ import time import yaml import random -from novaclient import client as nClient_v2, exceptions as nvExceptions -from novaclient import api_versions -import keystoneclient.v2_0.client as ksClient_v2 -from novaclient.v2.client import Client as nClient -import keystoneclient.v3.client as ksClient +from novaclient import client as nClient, exceptions as nvExceptions +from keystoneauth1.identity import v2, v3 +from keystoneauth1 import session import keystoneclient.exceptions as ksExceptions -import glanceclient.v2.client as glClient +from glanceclient import client as glClient import glanceclient.client as gl1Client import glanceclient.exc as gl1Exceptions -import cinderclient.v2.client as cClient_v2 +from cinderclient import client as cClient from httplib import HTTPException -from neutronclient.neutron import client as neClient_v2 -from neutronclient.v2_0 import client as neClient +from neutronclient.neutron import client as neClient from neutronclient.common import exceptions as neExceptions from requests.exceptions import ConnectionError @@ -74,137 +71,67 @@ class vimconnector(vimconn.vimconnector): 'url' is the keystone authorization url, 'url_admin' is not use ''' - self.osc_api_version = 'v2.0' - if config.get('APIversion') == 'v3.3': - self.osc_api_version = 'v3.3' - vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config) + self.osc_api_version = config.get('APIversion') + if self.osc_api_version != 'v3.3' and self.osc_api_version != 'v2.0' and self.osc_api_version: + raise vimconn.vimconnException("Invalid value '{}' for config:APIversion. " + "Allowed values are 'v3.3' or 'v2.0'".format(self.osc_api_version)) + vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, + config) - self.persistent_info = persistent_info - self.k_creds={} - self.n_creds={} - if self.config.get("insecure"): - self.k_creds["insecure"] = True - self.n_creds["insecure"] = True + self.insecure = self.config.get("insecure", False) if not url: raise TypeError, 'url param can not be NoneType' - self.k_creds['auth_url'] = url - self.n_creds['auth_url'] = url - if tenant_name: - self.k_creds['tenant_name'] = tenant_name - self.n_creds['project_id'] = tenant_name - if tenant_id: - self.k_creds['tenant_id'] = tenant_id - self.n_creds['tenant_id'] = tenant_id - if user: - self.k_creds['username'] = user - self.n_creds['username'] = user - if passwd: - self.k_creds['password'] = passwd - self.n_creds['api_key'] = passwd - if self.osc_api_version == 'v3.3': - self.k_creds['project_name'] = tenant_name - self.k_creds['project_id'] = tenant_id - if config.get('region_name'): - self.k_creds['region_name'] = config.get('region_name') - self.n_creds['region_name'] = config.get('region_name') + self.auth_url = url + self.tenant_name = tenant_name + self.tenant_id = tenant_id + self.user = user + self.passwd = passwd + self.persistent_info = persistent_info + self.session = persistent_info.get('session', {'reload_client': True}) + self.nova = self.session.get('nova') + self.neutron = self.session.get('neutron') + self.cinder = self.session.get('cinder') + self.glance = self.session.get('glance') - self.reload_client = True self.logger = logging.getLogger('openmano.vim.openstack') if log_level: self.logger.setLevel( getattr(logging, log_level) ) def __setitem__(self,index, value): - '''Set individuals parameters + '''Set individuals parameters Throw TypeError, KeyError ''' - if index=='tenant_id': - self.reload_client=True - self.tenant_id = value - if self.osc_api_version == 'v3.3': - if value: - self.k_creds['project_id'] = value - self.n_creds['project_id'] = value - else: - del self.k_creds['project_id'] - del self.n_creds['project_id'] - else: - if value: - self.k_creds['tenant_id'] = value - self.n_creds['tenant_id'] = value - else: - del self.k_creds['tenant_id'] - del self.n_creds['tenant_id'] - elif index=='tenant_name': - self.reload_client=True - self.tenant_name = value - if self.osc_api_version == 'v3.3': - if value: - self.k_creds['project_name'] = value - self.n_creds['project_name'] = value - else: - del self.k_creds['project_name'] - del self.n_creds['project_name'] - else: - if value: - self.k_creds['tenant_name'] = value - self.n_creds['project_id'] = value - else: - del self.k_creds['tenant_name'] - del self.n_creds['project_id'] - elif index=='user': - self.reload_client=True - self.user = value - if value: - self.k_creds['username'] = value - self.n_creds['username'] = value - else: - del self.k_creds['username'] - del self.n_creds['username'] - elif index=='passwd': - self.reload_client=True - self.passwd = value - if value: - self.k_creds['password'] = value - self.n_creds['api_key'] = value - else: - del self.k_creds['password'] - del self.n_creds['api_key'] - elif index=='url': - self.reload_client=True - self.url = value - if value: - self.k_creds['auth_url'] = value - self.n_creds['auth_url'] = value - else: - raise TypeError, 'url param can not be NoneType' - else: - vimconn.vimconnector.__setitem__(self,index, value) + self.session['reload_client'] = True + vimconn.vimconnector.__setitem__(self,index, value) def _reload_connection(self): '''Called before any operation, it check if credentials has changed Throw keystoneclient.apiclient.exceptions.AuthorizationFailure ''' #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-) - if self.reload_client: - #test valid params - if len(self.n_creds) <4: - raise ksExceptions.ClientException("Not enough parameters to connect to openstack") - if self.osc_api_version == 'v3.3': - self.nova = nClient(api_version=api_versions.APIVersion(version_str='2.0'), **self.n_creds) - #TODO To be updated for v3 - #self.cinder = cClient.Client(**self.n_creds) - self.keystone = ksClient.Client(**self.k_creds) - self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL') - self.neutron = neClient.Client(api_version=api_versions.APIVersion(version_str='2.0'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds) + if self.session['reload_client']: + if self.osc_api_version == 'v3.3' or self.osc_api_version == '3' or \ + (not self.osc_api_version and self.auth_url.split("/")[-1] == "v3"): + auth = v3.Password(auth_url=self.auth_url, + username=self.user, + password=self.passwd, + project_name=self.tenant_name, + project_id=self.tenant_id, + project_domain_id=self.config.get('project_domain_id', 'default'), + user_domain_id=self.config.get('user_domain_id', 'default')) else: - self.nova = nClient_v2.Client(version='2', **self.n_creds) - self.cinder = cClient_v2.Client(**self.n_creds) - self.keystone = ksClient_v2.Client(**self.k_creds) - self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL') - self.neutron = neClient_v2.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds) - self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL') - self.glance = glClient.Client(self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds - self.reload_client = False + auth = v2.Password(auth_url=self.auth_url, + username=self.user, + password=self.passwd, + tenant_name=self.tenant_name, + tenant_id=self.tenant_id) + sess = session.Session(auth=auth, verify=not self.insecure) + self.nova = self.session['nova'] = nClient.Client("2.1", session=sess) + self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess) + self.cinder = self.session['cinder'] = cClient.Client(2, session=sess) + self.glance = self.session['glance'] = glClient.Client(2, session=sess) + self.session['reload_client'] = False + self.persistent_info['session'] = self.session def __net_os2mano(self, net_list_dict): '''Transform the net openstack format to mano format @@ -611,6 +538,7 @@ class vimconnector(vimconn.vimconnector): metadata: metadata of the image Returns the image_id ''' + # ALF TODO: revise and change for the new method or session #using version 1 of glance client glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds retry=0 -- 2.25.1 From f716aeaa85e65b5d23567d3dea6455bac2377e26 Mon Sep 17 00:00:00 2001 From: tierno Date: Wed, 21 Jun 2017 18:01:40 +0200 Subject: [PATCH 16/16] vimconn_openstack v3 fix tenant managing Change-Id: Iad590e1722ee9cb4e1cff01a9a4e2182ff199022 Signed-off-by: tierno --- osm_ro/vimconn_openstack.py | 85 ++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index bdc0bb58..0c7d91cd 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -40,6 +40,8 @@ from novaclient import client as nClient, exceptions as nvExceptions from keystoneauth1.identity import v2, v3 from keystoneauth1 import session import keystoneclient.exceptions as ksExceptions +import keystoneclient.v3.client as ksClient_v3 +import keystoneclient.v2_0.client as ksClient_v2 from glanceclient import client as glClient import glanceclient.client as gl1Client import glanceclient.exc as gl1Exceptions @@ -71,48 +73,63 @@ class vimconnector(vimconn.vimconnector): 'url' is the keystone authorization url, 'url_admin' is not use ''' - self.osc_api_version = config.get('APIversion') - if self.osc_api_version != 'v3.3' and self.osc_api_version != 'v2.0' and self.osc_api_version: + api_version = config.get('APIversion') + if api_version and api_version not in ('v3.3', 'v2.0', '2', '3'): raise vimconn.vimconnException("Invalid value '{}' for config:APIversion. " - "Allowed values are 'v3.3' or 'v2.0'".format(self.osc_api_version)) + "Allowed values are 'v3.3', 'v2.0', '2' or '3'".format(api_version)) vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config) self.insecure = self.config.get("insecure", False) if not url: raise TypeError, 'url param can not be NoneType' - self.auth_url = url - self.tenant_name = tenant_name - self.tenant_id = tenant_id - self.user = user - self.passwd = passwd self.persistent_info = persistent_info self.session = persistent_info.get('session', {'reload_client': True}) self.nova = self.session.get('nova') self.neutron = self.session.get('neutron') self.cinder = self.session.get('cinder') self.glance = self.session.get('glance') + self.keystone = self.session.get('keystone') + self.api_version3 = self.session.get('api_version3') self.logger = logging.getLogger('openmano.vim.openstack') if log_level: self.logger.setLevel( getattr(logging, log_level) ) - - def __setitem__(self,index, value): - '''Set individuals parameters - Throw TypeError, KeyError - ''' + + def __getitem__(self, index): + """Get individuals parameters. + Throw KeyError""" + if index == 'project_domain_id': + return self.config.get("project_domain_id") + elif index == 'user_domain_id': + return self.config.get("user_domain_id") + else: + vimconn.vimconnector.__getitem__(self, index) + + def __setitem__(self, index, value): + """Set individuals parameters and it is marked as dirty so to force connection reload. + Throw KeyError""" + if index == 'project_domain_id': + self.config["project_domain_id"] = value + elif index == 'user_domain_id': + self.config["user_domain_id"] = value + else: + vimconn.vimconnector.__setitem__(self, index, value) self.session['reload_client'] = True - vimconn.vimconnector.__setitem__(self,index, value) - + def _reload_connection(self): '''Called before any operation, it check if credentials has changed Throw keystoneclient.apiclient.exceptions.AuthorizationFailure ''' #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-) if self.session['reload_client']: - if self.osc_api_version == 'v3.3' or self.osc_api_version == '3' or \ - (not self.osc_api_version and self.auth_url.split("/")[-1] == "v3"): - auth = v3.Password(auth_url=self.auth_url, + if self.config.get('APIversion'): + self.api_version3 = self.config['APIversion'] == 'v3.3' or self.config['APIversion'] == '3' + else: # get from ending auth_url that end with v3 or with v2.0 + self.api_version3 = self.url.split("/")[-1] == "v3" + self.session['api_version3'] = self.api_version3 + if self.api_version3: + auth = v3.Password(auth_url=self.url, username=self.user, password=self.passwd, project_name=self.tenant_name, @@ -120,12 +137,17 @@ class vimconnector(vimconn.vimconnector): project_domain_id=self.config.get('project_domain_id', 'default'), user_domain_id=self.config.get('user_domain_id', 'default')) else: - auth = v2.Password(auth_url=self.auth_url, + auth = v2.Password(auth_url=self.url, username=self.user, password=self.passwd, tenant_name=self.tenant_name, tenant_id=self.tenant_id) sess = session.Session(auth=auth, verify=not self.insecure) + if self.api_version3: + self.keystone = ksClient_v3.Client(session=sess) + else: + self.keystone = ksClient_v2.Client(session=sess) + self.session['keystone'] = self.keystone self.nova = self.session['nova'] = nClient.Client("2.1", session=sess) self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess) self.cinder = self.session['cinder'] = cClient.Client(2, session=sess) @@ -163,7 +185,7 @@ class vimconnector(vimconn.vimconnector): raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception)) elif isinstance(exception, nvExceptions.Conflict): raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception)) - else: # () + else: # () raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) def get_tenant_list(self, filter_dict={}): @@ -177,15 +199,17 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict)) try: self._reload_connection() - if self.osc_api_version == 'v3.3': - project_class_list=self.keystone.projects.findall(**filter_dict) + if self.api_version3: + project_class_list = self.keystone.projects.list(name=filter_dict.get("name")) else: - project_class_list=self.keystone.tenants.findall(**filter_dict) + project_class_list = self.keystone.tenants.findall(**filter_dict) project_list=[] for project in project_class_list: + if filter_dict.get('id') and filter_dict["id"] != project.id: + continue project_list.append(project.to_dict()) return project_list - except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e: + except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e: self._format_exception(e) def new_tenant(self, tenant_name, tenant_description): @@ -193,10 +217,11 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("Adding a new tenant name: %s", tenant_name) try: self._reload_connection() - if self.osc_api_version == 'v3.3': - project=self.keystone.projects.create(tenant_name, tenant_description) + if self.api_version3: + project = self.keystone.projects.create(tenant_name, self.config.get("project_domain_id", "default"), + description=tenant_description, is_domain=False) else: - project=self.keystone.tenants.create(tenant_name, tenant_description) + project = self.keystone.tenants.create(tenant_name, tenant_description) return project.id except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e: self._format_exception(e) @@ -206,7 +231,7 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("Deleting tenant %s from VIM", tenant_id) try: self._reload_connection() - if self.osc_api_version == 'v3.3': + if self.api_version3: self.keystone.projects.delete(tenant_id) else: self.keystone.tenants.delete(tenant_id) @@ -285,8 +310,8 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict)) try: self._reload_connection() - if self.osc_api_version == 'v3.3' and "tenant_id" in filter_dict: - filter_dict['project_id'] = filter_dict.pop('tenant_id') + if self.api_version3 and "tenant_id" in filter_dict: + filter_dict['project_id'] = filter_dict.pop('tenant_id') #TODO check net_dict=self.neutron.list_networks(**filter_dict) net_list=net_dict["networks"] self.__net_os2mano(net_list) -- 2.25.1