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',
267 self.orchestrator['object'] = cfy_client
269 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
271 self.__logger.info("Attemps running status of the Manager")
272 for loop in range(10):
275 "status %s", cfy_client.manager.get_status())
276 cfy_status = cfy_client.manager.get_status()['status']
278 "The current manager status is %s", cfy_status)
279 if str(cfy_status) != 'running':
280 raise Exception("Cloudify Manager isn't up and running")
282 except Exception: # pylint: disable=broad-except
284 "try %s: Cloudify Manager isn't up and running", loop + 1)
287 self.logger.error("Cloudify Manager isn't up and running")
290 duration = time.time() - start_time
292 self.__logger.info("Put private keypair in manager")
293 if manager_creator.vm_ssh_active(block=True):
294 ssh = manager_creator.ssh_client()
295 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
296 scp.put(kp_file, '~/')
297 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
298 self.run_blocking_ssh_command(ssh, cmd)
299 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
300 self.run_blocking_ssh_command(ssh, cmd)
301 # cmd2 is badly unpinned by Cloudify
302 cmd = "sudo yum install -y gcc python-devel python-cmd2"
303 self.run_blocking_ssh_command(
304 ssh, cmd, "Unable to install packages on manager")
306 self.__logger.error("Cannot connect to manager")
309 self.details['orchestrator'].update(status='PASS', duration=duration)
311 self.__logger.info("Get or create flavor for vrouter")
312 flavor_settings = FlavorConfig(
314 self.vnf['requirements']['flavor']['name'],
316 ram=self.vnf['requirements']['flavor']['ram_min'],
318 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
319 flavor = flavor_creator.create()
320 self.created_object.append(flavor_creator)
323 glance = glance_utils.glance_client(snaps_creds)
324 image = glance_utils.get_image(glance, "vyos1.1.7")
325 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
326 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
327 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
328 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
329 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
330 self.vnf['inputs'].update(dict(
331 keystone_username=snaps_creds.username))
332 self.vnf['inputs'].update(dict(
333 keystone_password=snaps_creds.password))
334 self.vnf['inputs'].update(dict(
335 keystone_tenant_name=snaps_creds.project_name))
336 self.vnf['inputs'].update(dict(
337 keystone_user_domain_name=snaps_creds.user_domain_name))
338 self.vnf['inputs'].update(dict(
339 keystone_project_domain_name=snaps_creds.project_domain_name))
340 self.vnf['inputs'].update(dict(
341 region=snaps_creds.region_name))
342 self.vnf['inputs'].update(dict(
343 keystone_url=keystone_utils.get_endpoint(
344 snaps_creds, 'identity')))
346 credentials = {"snaps_creds": snaps_creds}
347 self.util_info = {"credentials": credentials,
349 "vnf_data_dir": self.util.vnf_data_dir}
353 def deploy_vnf(self):
354 start_time = time.time()
356 self.__logger.info("Upload VNFD")
357 cfy_client = self.orchestrator['object']
358 descriptor = self.vnf['descriptor']
359 self.deployment_name = descriptor.get('name')
361 cfy_client.blueprints.upload(
362 descriptor.get('file_name'), descriptor.get('name'))
364 self.__logger.info("Create VNF Instance")
365 cfy_client.deployments.create(
366 descriptor.get('name'), descriptor.get('name'),
367 self.vnf.get('inputs'))
370 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
371 self.__logger, timeout=7200)
373 self.__logger.info("Start the VNF Instance deployment")
374 execution = cfy_client.executions.start(descriptor.get('name'),
377 execution = wait_for_execution(cfy_client, execution, self.__logger)
379 duration = time.time() - start_time
381 self.__logger.info(execution)
382 if execution.status == 'terminated':
383 self.details['vnf'].update(status='PASS', duration=duration)
386 self.details['vnf'].update(status='FAIL', duration=duration)
391 start_time = time.time()
392 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
393 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,
406 cfy_client = self.orchestrator['object']
407 dep_name = self.vnf['descriptor'].get('name')
408 # kill existing execution
409 self.__logger.info('Deleting the current deployment')
410 exec_list = cfy_client.executions.list(dep_name)
411 for execution in exec_list:
412 if execution['status'] == "started":
414 cfy_client.executions.cancel(
415 execution['id'], force=True)
416 except Exception: # pylint: disable=broad-except
417 self.__logger.warn("Can't cancel the current exec")
419 execution = cfy_client.executions.start(
420 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
422 wait_for_execution(cfy_client, execution, self.__logger)
423 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
424 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
425 except Exception: # pylint: disable=broad-except
426 self.__logger.exception("Some issue during the undeployment ..")
428 super(CloudifyVrouter, self).clean()
430 def get_vnf_info_list(self, target_vnf_name):
431 return self.util.get_vnf_info_list(
432 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
435 def wait_for_execution(client, execution, logger, timeout=7200, ):
436 """Wait for a workflow execution on Cloudify Manager."""
437 # if execution already ended - return without waiting
438 if execution.status in Execution.END_STATES:
441 if timeout is not None:
442 deadline = time.time() + timeout
444 # Poll for execution status and execution logs, until execution ends
445 # and we receive an event of type in WORKFLOW_END_TYPES
449 execution_ended = False
451 event_list = client.events.list(
452 execution_id=execution.id, _offset=offset, _size=batch_size,
453 include_logs=True, sort='@timestamp').items
455 offset = offset + len(event_list)
456 for event in event_list:
457 logger.debug(event.get('message'))
459 if timeout is not None:
460 if time.time() > deadline:
462 'execution of operation {0} for deployment {1} '
463 'timed out'.format(execution.workflow_id,
464 execution.deployment_id))
466 # update the remaining timeout
467 timeout = deadline - time.time()
469 if not execution_ended:
470 execution = client.executions.get(execution.id)
471 execution_ended = execution.status in Execution.END_STATES
481 def get_execution_id(client, deployment_id):
483 Get the execution id of a env preparation.
484 network, security group, fip, VM creation
486 executions = client.executions.list(deployment_id=deployment_id)
487 for execution in executions:
488 if execution.workflow_id == 'create_deployment_environment':
490 raise RuntimeError('Failed to get create_deployment_environment '
491 'workflow execution.'
492 'Available executions: {0}'.format(executions))