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)
138 def deploy_orchestrator(self):
139 # pylint: disable=too-many-locals,too-many-statements
141 Deploy Cloudify Manager.
142 network, security group, fip, VM creation
145 start_time = time.time()
147 # orchestrator VM flavor
148 self.__logger.info("Get or create flavor for cloudify manager vm ...")
149 flavor_settings = FlavorConfig(
151 self.orchestrator['requirements']['flavor']['name'],
153 ram=self.orchestrator['requirements']['flavor']['ram_min'],
155 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
156 flavor_creator.create()
157 self.created_object.append(flavor_creator)
159 user_creator = OpenStackUser(
162 name='cloudify_network_bug-{}'.format(self.uuid),
163 password=str(uuid.uuid4()),
164 project_name=self.tenant_name,
165 domain_name=self.snaps_creds.user_domain_name,
166 roles={'_member_': self.tenant_name}))
167 user_creator.create()
168 self.created_object.append(user_creator)
170 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
171 self.__logger.debug("snaps creds: %s", snaps_creds)
173 self.__logger.info("Creating keypair ...")
174 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
175 keypair_settings = KeypairConfig(
176 name='cloudify_vrouter_kp-{}'.format(self.uuid),
177 private_filepath=kp_file)
178 keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
179 keypair_creator.create()
180 self.created_object.append(keypair_creator)
182 self.__logger.info("Upload some OS images if it doesn't exist")
183 for image_name, image_file in self.images.iteritems():
184 self.__logger.info("image: %s, file: %s", image_name, image_file)
185 if image_file and image_name:
186 image_creator = OpenStackImage(
189 name=image_name, image_user='cloud',
190 img_format='qcow2', image_file=image_file))
191 image_creator.create()
192 self.created_object.append(image_creator)
194 self.__logger.info("Creating full network ...")
195 subnet_settings = SubnetConfig(
196 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
197 cidr='10.67.79.0/24',
198 dns_nameservers=[env.get('NAMESERVER')])
199 network_settings = NetworkConfig(
200 name='cloudify_vrouter_network-{}'.format(self.uuid),
201 subnet_settings=[subnet_settings])
202 network_creator = OpenStackNetwork(snaps_creds, network_settings)
203 network_creator.create()
204 self.created_object.append(network_creator)
205 ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
206 router_creator = OpenStackRouter(
209 name='cloudify_vrouter_router-{}'.format(self.uuid),
210 external_gateway=ext_net_name,
211 internal_subnets=[subnet_settings.name]))
212 router_creator.create()
213 self.created_object.append(router_creator)
215 # security group creation
216 self.__logger.info("Creating security group for cloudify manager vm")
219 SecurityGroupRuleConfig(
220 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
221 direction=Direction.ingress,
222 protocol=Protocol.tcp, port_range_min=1,
223 port_range_max=65535))
225 SecurityGroupRuleConfig(
226 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
227 direction=Direction.ingress,
228 protocol=Protocol.udp, port_range_min=1,
229 port_range_max=65535))
230 security_group_creator = OpenStackSecurityGroup(
233 name="sg-cloudify-manager-{}".format(self.uuid),
234 rule_settings=sg_rules))
235 security_group_creator.create()
236 self.created_object.append(security_group_creator)
238 image_settings = ImageConfig(
239 name=self.orchestrator['requirements']['os_image'],
240 image_user='centos', exists=True)
241 port_settings = PortConfig(
242 name='cloudify_manager_port-{}'.format(self.uuid),
243 network_name=network_settings.name)
244 manager_settings = VmInstanceConfig(
245 name='cloudify_manager-{}'.format(self.uuid),
246 flavor=flavor_settings.name,
247 port_settings=[port_settings],
248 security_group_names=[
249 security_group_creator.sec_grp_settings.name],
250 floating_ip_settings=[FloatingIpConfig(
251 name='cloudify_manager_fip-{}'.format(self.uuid),
252 port_name=port_settings.name,
253 router_name=router_creator.router_settings.name)])
254 manager_creator = OpenStackVmInstance(
255 snaps_creds, manager_settings, image_settings,
258 self.__logger.info("Creating cloudify manager VM")
259 manager_creator.create()
260 self.created_object.append(manager_creator)
262 cfy_client = CloudifyClient(
263 host=manager_creator.get_floating_ip().ip,
264 username='admin', password='admin', tenant='default_tenant')
266 self.orchestrator['object'] = cfy_client
268 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
270 self.__logger.info("Attemps running status of the Manager")
271 for loop in range(10):
274 "status %s", cfy_client.manager.get_status())
275 cfy_status = cfy_client.manager.get_status()['status']
277 "The current manager status is %s", cfy_status)
278 if str(cfy_status) != 'running':
279 raise Exception("Cloudify Manager isn't up and running")
281 except Exception: # pylint: disable=broad-except
283 "try %s: Cloudify Manager isn't up and running", loop + 1)
286 self.logger.error("Cloudify Manager isn't up and running")
289 duration = time.time() - start_time
291 self.__logger.info("Put private keypair in manager")
292 if manager_creator.vm_ssh_active(block=True):
293 ssh = manager_creator.ssh_client()
294 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
295 scp.put(kp_file, '~/')
296 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
297 self.run_blocking_ssh_command(ssh, cmd)
298 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
299 self.run_blocking_ssh_command(ssh, cmd)
300 # cmd2 is badly unpinned by Cloudify
301 cmd = "sudo yum install -y gcc python-devel python-cmd2"
302 self.run_blocking_ssh_command(
303 ssh, cmd, "Unable to install packages on manager")
305 self.__logger.error("Cannot connect to manager")
308 self.details['orchestrator'].update(status='PASS', duration=duration)
310 self.__logger.info("Get or create flavor for vrouter")
311 flavor_settings = FlavorConfig(
313 self.vnf['requirements']['flavor']['name'],
315 ram=self.vnf['requirements']['flavor']['ram_min'],
317 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
318 flavor = flavor_creator.create()
319 self.created_object.append(flavor_creator)
322 glance = glance_utils.glance_client(snaps_creds)
323 image = glance_utils.get_image(glance, "vyos1.1.7")
324 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
325 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
326 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
327 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
328 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
329 self.vnf['inputs'].update(dict(
330 keystone_username=snaps_creds.username))
331 self.vnf['inputs'].update(dict(
332 keystone_password=snaps_creds.password))
333 self.vnf['inputs'].update(dict(
334 keystone_tenant_name=snaps_creds.project_name))
335 self.vnf['inputs'].update(dict(
336 keystone_user_domain_name=snaps_creds.user_domain_name))
337 self.vnf['inputs'].update(dict(
338 keystone_project_domain_name=snaps_creds.project_domain_name))
339 self.vnf['inputs'].update(dict(
340 region=snaps_creds.region_name))
341 self.vnf['inputs'].update(dict(
342 keystone_url=keystone_utils.get_endpoint(
343 snaps_creds, 'identity')))
345 credentials = {"snaps_creds": snaps_creds}
346 self.util_info = {"credentials": credentials,
348 "vnf_data_dir": self.util.vnf_data_dir}
352 def deploy_vnf(self):
353 start_time = time.time()
355 self.__logger.info("Upload VNFD")
356 cfy_client = self.orchestrator['object']
357 descriptor = self.vnf['descriptor']
358 self.deployment_name = descriptor.get('name')
360 cfy_client.blueprints.upload(
361 descriptor.get('file_name'), descriptor.get('name'))
363 self.__logger.info("Create VNF Instance")
364 cfy_client.deployments.create(
365 descriptor.get('name'), descriptor.get('name'),
366 self.vnf.get('inputs'))
369 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
370 self.__logger, timeout=7200)
372 self.__logger.info("Start the VNF Instance deployment")
373 execution = cfy_client.executions.start(descriptor.get('name'),
376 execution = wait_for_execution(cfy_client, execution, self.__logger)
378 duration = time.time() - start_time
380 self.__logger.info(execution)
381 if execution.status == 'terminated':
382 self.details['vnf'].update(status='PASS', duration=duration)
385 self.details['vnf'].update(status='FAIL', duration=duration)
390 start_time = time.time()
391 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
392 duration = time.time() - start_time
394 self.details['test_vnf'].update(
395 status='PASS', result='OK', full_result=test_result_data,
398 self.details['test_vnf'].update(
399 status='FAIL', result='NG', full_result=test_result_data,
405 cfy_client = self.orchestrator['object']
406 dep_name = self.vnf['descriptor'].get('name')
407 # kill existing execution
408 self.__logger.info('Deleting the current deployment')
409 exec_list = cfy_client.executions.list(dep_name)
410 for execution in exec_list:
411 if execution['status'] == "started":
413 cfy_client.executions.cancel(
414 execution['id'], force=True)
415 except Exception: # pylint: disable=broad-except
416 self.__logger.warn("Can't cancel the current exec")
418 execution = cfy_client.executions.start(
419 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
421 wait_for_execution(cfy_client, execution, self.__logger)
422 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
423 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
424 except Exception: # pylint: disable=broad-except
425 self.__logger.exception("Some issue during the undeployment ..")
427 super(CloudifyVrouter, self).clean()
429 def get_vnf_info_list(self, target_vnf_name):
430 return self.util.get_vnf_info_list(
431 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
434 def wait_for_execution(client, execution, logger, timeout=7200, ):
435 """Wait for a workflow execution on Cloudify Manager."""
436 # if execution already ended - return without waiting
437 if execution.status in Execution.END_STATES:
440 if timeout is not None:
441 deadline = time.time() + timeout
443 # Poll for execution status and execution logs, until execution ends
444 # and we receive an event of type in WORKFLOW_END_TYPES
448 execution_ended = False
450 event_list = client.events.list(
451 execution_id=execution.id, _offset=offset, _size=batch_size,
452 include_logs=True, sort='@timestamp').items
454 offset = offset + len(event_list)
455 for event in event_list:
456 logger.debug(event.get('message'))
458 if timeout is not None:
459 if time.time() > deadline:
461 'execution of operation {0} for deployment {1} '
462 'timed out'.format(execution.workflow_id,
463 execution.deployment_id))
465 # update the remaining timeout
466 timeout = deadline - time.time()
468 if not execution_ended:
469 execution = client.executions.get(execution.id)
470 execution_ended = execution.status in Execution.END_STATES
480 def get_execution_id(client, deployment_id):
482 Get the execution id of a env preparation.
483 network, security group, fip, VM creation
485 executions = client.executions.list(deployment_id=deployment_id)
486 for execution in executions:
487 if execution.workflow_id == 'create_deployment_environment':
489 raise RuntimeError('Failed to get create_deployment_environment '
490 'workflow execution.'
491 'Available executions: {0}'.format(executions))