From e338c837cfa7476ad83e355b6149c37adc1715a5 Mon Sep 17 00:00:00 2001 From: Cédric Ollivier Date: Wed, 31 Jan 2018 22:20:42 +0100 Subject: Fix JujuEpc prepare() and deploy_orchestrator() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It bypasses a known unfixed Juju bug [1] by creating a second '_member_' user [1]. An 'admin' user is still required to create flavors and by snaps when listing virtual machines. It highlights a possible bug in user_creator.get_os_creds(). It partially rewrites the testcase by cleaning instance attributes and by taking several os.system() returns into account. It sets the new password automatically generated [2] and starts switching from openstack utils to snaps in vEPC. This patchset must be completed by unit tests and by another patch fully switching to snaps in the last methods. It should be noted that the testcase sets 8.8.8.8 as dns server which could raise side effects. [1] https://bugs.launchpad.net/juju/+bug/1710848 [2] https://gerrit.opnfv.org/gerrit/#/c/50747/ Change-Id: Id18b4b185aa1825196e91f6692338169f51faea8 Signed-off-by: Cédric Ollivier --- functest/opnfv_tests/vnf/epc/juju_epc.py | 350 ++++++++++++++++--------------- 1 file changed, 177 insertions(+), 173 deletions(-) (limited to 'functest/opnfv_tests/vnf') diff --git a/functest/opnfv_tests/vnf/epc/juju_epc.py b/functest/opnfv_tests/vnf/epc/juju_epc.py index 052efd3f..4ca540f1 100644 --- a/functest/opnfv_tests/vnf/epc/juju_epc.py +++ b/functest/opnfv_tests/vnf/epc/juju_epc.py @@ -8,11 +8,13 @@ # http://www.apache.org/licenses/LICENSE-2.0 """Juju testcase implementation.""" +import errno import logging import os import time import json import sys +import uuid from copy import deepcopy from urlparse import urljoin @@ -26,16 +28,49 @@ from snaps.config.flavor import FlavorConfig from snaps.config.image import ImageConfig from snaps.config.network import NetworkConfig, SubnetConfig from snaps.config.router import RouterConfig +from snaps.config.user import UserConfig from snaps.openstack.create_flavor import OpenStackFlavor from snaps.openstack.create_image import OpenStackImage from snaps.openstack.create_network import OpenStackNetwork from snaps.openstack.create_router import OpenStackRouter +from snaps.openstack.create_user import OpenStackUser from snaps.openstack.utils import keystone_utils +from snaps.openstack.utils import neutron_utils +from snaps.openstack.utils import nova_utils import yaml __author__ = "Amarendra Meher " __author__ = "Soumaya K Nayek " +CLOUD_TEMPLATE = """clouds: + abot-epc: + type: openstack + auth-types: [userpass] + endpoint: {url} + regions: + {region}: + endpoint: {url}""" + +CREDS_TEMPLATE2 = """credentials: + abot-epc: + default-credential: abot-epc + abot-epc: + auth-type: userpass + password: {pass} + project-domain-name: {project_domain_n} + tenant-name: {tenant_n}""" + +CREDS_TEMPLATE3 = """credentials: + abot-epc: + default-credential: abot-epc + abot-epc: + auth-type: userpass + password: {pass} + project-domain-name: {project_domain_n} + tenant-name: {tenant_n} + user-domain-name: {user_domain_n} + username: {user_n}""" + class JujuEpc(vnf.VnfOnBoarding): # pylint:disable=too-many-instance-attributes @@ -54,103 +89,127 @@ class JujuEpc(vnf.VnfOnBoarding): self.case_dir = pkg_resources.resource_filename( 'functest', 'opnfv_tests/vnf/epc') try: - self.config = CONST.__getattribute__( - 'vnf_{}_config'.format(self.case_name)) + self.config = getattr( + CONST, 'vnf_{}_config'.format(self.case_name)) except Exception: raise Exception("VNF config file not found") - config_file = os.path.join(self.case_dir, self.config) - self.orchestrator = dict( - requirements=get_config("orchestrator.requirements", config_file), - ) + self.config_file = os.path.join(self.case_dir, self.config) + self.orchestrator = dict(requirements=get_config( + "orchestrator.requirements", self.config_file)) self.created_object = [] - self.snaps_creds = snaps_utils.get_credentials() self.details['orchestrator'] = dict( - name=get_config("orchestrator.name", config_file), - version=get_config("orchestrator.version", config_file), + name=get_config("orchestrator.name", self.config_file), + version=get_config("orchestrator.version", self.config_file), status='ERROR', result='' ) self.vnf = dict( - descriptor=get_config("vnf.descriptor", config_file), - requirements=get_config("vnf.requirements", config_file) + descriptor=get_config("vnf.descriptor", self.config_file), + requirements=get_config("vnf.requirements", self.config_file) ) self.details['vnf'] = dict( descriptor_version=self.vnf['descriptor']['version'], - name=get_config("vnf.name", config_file), - version=get_config("vnf.version", config_file), + name=get_config("vnf.name", self.config_file), + version=get_config("vnf.version", self.config_file), ) self.__logger.debug("VNF configuration: %s", self.vnf) self.details['test_vnf'] = dict( - name=get_config("vnf_test_suite.name", config_file), - version=get_config("vnf_test_suite.version", config_file), - tag_name=get_config("vnf_test_suite.tag_name", config_file) + name=get_config("vnf_test_suite.name", self.config_file), + version=get_config("vnf_test_suite.version", self.config_file), + tag_name=get_config("vnf_test_suite.tag_name", self.config_file) ) - self.images = get_config("tenant_images", config_file) - self.__logger.info("Images needed for vEPC: %s", self.images) - self.keystone_client = os_utils.get_keystone_client() - self.glance_client = os_utils.get_glance_client() - self.neutron_client = os_utils.get_neutron_client() - self.nova_client = os_utils.get_nova_client() - self.sec_group_id = None self.public_auth_url = None - self.creds = None - self.filename = None - def prepare(self): - """Prepare testcase (Additional pre-configuration steps).""" - self.__logger.debug("OS Credentials: %s", os_utils.get_credentials()) + self.res_dir = os.path.join( + getattr(CONST, 'dir_results'), self.case_name) - super(JujuEpc, self).prepare() + def _bypass_juju_network_discovery_bug(self, name): + user_creator = OpenStackUser( + self.snaps_creds, + UserConfig( + name=name, password=str(uuid.uuid4()), + roles={'_member_': self.tenant_name})) + user_creator.create() + self.created_object.append(user_creator) + return user_creator + + def _register_cloud(self): + self.__logger.info("Creating Cloud for Abot-epc .....") + clouds_yaml = os.path.join(self.res_dir, "clouds.yaml") + cloud_data = { + 'url': self.public_auth_url, + 'region': os.environ.get( + "OS_REGION_NAME", self.default_region_name)} + with open(clouds_yaml, 'w') as yfile: + yfile.write(CLOUD_TEMPLATE.format(**cloud_data)) + if os.system( + 'juju add-cloud abot-epc -f {} --replace'.format(clouds_yaml)): + raise vnf.VnfPreparationException + + def _register_credentials_v2(self): + self.__logger.info("Creating Credentials for Abot-epc .....") + user_creator = self._bypass_juju_network_discovery_bug( + 'juju_network_discovery_bug') + snaps_creds = user_creator.get_os_creds('juju_network_discovery_bug') + credentials_yaml = os.path.join(self.res_dir, "credentials.yaml") + # 'tenant_n' should habe been equal to snaps_creds.project_name + # user_creator.get_os_creds() must be checked + creds_data = { + 'pass': snaps_creds.password, + 'tenant_n': self.snaps_creds.project_name, + 'user_n': snaps_creds.username} + with open(credentials_yaml, 'w') as yfile: + yfile.write(CREDS_TEMPLATE2.format(**creds_data)) + if os.system( + 'juju add-credential abot-epc -f {} --replace'.format( + credentials_yaml)): + raise vnf.VnfPreparationException + + def _register_credentials_v3(self): + self.__logger.info("Creating Credentials for Abot-epc .....") + user_creator = self._bypass_juju_network_discovery_bug( + 'juju_network_discovery_bug') + snaps_creds = user_creator.get_os_creds('juju_network_discovery_bug') + credentials_yaml = os.path.join(self.res_dir, "credentials.yaml") + # 'tenant_n' should habe been equal to snaps_creds.project_name + # user_creator.get_os_creds() must be checked + creds_data = { + 'pass': snaps_creds.password, + 'tenant_n': self.snaps_creds.project_name, + 'user_n': snaps_creds.username, + 'project_domain_n': snaps_creds.project_domain_name, + 'user_domain_n': snaps_creds.user_domain_name} + with open(credentials_yaml, 'w') as yfile: + yfile.write(CREDS_TEMPLATE3.format(**creds_data)) + if os.system( + 'juju add-credential abot-epc -f {} --replace'.format( + credentials_yaml)): + raise vnf.VnfPreparationException + def prepare(self): + """Prepare testcase (Additional pre-configuration steps).""" self.__logger.info("Additional pre-configuration steps") + super(JujuEpc, self).prepare() + try: + os.makedirs(self.res_dir) + except OSError as ex: + if ex.errno != errno.EEXIST: + self.__logger.exception("Cannot create %s", self.res_dir) + raise vnf.VnfPreparationException self.public_auth_url = keystone_utils.get_endpoint( self.snaps_creds, 'identity') # it enforces a versioned public identity endpoint as juju simply # adds /auth/tokens wich fails vs an unversioned endpoint. if not self.public_auth_url.endswith(('v3', 'v3/', 'v2.0', 'v2.0/')): self.public_auth_url = urljoin(self.public_auth_url, 'v3') - - self.creds = { - "tenant": self.tenant_name, - "username": self.tenant_name, - "password": self.tenant_name, - "auth_url": os_utils.get_credentials()['auth_url'] - } - - cloud_data = { - 'url': self.public_auth_url, - 'pass': self.tenant_name, - 'tenant_n': self.tenant_name, - 'user_n': self.tenant_name, - 'region': os.environ.get( - "OS_REGION_NAME", self.default_region_name) - } - self.__logger.info("Cloud DATA: %s", cloud_data) - self.filename = os.path.join(self.case_dir, 'abot-epc.yaml') - self.__logger.info("Create %s to add cloud info", self.filename) - write_config(self.filename, CLOUD_TEMPLATE, **cloud_data) - + self._register_cloud() if self.snaps_creds.identity_api_version == 3: - append_config( - self.filename, '{}'.format( - os_utils.get_credentials()['project_domain_name']), - '{}'.format(os_utils.get_credentials()['user_domain_name'])) - - self.__logger.info("Upload some OS images if it doesn't exist") - for image_name, image_file in self.images.iteritems(): - self.__logger.info("image: %s, file: %s", image_name, image_file) - if image_file and image_name: - image_creator = OpenStackImage( - self.snaps_creds, - ImageConfig(name=image_name, - image_user='cloud', - img_format='qcow2', - image_file=image_file)) - image_creator.create() - self.created_object.append(image_creator) + self._register_credentials_v3() + else: + self._register_credentials_v2() def deploy_orchestrator(self): # pylint: disable=too-many-locals """ @@ -159,32 +218,31 @@ class JujuEpc(vnf.VnfOnBoarding): Bootstrap juju """ self.__logger.info("Deployed Orchestrator") - private_net_name = CONST.__getattribute__( - 'vnf_{}_private_net_name'.format(self.case_name)) - private_subnet_name = CONST.__getattribute__( - 'vnf_{}_private_subnet_name'.format(self.case_name)) - private_subnet_cidr = CONST.__getattribute__( - 'vnf_{}_private_subnet_cidr'.format(self.case_name)) - abot_router = CONST.__getattribute__( - 'vnf_{}_external_router'.format(self.case_name)) - dns_nameserver = CONST.__getattribute__( - 'vnf_{}_dns_nameserver'.format(self.case_name)) + private_net_name = getattr( + CONST, 'vnf_{}_private_net_name'.format(self.case_name)) + private_subnet_name = getattr( + CONST, 'vnf_{}_private_subnet_name'.format(self.case_name)) + private_subnet_cidr = getattr( + CONST, 'vnf_{}_private_subnet_cidr'.format(self.case_name)) + abot_router = getattr( + CONST, 'vnf_{}_external_router'.format(self.case_name)) + dns_nameserver = getattr( + CONST, 'vnf_{}_dns_nameserver'.format(self.case_name)) self.__logger.info("Creating full network ...") - subnet_settings = SubnetConfig(name=private_subnet_name, - cidr=private_subnet_cidr, - dns_nameservers=dns_nameserver) - network_settings = NetworkConfig(name=private_net_name, - subnet_settings=[subnet_settings]) + subnet_settings = SubnetConfig( + name=private_subnet_name, cidr=private_subnet_cidr, + dns_nameservers=dns_nameserver) + network_settings = NetworkConfig( + name=private_net_name, subnet_settings=[subnet_settings]) network_creator = OpenStackNetwork(self.snaps_creds, network_settings) - network_creator.create() + net_id = network_creator.create().id self.created_object.append(network_creator) ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds) self.__logger.info("Creating network Router ....") router_creator = OpenStackRouter( - self.snaps_creds, - RouterConfig( + self.snaps_creds, RouterConfig( name=abot_router, external_gateway=ext_net_name, internal_subnets=[subnet_settings.name])) @@ -194,33 +252,35 @@ class JujuEpc(vnf.VnfOnBoarding): flavor_settings = FlavorConfig( name=self.orchestrator['requirements']['flavor']['name'], ram=self.orchestrator['requirements']['flavor']['ram_min'], - disk=10, - vcpus=1) + disk=10, vcpus=1) flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings) - self.__logger.info("Juju Bootstrap: Skip creation of flavors") flavor_creator.create() self.created_object.append(flavor_creator) - self.__logger.info("Creating Cloud for Abot-epc .....") - os.system('juju add-cloud abot-epc -f {}'.format(self.filename)) - os.system('juju add-credential abot-epc -f {}'.format(self.filename)) - for image_name in self.images.keys(): - self.__logger.info("Generating Metadata for %s", image_name) - image_id = os_utils.get_image_id(self.glance_client, image_name) - os.system( - 'juju metadata generate-image -d ~ -i {} -s {} -r ' - '{} -u {}'.format( - image_id, image_name, - os.environ.get("OS_REGION_NAME", self.default_region_name), - self.public_auth_url)) - net_id = os_utils.get_network_id(self.neutron_client, private_net_name) + self.__logger.info("Upload some OS images if it doesn't exist") + images = get_config("tenant_images", self.config_file) + self.__logger.info("Images needed for vEPC: %s", images) + for image_name, image_file in images.iteritems(): + self.__logger.info("image: %s, file: %s", image_name, image_file) + if image_file and image_name: + image_creator = OpenStackImage(self.snaps_creds, ImageConfig( + name=image_name, image_user='cloud', img_format='qcow2', + image_file=image_file)) + image_id = image_creator.create().id + os.system( + 'juju metadata generate-image -d ~ -i {} -s {} -r ' + '{} -u {}'.format( + image_id, image_name, + os.environ.get( + "OS_REGION_NAME", self.default_region_name), + self.public_auth_url)) + self.created_object.append(image_creator) self.__logger.info("Credential information : %s", net_id) - juju_bootstrap_command = ('juju bootstrap abot-epc abot-controller ' - '--config network={} --metadata-source ~ ' - '--config ssl-hostname-verification=false ' - '--constraints mem=2G --bootstrap-series ' - 'xenial ' - '--config use-floating-ip=true --debug'. - format(net_id)) + juju_bootstrap_command = ( + 'juju bootstrap abot-epc abot-controller --config network={} ' + '--metadata-source ~ --config ssl-hostname-verification=false ' + '--constraints mem=2G --bootstrap-series xenial ' + '--config use-floating-ip=true --debug ' + '--config use-default-secgroup=true'.format(net_id)) os.system(juju_bootstrap_command) return True @@ -243,19 +303,21 @@ class JujuEpc(vnf.VnfOnBoarding): status = os.system('juju-wait') self.__logger.info("juju wait completed: %s", status) self.__logger.info("Deployed Abot-epc on Openstack") + nova_client = nova_utils.nova_client(self.snaps_creds) + neutron_client = neutron_utils.neutron_client(self.snaps_creds) if status == 0: - instances = os_utils.get_instances(self.nova_client) + instances = os_utils.get_instances(nova_client) for items in instances: - metadata = get_instance_metadata(self.nova_client, items) + metadata = get_instance_metadata(nova_client, items) if 'juju-units-deployed' in metadata: sec_group = ('juju-' + metadata['juju-controller-uuid'] + '-' + metadata['juju-model-uuid']) self.sec_group_id = os_utils.get_security_group_id( - self.neutron_client, sec_group) + neutron_client, sec_group) break self.__logger.info("Adding Security group rule....") - os_utils.create_secgroup_rule(self.neutron_client, - self.sec_group_id, 'ingress', 132) + os_utils.create_secgroup_rule( + neutron_client, self.sec_group_id, 'ingress', 132) self.__logger.info("Copying the feature files to Abot_node ") os.system('juju scp -- -r {}/featureFiles abot-' 'epc-basic/0:~/'.format(self.case_dir)) @@ -312,9 +374,6 @@ class JujuEpc(vnf.VnfOnBoarding): testresult = os.path.join(self.case_dir, 'TestResults.json') if os.path.exists(testresult): os.remove(testresult) - self.__logger.info("Removing %s file ", self.filename) - if os.path.exists(self.filename): - os.remove(self.filename) self.__logger.info("Destroying Orchestrator...") os.system('juju destroy-controller -y abot-controller ' '--destroy-all-models') @@ -324,27 +383,8 @@ class JujuEpc(vnf.VnfOnBoarding): if not self.orchestrator['requirements']['preserve_setup']: self.__logger.info('Remove the Abot_epc OS object ..') - for creator in reversed(self.created_object): - try: - creator.clean() - except Exception as exc: # pylint: disable=broad-except - self.__logger.error('Unexpected error cleaning - %s', exc) - - self.__logger.info("Releasing all the floating IPs") - floating_ips = os_utils.get_floating_ips(self.neutron_client) - tenant_id = os_utils.get_tenant_id(self.keystone_client, - self.tenant_name) - self.__logger.info("TENANT ID : %s", tenant_id) - for item in floating_ips: - if item['tenant_id'] == tenant_id: - os_utils.delete_floating_ip(self.neutron_client, - item['id']) - self.__logger.info("Cleaning Projects and Users") - for creator in reversed(self.created_object): - try: - creator.clean() - except Exception as exc: # pylint: disable=broad-except - self.__logger.error('Unexpected error cleaning - %s', exc) + super(JujuEpc, self).clean() + return True @@ -456,39 +496,3 @@ def get_instance_metadata(nova_client, instance): except Exception as exc: # pylint: disable=broad-except logging.error("Error [get_instance_status(nova_client)]: %s", exc) return None - - -CLOUD_TEMPLATE = """clouds: - abot-epc: - type: openstack - auth-types: [userpass] - endpoint: {url} - regions: - {region}: - endpoint: {url} -credentials: - abot-epc: - abot-epc: - auth-type: userpass - password: {pass} - tenant-name: {tenant_n} - username: {user_n}""" - - -def write_config(fname, template, **kwargs): - """ Generate yaml from template for addinh cloud in juju """ - with open(fname, 'w') as yfile: - yfile.write(template.format(**kwargs)) - - -def append_config(file_name, p_domain, u_domain): - """ Append values into a yaml file """ - with open(file_name) as yfile: - doc = yaml.load(yfile) - doc['credentials']['abot-epc']['abot-epc']['project-domain-name'] = ( - p_domain) - doc['credentials']['abot-epc']['abot-epc']['user-domain-name'] = ( - u_domain) - - with open(file_name, 'w') as yfile: - yaml.safe_dump(doc, yfile, default_flow_style=False) -- cgit 1.2.3-korg