From: tierno Date: Fri, 14 Jul 2017 11:25:24 +0000 (+0200) Subject: Merge branch 'v2.0' tag 2.0.2 X-Git-Tag: v3.0.0~20 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=17ab9d223b90f867b41e9423f21915fbe336f9db;hp=45483bf232a8ad3d7d5106f688adb2b6f54309f6;p=osm%2FRO.git Merge branch 'v2.0' tag 2.0.2 Change-Id: I9f418d8247af586a2de3d3cf736d26950d74ccf0 Signed-off-by: tierno --- diff --git a/Dockerfile b/Dockerfile index 82c3ca98..254027cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:16.04 RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get -y install git build-essential && \ + DEBIAN_FRONTEND=noninteractive apt-get -y install git build-essential apt-utils && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python python-dev python-all python-stdeb fakeroot pypi2deb && \ DEBIAN_FRONTEND=noninteractive apt-get -y install python-pip libmysqlclient-dev libssl-dev libffi-dev && \ DEBIAN_FRONTEND=noninteractive pip install --upgrade pip && \ diff --git a/Jenkinsfile b/Jenkinsfile index 13a85d03..bc6c2d0f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,36 +1,30 @@ -pipeline { - agent any - stages { - stage("Build") { - agent { - dockerfile true - } - steps { - sh 'make package' - stash name: "deb-files", includes: ".build/*.deb" - } - } - stage("Unittest") { - agent { - dockerfile true - } - steps { - sh 'echo "UNITTEST"' - } - } - stage("Repo Component") { - agent any - steps { - unstash "deb-files" - sh ''' - mkdir -p pool/RO - mv .build/*.deb pool/RO/ - mkdir -p dists/ReleaseOne/unstable/RO/binary-amd64/ - apt-ftparchive packages pool/RO > dists/ReleaseOne/unstable/RO/binary-amd64/Packages - gzip -9fk dists/ReleaseOne/unstable/RO/binary-amd64/Packages - ''' - archiveArtifacts artifacts: "dists/**,pool/RO/*.deb" - } - } - } +properties([ + parameters([ + string(defaultValue: env.BRANCH_NAME, description: '', name: 'GERRIT_BRANCH'), + string(defaultValue: 'osm/RO', description: '', name: 'GERRIT_PROJECT'), + string(defaultValue: env.GERRIT_REFSPEC, description: '', name: 'GERRIT_REFSPEC'), + string(defaultValue: env.GERRIT_PATCHSET_REVISION, description: '', name: 'GERRIT_PATCHSET_REVISION'), + string(defaultValue: 'https://osm.etsi.org/gerrit', description: '', name: 'PROJECT_URL_PREFIX'), + booleanParam(defaultValue: false, description: '', name: 'TEST_INSTALL'), + ]) +]) + +def devops_checkout() { + dir('devops') { + git url: "${PROJECT_URL_PREFIX}/osm/devops", branch: params.GERRIT_BRANCH + } +} + +node { + checkout scm + devops_checkout() + + ci_helper = load "devops/jenkins/ci-pipelines/ci_stage_2.groovy" + ci_helper.ci_pipeline( 'RO', + params.PROJECT_URL_PREFIX, + params.GERRIT_PROJECT, + params.GERRIT_BRANCH, + params.GERRIT_REFSPEC, + params.GERRIT_PATCHSET_REVISION, + params.TEST_INSTALL) } diff --git a/database_utils/migrate_mano_db.sh b/database_utils/migrate_mano_db.sh index d57194e0..52d8cb1a 100755 --- a/database_utils/migrate_mano_db.sh +++ b/database_utils/migrate_mano_db.sh @@ -27,13 +27,13 @@ DBUSER="mano" DBPASS="" -DEFAULT_DBPASS="maopw" +DEFAULT_DBPASS="manopw" DBHOST="" DBPORT="3306" DBNAME="mano_db" QUIET_MODE="" #TODO update it with the last database version -LAST_DB_VERSION=21 +LAST_DB_VERSION=22 # Detect paths MYSQL=$(which mysql) @@ -189,6 +189,7 @@ fi #[ $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 +#[ $OPENMANO_VER_NUM -ge 5016 ] && DB_VERSION=22 #0.5.16 => 22 #TODO ... put next versions here function upgrade_to_1(){ @@ -772,6 +773,19 @@ function downgrade_from_21(){ echo "DELETE FROM schema_version WHERE version_int='21';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 } +function upgrade_to_22(){ + # echo " upgrade database from version 0.21 to version 0.22" + echo " Changed type of ram in 'flavors' from SMALLINT to MEDIUMINT" + echo "ALTER TABLE flavors CHANGE COLUMN ram ram MEDIUMINT(7) UNSIGNED NULL DEFAULT NULL AFTER disk;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo "INSERT INTO schema_version (version_int, version, openmano_ver, comments, date) VALUES (22, '0.22', '0.5.16', 'Changed type of ram in flavors from SMALLINT to MEDIUMINT', '2017-06-02');" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 +} +function downgrade_from_22(){ + # echo " downgrade database from version 0.22 to version 0.21" + echo " Changed type of ram in 'flavors' from MEDIUMINT to SMALLINT" + echo "ALTER TABLE flavors CHANGE COLUMN ram ram SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER disk;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1 + echo "DELETE FROM schema_version WHERE version_int='22';" | $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/devops-stages/stage-archive.sh b/devops-stages/stage-archive.sh new file mode 100755 index 00000000..cc1cfc05 --- /dev/null +++ b/devops-stages/stage-archive.sh @@ -0,0 +1,8 @@ +#!/bin/sh +rm -rf pool +rm -rf dists +mkdir -p pool/RO +mv .build/*.deb pool/RO/ +mkdir -p dists/unstable/RO/binary-amd64/ +apt-ftparchive packages pool/RO > dists/unstable/RO/binary-amd64/Packages +gzip -9fk dists/unstable/RO/binary-amd64/Packages diff --git a/devops-stages/stage-build.sh b/devops-stages/stage-build.sh new file mode 100755 index 00000000..85054995 --- /dev/null +++ b/devops-stages/stage-build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +make package diff --git a/devops-stages/stage-test.sh b/devops-stages/stage-test.sh new file mode 100755 index 00000000..49296c71 --- /dev/null +++ b/devops-stages/stage-test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo "UNITTEST" diff --git a/openmano b/openmano index 6c0d15b2..ec588a98 100755 --- a/openmano +++ b/openmano @@ -23,13 +23,13 @@ # contact with: nfvlabs@tid.es ## -''' +""" openmano client used to interact with openmano-server (openmanod) -''' +""" __author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes" __date__ = "$09-oct-2014 09:09:48$" -__version__ = "0.4.14-r521" -version_date = "May 2017" +__version__ = "0.4.15-r525" +version_date = "Jul 2017" from argcomplete.completers import FilesCompleter import os @@ -1033,6 +1033,7 @@ def datacenter_delete(args): print content['error']['description'] return result + def datacenter_list(args): #print "datacenter-list",args tenant='any' if args.all else _get_tenant() @@ -1050,6 +1051,7 @@ def datacenter_list(args): args.verbose += 1 return _print_verbose(mano_response, args.verbose) + def datacenter_sdn_port_mapping_set(args): tenant = _get_tenant() datacenter = _get_datacenter(args.name, tenant) @@ -1058,30 +1060,37 @@ def datacenter_sdn_port_mapping_set(args): if not args.file: raise OpenmanoCLIError( "No yaml/json has been provided specifying the SDN port mapping") + sdn_port_mapping = _load_file_or_yaml(args.file) + payload_req = json.dumps({"sdn_port_mapping": sdn_port_mapping}) - port_mapping = yaml.load(datacenter_sdn_port_mapping_list(args)) - if 'error' in port_mapping: + # read + URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter) + mano_response = requests.get(URLrequest) + logger.debug("openmano response: %s", mano_response.text) + port_mapping = mano_response.json() + if mano_response.status_code != 200: + str(mano_response.json()) 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)) if not (len(r) > 0 and r[0].lower() == "y"): return 0 - args.force = True - print datacenter_sdn_port_mapping_clear(args) - sdn_port_mapping = _load_file_or_yaml(args.file) - payload_req = json.dumps({"sdn_port_mapping": sdn_port_mapping}) + # clear + URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter) + mano_response = requests.delete(URLrequest) + logger.debug("openmano response: %s", mano_response.text) + if mano_response.status_code != 200: + return _print_verbose(mano_response, args.verbose) + # set URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter) logger.debug("openmano request: %s", payload_req) mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req) logger.debug("openmano response: %s", mano_response.text) + return _print_verbose(mano_response, args.verbose) - if mano_response.status_code == 200: - return yaml.safe_dump(mano_response.json()) - else: - return mano_response.content def datacenter_sdn_port_mapping_list(args): tenant = _get_tenant() @@ -1091,10 +1100,8 @@ def datacenter_sdn_port_mapping_list(args): mano_response = requests.get(URLrequest) logger.debug("openmano response: %s", mano_response.text) - if mano_response.status_code != 200: - return mano_response.content + return _print_verbose(mano_response, 4) - return yaml.safe_dump(mano_response.json()) def datacenter_sdn_port_mapping_clear(args): tenant = _get_tenant() @@ -1102,26 +1109,27 @@ def datacenter_sdn_port_mapping_clear(args): if not args.force: r = raw_input("Clean SDN port mapping for datacenter %s (y/N)? " %(datacenter)) - if not (len(r)>0 and r[0].lower()=="y"): + if not (len(r) > 0 and r[0].lower() == "y"): return 0 URLrequest = "http://%s:%s/openmano/%s/datacenters/%s/sdn_mapping" % (mano_host, mano_port, tenant, datacenter) mano_response = requests.delete(URLrequest) logger.debug("openmano response: %s", mano_response.text) - if mano_response.status_code != 200: - if "No port mapping for datacenter" in mano_response.content: - return "No port mapping for datacenter " + datacenter + " has been found" - return mano_response.content + return _print_verbose(mano_response, args.verbose) - return yaml.safe_dump(mano_response.json()) def sdn_controller_create(args): tenant = _get_tenant() headers_req = {'Accept': 'application/json', 'content-type': 'application/json'} - if not (args.ip and args.port and args.dpid and args.type): - raise OpenmanoCLIError("The following arguments are required: ip, port, dpid, type") + error_msg=[] + if not args.ip: error_msg.append("'ip'") + if not args.port: error_msg.append("'port'") + if not args.dpid: error_msg.append("'dpid'") + if not args.type: error_msg.append("'type'") + if error_msg: + raise OpenmanoCLIError("The following arguments are required: " + ",".join(error_msg)) controller_dict = {} controller_dict['name'] = args.name @@ -1145,42 +1153,41 @@ def sdn_controller_create(args): mano_response = requests.post(URLrequest, headers=headers_req, data=payload_req) logger.debug("openmano response: %s", mano_response.text) result = _print_verbose(mano_response, args.verbose) - return result + def sdn_controller_edit(args): tenant = _get_tenant() controller_uuid = _get_item_uuid("sdn_controllers", args.name, tenant) headers_req = {'Accept': 'application/json', 'content-type': 'application/json'} - if not (args.new_name or args.ip or args.port or args.dpid or args.type): - raise OpenmanoCLIError("At least one parameter must be editd") - - if not args.force: - r = raw_input("Update SDN controller %s (y/N)? " %(args.name)) - if not (len(r)>0 and r[0].lower()=="y"): - return 0 - controller_dict = {} - if args.new_name != None: + if args.new_name: controller_dict['name'] = args.new_name - if args.ip != None: + if args.ip: controller_dict['ip'] = args.ip - if args.port != None: + if args.port: controller_dict['port'] = int(args.port) - if args.dpid != None: + if args.dpid: controller_dict['dpid'] = args.dpid - if args.type != None: + if args.type: controller_dict['type'] = args.type - if args.description != None: + if args.description: controller_dict['description'] = args.description - if args.user != None: + if args.user: controller_dict['user'] = args.user - if args.password != None: + if args.password: controller_dict['password'] = args.password - payload_req = json.dumps({"sdn_controller": controller_dict}) + if not controller_dict: + raise OpenmanoCLIError("At least one parameter must be edited") + if not args.force: + r = raw_input("Update SDN controller {} (y/N)? ".format(args.name)) + if not (len(r) > 0 and r[0].lower() == "y"): + return 0 + + payload_req = json.dumps({"sdn_controller": controller_dict}) # print payload_req URLrequest = "http://%s:%s/openmano/%s/sdn_controllers/%s" % (mano_host, mano_port, tenant, controller_uuid) @@ -1188,9 +1195,9 @@ def sdn_controller_edit(args): mano_response = requests.put(URLrequest, headers=headers_req, data=payload_req) logger.debug("openmano response: %s", mano_response.text) result = _print_verbose(mano_response, args.verbose) - return result + def sdn_controller_list(args): tenant = _get_tenant() headers_req = {'Accept': 'application/json', 'content-type': 'application/json'} @@ -1208,8 +1215,9 @@ def sdn_controller_list(args): if args.name!=None: args.verbose += 1 - result = json.dumps(mano_response.json(), indent=4) - return result + # json.dumps(mano_response.json(), indent=4) + return _print_verbose(mano_response, args.verbose) + def sdn_controller_delete(args): tenant = _get_tenant() @@ -1223,9 +1231,7 @@ def sdn_controller_delete(args): URLrequest = "http://%s:%s/openmano/%s/sdn_controllers/%s" % (mano_host, mano_port, tenant, controller_uuid) mano_response = requests.delete(URLrequest) logger.debug("openmano response: %s", mano_response.text) - result = _print_verbose(mano_response, args.verbose) - - return result + return _print_verbose(mano_response, args.verbose) def vim_action(args): #print "datacenter-net-action",args @@ -1284,6 +1290,7 @@ 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: @@ -1299,6 +1306,7 @@ def _get_items(item, item_name_id=None, datacenter=None, tenant=None): return mano_response + def vim_net_sdn_attach(args): #Verify the network exists in the vim tenant = _get_tenant() @@ -1324,8 +1332,7 @@ def vim_net_sdn_attach(args): 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 + return result def vim_net_sdn_detach(args): @@ -1358,8 +1365,8 @@ def vim_net_sdn_detach(args): mano_response = requests.delete(URLrequest) logger.debug("openmano response: %s", mano_response.text) result = _print_verbose(mano_response, args.verbose) + return result - return def datacenter_net_action(args): if args.action == "net-update": @@ -1471,6 +1478,7 @@ def datacenter_netmap_action(args): logger.debug("openmano response: %s", mano_response.text ) return _print_verbose(mano_response, args.verbose) + def element_edit(args): element = _get_item_uuid(args.element, args.name) headers_req = {'Accept': 'application/json', 'content-type': 'application/json'} @@ -1544,6 +1552,7 @@ def datacenter_edit(args): args.verbose += 1 return _print_verbose(mano_response, args.verbose) + def version(args): headers_req = {'Accept': 'application/json', 'content-type': 'application/json'} URLrequest = "http://%s:%s/openmano/version" % (mano_host, mano_port) diff --git a/openmanod b/openmanod index 8f8c3f1b..fbd8e2e2 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.15-r524" -version_date = "Jun 2017" -database_version = 21 #expected database schema version +__version__ = "0.5.17-r526" +version_date = "Jul 2017" +database_version = 22 #expected database schema version global global_config global logger diff --git a/osm_ro/httpserver.py b/osm_ro/httpserver.py index 94544f6b..f9cbb9b4 100644 --- a/osm_ro/httpserver.py +++ b/osm_ro/httpserver.py @@ -1376,10 +1376,18 @@ def http_get_instance_id(tenant_id, instance_id): nfvo.refresh_instance(mydb, tenant_id, instance_dict) except (nfvo.NfvoException, db_base_Exception) as e: logger.warn("nfvo.refresh_instance couldn't refresh the status of the instance: %s" % str(e)) - #obtain data with results upated + # obtain data with results upated instance = mydb.get_instance_scenario(instance_id, tenant_id) + # Workaround to SO, convert vnfs:vms:interfaces:ip_address from ";" separated list to report the first value + for vnf in instance.get("vnfs", ()): + for vm in vnf.get("vms", ()): + for iface in vm.get("interfaces", ()): + if iface.get("ip_address"): + index = iface["ip_address"].find(";") + if index >= 0: + iface["ip_address"] = iface["ip_address"][:index] convert_datetime2str(instance) - #print json.dumps(instance, indent=4) + # print json.dumps(instance, indent=4) return format_out(instance) except (nfvo.NfvoException, db_base_Exception) as e: logger.error("http_get_instance_id error {}: {}".format(e.http_code, str(e))) diff --git a/osm_ro/nfvo.py b/osm_ro/nfvo.py index d45a3c79..a5599418 100644 --- a/osm_ro/nfvo.py +++ b/osm_ro/nfvo.py @@ -1923,6 +1923,7 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i except db_base_Exception as e: raise NfvoException("{} {}".format(type(e).__name__ , str(e)), e.http_code) + def get_datacenter_by_name_uuid(mydb, tenant_id, datacenter_id_name=None, **extra_filter): datacenter_id = None datacenter_name = None @@ -2789,20 +2790,24 @@ def new_datacenter(mydb, datacenter_descriptor): def edit_datacenter(mydb, datacenter_id_name, datacenter_descriptor): - #obtain data, check that only one exist + # obtain data, check that only one exist datacenter = mydb.get_table_by_uuid_name('datacenters', datacenter_id_name) - #edit data + + # edit data datacenter_id = datacenter['uuid'] where={'uuid': datacenter['uuid']} + remove_port_mapping = False if "config" in datacenter_descriptor: - if datacenter_descriptor['config']!=None: + if datacenter_descriptor['config'] != None: try: new_config_dict = datacenter_descriptor["config"] #delete null fields to_delete=[] for k in new_config_dict: - if new_config_dict[k]==None: + if new_config_dict[k] == None: to_delete.append(k) + if k == 'sdn-controller': + remove_port_mapping = True config_text = datacenter.get("config") if not config_text: @@ -2814,7 +2819,16 @@ def edit_datacenter(mydb, datacenter_id_name, datacenter_descriptor): del config_dict[k] except Exception as e: raise NfvoException("Bad format at datacenter:config " + str(e), HTTP_Bad_Request) - datacenter_descriptor["config"]= yaml.safe_dump(config_dict,default_flow_style=True,width=256) if len(config_dict)>0 else None + if config_dict: + datacenter_descriptor["config"] = yaml.safe_dump(config_dict, default_flow_style=True, width=256) + else: + datacenter_descriptor["config"] = None + if remove_port_mapping: + try: + datacenter_sdn_port_mapping_delete(mydb, None, datacenter_id) + except ovimException as e: + logger.error("Error deleting datacenter-port-mapping " + str(e)) + mydb.update_rows('datacenters', datacenter_descriptor, where) return datacenter_id @@ -2823,6 +2837,10 @@ def delete_datacenter(mydb, datacenter): #get nfvo_tenant info datacenter_dict = mydb.get_table_by_uuid_name('datacenters', datacenter, 'datacenter') mydb.delete_row_by_id("datacenters", datacenter_dict['uuid']) + try: + datacenter_sdn_port_mapping_delete(mydb, None, datacenter_dict['uuid']) + except ovimException as e: + logger.error("Error deleting datacenter-port-mapping " + str(e)) return datacenter_dict['uuid'] + " " + datacenter_dict['name'] diff --git a/osm_ro/vimconn_aws.py b/osm_ro/vimconn_aws.py index f0eebb66..6621345b 100644 --- a/osm_ro/vimconn_aws.py +++ b/osm_ro/vimconn_aws.py @@ -351,12 +351,12 @@ class vimconnector(vimconn.vimconnector): if filter_dict != {}: if 'tenant_id' in filter_dict: tfilters['vpcId'] = filter_dict['tenant_id'] - subnets = self.conn_vpc.get_all_subnets(subnet_ids=filter_dict.get('id', None), filters=tfilters) + subnets = self.conn_vpc.get_all_subnets(subnet_ids=filter_dict.get('name', None), filters=tfilters) net_list = [] for net in subnets: net_list.append( {'id': str(net.id), 'name': str(net.id), 'status': str(net.state), 'vpc_id': str(net.vpc_id), - 'cidr_block': str(net.cidr_block)}) + 'cidr_block': str(net.cidr_block), 'type': 'bridge'}) return net_list except Exception as e: self.format_vimconn_exception(e) @@ -796,7 +796,10 @@ class vimconnector(vimconn.vimconnector): interface_dict['vim_interface_id'] = interface.id interface_dict['vim_net_id'] = interface.subnet_id interface_dict['mac_address'] = interface.mac_address - interface_dict['ip_address'] = interface.private_ip_address + if hasattr(interface, 'publicIp') and interface.publicIp != None: + interface_dict['ip_address'] = interface.publicIp + ";" + interface.private_ip_address + else: + interface_dict['ip_address'] = interface.private_ip_address instance_dict['interfaces'].append(interface_dict) except Exception as e: self.logger.error("Exception getting vm status: %s", str(e), exc_info=True) diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index 6ac70ba6..bcb80ae1 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -169,9 +169,7 @@ class vimconnector(vimconn.vimconnector): net['type']='data' else: net['type']='bridge' - - - + def _format_exception(self, exception): '''Transform a keystone, nova, neutron exception into a vimconn exception''' if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, @@ -185,7 +183,10 @@ 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)) + elif isinstance(exception, vimconn.vimconnException): + raise else: # () + self.logger.error("General Exception " + str(exception), exc_info=True) raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) def get_tenant_list(self, filter_dict={}): @@ -266,19 +267,19 @@ class vimconnector(vimconn.vimconnector): ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand) if 'ip_version' not in ip_profile: ip_profile['ip_version'] = "IPv4" - subnet={"name":net_name+"-subnet", + subnet = {"name":net_name+"-subnet", "network_id": new_net["network"]["id"], "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6, "cidr": ip_profile['subnet_address'] } - if 'gateway_address' in ip_profile: - subnet['gateway_ip'] = ip_profile['gateway_address'] + # Gateway should be set to None if not needed. Otherwise openstack assigns one by default + subnet['gateway_ip'] = ip_profile.get('gateway_address') if ip_profile.get('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: - subnet['allocation_pools']=[] + subnet['allocation_pools'] = [] subnet['allocation_pools'].append(dict()) subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address'] if 'dhcp_count' in ip_profile: @@ -672,6 +673,25 @@ class vimconnector(vimconn.vimconnector): except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: self._format_exception(e) + def __wait_for_vm(self, vm_id, status): + """wait until vm is in the desired status and return True. + If the VM gets in ERROR status, return false. + If the timeout is reached generate an exception""" + elapsed_time = 0 + while elapsed_time < server_timeout: + vm_status = self.nova.servers.get(vm_id).status + if vm_status == status: + return True + if vm_status == 'ERROR': + return False + time.sleep(1) + elapsed_time += 1 + + # if we exceeded the timeout rollback + if elapsed_time >= server_timeout: + raise vimconn.vimconnException('Timeout waiting for instance ' + vm_id + ' to get ' + status, + http_code=vimconn.HTTP_Request_Timeout) + def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None,disk_list=None): '''Adds a VM instance to VIM Params: @@ -692,11 +712,14 @@ class vimconnector(vimconn.vimconnector): ''' self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list)) try: + server = None metadata={} net_list_vim=[] - external_network=[] #list of external networks to be connected to instance, later on used to create floating_ip + external_network=[] # list of external networks to be connected to instance, later on used to create floating_ip + no_secured_ports = [] # List of port-is with port-security disabled self._reload_connection() - metadata_vpci={} #For a specific neutron plugin + metadata_vpci={} # For a specific neutron plugin + block_device_mapping = None for net in net_list: if not net.get("net_id"): #skip non connected iface continue @@ -715,7 +738,7 @@ class vimconnector(vimconn.vimconnector): metadata_vpci["VF"]=[] metadata_vpci["VF"].append([ net["vpci"], "" ]) port_dict["binding:vnic_type"]="direct" - else: #For PT + else: # For PT if "vpci" in net: if "PF" not in metadata_vpci: metadata_vpci["PF"]=[] @@ -725,12 +748,15 @@ class vimconnector(vimconn.vimconnector): port_dict["name"]=name if net.get("mac_address"): port_dict["mac_address"]=net["mac_address"] - if net.get("port_security") == False: - port_dict["port_security_enabled"]=net["port_security"] new_port = self.neutron.create_port({"port": port_dict }) net["mac_adress"] = new_port["port"]["mac_address"] net["vim_id"] = new_port["port"]["id"] - net["ip"] = new_port["port"].get("fixed_ips", [{}])[0].get("ip_address") + # if try to use a network without subnetwork, it will return a emtpy list + fixed_ips = new_port["port"].get("fixed_ips") + if fixed_ips: + net["ip"] = fixed_ips[0].get("ip_address") + else: + net["ip"] = None net_list_vim.append({"port-id": new_port["port"]["id"]}) if net.get('floating_ip', False): @@ -740,6 +766,11 @@ class vimconnector(vimconn.vimconnector): net['exit_on_floating_ip_error'] = False external_network.append(net) + # If port security is disabled when the port has not yet been attached to the VM, then all vm traffic is dropped. + # As a workaround we wait until the VM is active and then disable the port-security + if net.get("port_security") == False: + no_secured_ports.append(new_port["port"]["id"]) + if metadata_vpci: metadata = {"pci_assignement": json.dumps(metadata_vpci)} if len(metadata["pci_assignement"]) >255: @@ -805,10 +836,9 @@ class vimconnector(vimconn.vimconnector): userdata = cloud_config #Create additional volumes in case these are present in disk_list - block_device_mapping = None base_disk_index = ord('b') if disk_list != None: - block_device_mapping = dict() + block_device_mapping = {} for disk in disk_list: if 'image_id' in disk: volume = self.cinder.volumes.create(size = disk['size'],name = name + '_vd' + @@ -845,33 +875,42 @@ 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'), key_name=self.config.get('keypair'), userdata=userdata, - config_drive = config_drive, - block_device_mapping = block_device_mapping + config_drive=config_drive, + block_device_mapping=block_device_mapping ) # , description=description) + + # Previously mentioned workaround to wait until the VM is active and then disable the port-security + if no_secured_ports: + self.__wait_for_vm(server.id, 'ACTIVE') + + for port_id in no_secured_ports: + try: + self.neutron.update_port(port_id, {"port": {"port_security_enabled": False, "security_groups": None} }) + + except Exception as e: + self.logger.error("It was not possible to disable port security for port {}".format(port_id)) + self.delete_vminstance(server.id) + raise + #print "DONE :-)", server pool_id = None floating_ips = self.neutron.list_floatingips().get("floatingips", ()) - for floating_network in external_network: - try: - # wait until vm is active - elapsed_time = 0 - while elapsed_time < server_timeout: - status = self.nova.servers.get(server.id).status - if status == 'ACTIVE': - break - time.sleep(1) - elapsed_time += 1 - #if we exceeded the timeout rollback - if elapsed_time >= server_timeout: - raise vimconn.vimconnException('Timeout creating instance ' + name, - http_code=vimconn.HTTP_Request_Timeout) + if external_network: + self.__wait_for_vm(server.id, 'ACTIVE') + for floating_network in external_network: + try: assigned = False while(assigned == False): if floating_ips: @@ -915,26 +954,31 @@ class vimconnector(vimconn.vimconnector): if not floating_network['exit_on_floating_ip_error']: self.logger.warn("Cannot create floating_ip. %s", str(e)) continue - self.delete_vminstance(server.id) raise return server.id # except nvExceptions.NotFound as e: # error_value=-vimconn.HTTP_Not_Found # error_text= "vm instance %s not found" % vm_id - except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e: +# except TypeError as e: +# raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request) + + except Exception as e: # delete the volumes we just created - if block_device_mapping != None: + if block_device_mapping: for volume_id in block_device_mapping.itervalues(): self.cinder.volumes.delete(volume_id) - # delete ports we just created - for net_item in net_list_vim: - if 'port-id' in net_item: - self.neutron.delete_port(net_item['port-id']) + # Delete the VM + if server != None: + self.delete_vminstance(server.id) + else: + # delete ports we just created + for net_item in net_list_vim: + if 'port-id' in net_item: + self.neutron.delete_port(net_item['port-id']) + self._format_exception(e) - except TypeError as e: - raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request) def get_vminstance(self,vm_id): '''Returns the VM instance information from VIM''' diff --git a/osm_ro/vimconn_vmware.py b/osm_ro/vimconn_vmware.py index a33ca4e6..d8ca463f 100644 --- a/osm_ro/vimconn_vmware.py +++ b/osm_ro/vimconn_vmware.py @@ -357,6 +357,11 @@ class vimconnector(vimconn.vimconnector): Returns: The return vca object that letter can be used to connect to vcloud direct as admin """ + vca = self.connect() + if not vca: + raise vimconn.vimconnConnectionException("self.connect() is failed.") + + self.vca = vca try: if self.org_uuid is None: org_dict = self.get_org_list() @@ -495,19 +500,16 @@ class vimconnector(vimconn.vimconnector): """ self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name)) - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") if not self.tenant_name: raise vimconn.vimconnConnectionException("Tenant name is empty.") - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name)) vdc_uuid = vdc.get_id().split(":")[3] - networks = vca.get_networks(vdc.get_name()) + networks = self.vca.get_networks(vdc.get_name()) network_list = [] try: for network in networks: @@ -553,21 +555,18 @@ class vimconnector(vimconn.vimconnector): List can be empty """ - self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name)) - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") + self.logger.debug("get_network_list(): retrieving network list for vcd {}".format(self.tenant_name)) if not self.tenant_name: raise vimconn.vimconnConnectionException("Tenant name is empty.") - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name)) try: vdcid = vdc.get_id().split(":")[3] - networks = vca.get_networks(vdc.get_name()) + networks = self.vca.get_networks(vdc.get_name()) network_list = [] for network in networks: @@ -613,15 +612,11 @@ class vimconnector(vimconn.vimconnector): """Method obtains network details of net_id VIM network Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]""" - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") - try: - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() vdc_id = vdc.get_id().split(":")[3] - networks = vca.get_networks(vdc.get_name()) + networks = self.vca.get_networks(vdc.get_name()) filter_dict = {} for network in networks: @@ -652,10 +647,6 @@ class vimconnector(vimconn.vimconnector): Returns the network identifier or raise an exception """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name)) - # ############# Stub code for SRIOV ################# # dvport_group = self.get_dvport_group(net_id) # if dvport_group: @@ -693,10 +684,6 @@ class vimconnector(vimconn.vimconnector): """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") - dict_entry = {} try: for net in net_list: @@ -754,6 +741,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") @@ -1077,9 +1071,6 @@ class vimconnector(vimconn.vimconnector): Return: if image uploaded correct method will provide image catalog UUID. """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") if not path: raise vimconn.vimconnException("Image path can't be None.") @@ -1104,21 +1095,21 @@ class vimconnector(vimconn.vimconnector): "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name)) try: - catalogs = vca.get_catalogs() + catalogs = self.vca.get_catalogs() except Exception as exp: self.logger.debug("Failed get catalogs() with Exception {} ".format(exp)) raise vimconn.vimconnException("Failed get catalogs() with Exception {} ".format(exp)) if len(catalogs) == 0: self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name)) - result = self.create_vimcatalog(vca, catalog_md5_name) + result = self.create_vimcatalog(self.vca, catalog_md5_name) if not result: raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name)) - result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name, + result = self.upload_vimimage(vca=self.vca, catalog_name=catalog_md5_name, media_name=filename, medial_file_name=path, progress=progress) if not result: raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name)) - return self.get_catalogid(catalog_name, vca.get_catalogs()) + return self.get_catalogid(catalog_name, self.vca.get_catalogs()) else: for catalog in catalogs: # search for existing catalog if we find same name we return ID @@ -1127,20 +1118,20 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("Found existing catalog entry for {} " "catalog id {}".format(catalog_name, self.get_catalogid(catalog_md5_name, catalogs))) - return self.get_catalogid(catalog_md5_name, vca.get_catalogs()) + return self.get_catalogid(catalog_md5_name, self.vca.get_catalogs()) # if we didn't find existing catalog we create a new one and upload image. self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name)) - result = self.create_vimcatalog(vca, catalog_md5_name) + result = self.create_vimcatalog(self.vca, catalog_md5_name) if not result: raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name)) - result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name, + result = self.upload_vimimage(vca=self.vca, catalog_name=catalog_md5_name, media_name=filename, medial_file_name=path, progress=progress) if not result: raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name)) - return self.get_catalogid(catalog_md5_name, vca.get_catalogs()) + return self.get_catalogid(catalog_md5_name, self.vca.get_catalogs()) def get_image_list(self, filter_dict={}): '''Obtain tenant images from VIM @@ -1153,12 +1144,10 @@ class vimconnector(vimconn.vimconnector): [{}, ...] List can be empty ''' - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") + try: image_list = [] - catalogs = vca.get_catalogs() + catalogs = self.vca.get_catalogs() if len(catalogs) == 0: return image_list else: @@ -1229,7 +1218,7 @@ class vimconnector(vimconn.vimconnector): return False return False - def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None): + def get_namebyvappid(self, vdc=None, vapp_uuid=None): """Method returns vApp name from vCD and lookup done by vapp_id. Args: @@ -1248,8 +1237,13 @@ class vimconnector(vimconn.vimconnector): # we care only about UUID the rest doesn't matter vappid = ref.href.split("vapp")[1][1:] if vappid == vapp_uuid: - response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify, + response = Http.get(ref.href, headers=self.vca.vcloud_session.get_vcloud_headers(), verify=self.vca.verify, logger=self.logger) + + #Retry login if session expired & retry sending request + if response.status_code == 403: + response = self.retry_rest('GET', ref.href) + tree = XmlElementTree.fromstring(response.content) return tree.attrib['name'] except Exception as e: @@ -1284,9 +1278,6 @@ class vimconnector(vimconn.vimconnector): self.logger.info("Creating new instance for entry {}".format(name)) self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {}".format( description, start, image_id, flavor_id, net_list, cloud_config, disk_list)) - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") #new vm name = vmname + tenant_id + uuid new_vm_name = [name, '-', str(uuid.uuid4())] @@ -1298,11 +1289,15 @@ class vimconnector(vimconn.vimconnector): # return vapp_uuid # we check for presence of VDC, Catalog entry and Flavor. - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: raise vimconn.vimconnNotFoundException( "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name)) - catalogs = vca.get_catalogs() + catalogs = self.vca.get_catalogs() + if catalogs is None: + #Retry once, if failed by refreshing token + self.get_token() + catalogs = self.vca.get_catalogs() if catalogs is None: raise vimconn.vimconnNotFoundException( "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name)) @@ -1319,6 +1314,7 @@ class vimconnector(vimconn.vimconnector): vm_cpus = None vm_memory = None vm_disk = None + numas = None if flavor_id is not None: if flavor_id not in vimconnector.flavorlist: @@ -1371,19 +1367,26 @@ class vimconnector(vimconn.vimconnector): # use: 'data', 'bridge', 'mgmt' # create vApp. Set vcpu and ram based on flavor id. try: - vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName, - self.get_catalogbyid(image_id, catalogs), - network_name=None, # None while creating vapp - network_mode=network_mode, - vm_name=vmname_andid, - vm_cpus=vm_cpus, # can be None if flavor is None - vm_memory=vm_memory) # can be None if flavor is None + for retry in (1,2): + vapptask = self.vca.create_vapp(self.tenant_name, vmname_andid, templateName, + self.get_catalogbyid(image_id, catalogs), + network_name=None, # None while creating vapp + network_mode=network_mode, + vm_name=vmname_andid, + vm_cpus=vm_cpus, # can be None if flavor is None + vm_memory=vm_memory) # can be None if flavor is None + + if not vapptask and retry==1: + self.get_token() # Retry getting token + continue + else: + break if vapptask is None or vapptask is False: raise vimconn.vimconnUnexpectedResponse( "new_vminstance(): failed to create vApp {}".format(vmname_andid)) if type(vapptask) is VappTask: - vca.block_until_completed(vapptask) + self.vca.block_until_completed(vapptask) except Exception as exp: raise vimconn.vimconnUnexpectedResponse( @@ -1391,14 +1394,14 @@ class vimconnector(vimconn.vimconnector): # we should have now vapp in undeployed state. try: - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid) - vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid) + vapp_uuid = self.get_vappid(self.get_vdc_details(), vmname_andid) + except Exception as exp: raise vimconn.vimconnUnexpectedResponse( "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}" .format(vmname_andid, exp)) - if vapp is None: + if vapp_uuid is None: raise vimconn.vimconnUnexpectedResponse( "new_vminstance(): Failed to retrieve vApp {} after creation".format( vmname_andid)) @@ -1433,6 +1436,8 @@ class vimconnector(vimconn.vimconnector): pci_devices_info, vmname_andid) ) + + vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid) # Modify vm disk if vm_disk: #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled @@ -1464,7 +1469,7 @@ class vimconnector(vimconn.vimconnector): if added_existing_disk: time.sleep(5) added_existing_disk = False - self.add_new_disk(vca, vapp_uuid, disk['size']) + self.add_new_disk(vapp_uuid, disk['size']) if numas: # Assigning numa affinity setting @@ -1500,12 +1505,14 @@ class vimconnector(vimconn.vimconnector): - NONE (No IP addressing mode specified.)""" if primary_netname is not None: - nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name)) + nets = filter(lambda n: n.name == interface_net_name, self.vca.get_networks(self.tenant_name)) if len(nets) == 1: self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name)) + + vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid) task = vapp.connect_to_network(nets[0].name, nets[0].href) if type(task) is GenericTask: - vca.block_until_completed(task) + self.vca.block_until_completed(task) # connect network to VM - with all DHCP by default type_list = ['PF','VF','VFnotShared'] @@ -1529,6 +1536,7 @@ class vimconnector(vimconn.vimconnector): net) nicIndex += 1 + vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid) # cloud-init for ssh-key injection if cloud_config: self.cloud_init(vapp,cloud_config) @@ -1537,7 +1545,7 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name)) deploytask = vapp.deploy(powerOn=False) if type(deploytask) is GenericTask: - vca.block_until_completed(deploytask) + self.vca.block_until_completed(deploytask) # ############# Stub code for SRIOV ################# #Add SRIOV @@ -1574,28 +1582,32 @@ class vimconnector(vimconn.vimconnector): str(memReserve),str(vm_obj))) self.logger.debug("new_vminstance(): power on vApp {} ".format(name)) + + vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid) poweron_task = vapp.poweron() if type(poweron_task) is GenericTask: - vca.block_until_completed(poweron_task) + self.vca.block_until_completed(poweron_task) except Exception as exp : # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception - self.logger.debug("new_vminstance(): Failed create new vm instance {}".format(name, exp)) - raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name, exp)) + self.logger.debug("new_vminstance(): Failed create new vm instance {} with exception {}" + .format(name, exp)) + raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {} with exception {}" + .format(name, exp)) # check if vApp deployed and if that the case return vApp UUID otherwise -1 wait_time = 0 vapp_uuid = None while wait_time <= MAX_WAIT_TIME: try: - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid) + vapp = self.vca.get_vapp(self.get_vdc_details(), vmname_andid) except Exception as exp: raise vimconn.vimconnUnexpectedResponse( "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}" .format(vmname_andid, exp)) if vapp and vapp.me.deployed: - vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid) + vapp_uuid = self.get_vappid(self.get_vdc_details(), vmname_andid) break else: self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name)) @@ -1627,11 +1639,8 @@ class vimconnector(vimconn.vimconnector): """Returns the VM instance information from VIM""" self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid)) - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: raise vimconn.vimconnConnectionException( "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) @@ -1676,11 +1685,8 @@ class vimconnector(vimconn.vimconnector): """ self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid)) - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format( self.tenant_name)) @@ -1688,7 +1694,7 @@ class vimconnector(vimconn.vimconnector): "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) try: - vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid) + vapp_name = self.get_namebyvappid(vdc, vm__vim_uuid) if vapp_name is None: self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid) @@ -1696,7 +1702,7 @@ class vimconnector(vimconn.vimconnector): self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid)) # Delete vApp and wait for status change if task executed and vApp is None. - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) + vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name) if vapp: if vapp.me.deployed: @@ -1705,14 +1711,14 @@ class vimconnector(vimconn.vimconnector): powered_off = False wait_time = 0 while wait_time <= MAX_WAIT_TIME: - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) + vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name) if not vapp: self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid) power_off_task = vapp.poweroff() if type(power_off_task) is GenericTask: - result = vca.block_until_completed(power_off_task) + result = self.vca.block_until_completed(power_off_task) if result: powered_off = True break @@ -1731,14 +1737,14 @@ class vimconnector(vimconn.vimconnector): wait_time = 0 undeployed = False while wait_time <= MAX_WAIT_TIME: - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) + vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name) if not vapp: self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid) undeploy_task = vapp.undeploy(action='powerOff') if type(undeploy_task) is GenericTask: - result = vca.block_until_completed(undeploy_task) + result = self.vca.block_until_completed(undeploy_task) if result: undeployed = True break @@ -1753,14 +1759,14 @@ class vimconnector(vimconn.vimconnector): # delete vapp self.logger.info("Start deletion of vApp {} ".format(vapp_name)) - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) + vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name) if vapp is not None: wait_time = 0 result = False while wait_time <= MAX_WAIT_TIME: - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) + vapp = self.vca.get_vapp(self.get_vdc_details(), vapp_name) if not vapp: self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid) @@ -1768,8 +1774,8 @@ class vimconnector(vimconn.vimconnector): delete_task = vapp.delete() if type(delete_task) is GenericTask: - vca.block_until_completed(delete_task) - result = vca.block_until_completed(delete_task) + self.vca.block_until_completed(delete_task) + result = self.vca.block_until_completed(delete_task) if result: break else: @@ -1785,7 +1791,7 @@ class vimconnector(vimconn.vimconnector): self.logger.debug(traceback.format_exc()) raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid)) - if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None: + if self.vca.get_vapp(self.get_vdc_details(), vapp_name) is None: self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid)) return vm__vim_uuid else: @@ -1817,25 +1823,21 @@ class vimconnector(vimconn.vimconnector): self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list)) - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") - - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) vms_dict = {} nsx_edge_list = [] for vmuuid in vm_list: - vmname = self.get_namebyvappid(vca, vdc, vmuuid) + vmname = self.get_namebyvappid(self.get_vdc_details(), vmuuid) if vmname is not None: try: - the_vapp = vca.get_vapp(vdc, vmname) + vm_pci_details = self.get_vm_pci_details(vmuuid) + the_vapp = self.vca.get_vapp(self.get_vdc_details(), vmname) vm_info = the_vapp.get_vms_details() vm_status = vm_info[0]['status'] - vm_pci_details = self.get_vm_pci_details(vmuuid) vm_info[0].update(vm_pci_details) vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()], @@ -1984,15 +1986,11 @@ class vimconnector(vimconn.vimconnector): if vm__vim_uuid is None or action_dict is None: raise vimconn.vimconnException("Invalid request. VM id or action is None.") - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") - - vdc = vca.get_vdc(self.tenant_name) + vdc = self.get_vdc_details() if vdc is None: return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name) - vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid) + vapp_name = self.get_namebyvappid(vdc, vm__vim_uuid) if vapp_name is None: self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) @@ -2000,7 +1998,7 @@ class vimconnector(vimconn.vimconnector): self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid)) try: - the_vapp = vca.get_vapp(vdc, vapp_name) + the_vapp = self.vca.get_vapp(vdc, vapp_name) # TODO fix all status if "start" in action_dict: vm_info = the_vapp.get_vms_details() @@ -2008,28 +2006,28 @@ class vimconnector(vimconn.vimconnector): self.logger.info("action_vminstance: Power on vApp: {}".format(vapp_name)) if vm_status == "Suspended" or vm_status == "Powered off": power_on_task = the_vapp.poweron() - result = vca.block_until_completed(power_on_task) + result = self.vca.block_until_completed(power_on_task) self.instance_actions_result("start", result, vapp_name) elif "rebuild" in action_dict: self.logger.info("action_vminstance: Rebuild vApp: {}".format(vapp_name)) rebuild_task = the_vapp.deploy(powerOn=True) - result = vca.block_until_completed(rebuild_task) + result = self.vca.block_until_completed(rebuild_task) self.instance_actions_result("rebuild", result, vapp_name) elif "pause" in action_dict: self.logger.info("action_vminstance: pause vApp: {}".format(vapp_name)) pause_task = the_vapp.undeploy(action='suspend') - result = vca.block_until_completed(pause_task) + result = self.vca.block_until_completed(pause_task) self.instance_actions_result("pause", result, vapp_name) elif "resume" in action_dict: self.logger.info("action_vminstance: resume vApp: {}".format(vapp_name)) power_task = the_vapp.poweron() - result = vca.block_until_completed(power_task) + result = self.vca.block_until_completed(power_task) self.instance_actions_result("resume", result, vapp_name) elif "shutoff" in action_dict or "shutdown" in action_dict: action_name , value = action_dict.items()[0] self.logger.info("action_vminstance: {} vApp: {}".format(action_name, vapp_name)) power_off_task = the_vapp.undeploy(action='powerOff') - result = vca.block_until_completed(power_off_task) + result = self.vca.block_until_completed(power_off_task) if action_name == "shutdown": self.instance_actions_result("shutdown", result, vapp_name) else: @@ -2126,10 +2124,6 @@ class vimconnector(vimconn.vimconnector): The return network name. """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") - if not network_uuid: return None @@ -2156,10 +2150,6 @@ class vimconnector(vimconn.vimconnector): network_uuid: network_id """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed.") - if not network_name: self.logger.debug("get_network_id_by_name() : Network name is empty") return None @@ -2189,18 +2179,18 @@ class vimconnector(vimconn.vimconnector): The return XML respond """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") - - url_list = [vca.host, '/api/org'] + url_list = [self.vca.host, '/api/org'] vm_list_rest_call = ''.join(url_list) - if not (not vca.vcloud_session or not vca.vcloud_session.organization): + if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization): response = Http.get(url=vm_list_rest_call, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + response = self.retry_rest('GET', vm_list_rest_call) + if response.status_code == requests.codes.ok: return response.content @@ -2218,21 +2208,22 @@ class vimconnector(vimconn.vimconnector): The return XML respond """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") - if org_uuid is None: return None - url_list = [vca.host, '/api/org/', org_uuid] + url_list = [self.vca.host, '/api/org/', org_uuid] vm_list_rest_call = ''.join(url_list) - if not (not vca.vcloud_session or not vca.vcloud_session.organization): + if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization): response = Http.get(url=vm_list_rest_call, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + #Retry login if session expired & retry sending request + if response.status_code == 403: + response = self.retry_rest('GET', vm_list_rest_call) + if response.status_code == requests.codes.ok: return response.content @@ -2253,9 +2244,6 @@ class vimconnector(vimconn.vimconnector): """ org_dict = {} - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") if org_uuid is None: return org_dict @@ -2293,9 +2281,6 @@ class vimconnector(vimconn.vimconnector): """ org_dict = {} - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") content = self.list_org_action() try: @@ -2467,21 +2452,22 @@ class vimconnector(vimconn.vimconnector): The return XML respond """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") - if network_uuid is None: return None - url_list = [vca.host, '/api/network/', network_uuid] + url_list = [self.vca.host, '/api/network/', network_uuid] vm_list_rest_call = ''.join(url_list) - if not (not vca.vcloud_session or not vca.vcloud_session.organization): + if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization): response = Http.get(url=vm_list_rest_call, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + #Retry login if session expired & retry sending request + if response.status_code == 403: + response = self.retry_rest('GET', vm_list_rest_call) + if response.status_code == requests.codes.ok: return response.content @@ -3044,7 +3030,7 @@ class vimconnector(vimconn.vimconnector): if need_admin_access: vca = self.connect_as_admin() else: - vca = self.connect() + vca = self.vca if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed") @@ -3060,6 +3046,10 @@ class vimconnector(vimconn.vimconnector): verify=vca.verify, logger=vca.logger) + if response.status_code == 403: + if need_admin_access == False: + response = self.retry_rest('GET', get_vapp_restcall) + if response.status_code != requests.codes.ok: self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall, response.status_code)) @@ -3166,21 +3156,20 @@ class vimconnector(vimconn.vimconnector): def acuire_console(self, vm_uuid=None): - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") if vm_uuid is None: return None - if not (not vca.vcloud_session or not vca.vcloud_session.organization): + if not (not self.vca.vcloud_session or not self.vca.vcloud_session.organization): vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid) console_dict = vm_dict['acquireTicket'] console_rest_call = console_dict['href'] response = Http.post(url=console_rest_call, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + if response.status_code == 403: + response = self.retry_rest('POST', console_rest_call) if response.status_code == requests.codes.ok: return response.content @@ -3237,17 +3226,17 @@ class vimconnector(vimconn.vimconnector): Returns: The return network uuid or return None """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") if disk_href is None or disk_size is None: return None - if vca.vcloud_session and vca.vcloud_session.organization: + if self.vca.vcloud_session and self.vca.vcloud_session.organization: response = Http.get(url=disk_href, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + response = self.retry_rest('GET', disk_href) if response.status_code != requests.codes.ok: self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href, @@ -3269,13 +3258,17 @@ class vimconnector(vimconn.vimconnector): xml_declaration=True) #Send PUT request to modify disk size - headers = vca.vcloud_session.get_vcloud_headers() + headers = self.vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1' response = Http.put(url=disk_href, data=data, headers=headers, - verify=vca.verify, logger=self.logger) + verify=self.vca.verify, logger=self.logger) + + if response.status_code == 403: + add_headers = {'Content-Type': headers['Content-Type']} + response = self.retry_rest('PUT', disk_href, add_headers, data) if response.status_code != 202: self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href, @@ -3283,7 +3276,7 @@ class vimconnector(vimconn.vimconnector): else: modify_disk_task = taskType.parseString(response.content, True) if type(modify_disk_task) is GenericTask: - status = vca.block_until_completed(modify_disk_task) + status = self.vca.block_until_completed(modify_disk_task) return status return None @@ -3668,9 +3661,6 @@ class vimconnector(vimconn.vimconnector): Returns: None """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("Failed to connect vCloud director") try: ip_address = None @@ -3691,12 +3681,16 @@ class vimconnector(vimconn.vimconnector): for vms in vapp._get_vms(): vm_id = (vms.id).split(':')[-1] - url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id) + url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.vca.host, vm_id) response = Http.get(url=url_rest_call, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + response = self.retry_rest('GET', url_rest_call) + if response.status_code != 200: self.logger.error("REST call {} failed reason : {}"\ "status code : {}".format(url_rest_call, @@ -3734,11 +3728,16 @@ class vimconnector(vimconn.vimconnector): data = data.replace('\n','\n{}\n'.format(new_item)) - headers = vca.vcloud_session.get_vcloud_headers() + headers = self.vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml' response = Http.put(url=url_rest_call, headers=headers, data=data, - verify=vca.verify, - logger=vca.logger) + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + add_headers = {'Content-Type': headers['Content-Type']} + response = self.retry_rest('PUT', url_rest_call, add_headers, data) + if response.status_code != 202: self.logger.error("REST call {} failed reason : {}"\ "status code : {} ".format(url_rest_call, @@ -3749,7 +3748,7 @@ class vimconnector(vimconn.vimconnector): else: nic_task = taskType.parseString(response.content, True) if isinstance(nic_task, GenericTask): - vca.block_until_completed(nic_task) + self.vca.block_until_completed(nic_task) self.logger.info("add_network_adapter_to_vms(): VM {} conneced to "\ "default NIC type".format(vm_id)) else: @@ -3759,12 +3758,16 @@ class vimconnector(vimconn.vimconnector): for vms in vapp._get_vms(): vm_id = (vms.id).split(':')[-1] - url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id) + url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.vca.host, vm_id) response = Http.get(url=url_rest_call, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + response = self.retry_rest('GET', url_rest_call) + if response.status_code != 200: self.logger.error("REST call {} failed reason : {}"\ "status code : {}".format(url_rest_call, @@ -3803,11 +3806,15 @@ class vimconnector(vimconn.vimconnector): data = data.replace('\n','\n{}\n'.format(new_item)) - headers = vca.vcloud_session.get_vcloud_headers() + headers = self.vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml' response = Http.put(url=url_rest_call, headers=headers, data=data, - verify=vca.verify, - logger=vca.logger) + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + add_headers = {'Content-Type': headers['Content-Type']} + response = self.retry_rest('PUT', url_rest_call, add_headers, data) if response.status_code != 202: self.logger.error("REST call {} failed reason : {}"\ @@ -3819,7 +3826,7 @@ class vimconnector(vimconn.vimconnector): else: nic_task = taskType.parseString(response.content, True) if isinstance(nic_task, GenericTask): - vca.block_until_completed(nic_task) + self.vca.block_until_completed(nic_task) self.logger.info("add_network_adapter_to_vms(): VM {} "\ "conneced to NIC type {}".format(vm_id, nic_type)) else: @@ -3902,9 +3909,6 @@ class vimconnector(vimconn.vimconnector): 'owner': (optional) file owner, string with the format 'owner:group' 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk """ - vca = self.connect() - if not vca: - raise vimconn.vimconnConnectionException("Failed to connect vCloud director") try: if isinstance(cloud_config, dict): @@ -3966,7 +3970,7 @@ class vimconnector(vimconn.vimconnector): vm_name = vm.name task = vapp.customize_guest_os(vm_name, customization_script=customize_script) if isinstance(task, GenericTask): - vca.block_until_completed(task) + self.vca.block_until_completed(task) self.logger.info("cloud_init : customized guest os task "\ "completed for VM {}".format(vm_name)) else: @@ -3979,7 +3983,7 @@ class vimconnector(vimconn.vimconnector): "ssh-key".format(exp)) - def add_new_disk(self, vca, vapp_uuid, disk_size): + def add_new_disk(self, vapp_uuid, disk_size): """ Method to create an empty vm disk @@ -4001,7 +4005,7 @@ class vimconnector(vimconn.vimconnector): if vm_details and "vm_virtual_hardware" in vm_details: self.logger.info("Adding disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size)) disk_href = vm_details["vm_virtual_hardware"]["disk_edit_href"] - status = self.add_new_disk_rest(vca, disk_href, disk_size_mb) + status = self.add_new_disk_rest(disk_href, disk_size_mb) except Exception as exp: msg = "Error occurred while creating new disk {}.".format(exp) @@ -4015,7 +4019,7 @@ class vimconnector(vimconn.vimconnector): self.rollback_newvm(vapp_uuid, msg) - def add_new_disk_rest(self, vca, disk_href, disk_size_mb): + def add_new_disk_rest(self, disk_href, disk_size_mb): """ Retrives vApp Disks section & add new empty disk @@ -4026,11 +4030,14 @@ class vimconnector(vimconn.vimconnector): Returns: Status of add new disk task """ status = False - if vca.vcloud_session and vca.vcloud_session.organization: + if self.vca.vcloud_session and self.vca.vcloud_session.organization: response = Http.get(url=disk_href, - headers=vca.vcloud_session.get_vcloud_headers(), - verify=vca.verify, - logger=vca.logger) + headers=self.vca.vcloud_session.get_vcloud_headers(), + verify=self.vca.verify, + logger=self.vca.logger) + + if response.status_code == 403: + response = self.retry_rest('GET', disk_href) if response.status_code != requests.codes.ok: self.logger.error("add_new_disk_rest: GET REST API call {} failed. Return status code {}" @@ -4069,13 +4076,17 @@ class vimconnector(vimconn.vimconnector): new_data = new_data.replace('\n', '\n{}\n'.format(new_item)) # Send PUT request to modify virtual hardware section with new disk - headers = vca.vcloud_session.get_vcloud_headers() + headers = self.vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1' response = Http.put(url=disk_href, data=new_data, headers=headers, - verify=vca.verify, logger=self.logger) + verify=self.vca.verify, logger=self.logger) + + if response.status_code == 403: + add_headers = {'Content-Type': headers['Content-Type']} + response = self.retry_rest('PUT', disk_href, add_headers, new_data) if response.status_code != 202: self.logger.error("PUT REST API call {} failed. Return status code {}. Response Content:{}" @@ -4083,7 +4094,7 @@ class vimconnector(vimconn.vimconnector): else: add_disk_task = taskType.parseString(response.content, True) if type(add_disk_task) is GenericTask: - status = vca.block_until_completed(add_disk_task) + status = self.vca.block_until_completed(add_disk_task) if not status: self.logger.error("Add new disk REST task failed to add {} MB disk".format(disk_size_mb)) @@ -4909,3 +4920,95 @@ class vimconnector(vimconn.vimconnector): "getting media details") raise vimconn.vimconnException(message=exp) + + def retry_rest(self, api, url, add_headers=None, data=None): + """ Method to get Token & retry respective REST request + Args: + api - REST API - Can be one of 'GET' or 'PUT' or 'POST' + url - request url to be used + add_headers - Additional headers (optional) + data - Request payload data to be passed in request + Returns: + response - Response of request + """ + response = None + + #Get token + self.get_token() + + headers=self.vca.vcloud_session.get_vcloud_headers() + + if add_headers: + headers.update(add_headers) + + if api == 'GET': + response = Http.get(url=url, + headers=headers, + verify=self.vca.verify, + logger=self.vca.logger) + return response + elif api == 'PUT': + if headers: + headers.append + response = Http.put(url=url, + data=data, + headers=headers, + verify=self.vca.verify, logger=self.logger) + return response + elif api == 'POST': + response = Http.post(url=url, + headers=headers, + verify=self.vca.verify, + logger=self.vca.logger) + + def get_token(self): + """ Generate a new token if expired + + Returns: + The return vca object that letter can be used to connect to vCloud director as admin for VDC + """ + vca = None + + try: + self.logger.debug("Generate token for vca {} as {} to datacenter {}.".format(self.org_name, + self.user, + self.org_name)) + vca = VCA(host=self.url, + username=self.user, + service_type=STANDALONE, + version=VCAVERSION, + verify=False, + log=False) + + result = vca.login(password=self.passwd, org=self.org_name) + if result is True: + result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url) + if result is True: + self.logger.info( + "Successfully generated token for vcloud direct org: {} as user: {}".format(self.org_name, self.user)) + #Update vca + self.vca = vca + return + + except: + raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: " + "{} as user: {}".format(self.org_name, self.user)) + + if not vca or not result: + raise vimconn.vimconnConnectionException("self.connect() is failed while reconnecting") + + + def get_vdc_details(self): + """ Get VDC details using pyVcloud Lib + + Returns vdc object + """ + vdc = self.vca.get_vdc(self.tenant_name) + + #Retry once, if failed by refreshing token + if vdc is None: + self.get_token() + vdc = self.vca.get_vdc(self.tenant_name) + + return vdc + diff --git a/test/test_RO.py b/test/test_RO.py index 0d5e6243..21af36cd 100755 --- a/test/test_RO.py +++ b/test/test_RO.py @@ -43,6 +43,7 @@ import sys import time from pyvcloud.vcloudair import VCA import uuid +import json global test_config # used for global variables with the test configuration test_config = {} @@ -484,6 +485,40 @@ class test_vimconn_new_network(test_base): else: logger.info("Failed to delete network id {}".format(self.__class__.network_id)) + def test_050_refresh_nets_status(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 + # creating new network + network_name = _get_random_string(20) + net_type = 'bridge' + network_id = test_config["vim_conn"].new_network(net_name=network_name, + net_type=net_type) + # refresh net status + net_dict = test_config["vim_conn"].refresh_nets_status([network_id]) + for attr in net_dict[network_id]: + if attr == 'status': + self.assertEqual(net_dict[network_id][attr], 'ACTIVE') + + # Deleting created network + result = test_config["vim_conn"].delete_network(network_id) + if result: + logger.info("Network id {} sucessfully deleted".format(network_id)) + else: + logger.info("Failed to delete network id {}".format(network_id)) + + def test_060_refresh_nets_status_negative(self): + unknown_net_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 + + # refresh net status + net_dict = test_config["vim_conn"].refresh_nets_status([unknown_net_id]) + self.assertEqual(net_dict, {}) + class test_vimconn_get_network_list(test_base): # test_index = 1 network_name = None @@ -825,6 +860,478 @@ 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 + self.__class__.flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + self.assertEqual(type(self.__class__.flavor_id),str) + self.assertIsInstance(uuid.UUID(self.__class__.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) + +class test_vimconn_get_image_id_from_path(test_base): + + def test_000_get_image_id_from_path(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"].get_image_id_from_path( image_path ) + self.assertEqual(type(image_id),str) + else: + self.skipTest("Skipping test as image file not present at RO container") + + def test_010_get_image_id_from_path_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) + +class test_vimconn_get_image_list(test_base): + image_name = None + image_id = None + + def test_000_get_image_list(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_list = test_config["vim_conn"].get_image_list() + + for item in image_list: + if 'name' in item: + self.__class__.image_name = item['name'] + self.__class__.image_id = item['id'] + self.assertEqual(type(self.__class__.image_name),str) + self.assertEqual(type(self.__class__.image_id),str) + + def test_010_get_image_list_by_name(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_list = test_config["vim_conn"].get_image_list({'name': self.__class__.image_name}) + + for item in image_list: + self.assertEqual(type(item['id']), str) + self.assertEqual(item['id'], self.__class__.image_id) + self.assertEqual(type(item['name']), str) + self.assertEqual(item['name'], self.__class__.image_name) + + def test_020_get_image_list_by_id(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 + + filter_image_list = test_config["vim_conn"].get_image_list({'id': self.__class__.image_id}) + + for item1 in filter_image_list: + self.assertEqual(type(item1.get('id')), str) + self.assertEqual(item1.get('id'), self.__class__.image_id) + self.assertEqual(type(item1.get('name')), str) + self.assertEqual(item1.get('name'), self.__class__.image_name) + + def test_030_get_image_list_negative(self): + Non_exist_image_id = 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 + image_list = test_config["vim_conn"].get_image_list({'name': 'Unknown_name', 'id': Non_exist_image_id}) + + self.assertIsNotNone(image_list, None) + self.assertEqual(image_list, []) + +class test_vimconn_new_vminstance(test_base): + network_name = None + net_type = None + network_id = None + image_id = None + instance_id = None + + def setUp(self): + # create network + self.__class__.network_name = _get_random_string(20) + self.__class__.net_type = 'bridge' + + self.__class__.network_id = test_config["vim_conn"].new_network(net_name=self.__class__.network_name, + net_type=self.__class__.net_type) + + def tearDown(self): + test_base.tearDown(self) + # 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_new_vminstance(self): + vpci = "0000:00:11.0" + name = "eth0" + + flavor_data = {'ram': 1024, 'vcpus': 1, 'disk': 10} + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + + # find image name and image id + if test_config['image_name']: + image_list = test_config['vim_conn'].get_image_list({'name': test_config['image_name']}) + if len(image_list) == 0: + raise Exception("Image {} is not found at VIM".format(test_config['image_name'])) + else: + self.__class__.image_id = image_list[0]['id'] + else: + image_list = test_config['vim_conn'].get_image_list() + if len(image_list) == 0: + raise Exception("Not found any image at VIM") + else: + self.__class__.image_id = image_list[0]['id'] + + 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 + + net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'vpci': vpci, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}] + + self.__class__.instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, flavor_id=flavor_id, net_list=net_list) + + self.assertEqual(type(self.__class__.instance_id),str) + + def test_010_new_vminstance_by_model(self): + flavor_data = {'ram': 1024, 'vcpus': 2, 'disk': 10} + model_name = 'e1000' + name = 'eth0' + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + + 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 + + net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'model': model_name, 'type': 'virtual', 'net_id': self.__class__.network_id}] + + instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, + flavor_id=flavor_id, + net_list=net_list) + self.assertEqual(type(instance_id),str) + # Deleting created vm instance + logger.info("Deleting created vm intance") + test_config["vim_conn"].delete_vminstance(instance_id) + time.sleep(10) + + def test_020_new_vminstance_by_net_use(self): + flavor_data = {'ram': 1024, 'vcpus': 2, 'disk': 10} + net_use = 'data' + name = 'eth0' + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + + 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 + + net_list = [{'use': net_use, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}] + + instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, + flavor_id=flavor_id, + net_list=net_list) + self.assertEqual(type(instance_id),str) + # Deleting created vm instance + logger.info("Deleting created vm intance") + test_config["vim_conn"].delete_vminstance(instance_id) + time.sleep(10) + + def test_030_new_vminstance_by_net_type(self): + flavor_data = {'ram': 1024, 'vcpus': 2, 'disk': 10} + _type = 'VF' + name = 'eth0' + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + + 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 + + net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': _type, 'net_id': self.__class__.network_id}] + + instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, + flavor_id=flavor_id, + net_list=net_list) + self.assertEqual(type(instance_id),str) + # Deleting created vm instance + logger.info("Deleting created vm intance") + test_config["vim_conn"].delete_vminstance(instance_id) + time.sleep(10) + + def test_040_new_vminstance_by_cloud_config(self): + flavor_data = {'ram': 1024, 'vcpus': 2, 'disk': 10} + name = 'eth0' + user_name = 'test_user' + + key_pairs = ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCy2w9GHMKKNkpCmrDK2ovc3XBYDETuLWwaW24S+feHhLBQiZlzh3gSQoINlA+2ycM9zYbxl4BGzEzpTVyCQFZv5PidG4m6ox7LR+KYkDcITMyjsVuQJKDvt6oZvRt6KbChcCi0n2JJD/oUiJbBFagDBlRslbaFI2mmqmhLlJ5TLDtmYxzBLpjuX4m4tv+pdmQVfg7DYHsoy0hllhjtcDlt1nn05WgWYRTu7mfQTWfVTavu+OjIX3e0WN6NW7yIBWZcE/Q9lC0II3W7PZDE3QaT55se4SPIO2JTdqsx6XGbekdG1n6adlduOI27sOU5m4doiyJ8554yVbuDB/z5lRBD alfonso.tiernosepulveda@telefonica.com'] + + users_data = [{'key-pairs': ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCy2w9GHMKKNkpCmrDK2ovc3XBYDETuLWwaW24S+feHhLBQiZlzh3gSQoINlA+2ycM9zYbxl4BGzEzpTVyCQFZv5PidG4m6ox7LR+KYkDcITMyjsVuQJKDvt6oZvRt6KbChcCi0n2JJD/oUiJbBFagDBlRslbaFI2mmqmhLlJ5TLDtmYxzBLpjuX4m4tv+pdmQVfg7DYHsoy0hllhjtcDlt1nn05WgWYRTu7mfQTWfVTavu+OjIX3e0WN6NW7yIBWZcE/Q9lC0II3W7PZDE3QaT55se4SPIO2JTdqsx6XGbekdG1n6adlduOI27sOU5m4doiyJ8554yVbuDB/z5lRBD alfonso.tiernosepulveda@telefonica.com'], 'name': user_name}] + + cloud_data = {'config-files': [{'content': 'auto enp0s3\niface enp0s3 inet dhcp\n', 'dest': '/etc/network/interfaces.d/enp0s3.cfg', 'owner': 'root:root', 'permissions': '0644'}, {'content': '#! /bin/bash\nls -al >> /var/log/osm.log\n', 'dest': '/etc/rc.local', 'permissions': '0755'}, {'content': 'file content', 'dest': '/etc/test_delete'}], 'boot-data-drive': True, 'key-pairs': key_pairs, 'users': users_data } + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + + 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 + + net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}] + + instance_id = test_config["vim_conn"].new_vminstance(name='Cloud_vm', image_id=self.__class__.image_id, + flavor_id=flavor_id, + net_list=net_list, + cloud_config=cloud_data) + self.assertEqual(type(instance_id),str) + # Deleting created vm instance + logger.info("Deleting created vm intance") + test_config["vim_conn"].delete_vminstance(instance_id) + time.sleep(10) + + def test_050_new_vminstance_by_disk_list(self): + flavor_data = {'ram': 1024, 'vcpus': 2, 'disk': 10} + name = 'eth0' + + device_data = [{'image_id': self.__class__.image_id, 'size': '5'}] + + # create new flavor + flavor_id = test_config["vim_conn"].new_flavor(flavor_data) + + 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 + + net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}] + + instance_id = test_config["vim_conn"].new_vminstance(name='VM_test1', image_id=self.__class__.image_id, + flavor_id=flavor_id, + net_list=net_list, + disk_list=device_data) + self.assertEqual(type(instance_id),str) + # Deleting created vm instance + logger.info("Deleting created vm intance") + test_config["vim_conn"].delete_vminstance(instance_id) + time.sleep(10) + + def test_060_new_vminstance_negative(self): + unknown_flavor_id = str(uuid.uuid4()) + unknown_image_id = str(uuid.uuid4()) + name = 'eth2' + + 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 + + net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}] + + with self.assertRaises(Exception) as context: + test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=unknown_image_id, + flavor_id=unknown_flavor_id, + net_list=net_list) + self.assertEqual((context.exception).http_code, 404) + + def test_070_get_vminstance(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 + + # Get instance by its id + vm_info = test_config["vim_conn"].get_vminstance(self.__class__.instance_id) + + if test_config['vimtype'] == 'vmware': + for attr in vm_info: + if attr == 'status': + self.assertEqual(vm_info[attr], 'ACTIVE') + if attr == 'hostId': + self.assertEqual(type(vm_info[attr]), str) + if attr == 'interfaces': + self.assertEqual(type(vm_info[attr]), list) + self.assertEqual(vm_info[attr][0]['IsConnected'], 'true') + if attr == 'IsEnabled': + self.assertEqual(vm_info[attr], 'true') + + def test_080_get_vminstance_negative(self): + unknown_instance_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_vminstance(unknown_instance_id) + + self.assertEqual((context.exception).http_code, 404) + + def test_090_refresh_vms_status(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 + vm_list = [] + vm_list.append(self.__class__.instance_id) + + # refresh vm status + vm_info = test_config["vim_conn"].refresh_vms_status(vm_list) + for attr in vm_info[self.__class__.instance_id]: + if attr == 'status': + self.assertEqual(vm_info[self.__class__.instance_id][attr], 'ACTIVE') + if attr == 'interfaces': + self.assertEqual(type(vm_info[self.__class__.instance_id][attr]), list) + + def test_100_refresh_vms_status_negative(self): + unknown_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 + + vm_dict = test_config["vim_conn"].refresh_vms_status([unknown_id]) + self.assertEqual(vm_dict, {}) + + def test_110_action_vminstance(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 + + action_list = ['shutdown','start','shutoff','rebuild','pause','resume'] + # various action on vminstace + for action in action_list: + instance_id = test_config["vim_conn"].action_vminstance(self.__class__.instance_id, + { action: None}) + self.assertEqual(instance_id, self.__class__.instance_id) + + def test_120_action_vminstance_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 + + action = 'start' + with self.assertRaises(Exception) as context: + test_config["vim_conn"].action_vminstance(non_exist_id, { action: None}) + + self.assertEqual((context.exception).http_code, 400) + + def test_130_delete_vminstance(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 + + # Deleting created vm instance + logger.info("Deleting created vm instance") + test_config["vim_conn"].delete_vminstance(self.__class__.instance_id) + time.sleep(10) ''' IMPORTANT NOTE @@ -978,6 +1485,8 @@ 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 + test_config['image_name'] = args.image_name # 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 +1742,8 @@ 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") + vimconn_parser.add_argument('-n', '--image-name', dest='image_name', help="Provide image name for test") # TODO add optional arguments for vimconn tests # vimconn_parser.add_argument("-i", '--image-name', dest='image_name', help='')) diff --git a/test/test_openmanocli.sh b/test/test_openmanocli.sh new file mode 100755 index 00000000..9bbeea56 --- /dev/null +++ b/test/test_openmanocli.sh @@ -0,0 +1,207 @@ +#!/bin/bash + +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## + +#This script can be used as a basic test of openmano. +#WARNING: It destroy the database content + + +function usage(){ + echo -e "usage: ${BASH_SOURCE[0]} [OPTIONS] \n test openmano with fake tenant, datancenters, etc."\ + "It assumes that you have configured openmano cli with HOST,PORT,TENANT with environment variables" + "If not, it will use by default localhost:9080 and creates a new TENANT" + echo -e " -h --help shows this help" +} + +function is_valid_uuid(){ + echo "$1" | grep -q -E '^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$' && return 0 + return 1 +} + +DIRNAME=$(dirname $(readlink -f ${BASH_SOURCE[0]})) +DIRmano=$(dirname $DIRNAME) +DIRscript=${DIRmano}/scripts + +#detect paths of executables, preceding the relative paths +openmano=openmano && [[ -x "${DIRmano}/openmano" ]] && openmano="${DIRmano}/openmano" +service_openmano=service-openmano && [[ -x "$DIRscript/service-openmano" ]] && + service_openmano="$DIRscript/service-openmano" +initopenvim="initopenvim" +openvim="openvim" + +function _exit() +{ + EXIT_STATUS=$1 + for item in $ToDelete + do + command=${item%%:*} + uuid=${item#*:} + [[ $command == "datacenter-detach" ]] && force="" || force=-f + printf "%-50s" "$command $uuid:" + ! $openmano $command $uuid $force >> /dev/null && echo FAIL && EXIT_STATUS=1 || echo OK + done + [[ ${BASH_SOURCE[0]} != $0 ]] && return $1 || exit $EXIT_STATUS +} + + +# process options +source ${DIRscript}/get-options.sh "force:-f help:h insert-bashrc init-openvim:initopenvim install-openvim screen" \ + $* || _exit 1 + +# help +[ -n "$option_help" ] && usage && _exit 0 + + +ToDelete="" +DCs="dc-fake1-openstack dc-fake2-openvim" #dc-fake3-vmware +Ts="fake-tenant1 fake-tenand2" +SDNs="sdn-fake1-opendaylight sdn-fake2-floodlight sdn-fake3-onos" + +for T in $Ts +do + printf "%-50s" "Creating fake tenant '$T':" + ! result=`$openmano tenant-create "$T"` && echo FAIL && echo " $result" && _exit 1 + tenant=`echo $result |gawk '{print $1}'` + ! is_valid_uuid $tenant && echo "FAIL" && echo " $result" && _exit 1 + echo $tenant + ToDelete="tenant-delete:$tenant $ToDelete" + [[ -z "$OPENMANO_TENANT" ]] && export OPENMANO_TENANT=$tenant +done + +index=0 +for DC in $DCs +do + index=$((index+1)) + printf "%-50s" "Creating datacenter '$DC':" + ! result=`$openmano datacenter-create "$DC" "http://$DC/v2.0" --type=${DC##*-} --config='{insecure: True}'` && + echo FAIL && echo " $result" && _exit 1 + datacenter=`echo $result |gawk '{print $1}'` + ! is_valid_uuid $datacenter && echo "FAIL" && echo " $result" && _exit 1 + echo $datacenter + eval DC${index}=$datacenter + ToDelete="datacenter-delete:$datacenter $ToDelete" + [[ -z "$datacenter_empty" ]] && datacenter_empty=datacenter + + printf "%-50s" "Attaching openmano tenant to the datacenter:" + ! result=`$openmano datacenter-attach "$DC" --vim-tenant-name=osm --config='{insecure: False}'` && + echo FAIL && echo " $result" && _exit 1 + ToDelete="datacenter-detach:$datacenter $ToDelete" + echo OK +done + +printf "%-50s" "Datacenter list:" +! result=`$openmano datacenter-list` && + echo "FAIL" && echo " $result" && _exit 1 +for verbose in "" -v -vv -vvv +do + ! result=`$openmano datacenter-list "$DC" $verbose` && + echo "FAIL" && echo " $result" && _exit 1 +done +echo OK + +dpid_prefix=55:56:57:58:59:60:61:0 +dpid_sufix=0 +for SDN in $SDNs +do + printf "%-50s" "Creating SDN controller '$SDN':" + ! result=`$openmano sdn-controller-create "$SDN" --ip 4.5.6.7 --port 80 --type=${SDN##*-} \ + --user user --passwd p --dpid=${dpid_prefix}${dpid_sufix}` && echo "FAIL" && echo " $result" && _exit 1 + sdn=`echo $result |gawk '{print $1}'` + #check a valid uuid is obtained + ! is_valid_uuid $sdn && echo "FAIL" && echo " $result" && _exit 1 + echo $sdn + ToDelete="sdn-controller-delete:$sdn $ToDelete" + dpid_sufix=$((dpid_sufix+1)) + +done +printf "%-50s" "Edit SDN-controller:" +for edit in user=u password=p ip=5.6.6.7 port=81 name=name dpid=45:55:54:45:44:44:55:67 +do + ! result=`$openmano sdn-controller-edit $sdn -f --"${edit}"` && + echo "FAIL" && echo " $result" && _exit 1 +done +echo OK + +printf "%-50s" "SDN-controller list:" +! result=`$openmano sdn-controller-list` && + echo "FAIL" && echo " $result" && _exit 1 +for verbose in "" -v -vv -vvv +do + ! result=`$openmano sdn-controller-list "$sdn" $verbose` && + echo "FAIL" && echo " $result" && _exit 1 +done +echo OK + +printf "%-50s" "Add sdn to datacenter:" +! result=`$openmano datacenter-edit -f $DC --sdn-controller $SDN` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Clear Port mapping:" +! result=`$openmano datacenter-sdn-port-mapping-clear -f $DC` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Set Port mapping:" +! result=`$openmano datacenter-sdn-port-mapping-set -f $DC ${DIRmano}/sdn/sdn_port_mapping.yaml` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "List Port mapping:" +for verbose in "" -v -vv -vvv +do + ! result=`$openmano datacenter-sdn-port-mapping-list "$DC" $verbose` && + echo "FAIL" && echo " $result" && _exit 1 +done +echo OK + +printf "%-50s" "Set again Port mapping:" +! result=`$openmano datacenter-sdn-port-mapping-set -f $DC ${DIRmano}/sdn/sdn_port_mapping.yaml` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Clear again Port mapping:" +! result=`$openmano datacenter-sdn-port-mapping-clear -f $DC` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Set again Port mapping:" +! result=`$openmano datacenter-sdn-port-mapping-set -f $DC ${DIRmano}/sdn/sdn_port_mapping.yaml` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Remove datacenter sdn:" +! result=`$openmano datacenter-edit -f $DC --sdn-controller null` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Negative list port mapping:" +result=`$openmano datacenter-sdn-port-mapping-list $DC` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Add again datacenter sdn:" +! result=`$openmano datacenter-edit -f $DC --sdn-controller $SDN` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +printf "%-50s" "Empty list port mapping:" +! [[ `$openmano datacenter-sdn-port-mapping-list $DC | wc -l` -eq 6 ]] && + echo "FAIL" && _exit 1 || echo OK + +printf "%-50s" "Set again Port mapping:" +! result=`$openmano datacenter-sdn-port-mapping-set -f $DC ${DIRmano}/sdn/sdn_port_mapping.yaml` && + echo "FAIL" && echo " $result" && _exit 1 || echo OK + +_exit 0 +