3 # Copyright (c) 2016 Rebaca and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 """Juju testcase implementation."""
18 from copy import deepcopy
19 from urlparse import urljoin
23 from functest.core import vnf
24 from functest.opnfv_tests.openstack.snaps import snaps_utils
25 from functest.utils import config
26 from functest.utils import env
28 from snaps.config.flavor import FlavorConfig
29 from snaps.config.image import ImageConfig
30 from snaps.config.network import NetworkConfig, SubnetConfig
31 from snaps.config.router import RouterConfig
32 from snaps.config.security_group import (
33 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
34 from snaps.config.user import UserConfig
35 from snaps.openstack.create_flavor import OpenStackFlavor
36 from snaps.openstack.create_image import OpenStackImage
37 from snaps.openstack.create_network import OpenStackNetwork
38 from snaps.openstack.create_router import OpenStackRouter
39 from snaps.openstack.create_security_group import OpenStackSecurityGroup
40 from snaps.openstack.create_user import OpenStackUser
41 from snaps.openstack.utils import keystone_utils
42 from snaps.openstack.utils import nova_utils
44 __author__ = "Amarendra Meher <amarendra@rebaca.com>"
45 __author__ = "Soumaya K Nayek <soumaya.nayek@rebaca.com>"
47 CLOUD_TEMPLATE = """clouds:
50 auth-types: [userpass]
56 CREDS_TEMPLATE2 = """credentials:
58 default-credential: abot-epc
62 project-domain-name: {project_domain_n}
63 tenant-name: {tenant_n}"""
65 CREDS_TEMPLATE3 = """credentials:
67 default-credential: abot-epc
71 project-domain-name: {project_domain_n}
72 tenant-name: {tenant_n}
73 user-domain-name: {user_domain_n}
77 class JujuEpc(vnf.VnfOnBoarding):
78 # pylint:disable=too-many-instance-attributes
79 """Abot EPC deployed with JUJU Orchestrator Case"""
81 __logger = logging.getLogger(__name__)
83 def __init__(self, **kwargs):
84 if "case_name" not in kwargs:
85 kwargs["case_name"] = "juju_epc"
86 super(JujuEpc, self).__init__(**kwargs)
88 # Retrieve the configuration
89 self.case_dir = pkg_resources.resource_filename(
90 'functest', 'opnfv_tests/vnf/epc')
92 self.config = getattr(
93 config.CONF, 'vnf_{}_config'.format(self.case_name))
95 raise Exception("VNF config file not found")
96 self.config_file = os.path.join(self.case_dir, self.config)
97 self.orchestrator = dict(requirements=get_config(
98 "orchestrator.requirements", self.config_file))
100 self.created_object = []
101 self.details['orchestrator'] = dict(
102 name=get_config("orchestrator.name", self.config_file),
103 version=get_config("orchestrator.version", self.config_file),
109 descriptor=get_config("vnf.descriptor", self.config_file),
110 requirements=get_config("vnf.requirements", self.config_file)
112 self.details['vnf'] = dict(
113 descriptor_version=self.vnf['descriptor']['version'],
114 name=get_config("vnf.name", self.config_file),
115 version=get_config("vnf.version", self.config_file),
117 self.__logger.debug("VNF configuration: %s", self.vnf)
119 self.details['test_vnf'] = dict(
120 name=get_config("vnf_test_suite.name", self.config_file),
121 version=get_config("vnf_test_suite.version", self.config_file),
122 tag_name=get_config("vnf_test_suite.tag_name", self.config_file)
124 self.public_auth_url = None
126 self.res_dir = os.path.join(
127 getattr(config.CONF, 'dir_results'), self.case_name)
129 def _bypass_juju_network_discovery_bug(self, name):
130 user_creator = OpenStackUser(
133 name=name, password=str(uuid.uuid4()),
134 domain=self.snaps_creds.user_domain_name,
135 roles={'_member_': self.tenant_name}))
136 user_creator.create()
137 self.created_object.append(user_creator)
140 def _register_cloud(self):
141 self.__logger.info("Creating Cloud for Abot-epc .....")
142 clouds_yaml = os.path.join(self.res_dir, "clouds.yaml")
143 # It allows gating APEX and ensures this testcase is working till
144 # https://jira.opnfv.org/browse/APEX-570 is fixed in APEX.
145 # It must be removed as soon as possible to disable per installer
146 # processing in Functest.
147 region = self.snaps_creds.region_name
148 if not region and env.get('INSTALLER_TYPE') == 'apex':
151 'url': self.public_auth_url,
153 with open(clouds_yaml, 'w') as yfile:
154 yfile.write(CLOUD_TEMPLATE.format(**cloud_data))
156 'juju add-cloud abot-epc -f {} --replace'.format(clouds_yaml)):
157 raise vnf.VnfPreparationException
159 def _register_credentials_v2(self):
160 self.__logger.info("Creating Credentials for Abot-epc .....")
161 user_creator = self._bypass_juju_network_discovery_bug(
162 'juju_network_discovery_bug')
163 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
164 credentials_yaml = os.path.join(self.res_dir, "credentials.yaml")
166 'pass': snaps_creds.password,
167 'tenant_n': snaps_creds.project_name,
168 'user_n': snaps_creds.username}
169 with open(credentials_yaml, 'w') as yfile:
170 yfile.write(CREDS_TEMPLATE2.format(**creds_data))
172 'juju add-credential abot-epc -f {} --replace'.format(
174 raise vnf.VnfPreparationException
176 def _register_credentials_v3(self):
177 self.__logger.info("Creating Credentials for Abot-epc .....")
178 user_creator = self._bypass_juju_network_discovery_bug(
179 'juju_network_discovery_bug')
180 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
181 credentials_yaml = os.path.join(self.res_dir, "credentials.yaml")
183 'pass': snaps_creds.password,
184 'tenant_n': snaps_creds.project_name,
185 'user_n': snaps_creds.username,
186 'project_domain_n': snaps_creds.project_domain_name,
187 'user_domain_n': snaps_creds.user_domain_name}
188 with open(credentials_yaml, 'w') as yfile:
189 yfile.write(CREDS_TEMPLATE3.format(**creds_data))
191 'juju add-credential abot-epc -f {} --replace'.format(
193 raise vnf.VnfPreparationException
195 def _add_custom_rule(self, sec_grp_name):
196 """ To add custom rule for SCTP Traffic """
197 sec_grp_rules = list()
198 sec_grp_rules.append(
199 SecurityGroupRuleConfig(
200 sec_grp_name=sec_grp_name, direction=Direction.ingress,
201 protocol=Protocol.sctp))
202 security_group = OpenStackSecurityGroup(
206 rule_settings=sec_grp_rules))
207 security_group.create()
208 self.created_object.append(security_group)
211 """Prepare testcase (Additional pre-configuration steps)."""
212 self.__logger.info("Additional pre-configuration steps")
213 super(JujuEpc, self).prepare()
215 os.makedirs(self.res_dir)
216 except OSError as ex:
217 if ex.errno != errno.EEXIST:
218 self.__logger.exception("Cannot create %s", self.res_dir)
219 raise vnf.VnfPreparationException
220 self.public_auth_url = keystone_utils.get_endpoint(
221 self.snaps_creds, 'identity')
222 # it enforces a versioned public identity endpoint as juju simply
223 # adds /auth/tokens wich fails vs an unversioned endpoint.
224 if not self.public_auth_url.endswith(('v3', 'v3/', 'v2.0', 'v2.0/')):
225 self.public_auth_url = urljoin(self.public_auth_url, 'v3')
226 self._register_cloud()
227 if self.snaps_creds.identity_api_version == 3:
228 self._register_credentials_v3()
230 self._register_credentials_v2()
232 def deploy_orchestrator(self): # pylint: disable=too-many-locals
234 Create network, subnet, router
238 self.__logger.info("Deployed Orchestrator")
239 private_net_name = getattr(
240 config.CONF, 'vnf_{}_private_net_name'.format(self.case_name))
241 private_subnet_name = '{}-{}'.format(
243 'vnf_{}_private_subnet_name'.format(self.case_name)),
245 private_subnet_cidr = getattr(
246 config.CONF, 'vnf_{}_private_subnet_cidr'.format(self.case_name))
247 abot_router = '{}-{}'.format(
249 'vnf_{}_external_router'.format(self.case_name)),
251 self.__logger.info("Creating full network ...")
252 subnet_settings = SubnetConfig(
253 name=private_subnet_name, cidr=private_subnet_cidr)
254 network_settings = NetworkConfig(
255 name=private_net_name, subnet_settings=[subnet_settings])
256 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
257 net_id = network_creator.create().id
258 self.created_object.append(network_creator)
260 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
261 self.__logger.info("Creating network Router ....")
262 router_creator = OpenStackRouter(
263 self.snaps_creds, RouterConfig(
265 external_gateway=ext_net_name,
266 internal_subnets=[subnet_settings.name]))
267 router_creator.create()
268 self.created_object.append(router_creator)
269 self.__logger.info("Creating Flavor ....")
270 flavor_settings = FlavorConfig(
271 name=self.orchestrator['requirements']['flavor']['name'],
272 ram=self.orchestrator['requirements']['flavor']['ram_min'],
274 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
275 flavor_creator.create()
276 self.created_object.append(flavor_creator)
277 self.__logger.info("Upload some OS images if it doesn't exist")
278 images = get_config("tenant_images", self.config_file)
279 self.__logger.info("Images needed for vEPC: %s", images)
280 for image_name, image_file in images.iteritems():
281 self.__logger.info("image: %s, file: %s", image_name, image_file)
282 if image_file and image_name:
283 image_creator = OpenStackImage(self.snaps_creds, ImageConfig(
284 name=image_name, image_user='cloud', img_format='qcow2',
285 image_file=image_file))
286 image_id = image_creator.create().id
287 # It allows gating APEX and ensures this testcase is working
288 # till https://jira.opnfv.org/browse/APEX-570 is fixed in APEX.
289 # It must be removed as soon as possible to disable per
290 # installer processing in Functest.
291 region = self.snaps_creds.region_name
292 if not region and env.get('INSTALLER_TYPE') == 'apex':
295 'juju metadata generate-image -d ~ -i {} -s {} -r '
297 image_id, image_name, region,
298 self.public_auth_url))
299 self.created_object.append(image_creator)
300 self.__logger.info("Network ID : %s", net_id)
301 juju_bootstrap_command = (
302 'juju bootstrap abot-epc abot-controller --config network={} '
303 '--metadata-source ~ --config ssl-hostname-verification=false '
304 '--constraints mem=2G --bootstrap-series xenial '
305 '--config use-floating-ip=true --debug '
306 '--config use-default-secgroup=true'.format(net_id))
307 os.system(juju_bootstrap_command)
310 def deploy_vnf(self):
311 """Deploy ABOT-OAI-EPC."""
312 self.__logger.info("Upload VNFD")
313 descriptor = self.vnf['descriptor']
314 self.__logger.info("Get or create flavor for all Abot-EPC")
315 flavor_settings = FlavorConfig(
316 name=self.vnf['requirements']['flavor']['name'],
317 ram=self.vnf['requirements']['flavor']['ram_min'],
320 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
321 flavor_creator.create()
322 self.created_object.append(flavor_creator)
323 self.__logger.info("Deploying Abot-epc bundle file ...")
324 os.system('juju deploy {}'.format('/' + descriptor.get('file_name')))
325 self.__logger.info("Waiting for instances .....")
326 status = os.system('juju-wait')
327 self.__logger.info("juju wait completed: %s", status)
328 self.__logger.info("Deployed Abot-epc on Openstack")
329 nova_client = nova_utils.nova_client(self.snaps_creds)
331 instances = get_instances(nova_client)
332 self.__logger.info("List of Instance: %s", instances)
333 for items in instances:
334 metadata = get_instance_metadata(nova_client, items)
335 if 'juju-units-deployed' in metadata:
336 sec_group = ('juju-' +
337 metadata['juju-controller-uuid'] +
338 '-' + metadata['juju-model-uuid'])
339 self.__logger.info("Instance: %s", sec_group)
341 self.__logger.info("Adding Security group rule....")
342 # This will add sctp rule to a common Security Group Created
343 # by juju and shared to all deployed units.
344 self._add_custom_rule(sec_group)
345 self.__logger.info("Copying the feature files to Abot_node ")
346 os.system('juju scp -- -r {}/featureFiles abot-'
347 'epc-basic/0:~/'.format(self.case_dir))
348 self.__logger.info("Copying the feature files in Abot_node ")
349 os.system("juju ssh abot-epc-basic/0 'sudo rsync -azvv "
350 "~/featureFiles /etc/rebaca-test-suite"
354 epcstatus = os.system('juju status oai-epc | '
355 'grep {} | grep {} | grep {}'
356 .format('EPC', 'is', 'running'))
362 os.system('juju-wait')
367 """Run test on ABoT."""
368 start_time = time.time()
369 self.__logger.info("Running VNF Test cases....")
370 os.system('juju run-action abot-epc-basic/0 run '
371 'tagnames={}'.format(self.details['test_vnf']['tag_name']))
372 os.system('juju-wait')
373 duration = time.time() - start_time
374 self.__logger.info("Getting results from Abot node....")
375 os.system('juju scp abot-epc-basic/0:/var/lib/abot-'
376 'epc-basic/artifacts/TestResults.json {}/.'
377 .format(self.case_dir))
378 self.__logger.info("Parsing the Test results...")
379 res = (process_abot_test_result('{}/TestResults.'
380 'json'.format(self.case_dir)))
381 short_result = sig_test_format(res)
382 self.__logger.info(short_result)
383 self.details['test_vnf'].update(status='PASS',
388 self.__logger.info("Test VNF result: Passed: %d, Failed:"
389 "%d, Skipped: %d", short_result['passed'],
390 short_result['failures'], short_result['skipped'])
394 """Clean created objects/functions."""
396 if not self.orchestrator['requirements']['preserve_setup']:
397 self.__logger.info("Removing deployment files...")
398 testresult = os.path.join(self.case_dir, 'TestResults.json')
399 if os.path.exists(testresult):
400 os.remove(testresult)
401 self.__logger.info("Destroying Orchestrator...")
402 os.system('juju destroy-controller -y abot-controller '
403 '--destroy-all-models')
404 except Exception: # pylint: disable=broad-except
405 self.__logger.warn("Some issue during the undeployment ..")
406 self.__logger.warn("Tenant clean continue ..")
408 if not self.orchestrator['requirements']['preserve_setup']:
409 self.__logger.info('Remove the Abot_epc OS object ..')
410 super(JujuEpc, self).clean()
415 # ----------------------------------------------------------
419 # -----------------------------------------------------------
420 def get_config(parameter, file_path):
422 Returns the value of a given parameter in file.yaml
423 parameter must be given in string format with dots
424 Example: general.openstack.image_name
426 with open(file_path) as config_file:
427 file_yaml = yaml.safe_load(config_file)
430 for element in parameter.split("."):
431 value = value.get(element)
433 raise ValueError("The parameter %s is not defined in"
434 " reporting.yaml" % parameter)
438 def sig_test_format(sig_test):
440 Process the signaling result to have a short result
445 for data_test in sig_test:
446 if data_test['result'] == "passed":
448 elif data_test['result'] == "failed":
450 elif data_test['result'] == "skipped":
452 total_sig_test_result = {}
453 total_sig_test_result['passed'] = nb_passed
454 total_sig_test_result['failures'] = nb_failures
455 total_sig_test_result['skipped'] = nb_skipped
456 return total_sig_test_result
459 def process_abot_test_result(file_path):
460 """ Process ABoT Result """
461 with open(file_path) as test_result:
462 data = json.load(test_result)
465 tests = update_data(tests)
467 flatten_steps = tests['elements'][0].pop('flatten_steps')
468 for steps in flatten_steps:
469 steps['result'] = steps['step_status']
472 logging.error("Could not post data to ElasticSearch host")
477 def update_data(obj):
478 """ Update Result data"""
480 obj['feature_file'] = os.path.splitext(os.path.basename(obj['uri']))[0]
482 for element in obj['elements']:
483 element['final_result'] = "passed"
484 element['flatten_steps'] = []
486 for step in element['steps']:
488 step['result'][step['result']['status']] = 1
489 if step['result']['status'].lower() in ['fail', 'failed']:
490 element['final_result'] = "failed"
492 temp_dict['feature_file'] = obj['feature_file']
493 temp_dict['step_name'] = step['name']
494 temp_dict['step_status'] = step['result']['status']
495 temp_dict['step_duration'] = step['result'].get('duration', 0)
496 temp_dict['step_' + step['result']['status']] = 1
497 element['flatten_steps'].append(deepcopy(temp_dict))
499 # Need to put the tag in OBJ and not ELEMENT
501 element['tags'] = deepcopy(obj['tags'])
502 for tag in obj['tags']:
503 element[tag['name']] = 1
505 for tag in element['tags']:
506 element[tag['name']] = 1
508 except Exception: # pylint: disable=broad-except
509 logging.error("Error in updating data, %s", sys.exc_info()[0])
515 def get_instances(nova_client):
516 """ To get all vm info of a project """
518 instances = nova_client.servers.list()
520 except Exception as exc: # pylint: disable=broad-except
521 logging.error("Error [get_instances(nova_client)]: %s", exc)
525 def get_instance_metadata(nova_client, instance):
526 """ Get instance Metadata - Instance ID """
528 instance = nova_client.servers.get(instance.id)
529 return instance.metadata
530 except Exception as exc: # pylint: disable=broad-except
531 logging.error("Error [get_instance_status(nova_client)]: %s", exc)