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 # pylint: disable=missing-docstring
12 """vrouter testcase implementation."""
19 from cloudify_rest_client import CloudifyClient
20 from cloudify_rest_client.executions import Execution
21 from scp import SCPClient
23 from functest.opnfv_tests.openstack.snaps import snaps_utils
24 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
25 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
26 from functest.utils import config
27 from functest.utils import env
28 from functest.utils import functest_utils
30 from snaps.config.flavor import FlavorConfig
31 from snaps.config.image import ImageConfig
32 from snaps.config.keypair import KeypairConfig
33 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
34 from snaps.config.router import RouterConfig
35 from snaps.config.security_group import (
36 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
37 from snaps.config.user import UserConfig
38 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
40 from snaps.openstack.create_flavor import OpenStackFlavor
41 from snaps.openstack.create_image import OpenStackImage
42 from snaps.openstack.create_instance import OpenStackVmInstance
43 from snaps.openstack.create_keypairs import OpenStackKeypair
44 from snaps.openstack.create_network import OpenStackNetwork
45 from snaps.openstack.create_security_group import OpenStackSecurityGroup
46 from snaps.openstack.create_router import OpenStackRouter
47 from snaps.openstack.create_user import OpenStackUser
49 import snaps.openstack.utils.glance_utils as glance_utils
50 from snaps.openstack.utils import keystone_utils
53 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
56 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
57 # pylint: disable=too-many-instance-attributes
58 """vrouter testcase deployed with Cloudify Orchestrator."""
60 __logger = logging.getLogger(__name__)
63 def __init__(self, **kwargs):
64 if "case_name" not in kwargs:
65 kwargs["case_name"] = "vyos_vrouter"
66 super(CloudifyVrouter, self).__init__(**kwargs)
68 # Retrieve the configuration
70 self.config = getattr(
71 config.CONF, 'vnf_{}_config'.format(self.case_name))
73 raise Exception("VNF config file not found")
75 self.cfy_manager_ip = ''
76 self.deployment_name = ''
78 config_file = os.path.join(self.case_dir, self.config)
79 self.orchestrator = dict(
80 requirements=functest_utils.get_parameter_from_yaml(
81 "orchestrator.requirements", config_file),
83 self.details['orchestrator'] = dict(
84 name=functest_utils.get_parameter_from_yaml(
85 "orchestrator.name", config_file),
86 version=functest_utils.get_parameter_from_yaml(
87 "orchestrator.version", config_file),
91 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
92 self.__logger.debug("name = %s", self.name)
94 descriptor=functest_utils.get_parameter_from_yaml(
95 "vnf.descriptor", config_file),
96 inputs=functest_utils.get_parameter_from_yaml(
97 "vnf.inputs", config_file),
98 requirements=functest_utils.get_parameter_from_yaml(
99 "vnf.requirements", config_file)
101 self.details['vnf'] = dict(
102 descriptor_version=self.vnf['descriptor']['version'],
103 name=functest_utils.get_parameter_from_yaml(
104 "vnf.name", config_file),
105 version=functest_utils.get_parameter_from_yaml(
106 "vnf.version", config_file),
108 self.__logger.debug("VNF configuration: %s", self.vnf)
110 self.util = Utilvnf()
112 self.details['test_vnf'] = dict(
113 name=functest_utils.get_parameter_from_yaml(
114 "vnf_test_suite.name", config_file),
115 version=functest_utils.get_parameter_from_yaml(
116 "vnf_test_suite.version", config_file)
118 self.images = functest_utils.get_parameter_from_yaml(
119 "tenant_images", config_file)
120 self.__logger.info("Images needed for vrouter: %s", self.images)
123 def run_blocking_ssh_command(ssh, cmd,
124 error_msg="Unable to run this command"):
125 """Command to run ssh command with the exit status."""
126 (_, stdout, stderr) = ssh.exec_command(cmd)
127 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
128 if stdout.channel.recv_exit_status() != 0:
129 CloudifyVrouter.__logger.error(
130 "SSH %s stderr: %s", cmd, stderr.read())
131 raise Exception(error_msg)
134 super(CloudifyVrouter, self).prepare()
135 self.__logger.info("Additional pre-configuration steps")
136 self.util.set_credentials(self.snaps_creds)
137 self.__logger.info("Upload some OS images if it doesn't exist")
138 for image_name, image_file in self.images.iteritems():
139 self.__logger.info("image: %s, file: %s", image_name, image_file)
140 if image_file and image_name:
141 image_creator = OpenStackImage(
144 name=image_name, image_user='cloud',
145 img_format='qcow2', image_file=image_file))
146 image_creator.create()
147 self.created_object.append(image_creator)
149 def deploy_orchestrator(self):
150 # pylint: disable=too-many-locals,too-many-statements
152 Deploy Cloudify Manager.
153 network, security group, fip, VM creation
156 start_time = time.time()
157 self.__logger.info("Creating keypair ...")
158 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
159 keypair_settings = KeypairConfig(
160 name='cloudify_vrouter_kp-{}'.format(self.uuid),
161 private_filepath=kp_file)
162 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
163 keypair_creator.create()
164 self.created_object.append(keypair_creator)
166 self.__logger.info("Creating full network ...")
167 subnet_settings = SubnetConfig(
168 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
169 cidr='10.67.79.0/24',
170 dns_nameservers=[env.get('NAMESERVER')])
171 network_settings = NetworkConfig(
172 name='cloudify_vrouter_network-{}'.format(self.uuid),
173 subnet_settings=[subnet_settings])
174 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
175 network_creator.create()
176 self.created_object.append(network_creator)
177 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
178 router_creator = OpenStackRouter(
181 name='cloudify_vrouter_router-{}'.format(self.uuid),
182 external_gateway=ext_net_name,
183 internal_subnets=[subnet_settings.name]))
184 router_creator.create()
185 self.created_object.append(router_creator)
187 # security group creation
188 self.__logger.info("Creating security group for cloudify manager vm")
191 SecurityGroupRuleConfig(
192 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
193 direction=Direction.ingress,
194 protocol=Protocol.tcp, port_range_min=1,
195 port_range_max=65535))
197 SecurityGroupRuleConfig(
198 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
199 direction=Direction.ingress,
200 protocol=Protocol.udp, port_range_min=1,
201 port_range_max=65535))
203 security_group_creator = OpenStackSecurityGroup(
206 name="sg-cloudify-manager-{}".format(self.uuid),
207 rule_settings=sg_rules))
209 security_group_creator.create()
210 self.created_object.append(security_group_creator)
212 # orchestrator VM flavor
213 self.__logger.info("Get or create flavor for cloudify manager vm ...")
215 flavor_settings = FlavorConfig(
216 name=self.orchestrator['requirements']['flavor']['name'],
217 ram=self.orchestrator['requirements']['flavor']['ram_min'],
219 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
220 flavor_creator.create()
221 self.created_object.append(flavor_creator)
222 image_settings = ImageConfig(
223 name=self.orchestrator['requirements']['os_image'],
224 image_user='centos', exists=True)
226 port_settings = PortConfig(
227 name='cloudify_manager_port-{}'.format(self.uuid),
228 network_name=network_settings.name)
230 manager_settings = VmInstanceConfig(
231 name='cloudify_manager-{}'.format(self.uuid),
232 flavor=flavor_settings.name,
233 port_settings=[port_settings],
234 security_group_names=[
235 security_group_creator.sec_grp_settings.name],
236 floating_ip_settings=[FloatingIpConfig(
237 name='cloudify_manager_fip-{}'.format(self.uuid),
238 port_name=port_settings.name,
239 router_name=router_creator.router_settings.name)])
241 manager_creator = OpenStackVmInstance(
242 self.snaps_creds, manager_settings, image_settings,
245 self.__logger.info("Creating cloudify manager VM")
246 manager_creator.create()
247 self.created_object.append(manager_creator)
249 cfy_client = CloudifyClient(
250 host=manager_creator.get_floating_ip().ip,
251 username='admin', password='admin', tenant='default_tenant')
253 self.orchestrator['object'] = cfy_client
255 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
257 self.__logger.info("Attemps running status of the Manager")
260 while str(cfy_status) != 'running' and retry:
262 cfy_status = cfy_client.manager.get_status()['status']
264 "The current manager status is %s", cfy_status)
265 except Exception: # pylint: disable=broad-except
267 "Cloudify Manager isn't up and running. Retrying ...")
271 if str(cfy_status) == 'running':
272 self.__logger.info("Cloudify Manager is up and running")
274 raise Exception("Cloudify Manager isn't up and running")
276 duration = time.time() - start_time
278 self.__logger.info("Put private keypair in manager")
279 if manager_creator.vm_ssh_active(block=True):
280 ssh = manager_creator.ssh_client()
281 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
282 scp.put(kp_file, '~/')
283 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
284 self.run_blocking_ssh_command(ssh, cmd)
285 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
286 self.run_blocking_ssh_command(ssh, cmd)
287 cmd = "sudo yum install -y gcc python-devel"
288 self.run_blocking_ssh_command(
289 ssh, cmd, "Unable to install packages on manager")
291 self.details['orchestrator'].update(status='PASS', duration=duration)
293 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
297 def deploy_vnf(self):
298 start_time = time.time()
300 self.__logger.info("Upload VNFD")
301 cfy_client = self.orchestrator['object']
302 descriptor = self.vnf['descriptor']
303 self.deployment_name = descriptor.get('name')
305 cfy_client.blueprints.upload(
306 descriptor.get('file_name'), descriptor.get('name'))
308 self.__logger.info("Get or create flavor for vrouter")
309 flavor_settings = FlavorConfig(
310 name=self.vnf['requirements']['flavor']['name'],
311 ram=self.vnf['requirements']['flavor']['ram_min'],
313 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
314 flavor = flavor_creator.create()
315 self.created_object.append(flavor_creator)
318 glance = glance_utils.glance_client(self.snaps_creds)
319 image = glance_utils.get_image(glance, "vyos1.1.7")
321 user_creator = OpenStackUser(
324 name='cloudify_network_bug-{}'.format(self.uuid),
325 password=str(uuid.uuid4()),
326 project_name=self.tenant_name,
327 domain_name=self.snaps_creds.user_domain_name,
328 roles={'_member_': self.tenant_name}))
329 user_creator.create()
330 self.created_object.append(user_creator)
331 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
332 self.__logger.debug("snaps creds: %s", snaps_creds)
334 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
335 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
336 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
337 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
338 self.vnf['inputs'].update(dict(
339 keystone_username=snaps_creds.username))
340 self.vnf['inputs'].update(dict(
341 keystone_password=snaps_creds.password))
342 self.vnf['inputs'].update(dict(
343 keystone_tenant_name=snaps_creds.project_name))
344 self.vnf['inputs'].update(dict(
345 keystone_user_domain_name=snaps_creds.user_domain_name))
346 self.vnf['inputs'].update(dict(
347 keystone_project_domain_name=snaps_creds.project_domain_name))
348 self.vnf['inputs'].update(dict(
349 region=snaps_creds.region_name))
350 self.vnf['inputs'].update(dict(
351 keystone_url=keystone_utils.get_endpoint(
352 snaps_creds, 'identity')))
354 self.__logger.info("Create VNF Instance")
355 cfy_client.deployments.create(
356 descriptor.get('name'), descriptor.get('name'),
357 self.vnf.get('inputs'))
360 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
361 self.__logger, timeout=7200)
363 self.__logger.info("Start the VNF Instance deployment")
364 execution = cfy_client.executions.start(descriptor.get('name'),
367 execution = wait_for_execution(cfy_client, execution, self.__logger)
369 duration = time.time() - start_time
371 self.__logger.info(execution)
372 if execution.status == 'terminated':
373 self.details['vnf'].update(status='PASS', duration=duration)
376 self.details['vnf'].update(status='FAIL', duration=duration)
381 cfy_client = self.orchestrator['object']
382 credentials = {"snaps_creds": self.snaps_creds}
384 self.util_info = {"credentials": credentials,
386 "vnf_data_dir": self.util.vnf_data_dir}
388 start_time = time.time()
390 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
392 duration = time.time() - start_time
395 self.details['test_vnf'].update(
396 status='PASS', result='OK', full_result=test_result_data,
399 self.details['test_vnf'].update(
400 status='FAIL', result='NG', 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(
416 execution['id'], force=True)
417 except Exception: # pylint: disable=broad-except
418 self.__logger.warn("Can't cancel the current exec")
420 execution = cfy_client.executions.start(
421 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
423 wait_for_execution(cfy_client, execution, self.__logger)
424 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
425 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
426 except Exception: # pylint: disable=broad-except
427 self.__logger.exception("Some issue during the undeployment ..")
429 super(CloudifyVrouter, self).clean()
431 def get_vnf_info_list(self, target_vnf_name):
432 return self.util.get_vnf_info_list(
433 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
436 def wait_for_execution(client, execution, logger, timeout=7200, ):
437 """Wait for a workflow execution on Cloudify Manager."""
438 # if execution already ended - return without waiting
439 if execution.status in Execution.END_STATES:
442 if timeout is not None:
443 deadline = time.time() + timeout
445 # Poll for execution status and execution logs, until execution ends
446 # and we receive an event of type in WORKFLOW_END_TYPES
450 execution_ended = False
452 event_list = client.events.list(
453 execution_id=execution.id, _offset=offset, _size=batch_size,
454 include_logs=False, sort='@timestamp').items
456 offset = offset + len(event_list)
457 for event in event_list:
458 logger.debug(event.get('message'))
460 if timeout is not None:
461 if time.time() > deadline:
463 'execution of operation {0} for deployment {1} '
464 'timed out'.format(execution.workflow_id,
465 execution.deployment_id))
467 # update the remaining timeout
468 timeout = deadline - time.time()
470 if not execution_ended:
471 execution = client.executions.get(execution.id)
472 execution_ended = execution.status in Execution.END_STATES
482 def get_execution_id(client, deployment_id):
484 Get the execution id of a env preparation.
485 network, security group, fip, VM creation
487 executions = client.executions.list(deployment_id=deployment_id)
488 for execution in executions:
489 if execution.workflow_id == 'create_deployment_environment':
491 raise RuntimeError('Failed to get create_deployment_environment '
492 'workflow execution.'
493 'Available executions: {0}'.format(executions))