3 # Copyright (c) 2017 Orange 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
10 """CloudifyIms testcase implementation."""
16 from cloudify_rest_client import CloudifyClient
17 from cloudify_rest_client.executions import Execution
18 from scp import SCPClient
21 from functest.energy import energy
22 from functest.opnfv_tests.openstack.snaps import snaps_utils
23 import functest.opnfv_tests.vnf.ims.clearwater_ims_base as clearwater_ims_base
24 from functest.utils.constants import CONST
25 import functest.utils.openstack_utils as os_utils
27 from snaps.openstack.create_network import (NetworkSettings, SubnetSettings,
29 from snaps.openstack.create_security_group import (SecurityGroupSettings,
30 SecurityGroupRuleSettings,
32 OpenStackSecurityGroup)
33 from snaps.openstack.create_router import RouterSettings, OpenStackRouter
34 from snaps.openstack.create_instance import (VmInstanceSettings,
37 from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
38 from snaps.openstack.create_image import ImageSettings, OpenStackImage
39 from snaps.openstack.create_keypairs import KeypairSettings, OpenStackKeypair
40 from snaps.openstack.create_network import PortSettings
43 __author__ = "Valentin Boucher <valentin.boucher@orange.com>"
46 class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase):
47 """Clearwater vIMS deployed with Cloudify Orchestrator Case."""
49 __logger = logging.getLogger(__name__)
51 def __init__(self, **kwargs):
52 """Initialize CloudifyIms testcase object."""
53 if "case_name" not in kwargs:
54 kwargs["case_name"] = "cloudify_ims"
55 super(CloudifyIms, self).__init__(**kwargs)
57 # Retrieve the configuration
59 self.config = CONST.__getattribute__(
60 'vnf_{}_config'.format(self.case_name))
62 raise Exception("VNF config file not found")
66 config_file = os.path.join(self.case_dir, self.config)
67 self.orchestrator = dict(
68 requirements=get_config("orchestrator.requirements", config_file),
70 self.details['orchestrator'] = dict(
71 name=get_config("orchestrator.name", config_file),
72 version=get_config("orchestrator.version", config_file),
76 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
78 descriptor=get_config("vnf.descriptor", config_file),
79 inputs=get_config("vnf.inputs", config_file),
80 requirements=get_config("vnf.requirements", config_file)
82 self.details['vnf'] = dict(
83 descriptor_version=self.vnf['descriptor']['version'],
84 name=get_config("vnf.name", config_file),
85 version=get_config("vnf.version", config_file),
87 self.__logger.debug("VNF configuration: %s", self.vnf)
89 self.details['test_vnf'] = dict(
90 name=get_config("vnf_test_suite.name", config_file),
91 version=get_config("vnf_test_suite.version", config_file)
93 self.images = get_config("tenant_images", config_file)
94 self.__logger.info("Images needed for vIMS: %s", self.images)
97 """Prepare testscase (Additional pre-configuration steps)."""
98 super(CloudifyIms, self).prepare()
100 self.__logger.info("Additional pre-configuration steps")
102 compute_quotas = self.os_project.get_compute_quotas()
103 network_quotas = self.os_project.get_network_quotas()
106 self.vnf['requirements']['compute_quotas'].items()):
107 setattr(compute_quotas, key, value)
110 self.vnf['requirements']['network_quotas'].items()):
111 setattr(network_quotas, key, value)
113 compute_quotas = self.os_project.update_compute_quotas(compute_quotas)
114 network_quotas = self.os_project.update_network_quotas(network_quotas)
117 self.__logger.info("Upload some OS images if it doesn't exist")
118 for image_name, image_file in self.images.iteritems():
119 self.__logger.info("image: %s, file: %s", image_name, image_file)
120 if image_file and image_name:
121 image_creator = OpenStackImage(
123 ImageSettings(name=image_name,
126 image_file=image_file))
127 image_creator.create()
128 # self.created_object.append(image_creator)
130 def deploy_orchestrator(self):
132 Deploy Cloudify Manager.
134 network, security group, fip, VM creation
138 start_time = time.time()
139 self.__logger.info("Creating keypair ...")
140 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
141 keypair_settings = KeypairSettings(name='cloudify_ims_kp',
142 private_filepath=kp_file)
143 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
144 keypair_creator.create()
145 self.created_object.append(keypair_creator)
147 self.__logger.info("Creating full network ...")
148 subnet_settings = SubnetSettings(name='cloudify_ims_subnet',
149 cidr='10.67.79.0/24')
150 network_settings = NetworkSettings(name='cloudify_ims_network',
151 subnet_settings=[subnet_settings])
152 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
153 network_creator.create()
154 self.created_object.append(network_creator)
155 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
156 router_creator = OpenStackRouter(
159 name='cloudify_ims_router',
160 external_gateway=ext_net_name,
161 internal_subnets=[subnet_settings.name]))
162 router_creator.create()
163 self.created_object.append(router_creator)
165 # security group creation
166 self.__logger.info("Creating security group for cloudify manager vm")
169 SecurityGroupRuleSettings(sec_grp_name="sg-cloudify-manager",
170 direction=Direction.ingress,
171 protocol=Protocol.tcp, port_range_min=1,
172 port_range_max=65535))
174 SecurityGroupRuleSettings(sec_grp_name="sg-cloudify-manager",
175 direction=Direction.ingress,
176 protocol=Protocol.udp, port_range_min=1,
177 port_range_max=65535))
179 securit_group_creator = OpenStackSecurityGroup(
181 SecurityGroupSettings(
182 name="sg-cloudify-manager",
183 rule_settings=sg_rules))
185 securit_group_creator.create()
186 self.created_object.append(securit_group_creator)
188 # orchestrator VM flavor
189 self.__logger.info("Get or create flavor for cloudify manager vm ...")
191 flavor_settings = FlavorSettings(
192 name=self.orchestrator['requirements']['flavor']['name'],
193 ram=self.orchestrator['requirements']['flavor']['ram_min'],
196 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
197 flavor_creator.create()
198 self.created_object.append(flavor_creator)
199 image_settings = ImageSettings(
200 name=self.orchestrator['requirements']['os_image'],
204 port_settings = PortSettings(name='cloudify_manager_port',
205 network_name=network_settings.name)
207 manager_settings = VmInstanceSettings(
208 name='cloudify_manager',
209 flavor=flavor_settings.name,
210 port_settings=[port_settings],
211 security_group_names=[securit_group_creator.sec_grp_settings.name],
212 floating_ip_settings=[FloatingIpSettings(
213 name='cloudify_manager_fip',
214 port_name=port_settings.name,
215 router_name=router_creator.router_settings.name)])
217 manager_creator = OpenStackVmInstance(self.snaps_creds,
222 self.__logger.info("Creating cloudify manager VM")
223 manager_creator.create()
224 self.created_object.append(manager_creator)
226 public_auth_url = os_utils.get_endpoint('identity')
228 self.__logger.info("Set creds for cloudify manager")
229 cfy_creds = dict(keystone_username=self.tenant_name,
230 keystone_password=self.tenant_name,
231 keystone_tenant_name=self.tenant_name,
232 keystone_url=public_auth_url)
234 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
237 tenant='default_tenant')
239 self.orchestrator['object'] = cfy_client
241 self.__logger.info("Attemps running status of the Manager")
244 while str(cfy_status) != 'running' and retry:
246 cfy_status = cfy_client.manager.get_status()['status']
247 self.__logger.debug("The current manager status is %s",
249 except Exception: # pylint: disable=broad-except
250 self.__logger.warning("Cloudify Manager isn't " +
251 "up and running. Retrying ...")
255 if str(cfy_status) == 'running':
256 self.__logger.info("Cloudify Manager is up and running")
258 raise Exception("Cloudify Manager isn't up and running")
260 self.__logger.info("Put OpenStack creds in manager")
261 secrets_list = cfy_client.secrets.list()
262 for k, val in cfy_creds.iteritems():
263 if not any(d.get('key', None) == k for d in secrets_list):
264 cfy_client.secrets.create(k, val)
266 cfy_client.secrets.update(k, val)
268 duration = time.time() - start_time
270 self.__logger.info("Put private keypair in manager")
271 if manager_creator.vm_ssh_active(block=True):
272 ssh = manager_creator.ssh_client()
273 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
274 scp.put(kp_file, '~/')
275 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
276 run_blocking_ssh_command(ssh, cmd)
277 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
278 run_blocking_ssh_command(ssh, cmd)
279 cmd = "sudo yum install -y gcc python-devel"
280 run_blocking_ssh_command(ssh, cmd, "Unable to install packages \
283 self.details['orchestrator'].update(status='PASS', duration=duration)
285 self.vnf['inputs'].update(dict(
286 external_network_name=ext_net_name,
287 network_name=network_settings.name
289 self.result = 1/3 * 100
292 def deploy_vnf(self):
293 """Deploy Clearwater IMS."""
294 start_time = time.time()
296 self.__logger.info("Upload VNFD")
297 cfy_client = self.orchestrator['object']
298 descriptor = self.vnf['descriptor']
299 cfy_client.blueprints.publish_archive(descriptor.get('url'),
300 descriptor.get('name'),
301 descriptor.get('file_name'))
303 self.__logger.info("Get or create flavor for all clearwater vm")
304 flavor_settings = FlavorSettings(
305 name=self.vnf['requirements']['flavor']['name'],
306 ram=self.vnf['requirements']['flavor']['ram_min'],
309 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
310 flavor_creator.create()
311 self.created_object.append(flavor_creator)
313 self.vnf['inputs'].update(dict(
314 flavor_id=self.vnf['requirements']['flavor']['name'],
317 self.__logger.info("Create VNF Instance")
318 cfy_client.deployments.create(descriptor.get('name'),
319 descriptor.get('name'),
320 self.vnf.get('inputs'))
322 wait_for_execution(cfy_client,
323 _get_deployment_environment_creation_execution(
324 cfy_client, descriptor.get('name')),
328 self.__logger.info("Start the VNF Instance deployment")
329 execution = cfy_client.executions.start(descriptor.get('name'),
332 execution = wait_for_execution(cfy_client, execution, self.__logger)
334 duration = time.time() - start_time
336 self.__logger.info(execution)
337 if execution.status == 'terminated':
338 self.details['vnf'].update(status='PASS', duration=duration)
339 self.result += 1/3 * 100
342 self.details['vnf'].update(status='FAIL', duration=duration)
347 """Run test on clearwater ims instance."""
348 start_time = time.time()
350 cfy_client = self.orchestrator['object']
352 outputs = cfy_client.deployments.outputs.get(
353 self.vnf['descriptor'].get('name'))['outputs']
354 dns_ip = outputs['dns_ip']
355 ellis_ip = outputs['ellis_ip']
356 self.config_ellis(ellis_ip)
361 vims_test_result = self.run_clearwater_live_test(
363 public_domain=self.vnf['inputs']["public_domain"])
364 duration = time.time() - start_time
365 short_result, nb_test = sig_test_format(vims_test_result)
366 self.__logger.info(short_result)
367 self.details['test_vnf'].update(result=short_result,
368 full_result=vims_test_result,
371 vnf_test_rate = short_result['passed'] / nb_test
372 # orchestrator + vnf + test_vnf
373 self.result += vnf_test_rate / 3 * 100
374 except ZeroDivisionError:
375 self.__logger.error("No test has been executed")
376 self.details['test_vnf'].update(status='FAIL')
382 """Clean created objects/functions."""
384 cfy_client = self.orchestrator['object']
385 dep_name = self.vnf['descriptor'].get('name')
386 # kill existing execution
387 self.__logger.info('Deleting the current deployment')
388 exec_list = cfy_client.executions.list(dep_name)
389 for execution in exec_list:
390 if execution['status'] == "started":
392 cfy_client.executions.cancel(execution['id'],
394 except: # pylint: disable=broad-except
395 self.__logger.warn("Can't cancel the current exec")
397 execution = cfy_client.executions.start(
400 parameters=dict(ignore_failure=True),
403 wait_for_execution(cfy_client, execution, self.__logger)
404 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
405 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
406 except: # pylint: disable=broad-except
407 self.__logger.warn("Some issue during the undeployment ..")
408 self.__logger.warn("Tenant clean continue ..")
410 super(CloudifyIms, self).clean()
412 @energy.enable_recording
413 def run(self, **kwargs):
414 """Execute CloudifyIms test case."""
415 return super(CloudifyIms, self).run(**kwargs)
418 # ----------------------------------------------------------
422 # -----------------------------------------------------------
423 def get_config(parameter, file_path):
425 Get config parameter.
427 Returns the value of a given parameter in file.yaml
428 parameter must be given in string format with dots
429 Example: general.openstack.image_name
431 with open(file_path) as config_file:
432 file_yaml = yaml.safe_load(config_file)
435 for element in parameter.split("."):
436 value = value.get(element)
438 raise ValueError("The parameter %s is not defined in"
439 " reporting.yaml" % parameter)
443 def wait_for_execution(client, execution, logger, timeout=2400, ):
444 """Wait for a workflow execution on Cloudify Manager."""
445 # if execution already ended - return without waiting
446 if execution.status in Execution.END_STATES:
449 if timeout is not None:
450 deadline = time.time() + timeout
452 # Poll for execution status and execution logs, until execution ends
453 # and we receive an event of type in WORKFLOW_END_TYPES
457 execution_ended = False
459 event_list = client.events.list(
460 execution_id=execution.id,
464 sort='@timestamp').items
466 offset = offset + len(event_list)
467 for event in event_list:
468 logger.debug(event.get('message'))
470 if timeout is not None:
471 if time.time() > deadline:
473 'execution of operation {0} for deployment {1} '
474 'timed out'.format(execution.workflow_id,
475 execution.deployment_id))
477 # update the remaining timeout
478 timeout = deadline - time.time()
480 if not execution_ended:
481 execution = client.executions.get(execution.id)
482 execution_ended = execution.status in Execution.END_STATES
492 def _get_deployment_environment_creation_execution(client, deployment_id):
494 Get the execution id of a env preparation.
496 network, security group, fip, VM creation
498 executions = client.executions.list(deployment_id=deployment_id)
499 for execution in executions:
500 if execution.workflow_id == 'create_deployment_environment':
502 raise RuntimeError('Failed to get create_deployment_environment '
503 'workflow execution.'
504 'Available executions: {0}'.format(executions))
507 def sig_test_format(sig_test):
508 """Process the signaling result to have a short result."""
512 for data_test in sig_test:
513 if data_test['result'] == "Passed":
515 elif data_test['result'] == "Failed":
517 elif data_test['result'] == "Skipped":
519 short_sig_test_result = {}
520 short_sig_test_result['passed'] = nb_passed
521 short_sig_test_result['failures'] = nb_failures
522 short_sig_test_result['skipped'] = nb_skipped
523 nb_test = nb_passed + nb_skipped
524 return (short_sig_test_result, nb_test)
527 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
528 """Command to run ssh command with the exit status."""
529 stdin, stdout, stderr = ssh.exec_command(cmd)
530 if stdout.channel.recv_exit_status() != 0:
531 raise Exception(error_msg)