3 # Copyright (c) 2017 Okinawa Open Laboratory 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 """vrouter 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.opnfv_tests.openstack.snaps import snaps_utils
22 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
23 from functest.utils.constants import CONST
24 import functest.utils.openstack_utils as os_utils
28 from snaps.openstack.os_credentials import OSCreds
29 from snaps.openstack.create_network import (NetworkSettings, SubnetSettings,
31 from snaps.openstack.create_security_group import (SecurityGroupSettings,
32 SecurityGroupRuleSettings,
34 OpenStackSecurityGroup)
35 from snaps.openstack.create_router import RouterSettings, OpenStackRouter
36 from snaps.openstack.create_instance import (VmInstanceSettings,
39 from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
40 from snaps.openstack.create_image import ImageSettings, OpenStackImage
41 from snaps.openstack.create_keypairs import KeypairSettings, OpenStackKeypair
42 from snaps.openstack.create_network import PortSettings
43 import snaps.openstack.utils.glance_utils as glance_utils
45 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
47 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
50 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
51 """vrouter testcase deployed with Cloudify Orchestrator."""
53 __logger = logging.getLogger(__name__)
56 def __init__(self, **kwargs):
57 if "case_name" not in kwargs:
58 kwargs["case_name"] = "vyos_vrouter"
59 super(CloudifyVrouter, self).__init__(**kwargs)
61 # Retrieve the configuration
63 self.config = CONST.__getattribute__(
64 'vnf_{}_config'.format(self.case_name))
66 raise Exception("VNF config file not found")
69 self.created_object = []
71 self.cfy_manager_ip = ''
73 self.deployment_name = ''
75 config_file = os.path.join(self.case_dir, self.config)
76 self.orchestrator = dict(
77 requirements=get_config("orchestrator.requirements", config_file),
79 self.details['orchestrator'] = dict(
80 name=get_config("orchestrator.name", config_file),
81 version=get_config("orchestrator.version", config_file),
85 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
86 self.__logger.debug("name = %s", self.name)
88 descriptor=get_config("vnf.descriptor", config_file),
89 inputs=get_config("vnf.inputs", config_file),
90 requirements=get_config("vnf.requirements", config_file)
92 self.details['vnf'] = dict(
93 descriptor_version=self.vnf['descriptor']['version'],
94 name=get_config("vnf.name", config_file),
95 version=get_config("vnf.version", config_file),
97 self.__logger.debug("VNF configuration: %s", self.vnf)
101 self.details['test_vnf'] = dict(
102 name=get_config("vnf_test_suite.name", config_file),
103 version=get_config("vnf_test_suite.version", config_file)
105 self.images = get_config("tenant_images", config_file)
106 self.__logger.info("Images needed for vrouter: %s", self.images)
109 super(CloudifyVrouter, self).prepare()
111 self.__logger.info("Additional pre-configuration steps")
113 self.snaps_creds = OSCreds(
114 username=self.creds['username'],
115 password=self.creds['password'],
116 auth_url=self.creds['auth_url'],
117 project_name=self.creds['tenant'],
118 identity_api_version=int(os_utils.get_keystone_client_version()))
120 self.util.set_credentials(self.creds["username"],
121 self.creds["password"],
122 self.creds["auth_url"],
123 self.creds["tenant"])
126 self.__logger.info("Upload some OS images if it doesn't exist")
127 for image_name, image_file in self.images.iteritems():
128 self.__logger.info("image: %s, file: %s", image_name, image_file)
129 if image_file and image_name:
130 image_creator = OpenStackImage(
132 ImageSettings(name=image_name,
135 image_file=image_file))
136 image_creator.create()
137 self.created_object.append(image_creator)
139 def deploy_orchestrator(self):
141 Deploy Cloudify Manager.
142 network, security group, fip, VM creation
146 start_time = time.time()
147 self.__logger.info("Creating keypair ...")
148 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
149 keypair_settings = KeypairSettings(name='cloudify_vrouter_kp',
150 private_filepath=kp_file)
151 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
152 keypair_creator.create()
153 self.created_object.append(keypair_creator)
155 self.__logger.info("Creating full network ...")
156 subnet_settings = SubnetSettings(name='cloudify_vrouter_subnet',
157 cidr='10.67.79.0/24')
158 network_settings = NetworkSettings(name='cloudify_vrouter_network',
159 subnet_settings=[subnet_settings])
160 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
161 network_creator.create()
162 self.created_object.append(network_creator)
163 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
164 router_creator = OpenStackRouter(
167 name='cloudify_vrouter_router',
168 external_gateway=ext_net_name,
169 internal_subnets=[subnet_settings.name]))
170 router_creator.create()
171 self.created_object.append(router_creator)
173 # security group creation
174 self.__logger.info("Creating security group for cloudify manager vm")
177 SecurityGroupRuleSettings(sec_grp_name="sg-cloudify-manager",
178 direction=Direction.ingress,
179 protocol=Protocol.tcp, port_range_min=1,
180 port_range_max=65535))
182 SecurityGroupRuleSettings(sec_grp_name="sg-cloudify-manager",
183 direction=Direction.ingress,
184 protocol=Protocol.udp, port_range_min=1,
185 port_range_max=65535))
187 security_group_creator = OpenStackSecurityGroup(
189 SecurityGroupSettings(
190 name="sg-cloudify-manager",
191 rule_settings=sg_rules))
193 security_group_creator.create()
194 self.created_object.append(security_group_creator)
196 # orchestrator VM flavor
197 self.__logger.info("Get or create flavor for cloudify manager vm ...")
199 flavor_settings = FlavorSettings(
200 name=self.orchestrator['requirements']['flavor']['name'],
201 ram=self.orchestrator['requirements']['flavor']['ram_min'],
204 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
205 flavor_creator.create()
206 self.created_object.append(flavor_creator)
207 image_settings = ImageSettings(
208 name=self.orchestrator['requirements']['os_image'],
212 port_settings = PortSettings(name='cloudify_manager_port',
213 network_name=network_settings.name)
215 manager_settings = VmInstanceSettings(
216 name='cloudify_manager',
217 flavor=flavor_settings.name,
218 port_settings=[port_settings],
219 security_group_names=[
220 security_group_creator.sec_grp_settings.name],
221 floating_ip_settings=[FloatingIpSettings(
222 name='cloudify_manager_fip',
223 port_name=port_settings.name,
224 router_name=router_creator.router_settings.name)])
226 manager_creator = OpenStackVmInstance(self.snaps_creds,
231 self.__logger.info("Creating cloudify manager VM")
232 manager_creator.create()
233 self.created_object.append(manager_creator)
235 public_auth_url = os_utils.get_endpoint('identity')
237 self.__logger.info("Set creds for cloudify manager")
238 cfy_creds = dict(keystone_username=self.tenant_name,
239 keystone_password=self.tenant_name,
240 keystone_tenant_name=self.tenant_name,
241 keystone_url=public_auth_url)
243 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
246 tenant='default_tenant')
248 self.orchestrator['object'] = cfy_client
250 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
252 self.__logger.info("Attemps running status of the Manager")
255 while str(cfy_status) != 'running' and retry:
257 cfy_status = cfy_client.manager.get_status()['status']
258 self.__logger.debug("The current manager status is %s",
260 except Exception: # pylint: disable=broad-except
261 self.__logger.warning("Cloudify Manager isn't " +
262 "up and running. Retrying ...")
266 if str(cfy_status) == 'running':
267 self.__logger.info("Cloudify Manager is up and running")
269 raise Exception("Cloudify Manager isn't up and running")
271 self.__logger.info("Put OpenStack creds in manager")
272 secrets_list = cfy_client.secrets.list()
273 for k, val in cfy_creds.iteritems():
274 if not any(d.get('key', None) == k for d in secrets_list):
275 cfy_client.secrets.create(k, val)
277 cfy_client.secrets.update(k, val)
279 duration = time.time() - start_time
281 self.__logger.info("Put private keypair in manager")
282 if manager_creator.vm_ssh_active(block=True):
283 ssh = manager_creator.ssh_client()
284 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
285 scp.put(kp_file, '~/')
286 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
287 run_blocking_ssh_command(ssh, cmd)
288 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
289 run_blocking_ssh_command(ssh, cmd)
290 cmd = "sudo yum install -y gcc python-devel"
291 run_blocking_ssh_command(
292 ssh, cmd, "Unable to install packages on manager")
294 self.details['orchestrator'].update(status='PASS', duration=duration)
296 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
300 def deploy_vnf(self):
301 start_time = time.time()
303 self.__logger.info("Upload VNFD")
304 cfy_client = self.orchestrator['object']
305 descriptor = self.vnf['descriptor']
306 self.deployment_name = descriptor.get('name')
308 vrouter_blueprint_dir = os.path.join(self.data_dir,
309 self.util.blueprint_dir)
310 if not os.path.exists(vrouter_blueprint_dir):
311 Repo.clone_from(descriptor.get('url'),
312 vrouter_blueprint_dir,
313 branch=descriptor.get('version'))
315 cfy_client.blueprints.upload(vrouter_blueprint_dir +
316 self.util.blueprint_file_name,
317 descriptor.get('name'))
319 self.__logger.info("Get or create flavor for vrouter")
320 flavor_settings = FlavorSettings(
321 name=self.vnf['requirements']['flavor']['name'],
322 ram=self.vnf['requirements']['flavor']['ram_min'],
325 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
326 flavor = flavor_creator.create()
327 self.created_object.append(flavor_creator)
330 glance = glance_utils.glance_client(self.snaps_creds)
331 image = glance_utils.get_image(glance,
333 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
334 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
337 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
338 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
340 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
341 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
342 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
343 self.vnf['inputs'].update(
344 dict(keystone_url=os_utils.get_endpoint('identity')))
346 self.__logger.info("Create VNF Instance")
347 cfy_client.deployments.create(descriptor.get('name'),
348 descriptor.get('name'),
349 self.vnf.get('inputs'))
351 wait_for_execution(cfy_client,
353 cfy_client, descriptor.get('name')),
357 self.__logger.info("Start the VNF Instance deployment")
358 execution = cfy_client.executions.start(descriptor.get('name'),
361 execution = wait_for_execution(cfy_client, execution, self.__logger)
363 duration = time.time() - start_time
365 self.__logger.info(execution)
366 if execution.status == 'terminated':
367 self.details['vnf'].update(status='PASS', duration=duration)
370 self.details['vnf'].update(status='FAIL', duration=duration)
375 cfy_client = self.orchestrator['object']
376 credentials = {"username": self.creds["username"],
377 "password": self.creds["password"],
378 "auth_url": self.creds["auth_url"],
379 "tenant_name": self.creds["tenant"],
380 "region_name": os.environ['OS_REGION_NAME']}
382 self.util_info = {"credentials": credentials,
384 "vnf_data_dir": self.util.vnf_data_dir}
386 start_time = time.time()
388 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
390 duration = time.time() - start_time
393 self.details['test_vnf'].update(status='PASS',
395 full_result=test_result_data,
398 self.details['test_vnf'].update(status='FAIL',
400 full_result=test_result_data,
407 cfy_client = self.orchestrator['object']
408 dep_name = self.vnf['descriptor'].get('name')
409 # kill existing execution
410 self.__logger.info('Deleting the current deployment')
411 exec_list = cfy_client.executions.list(dep_name)
412 for execution in exec_list:
413 if execution['status'] == "started":
415 cfy_client.executions.cancel(execution['id'],
417 except: # pylint: disable=broad-except
418 self.__logger.warn("Can't cancel the current exec")
420 execution = cfy_client.executions.start(
423 parameters=dict(ignore_failure=True))
425 wait_for_execution(cfy_client, execution, self.__logger)
426 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
427 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
428 except: # pylint: disable=broad-except
429 self.__logger.warn("Some issue during the undeployment ..")
430 self.__logger.warn("Tenant clean continue ..")
432 self.__logger.info('Remove the cloudify manager OS object ..')
433 for creator in reversed(self.created_object):
436 except Exception as exc:
437 self.logger.error('Unexpected error cleaning - %s', exc)
439 super(CloudifyVrouter, self).clean()
441 def run(self, **kwargs):
442 """Execute CloudifyVrouter test case."""
443 return super(CloudifyVrouter, self).run(**kwargs)
445 def get_vnf_info_list(self, target_vnf_name):
446 return self.util.get_vnf_info_list(self.cfy_manager_ip,
447 self.deployment_name,
451 # ----------------------------------------------------------
455 # -----------------------------------------------------------
456 def get_config(parameter, file_path):
458 Get config parameter.
459 Returns the value of a given parameter in file.yaml
460 parameter must be given in string format with dots
461 Example: general.openstack.image_name
463 with open(file_path) as config_file:
464 file_yaml = yaml.safe_load(config_file)
467 for element in parameter.split("."):
468 value = value.get(element)
470 raise ValueError("The parameter %s is not defined in"
471 " reporting.yaml" % parameter)
475 def wait_for_execution(client, execution, logger, timeout=7200, ):
476 """Wait for a workflow execution on Cloudify Manager."""
477 # if execution already ended - return without waiting
478 if execution.status in Execution.END_STATES:
481 if timeout is not None:
482 deadline = time.time() + timeout
484 # Poll for execution status and execution logs, until execution ends
485 # and we receive an event of type in WORKFLOW_END_TYPES
489 execution_ended = False
491 event_list = client.events.list(
492 execution_id=execution.id,
496 sort='@timestamp').items
498 offset = offset + len(event_list)
499 for event in event_list:
500 logger.debug(event.get('message'))
502 if timeout is not None:
503 if time.time() > deadline:
505 'execution of operation {0} for deployment {1} '
506 'timed out'.format(execution.workflow_id,
507 execution.deployment_id))
509 # update the remaining timeout
510 timeout = deadline - time.time()
512 if not execution_ended:
513 execution = client.executions.get(execution.id)
514 execution_ended = execution.status in Execution.END_STATES
524 def get_execution_id(client, deployment_id):
526 Get the execution id of a env preparation.
527 network, security group, fip, VM creation
529 executions = client.executions.list(deployment_id=deployment_id)
530 for execution in executions:
531 if execution.workflow_id == 'create_deployment_environment':
533 raise RuntimeError('Failed to get create_deployment_environment '
534 'workflow execution.'
535 'Available executions: {0}'.format(executions))
538 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
539 """Command to run ssh command with the exit status."""
540 (_, stdout, _) = ssh.exec_command(cmd)
541 if stdout.channel.recv_exit_status() != 0:
542 raise Exception(error_msg)