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."""
18 from cloudify_rest_client import CloudifyClient
19 from cloudify_rest_client.executions import Execution
20 from scp import SCPClient
22 from functest.opnfv_tests.openstack.snaps import snaps_utils
23 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
24 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
25 from functest.utils.constants import CONST
26 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.vm_inst import FloatingIpConfig, VmInstanceConfig
39 from snaps.openstack.create_flavor import OpenStackFlavor
40 from snaps.openstack.create_image import OpenStackImage
41 from snaps.openstack.create_instance import OpenStackVmInstance
42 from snaps.openstack.create_keypairs import OpenStackKeypair
43 from snaps.openstack.create_network import OpenStackNetwork
44 from snaps.openstack.create_security_group import OpenStackSecurityGroup
45 from snaps.openstack.create_router import OpenStackRouter
47 import snaps.openstack.utils.glance_utils as glance_utils
48 from snaps.openstack.utils import keystone_utils
51 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
54 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
55 # pylint: disable=too-many-instance-attributes
56 """vrouter testcase deployed with Cloudify Orchestrator."""
58 __logger = logging.getLogger(__name__)
61 def __init__(self, **kwargs):
62 if "case_name" not in kwargs:
63 kwargs["case_name"] = "vyos_vrouter"
64 super(CloudifyVrouter, self).__init__(**kwargs)
66 # Retrieve the configuration
68 self.config = CONST.__getattribute__(
69 'vnf_{}_config'.format(self.case_name))
71 raise Exception("VNF config file not found")
74 self.created_object = []
76 self.cfy_manager_ip = ''
78 self.deployment_name = ''
80 config_file = os.path.join(self.case_dir, self.config)
81 self.orchestrator = dict(
82 requirements=functest_utils.get_parameter_from_yaml(
83 "orchestrator.requirements", config_file),
85 self.details['orchestrator'] = dict(
86 name=functest_utils.get_parameter_from_yaml(
87 "orchestrator.name", config_file),
88 version=functest_utils.get_parameter_from_yaml(
89 "orchestrator.version", config_file),
93 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
94 self.__logger.debug("name = %s", self.name)
96 descriptor=functest_utils.get_parameter_from_yaml(
97 "vnf.descriptor", config_file),
98 inputs=functest_utils.get_parameter_from_yaml(
99 "vnf.inputs", config_file),
100 requirements=functest_utils.get_parameter_from_yaml(
101 "vnf.requirements", config_file)
103 self.details['vnf'] = dict(
104 descriptor_version=self.vnf['descriptor']['version'],
105 name=functest_utils.get_parameter_from_yaml(
106 "vnf.name", config_file),
107 version=functest_utils.get_parameter_from_yaml(
108 "vnf.version", config_file),
110 self.__logger.debug("VNF configuration: %s", self.vnf)
112 self.util = Utilvnf()
114 self.details['test_vnf'] = dict(
115 name=functest_utils.get_parameter_from_yaml(
116 "vnf_test_suite.name", config_file),
117 version=functest_utils.get_parameter_from_yaml(
118 "vnf_test_suite.version", config_file)
120 self.images = functest_utils.get_parameter_from_yaml(
121 "tenant_images", config_file)
122 self.__logger.info("Images needed for vrouter: %s", self.images)
125 super(CloudifyVrouter, self).prepare()
127 self.__logger.info("Additional pre-configuration steps")
129 self.util.set_credentials(self.snaps_creds)
132 self.__logger.info("Upload some OS images if it doesn't exist")
133 for image_name, image_file in self.images.iteritems():
134 self.__logger.info("image: %s, file: %s", image_name, image_file)
135 if image_file and image_name:
136 image_creator = OpenStackImage(
138 ImageConfig(name=image_name,
141 image_file=image_file))
142 image_creator.create()
143 self.created_object.append(image_creator)
145 def deploy_orchestrator(self):
146 # pylint: disable=too-many-locals,too-many-statements
148 Deploy Cloudify Manager.
149 network, security group, fip, VM creation
153 start_time = time.time()
154 self.__logger.info("Creating keypair ...")
155 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
156 keypair_settings = KeypairConfig(name='cloudify_vrouter_kp',
157 private_filepath=kp_file)
158 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
159 keypair_creator.create()
160 self.created_object.append(keypair_creator)
162 self.__logger.info("Creating full network ...")
163 subnet_settings = SubnetConfig(name='cloudify_vrouter_subnet',
164 cidr='10.67.79.0/24')
165 network_settings = NetworkConfig(name='cloudify_vrouter_network',
166 subnet_settings=[subnet_settings])
167 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
168 network_creator.create()
169 self.created_object.append(network_creator)
170 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
171 router_creator = OpenStackRouter(
174 name='cloudify_vrouter_router',
175 external_gateway=ext_net_name,
176 internal_subnets=[subnet_settings.name]))
177 router_creator.create()
178 self.created_object.append(router_creator)
180 # security group creation
181 self.__logger.info("Creating security group for cloudify manager vm")
184 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
185 direction=Direction.ingress,
186 protocol=Protocol.tcp, port_range_min=1,
187 port_range_max=65535))
189 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
190 direction=Direction.ingress,
191 protocol=Protocol.udp, port_range_min=1,
192 port_range_max=65535))
194 security_group_creator = OpenStackSecurityGroup(
197 name="sg-cloudify-manager",
198 rule_settings=sg_rules))
200 security_group_creator.create()
201 self.created_object.append(security_group_creator)
203 # orchestrator VM flavor
204 self.__logger.info("Get or create flavor for cloudify manager vm ...")
206 flavor_settings = FlavorConfig(
207 name=self.orchestrator['requirements']['flavor']['name'],
208 ram=self.orchestrator['requirements']['flavor']['ram_min'],
211 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
212 flavor_creator.create()
213 self.created_object.append(flavor_creator)
214 image_settings = ImageConfig(
215 name=self.orchestrator['requirements']['os_image'],
219 port_settings = PortConfig(name='cloudify_manager_port',
220 network_name=network_settings.name)
222 manager_settings = VmInstanceConfig(
223 name='cloudify_manager',
224 flavor=flavor_settings.name,
225 port_settings=[port_settings],
226 security_group_names=[
227 security_group_creator.sec_grp_settings.name],
228 floating_ip_settings=[FloatingIpConfig(
229 name='cloudify_manager_fip',
230 port_name=port_settings.name,
231 router_name=router_creator.router_settings.name)])
233 manager_creator = OpenStackVmInstance(self.snaps_creds,
238 self.__logger.info("Creating cloudify manager VM")
239 manager_creator.create()
240 self.created_object.append(manager_creator)
242 public_auth_url = keystone_utils.get_endpoint(
243 self.snaps_creds, 'identity')
245 self.__logger.info("Set creds for cloudify manager")
246 cfy_creds = dict(keystone_username=self.tenant_name,
247 keystone_password=self.tenant_name,
248 keystone_tenant_name=self.tenant_name,
249 keystone_url=public_auth_url)
251 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
254 tenant='default_tenant')
256 self.orchestrator['object'] = cfy_client
258 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
260 self.__logger.info("Attemps running status of the Manager")
263 while str(cfy_status) != 'running' and retry:
265 cfy_status = cfy_client.manager.get_status()['status']
266 self.__logger.debug("The current manager status is %s",
268 except Exception: # pylint: disable=broad-except
269 self.__logger.warning("Cloudify Manager isn't " +
270 "up and running. Retrying ...")
274 if str(cfy_status) == 'running':
275 self.__logger.info("Cloudify Manager is up and running")
277 raise Exception("Cloudify Manager isn't up and running")
279 self.__logger.info("Put OpenStack creds in manager")
280 secrets_list = cfy_client.secrets.list()
281 for k, val in cfy_creds.iteritems():
282 if not any(d.get('key', None) == k for d in secrets_list):
283 cfy_client.secrets.create(k, val)
285 cfy_client.secrets.update(k, val)
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 run_blocking_ssh_command(ssh, cmd)
296 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
297 run_blocking_ssh_command(ssh, cmd)
298 cmd = "sudo yum install -y gcc python-devel"
299 run_blocking_ssh_command(
300 ssh, cmd, "Unable to install packages on manager")
302 self.details['orchestrator'].update(status='PASS', duration=duration)
304 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
308 def deploy_vnf(self):
309 start_time = time.time()
311 self.__logger.info("Upload VNFD")
312 cfy_client = self.orchestrator['object']
313 descriptor = self.vnf['descriptor']
314 self.deployment_name = descriptor.get('name')
316 vrouter_blueprint_dir = os.path.join(self.data_dir,
317 self.util.blueprint_dir)
318 if not os.path.exists(vrouter_blueprint_dir):
319 Repo.clone_from(descriptor.get('url'),
320 vrouter_blueprint_dir,
321 branch=descriptor.get('version'))
323 cfy_client.blueprints.upload(vrouter_blueprint_dir +
324 self.util.blueprint_file_name,
325 descriptor.get('name'))
327 self.__logger.info("Get or create flavor for vrouter")
328 flavor_settings = FlavorConfig(
329 name=self.vnf['requirements']['flavor']['name'],
330 ram=self.vnf['requirements']['flavor']['ram_min'],
333 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
334 flavor = flavor_creator.create()
335 self.created_object.append(flavor_creator)
338 glance = glance_utils.glance_client(self.snaps_creds)
339 image = glance_utils.get_image(glance,
341 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
342 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
345 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
346 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
348 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
349 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
350 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
351 self.vnf['inputs'].update(
352 dict(keystone_url=keystone_utils.get_endpoint(
353 self.snaps_creds, 'identity')))
355 self.__logger.info("Create VNF Instance")
356 cfy_client.deployments.create(descriptor.get('name'),
357 descriptor.get('name'),
358 self.vnf.get('inputs'))
360 wait_for_execution(cfy_client,
362 cfy_client, descriptor.get('name')),
366 self.__logger.info("Start the VNF Instance deployment")
367 execution = cfy_client.executions.start(descriptor.get('name'),
370 execution = wait_for_execution(cfy_client, execution, self.__logger)
372 duration = time.time() - start_time
374 self.__logger.info(execution)
375 if execution.status == 'terminated':
376 self.details['vnf'].update(status='PASS', duration=duration)
379 self.details['vnf'].update(status='FAIL', duration=duration)
384 cfy_client = self.orchestrator['object']
385 credentials = {"snaps_creds": self.snaps_creds,
386 "username": self.snaps_creds.username,
387 "password": self.snaps_creds.password,
388 "auth_url": self.snaps_creds.auth_url,
389 "tenant_name": self.snaps_creds.project_name}
391 self.util_info = {"credentials": credentials,
393 "vnf_data_dir": self.util.vnf_data_dir}
395 start_time = time.time()
397 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
399 duration = time.time() - start_time
402 self.details['test_vnf'].update(status='PASS',
404 full_result=test_result_data,
407 self.details['test_vnf'].update(status='FAIL',
409 full_result=test_result_data,
416 cfy_client = self.orchestrator['object']
417 dep_name = self.vnf['descriptor'].get('name')
418 # kill existing execution
419 self.__logger.info('Deleting the current deployment')
420 exec_list = cfy_client.executions.list(dep_name)
421 for execution in exec_list:
422 if execution['status'] == "started":
424 cfy_client.executions.cancel(execution['id'],
426 except Exception: # pylint: disable=broad-except
427 self.__logger.warn("Can't cancel the current exec")
429 execution = cfy_client.executions.start(
432 parameters=dict(ignore_failure=True))
434 wait_for_execution(cfy_client, execution, self.__logger)
435 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
436 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
437 except Exception: # pylint: disable=broad-except
438 self.__logger.warn("Some issue during the undeployment ..")
439 self.__logger.warn("Tenant clean continue ..")
440 super(CloudifyVrouter, self).clean()
442 def get_vnf_info_list(self, target_vnf_name):
443 return self.util.get_vnf_info_list(
444 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
447 def wait_for_execution(client, execution, logger, timeout=7200, ):
448 """Wait for a workflow execution on Cloudify Manager."""
449 # if execution already ended - return without waiting
450 if execution.status in Execution.END_STATES:
453 if timeout is not None:
454 deadline = time.time() + timeout
456 # Poll for execution status and execution logs, until execution ends
457 # and we receive an event of type in WORKFLOW_END_TYPES
461 execution_ended = False
463 event_list = client.events.list(
464 execution_id=execution.id,
468 sort='@timestamp').items
470 offset = offset + len(event_list)
471 for event in event_list:
472 logger.debug(event.get('message'))
474 if timeout is not None:
475 if time.time() > deadline:
477 'execution of operation {0} for deployment {1} '
478 'timed out'.format(execution.workflow_id,
479 execution.deployment_id))
481 # update the remaining timeout
482 timeout = deadline - time.time()
484 if not execution_ended:
485 execution = client.executions.get(execution.id)
486 execution_ended = execution.status in Execution.END_STATES
496 def get_execution_id(client, deployment_id):
498 Get the execution id of a env preparation.
499 network, security group, fip, VM creation
501 executions = client.executions.list(deployment_id=deployment_id)
502 for execution in executions:
503 if execution.workflow_id == 'create_deployment_environment':
505 raise RuntimeError('Failed to get create_deployment_environment '
506 'workflow execution.'
507 'Available executions: {0}'.format(executions))
510 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
511 """Command to run ssh command with the exit status."""
512 (_, stdout, _) = ssh.exec_command(cmd)
513 if stdout.channel.recv_exit_status() != 0:
514 raise Exception(error_msg)