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 snaps.config.flavor import FlavorConfig
24 from snaps.config.image import ImageConfig
25 from snaps.config.keypair import KeypairConfig
26 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
27 from snaps.config.router import RouterConfig
28 from snaps.config.security_group import (
29 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
30 from snaps.config.user import UserConfig
31 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
32 from snaps.openstack.create_flavor import OpenStackFlavor
33 from snaps.openstack.create_image import OpenStackImage
34 from snaps.openstack.create_instance import OpenStackVmInstance
35 from snaps.openstack.create_keypairs import OpenStackKeypair
36 from snaps.openstack.create_network import OpenStackNetwork
37 from snaps.openstack.create_security_group import OpenStackSecurityGroup
38 from snaps.openstack.create_router import OpenStackRouter
39 from snaps.openstack.create_user import OpenStackUser
40 import snaps.openstack.utils.glance_utils as glance_utils
41 from snaps.openstack.utils import keystone_utils
43 from functest.opnfv_tests.openstack.snaps import snaps_utils
44 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
45 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
46 from functest.utils import config
47 from functest.utils import env
48 from functest.utils import functest_utils
50 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
53 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
54 # pylint: disable=too-many-instance-attributes
55 """vrouter testcase deployed with Cloudify Orchestrator."""
57 __logger = logging.getLogger(__name__)
60 def __init__(self, **kwargs):
61 if "case_name" not in kwargs:
62 kwargs["case_name"] = "vyos_vrouter"
63 super(CloudifyVrouter, self).__init__(**kwargs)
65 # Retrieve the configuration
67 self.config = getattr(
68 config.CONF, 'vnf_{}_config'.format(self.case_name))
70 raise Exception("VNF config file not found")
72 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=functest_utils.get_parameter_from_yaml(
78 "orchestrator.requirements", config_file),
80 self.details['orchestrator'] = dict(
81 name=functest_utils.get_parameter_from_yaml(
82 "orchestrator.name", config_file),
83 version=functest_utils.get_parameter_from_yaml(
84 "orchestrator.version", config_file),
88 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
89 self.__logger.debug("name = %s", self.name)
91 descriptor=functest_utils.get_parameter_from_yaml(
92 "vnf.descriptor", config_file),
93 inputs=functest_utils.get_parameter_from_yaml(
94 "vnf.inputs", config_file),
95 requirements=functest_utils.get_parameter_from_yaml(
96 "vnf.requirements", config_file)
98 self.details['vnf'] = dict(
99 descriptor_version=self.vnf['descriptor']['version'],
100 name=functest_utils.get_parameter_from_yaml(
101 "vnf.name", config_file),
102 version=functest_utils.get_parameter_from_yaml(
103 "vnf.version", config_file),
105 self.__logger.debug("VNF configuration: %s", self.vnf)
107 self.util = Utilvnf()
109 self.details['test_vnf'] = dict(
110 name=functest_utils.get_parameter_from_yaml(
111 "vnf_test_suite.name", config_file),
112 version=functest_utils.get_parameter_from_yaml(
113 "vnf_test_suite.version", config_file)
115 self.images = functest_utils.get_parameter_from_yaml(
116 "tenant_images", config_file)
117 self.__logger.info("Images needed for vrouter: %s", self.images)
120 def run_blocking_ssh_command(ssh, cmd,
121 error_msg="Unable to run this command"):
122 """Command to run ssh command with the exit status."""
123 (_, stdout, stderr) = ssh.exec_command(cmd)
124 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
125 if stdout.channel.recv_exit_status() != 0:
126 CloudifyVrouter.__logger.error(
127 "SSH %s stderr: %s", cmd, stderr.read())
128 raise Exception(error_msg)
131 super(CloudifyVrouter, self).prepare()
132 self.__logger.info("Additional pre-configuration steps")
133 self.util.set_credentials(self.snaps_creds)
135 def deploy_orchestrator(self):
136 # pylint: disable=too-many-locals,too-many-statements
138 Deploy Cloudify Manager.
139 network, security group, fip, VM creation
142 start_time = time.time()
144 # orchestrator VM flavor
145 self.__logger.info("Get or create flavor for cloudify manager vm ...")
146 flavor_settings = FlavorConfig(
148 self.orchestrator['requirements']['flavor']['name'],
150 ram=self.orchestrator['requirements']['flavor']['ram_min'],
152 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
153 flavor_creator.create()
154 self.created_object.append(flavor_creator)
156 user_creator = OpenStackUser(
159 name='cloudify_network_bug-{}'.format(self.uuid),
160 password=str(uuid.uuid4()),
161 project_name=self.tenant_name,
162 domain_name=self.snaps_creds.user_domain_name,
163 roles={'_member_': self.tenant_name}))
164 user_creator.create()
165 self.created_object.append(user_creator)
167 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
168 self.__logger.debug("snaps creds: %s", snaps_creds)
170 self.__logger.info("Creating keypair ...")
171 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
172 keypair_settings = KeypairConfig(
173 name='cloudify_vrouter_kp-{}'.format(self.uuid),
174 private_filepath=kp_file)
175 keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
176 keypair_creator.create()
177 self.created_object.append(keypair_creator)
179 self.__logger.info("Upload some OS images if it doesn't exist")
180 for image_name, image_file in six.iteritems(self.images):
181 self.__logger.info("image: %s, file: %s", image_name, image_file)
182 if image_file and image_name:
183 image_creator = OpenStackImage(
186 name=image_name, image_user='cloud',
187 img_format='qcow2', image_file=image_file))
188 image_creator.create()
189 self.created_object.append(image_creator)
191 self.__logger.info("Creating full network ...")
192 subnet_settings = SubnetConfig(
193 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
194 cidr='10.67.79.0/24',
195 dns_nameservers=[env.get('NAMESERVER')])
196 network_settings = NetworkConfig(
197 name='cloudify_vrouter_network-{}'.format(self.uuid),
198 subnet_settings=[subnet_settings])
199 network_creator = OpenStackNetwork(snaps_creds, network_settings)
200 network_creator.create()
201 self.created_object.append(network_creator)
202 ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
203 router_creator = OpenStackRouter(
206 name='cloudify_vrouter_router-{}'.format(self.uuid),
207 external_gateway=ext_net_name,
208 internal_subnets=[subnet_settings.name]))
209 router_creator.create()
210 self.created_object.append(router_creator)
212 # security group creation
213 self.__logger.info("Creating security group for cloudify manager vm")
216 SecurityGroupRuleConfig(
217 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
218 direction=Direction.ingress,
219 protocol=Protocol.tcp, port_range_min=1,
220 port_range_max=65535))
222 SecurityGroupRuleConfig(
223 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
224 direction=Direction.ingress,
225 protocol=Protocol.udp, port_range_min=1,
226 port_range_max=65535))
227 security_group_creator = OpenStackSecurityGroup(
230 name="sg-cloudify-manager-{}".format(self.uuid),
231 rule_settings=sg_rules))
232 security_group_creator.create()
233 self.created_object.append(security_group_creator)
235 image_settings = ImageConfig(
236 name=self.orchestrator['requirements']['os_image'],
237 image_user='centos', exists=True)
238 port_settings = PortConfig(
239 name='cloudify_manager_port-{}'.format(self.uuid),
240 network_name=network_settings.name)
241 manager_settings = VmInstanceConfig(
242 name='cloudify_manager-{}'.format(self.uuid),
243 flavor=flavor_settings.name,
244 port_settings=[port_settings],
245 security_group_names=[
246 security_group_creator.sec_grp_settings.name],
247 floating_ip_settings=[FloatingIpConfig(
248 name='cloudify_manager_fip-{}'.format(self.uuid),
249 port_name=port_settings.name,
250 router_name=router_creator.router_settings.name)])
251 manager_creator = OpenStackVmInstance(
252 snaps_creds, manager_settings, image_settings,
255 self.__logger.info("Creating cloudify manager VM")
256 manager_creator.create()
257 self.created_object.append(manager_creator)
259 cfy_client = CloudifyClient(
260 host=manager_creator.get_floating_ip().ip,
261 username='admin', password='admin', tenant='default_tenant',
264 self.orchestrator['object'] = cfy_client
266 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
268 self.__logger.info("Attemps running status of the Manager")
269 for loop in range(10):
272 "status %s", cfy_client.manager.get_status())
273 cfy_status = cfy_client.manager.get_status()['status']
275 "The current manager status is %s", cfy_status)
276 if str(cfy_status) != 'running':
277 raise Exception("Cloudify Manager isn't up and running")
279 except Exception: # pylint: disable=broad-except
281 "try %s: Cloudify Manager isn't up and running", loop + 1)
284 self.logger.error("Cloudify Manager isn't up and running")
287 duration = time.time() - start_time
289 self.__logger.info("Put private keypair in manager")
290 if manager_creator.vm_ssh_active(block=True):
291 ssh = manager_creator.ssh_client()
292 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
293 scp.put(kp_file, '~/')
294 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
295 self.run_blocking_ssh_command(ssh, cmd)
296 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
297 self.run_blocking_ssh_command(ssh, cmd)
298 # cmd2 is badly unpinned by Cloudify
299 cmd = "sudo yum install -y gcc python-devel python-cmd2"
300 self.run_blocking_ssh_command(
301 ssh, cmd, "Unable to install packages on manager")
303 self.__logger.error("Cannot connect to manager")
306 self.details['orchestrator'].update(status='PASS', duration=duration)
308 self.__logger.info("Get or create flavor for vrouter")
309 flavor_settings = FlavorConfig(
311 self.vnf['requirements']['flavor']['name'],
313 ram=self.vnf['requirements']['flavor']['ram_min'],
315 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
316 flavor = flavor_creator.create()
317 self.created_object.append(flavor_creator)
320 glance = glance_utils.glance_client(snaps_creds)
321 image = glance_utils.get_image(glance, "vyos1.1.7")
322 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
323 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
324 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
325 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
326 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
327 self.vnf['inputs'].update(dict(
328 keystone_username=snaps_creds.username))
329 self.vnf['inputs'].update(dict(
330 keystone_password=snaps_creds.password))
331 self.vnf['inputs'].update(dict(
332 keystone_tenant_name=snaps_creds.project_name))
333 self.vnf['inputs'].update(dict(
334 keystone_user_domain_name=snaps_creds.user_domain_name))
335 self.vnf['inputs'].update(dict(
336 keystone_project_domain_name=snaps_creds.project_domain_name))
337 self.vnf['inputs'].update(dict(
338 region=snaps_creds.region_name))
339 self.vnf['inputs'].update(dict(
340 keystone_url=keystone_utils.get_endpoint(
341 snaps_creds, 'identity')))
343 credentials = {"snaps_creds": snaps_creds}
344 self.util_info = {"credentials": credentials,
346 "vnf_data_dir": self.util.vnf_data_dir}
350 def deploy_vnf(self):
351 start_time = time.time()
353 self.__logger.info("Upload VNFD")
354 cfy_client = self.orchestrator['object']
355 descriptor = self.vnf['descriptor']
356 self.deployment_name = descriptor.get('name')
358 cfy_client.blueprints.upload(
359 descriptor.get('file_name'), descriptor.get('name'))
361 self.__logger.info("Create VNF Instance")
362 cfy_client.deployments.create(
363 descriptor.get('name'), descriptor.get('name'),
364 self.vnf.get('inputs'))
367 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
368 self.__logger, timeout=7200)
370 self.__logger.info("Start the VNF Instance deployment")
371 execution = cfy_client.executions.start(descriptor.get('name'),
374 execution = wait_for_execution(cfy_client, execution, self.__logger)
376 duration = time.time() - start_time
378 self.__logger.info(execution)
379 if execution.status == 'terminated':
380 self.details['vnf'].update(status='PASS', duration=duration)
383 self.details['vnf'].update(status='FAIL', duration=duration)
388 start_time = time.time()
389 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
390 duration = time.time() - start_time
392 self.details['test_vnf'].update(
393 status='PASS', result='OK', full_result=test_result_data,
396 self.details['test_vnf'].update(
397 status='FAIL', result='NG', full_result=test_result_data,
403 cfy_client = self.orchestrator['object']
404 dep_name = self.vnf['descriptor'].get('name')
405 # kill existing execution
406 self.__logger.info('Deleting the current deployment')
407 exec_list = cfy_client.executions.list(dep_name)
408 for execution in exec_list:
409 if execution['status'] == "started":
411 cfy_client.executions.cancel(
412 execution['id'], force=True)
413 except Exception: # pylint: disable=broad-except
414 self.__logger.warn("Can't cancel the current exec")
416 execution = cfy_client.executions.start(
417 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
419 wait_for_execution(cfy_client, execution, self.__logger)
420 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
421 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
422 except Exception: # pylint: disable=broad-except
423 self.__logger.exception("Some issue during the undeployment ..")
425 super(CloudifyVrouter, self).clean()
427 def get_vnf_info_list(self, target_vnf_name):
428 return self.util.get_vnf_info_list(
429 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
432 def wait_for_execution(client, execution, logger, timeout=7200, ):
433 """Wait for a workflow execution on Cloudify Manager."""
434 # if execution already ended - return without waiting
435 if execution.status in Execution.END_STATES:
438 if timeout is not None:
439 deadline = time.time() + timeout
441 # Poll for execution status and execution logs, until execution ends
442 # and we receive an event of type in WORKFLOW_END_TYPES
446 execution_ended = False
448 event_list = client.events.list(
449 execution_id=execution.id, _offset=offset, _size=batch_size,
450 include_logs=True, sort='@timestamp').items
452 offset = offset + len(event_list)
453 for event in event_list:
454 logger.debug(event.get('message'))
456 if timeout is not None:
457 if time.time() > deadline:
459 'execution of operation {0} for deployment {1} '
460 'timed out'.format(execution.workflow_id,
461 execution.deployment_id))
463 # update the remaining timeout
464 timeout = deadline - time.time()
466 if not execution_ended:
467 execution = client.executions.get(execution.id)
468 execution_ended = execution.status in Execution.END_STATES
478 def get_execution_id(client, deployment_id):
480 Get the execution id of a env preparation.
481 network, security group, fip, VM creation
483 executions = client.executions.list(deployment_id=deployment_id)
484 for execution in executions:
485 if execution.workflow_id == 'create_deployment_environment':
487 raise RuntimeError('Failed to get create_deployment_environment '
488 'workflow execution.'
489 'Available executions: {0}'.format(executions))