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(
139 name=image_name, image_user='cloud',
140 img_format='qcow2', image_file=image_file))
141 image_creator.create()
142 self.created_object.append(image_creator)
144 def deploy_orchestrator(self):
145 # pylint: disable=too-many-locals,too-many-statements
147 Deploy Cloudify Manager.
148 network, security group, fip, VM creation
152 start_time = time.time()
153 self.__logger.info("Creating keypair ...")
154 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
155 keypair_settings = KeypairConfig(
156 name='cloudify_vrouter_kp-{}'.format(self.uuid),
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(
164 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
165 cidr='10.67.79.0/24')
166 network_settings = NetworkConfig(
167 name='cloudify_vrouter_network-{}'.format(self.uuid),
168 subnet_settings=[subnet_settings])
169 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
170 network_creator.create()
171 self.created_object.append(network_creator)
172 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
173 router_creator = OpenStackRouter(
176 name='cloudify_vrouter_router-{}'.format(self.uuid),
177 external_gateway=ext_net_name,
178 internal_subnets=[subnet_settings.name]))
179 router_creator.create()
180 self.created_object.append(router_creator)
182 # security group creation
183 self.__logger.info("Creating security group for cloudify manager vm")
186 SecurityGroupRuleConfig(
187 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
188 direction=Direction.ingress,
189 protocol=Protocol.tcp, port_range_min=1,
190 port_range_max=65535))
192 SecurityGroupRuleConfig(
193 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
194 direction=Direction.ingress,
195 protocol=Protocol.udp, port_range_min=1,
196 port_range_max=65535))
198 security_group_creator = OpenStackSecurityGroup(
201 name="sg-cloudify-manager-{}".format(self.uuid),
202 rule_settings=sg_rules))
204 security_group_creator.create()
205 self.created_object.append(security_group_creator)
207 # orchestrator VM flavor
208 self.__logger.info("Get or create flavor for cloudify manager vm ...")
210 flavor_settings = FlavorConfig(
211 name=self.orchestrator['requirements']['flavor']['name'],
212 ram=self.orchestrator['requirements']['flavor']['ram_min'],
215 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
216 flavor_creator.create()
217 self.created_object.append(flavor_creator)
218 image_settings = ImageConfig(
219 name=self.orchestrator['requirements']['os_image'],
223 port_settings = PortConfig(
224 name='cloudify_manager_port-{}'.format(self.uuid),
225 network_name=network_settings.name)
227 manager_settings = VmInstanceConfig(
228 name='cloudify_manager-{}'.format(self.uuid),
229 flavor=flavor_settings.name,
230 port_settings=[port_settings],
231 security_group_names=[
232 security_group_creator.sec_grp_settings.name],
233 floating_ip_settings=[FloatingIpConfig(
234 name='cloudify_manager_fip-{}'.format(self.uuid),
235 port_name=port_settings.name,
236 router_name=router_creator.router_settings.name)])
238 manager_creator = OpenStackVmInstance(
239 self.snaps_creds, manager_settings, image_settings,
242 self.__logger.info("Creating cloudify manager VM")
243 manager_creator.create()
244 self.created_object.append(manager_creator)
246 public_auth_url = keystone_utils.get_endpoint(
247 self.snaps_creds, 'identity')
249 self.__logger.info("Set creds for cloudify manager")
251 keystone_username=self.snaps_creds.username,
252 keystone_password=self.snaps_creds.password,
253 keystone_tenant_name=self.snaps_creds.project_name,
254 keystone_url=public_auth_url)
256 cfy_client = CloudifyClient(
257 host=manager_creator.get_floating_ip().ip,
258 username='admin', password='admin', tenant='default_tenant')
260 self.orchestrator['object'] = cfy_client
262 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
264 self.__logger.info("Attemps running status of the Manager")
267 while str(cfy_status) != 'running' and retry:
268 print cfy_client.manager.get_status()
270 cfy_status = cfy_client.manager.get_status()['status']
271 self.__logger.debug("The current manager status is %s",
273 except Exception: # pylint: disable=broad-except
274 self.__logger.exception(
275 "Cloudify Manager isn't up and running. Retrying ...")
279 if str(cfy_status) == 'running':
280 self.__logger.info("Cloudify Manager is up and running")
282 raise Exception("Cloudify Manager isn't up and running")
284 self.__logger.info("Put OpenStack creds in manager")
285 secrets_list = cfy_client.secrets.list()
286 for k, val in cfy_creds.iteritems():
287 if not any(d.get('key', None) == k for d in secrets_list):
288 cfy_client.secrets.create(k, val)
290 cfy_client.secrets.update(k, val)
292 duration = time.time() - start_time
294 self.__logger.info("Put private keypair in manager")
295 if manager_creator.vm_ssh_active(block=True):
296 ssh = manager_creator.ssh_client()
297 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
298 scp.put(kp_file, '~/')
299 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
300 run_blocking_ssh_command(ssh, cmd)
301 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
302 run_blocking_ssh_command(ssh, cmd)
303 cmd = "sudo yum install -y gcc python-devel"
304 run_blocking_ssh_command(
305 ssh, cmd, "Unable to install packages on manager")
307 self.details['orchestrator'].update(status='PASS', duration=duration)
309 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
313 def deploy_vnf(self):
314 start_time = time.time()
316 self.__logger.info("Upload VNFD")
317 cfy_client = self.orchestrator['object']
318 descriptor = self.vnf['descriptor']
319 self.deployment_name = descriptor.get('name')
321 vrouter_blueprint_dir = os.path.join(self.data_dir,
322 self.util.blueprint_dir)
323 if not os.path.exists(vrouter_blueprint_dir):
324 Repo.clone_from(descriptor.get('url'),
325 vrouter_blueprint_dir,
326 branch=descriptor.get('version'))
328 cfy_client.blueprints.upload(vrouter_blueprint_dir +
329 self.util.blueprint_file_name,
330 descriptor.get('name'))
332 self.__logger.info("Get or create flavor for vrouter")
333 flavor_settings = FlavorConfig(
334 name=self.vnf['requirements']['flavor']['name'],
335 ram=self.vnf['requirements']['flavor']['ram_min'],
338 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
339 flavor = flavor_creator.create()
340 self.created_object.append(flavor_creator)
343 glance = glance_utils.glance_client(self.snaps_creds)
344 image = glance_utils.get_image(glance,
346 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
347 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
350 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
351 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
353 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
354 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
355 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
356 self.vnf['inputs'].update(
357 dict(keystone_url=keystone_utils.get_endpoint(
358 self.snaps_creds, 'identity')))
360 self.__logger.info("Create VNF Instance")
361 cfy_client.deployments.create(descriptor.get('name'),
362 descriptor.get('name'),
363 self.vnf.get('inputs'))
365 wait_for_execution(cfy_client,
367 cfy_client, descriptor.get('name')),
371 self.__logger.info("Start the VNF Instance deployment")
372 execution = cfy_client.executions.start(descriptor.get('name'),
375 execution = wait_for_execution(cfy_client, execution, self.__logger)
377 duration = time.time() - start_time
379 self.__logger.info(execution)
380 if execution.status == 'terminated':
381 self.details['vnf'].update(status='PASS', duration=duration)
384 self.details['vnf'].update(status='FAIL', duration=duration)
389 cfy_client = self.orchestrator['object']
390 credentials = {"snaps_creds": self.snaps_creds,
391 "username": self.snaps_creds.username,
392 "password": self.snaps_creds.password,
393 "auth_url": self.snaps_creds.auth_url,
394 "tenant_name": self.snaps_creds.project_name}
396 self.util_info = {"credentials": credentials,
398 "vnf_data_dir": self.util.vnf_data_dir}
400 start_time = time.time()
402 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
404 duration = time.time() - start_time
407 self.details['test_vnf'].update(status='PASS',
409 full_result=test_result_data,
412 self.details['test_vnf'].update(status='FAIL',
414 full_result=test_result_data,
421 cfy_client = self.orchestrator['object']
422 dep_name = self.vnf['descriptor'].get('name')
423 # kill existing execution
424 self.__logger.info('Deleting the current deployment')
425 exec_list = cfy_client.executions.list(dep_name)
426 for execution in exec_list:
427 if execution['status'] == "started":
429 cfy_client.executions.cancel(execution['id'],
431 except Exception: # pylint: disable=broad-except
432 self.__logger.warn("Can't cancel the current exec")
434 execution = cfy_client.executions.start(
437 parameters=dict(ignore_failure=True))
439 wait_for_execution(cfy_client, execution, self.__logger)
440 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
441 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
442 except Exception: # pylint: disable=broad-except
443 self.__logger.warn("Some issue during the undeployment ..")
444 self.__logger.warn("Tenant clean continue ..")
445 super(CloudifyVrouter, self).clean()
447 def get_vnf_info_list(self, target_vnf_name):
448 return self.util.get_vnf_info_list(
449 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
452 def wait_for_execution(client, execution, logger, timeout=7200, ):
453 """Wait for a workflow execution on Cloudify Manager."""
454 # if execution already ended - return without waiting
455 if execution.status in Execution.END_STATES:
458 if timeout is not None:
459 deadline = time.time() + timeout
461 # Poll for execution status and execution logs, until execution ends
462 # and we receive an event of type in WORKFLOW_END_TYPES
466 execution_ended = False
468 event_list = client.events.list(
469 execution_id=execution.id,
473 sort='@timestamp').items
475 offset = offset + len(event_list)
476 for event in event_list:
477 logger.debug(event.get('message'))
479 if timeout is not None:
480 if time.time() > deadline:
482 'execution of operation {0} for deployment {1} '
483 'timed out'.format(execution.workflow_id,
484 execution.deployment_id))
486 # update the remaining timeout
487 timeout = deadline - time.time()
489 if not execution_ended:
490 execution = client.executions.get(execution.id)
491 execution_ended = execution.status in Execution.END_STATES
501 def get_execution_id(client, deployment_id):
503 Get the execution id of a env preparation.
504 network, security group, fip, VM creation
506 executions = client.executions.list(deployment_id=deployment_id)
507 for execution in executions:
508 if execution.workflow_id == 'create_deployment_environment':
510 raise RuntimeError('Failed to get create_deployment_environment '
511 'workflow execution.'
512 'Available executions: {0}'.format(executions))
515 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
516 """Command to run ssh command with the exit status."""
517 (_, stdout, _) = ssh.exec_command(cmd)
518 if stdout.channel.recv_exit_status() != 0:
519 raise Exception(error_msg)