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
56 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
59 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
60 # pylint: disable=too-many-instance-attributes
61 """vrouter testcase deployed with Cloudify Orchestrator."""
63 __logger = logging.getLogger(__name__)
66 def __init__(self, **kwargs):
67 if "case_name" not in kwargs:
68 kwargs["case_name"] = "vyos_vrouter"
69 super(CloudifyVrouter, self).__init__(**kwargs)
71 # Retrieve the configuration
73 self.config = getattr(
74 config.CONF, 'vnf_{}_config'.format(self.case_name))
76 raise Exception("VNF config file not found")
78 self.cfy_manager_ip = ''
79 self.deployment_name = ''
81 config_file = os.path.join(self.case_dir, self.config)
82 self.orchestrator = dict(
83 requirements=functest_utils.get_parameter_from_yaml(
84 "orchestrator.requirements", config_file),
86 self.details['orchestrator'] = dict(
87 name=functest_utils.get_parameter_from_yaml(
88 "orchestrator.name", config_file),
89 version=functest_utils.get_parameter_from_yaml(
90 "orchestrator.version", config_file),
94 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
95 self.__logger.debug("name = %s", self.name)
97 descriptor=functest_utils.get_parameter_from_yaml(
98 "vnf.descriptor", config_file),
99 inputs=functest_utils.get_parameter_from_yaml(
100 "vnf.inputs", config_file),
101 requirements=functest_utils.get_parameter_from_yaml(
102 "vnf.requirements", config_file)
104 self.details['vnf'] = dict(
105 descriptor_version=self.vnf['descriptor']['version'],
106 name=functest_utils.get_parameter_from_yaml(
107 "vnf.name", config_file),
108 version=functest_utils.get_parameter_from_yaml(
109 "vnf.version", config_file),
111 self.__logger.debug("VNF configuration: %s", self.vnf)
113 self.util = Utilvnf()
115 self.details['test_vnf'] = dict(
116 name=functest_utils.get_parameter_from_yaml(
117 "vnf_test_suite.name", config_file),
118 version=functest_utils.get_parameter_from_yaml(
119 "vnf_test_suite.version", config_file)
121 self.images = functest_utils.get_parameter_from_yaml(
122 "tenant_images", config_file)
123 self.__logger.info("Images needed for vrouter: %s", self.images)
126 def run_blocking_ssh_command(ssh, cmd,
127 error_msg="Unable to run this command"):
128 """Command to run ssh command with the exit status."""
129 (_, stdout, stderr) = ssh.exec_command(cmd)
130 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
131 if stdout.channel.recv_exit_status() != 0:
132 CloudifyVrouter.__logger.error(
133 "SSH %s stderr: %s", cmd, stderr.read())
134 raise Exception(error_msg)
137 super(CloudifyVrouter, self).prepare()
138 self.__logger.info("Additional pre-configuration steps")
139 self.util.set_credentials(self.snaps_creds)
141 def deploy_orchestrator(self):
142 # pylint: disable=too-many-locals,too-many-statements
144 Deploy Cloudify Manager.
145 network, security group, fip, VM creation
148 start_time = time.time()
150 # orchestrator VM flavor
151 self.__logger.info("Get or create flavor for cloudify manager vm ...")
152 flavor_settings = FlavorConfig(
154 self.orchestrator['requirements']['flavor']['name'],
156 ram=self.orchestrator['requirements']['flavor']['ram_min'],
158 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
159 flavor_creator.create()
160 self.created_object.append(flavor_creator)
162 user_creator = OpenStackUser(
165 name='cloudify_network_bug-{}'.format(self.uuid),
166 password=str(uuid.uuid4()),
167 project_name=self.tenant_name,
168 domain_name=self.snaps_creds.user_domain_name,
169 roles={'_member_': self.tenant_name}))
170 user_creator.create()
171 self.created_object.append(user_creator)
173 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
174 self.__logger.debug("snaps creds: %s", snaps_creds)
176 self.__logger.info("Creating keypair ...")
177 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
178 keypair_settings = KeypairConfig(
179 name='cloudify_vrouter_kp-{}'.format(self.uuid),
180 private_filepath=kp_file)
181 keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
182 keypair_creator.create()
183 self.created_object.append(keypair_creator)
185 self.__logger.info("Upload some OS images if it doesn't exist")
186 for image_name, image_file in six.iteritems(self.images):
187 self.__logger.info("image: %s, file: %s", image_name, image_file)
188 if image_file and image_name:
189 image_creator = OpenStackImage(
192 name=image_name, image_user='cloud',
193 img_format='qcow2', image_file=image_file))
194 image_creator.create()
195 self.created_object.append(image_creator)
197 self.__logger.info("Creating full network ...")
198 subnet_settings = SubnetConfig(
199 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
200 cidr='10.67.79.0/24',
201 dns_nameservers=[env.get('NAMESERVER')])
202 network_settings = NetworkConfig(
203 name='cloudify_vrouter_network-{}'.format(self.uuid),
204 subnet_settings=[subnet_settings])
205 network_creator = OpenStackNetwork(snaps_creds, network_settings)
206 network_creator.create()
207 self.created_object.append(network_creator)
208 ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
209 router_creator = OpenStackRouter(
212 name='cloudify_vrouter_router-{}'.format(self.uuid),
213 external_gateway=ext_net_name,
214 internal_subnets=[subnet_settings.name]))
215 router_creator.create()
216 self.created_object.append(router_creator)
218 # security group creation
219 self.__logger.info("Creating security group for cloudify manager vm")
222 SecurityGroupRuleConfig(
223 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
224 direction=Direction.ingress,
225 protocol=Protocol.tcp, port_range_min=1,
226 port_range_max=65535))
228 SecurityGroupRuleConfig(
229 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
230 direction=Direction.ingress,
231 protocol=Protocol.udp, port_range_min=1,
232 port_range_max=65535))
233 security_group_creator = OpenStackSecurityGroup(
236 name="sg-cloudify-manager-{}".format(self.uuid),
237 rule_settings=sg_rules))
238 security_group_creator.create()
239 self.created_object.append(security_group_creator)
241 image_settings = ImageConfig(
242 name=self.orchestrator['requirements']['os_image'],
243 image_user='centos', exists=True)
244 port_settings = PortConfig(
245 name='cloudify_manager_port-{}'.format(self.uuid),
246 network_name=network_settings.name)
247 manager_settings = VmInstanceConfig(
248 name='cloudify_manager-{}'.format(self.uuid),
249 flavor=flavor_settings.name,
250 port_settings=[port_settings],
251 security_group_names=[
252 security_group_creator.sec_grp_settings.name],
253 floating_ip_settings=[FloatingIpConfig(
254 name='cloudify_manager_fip-{}'.format(self.uuid),
255 port_name=port_settings.name,
256 router_name=router_creator.router_settings.name)])
257 manager_creator = OpenStackVmInstance(
258 snaps_creds, manager_settings, image_settings,
261 self.__logger.info("Creating cloudify manager VM")
262 manager_creator.create()
263 self.created_object.append(manager_creator)
265 cfy_client = CloudifyClient(
266 host=manager_creator.get_floating_ip().ip,
267 username='admin', password='admin', tenant='default_tenant',
270 self.orchestrator['object'] = cfy_client
272 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
274 self.__logger.info("Attemps running status of the Manager")
275 for loop in range(10):
278 "status %s", cfy_client.manager.get_status())
279 cfy_status = cfy_client.manager.get_status()['status']
281 "The current manager status is %s", cfy_status)
282 if str(cfy_status) != 'running':
283 raise Exception("Cloudify Manager isn't up and running")
285 except Exception: # pylint: disable=broad-except
287 "try %s: Cloudify Manager isn't up and running", loop + 1)
290 self.logger.error("Cloudify Manager isn't up and running")
293 duration = time.time() - start_time
295 self.__logger.info("Put private keypair in manager")
296 if manager_creator.vm_ssh_active(block=True):
297 ssh = manager_creator.ssh_client()
298 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
299 scp.put(kp_file, '~/')
300 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
301 self.run_blocking_ssh_command(ssh, cmd)
302 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
303 self.run_blocking_ssh_command(ssh, cmd)
304 # cmd2 is badly unpinned by Cloudify
305 cmd = "sudo yum install -y gcc python-devel python-cmd2"
306 self.run_blocking_ssh_command(
307 ssh, cmd, "Unable to install packages on manager")
309 self.__logger.error("Cannot connect to manager")
312 self.details['orchestrator'].update(status='PASS', duration=duration)
314 self.__logger.info("Get or create flavor for vrouter")
315 flavor_settings = FlavorConfig(
317 self.vnf['requirements']['flavor']['name'],
319 ram=self.vnf['requirements']['flavor']['ram_min'],
321 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
322 flavor = flavor_creator.create()
323 self.created_object.append(flavor_creator)
326 glance = glance_utils.glance_client(snaps_creds)
327 image = glance_utils.get_image(glance, "vyos1.1.7")
328 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
329 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
330 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
331 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
332 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
333 self.vnf['inputs'].update(dict(
334 keystone_username=snaps_creds.username))
335 self.vnf['inputs'].update(dict(
336 keystone_password=snaps_creds.password))
337 self.vnf['inputs'].update(dict(
338 keystone_tenant_name=snaps_creds.project_name))
339 self.vnf['inputs'].update(dict(
340 keystone_user_domain_name=snaps_creds.user_domain_name))
341 self.vnf['inputs'].update(dict(
342 keystone_project_domain_name=snaps_creds.project_domain_name))
343 self.vnf['inputs'].update(dict(
344 region=snaps_creds.region_name))
345 self.vnf['inputs'].update(dict(
346 keystone_url=keystone_utils.get_endpoint(
347 snaps_creds, 'identity')))
349 credentials = {"snaps_creds": snaps_creds}
350 self.util_info = {"credentials": credentials,
352 "vnf_data_dir": self.util.vnf_data_dir}
356 def deploy_vnf(self):
357 start_time = time.time()
359 self.__logger.info("Upload VNFD")
360 cfy_client = self.orchestrator['object']
361 descriptor = self.vnf['descriptor']
362 self.deployment_name = descriptor.get('name')
364 cfy_client.blueprints.upload(
365 descriptor.get('file_name'), descriptor.get('name'))
367 self.__logger.info("Create VNF Instance")
368 cfy_client.deployments.create(
369 descriptor.get('name'), descriptor.get('name'),
370 self.vnf.get('inputs'))
373 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
374 self.__logger, timeout=7200)
376 self.__logger.info("Start the VNF Instance deployment")
377 execution = cfy_client.executions.start(descriptor.get('name'),
380 execution = wait_for_execution(cfy_client, execution, self.__logger)
382 duration = time.time() - start_time
384 self.__logger.info(execution)
385 if execution.status == 'terminated':
386 self.details['vnf'].update(status='PASS', duration=duration)
389 self.details['vnf'].update(status='FAIL', duration=duration)
394 start_time = time.time()
395 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
396 duration = time.time() - start_time
398 self.details['test_vnf'].update(
399 status='PASS', result='OK', full_result=test_result_data,
402 self.details['test_vnf'].update(
403 status='FAIL', result='NG', full_result=test_result_data,
409 cfy_client = self.orchestrator['object']
410 dep_name = self.vnf['descriptor'].get('name')
411 # kill existing execution
412 self.__logger.info('Deleting the current deployment')
413 exec_list = cfy_client.executions.list(dep_name)
414 for execution in exec_list:
415 if execution['status'] == "started":
417 cfy_client.executions.cancel(
418 execution['id'], force=True)
419 except Exception: # pylint: disable=broad-except
420 self.__logger.warn("Can't cancel the current exec")
422 execution = cfy_client.executions.start(
423 dep_name, 'uninstall', 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 Exception: # pylint: disable=broad-except
429 self.__logger.exception("Some issue during the undeployment ..")
431 super(CloudifyVrouter, self).clean()
433 def get_vnf_info_list(self, target_vnf_name):
434 return self.util.get_vnf_info_list(
435 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
438 def wait_for_execution(client, execution, logger, timeout=7200, ):
439 """Wait for a workflow execution on Cloudify Manager."""
440 # if execution already ended - return without waiting
441 if execution.status in Execution.END_STATES:
444 if timeout is not None:
445 deadline = time.time() + timeout
447 # Poll for execution status and execution logs, until execution ends
448 # and we receive an event of type in WORKFLOW_END_TYPES
452 execution_ended = False
454 event_list = client.events.list(
455 execution_id=execution.id, _offset=offset, _size=batch_size,
456 include_logs=True, sort='@timestamp').items
458 offset = offset + len(event_list)
459 for event in event_list:
460 logger.debug(event.get('message'))
462 if timeout is not None:
463 if time.time() > deadline:
465 'execution of operation {0} for deployment {1} '
466 'timed out'.format(execution.workflow_id,
467 execution.deployment_id))
469 # update the remaining timeout
470 timeout = deadline - time.time()
472 if not execution_ended:
473 execution = client.executions.get(execution.id)
474 execution_ended = execution.status in Execution.END_STATES
484 def get_execution_id(client, deployment_id):
486 Get the execution id of a env preparation.
487 network, security group, fip, VM creation
489 executions = client.executions.list(deployment_id=deployment_id)
490 for execution in executions:
491 if execution.workflow_id == 'create_deployment_environment':
493 raise RuntimeError('Failed to get create_deployment_environment '
494 'workflow execution.'
495 'Available executions: {0}'.format(executions))