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:
269 cfy_status = cfy_client.manager.get_status()['status']
270 self.__logger.debug("The current manager status is %s",
272 except Exception: # pylint: disable=broad-except
273 self.__logger.exception(
274 "Cloudify Manager isn't up and running. Retrying ...")
278 if str(cfy_status) == 'running':
279 self.__logger.info("Cloudify Manager is up and running")
281 raise Exception("Cloudify Manager isn't up and running")
283 self.__logger.info("Put OpenStack creds in manager")
284 secrets_list = cfy_client.secrets.list()
285 for k, val in cfy_creds.iteritems():
286 if not any(d.get('key', None) == k for d in secrets_list):
287 cfy_client.secrets.create(k, val)
289 cfy_client.secrets.update(k, val)
291 duration = time.time() - start_time
293 self.__logger.info("Put private keypair in manager")
294 if manager_creator.vm_ssh_active(block=True):
295 ssh = manager_creator.ssh_client()
296 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
297 scp.put(kp_file, '~/')
298 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
299 run_blocking_ssh_command(ssh, cmd)
300 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
301 run_blocking_ssh_command(ssh, cmd)
302 cmd = "sudo yum install -y gcc python-devel"
303 run_blocking_ssh_command(
304 ssh, cmd, "Unable to install packages on manager")
306 self.details['orchestrator'].update(status='PASS', duration=duration)
308 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
312 def deploy_vnf(self):
313 start_time = time.time()
315 self.__logger.info("Upload VNFD")
316 cfy_client = self.orchestrator['object']
317 descriptor = self.vnf['descriptor']
318 self.deployment_name = descriptor.get('name')
320 vrouter_blueprint_dir = os.path.join(self.data_dir,
321 self.util.blueprint_dir)
322 if not os.path.exists(vrouter_blueprint_dir):
323 Repo.clone_from(descriptor.get('url'),
324 vrouter_blueprint_dir,
325 branch=descriptor.get('version'))
327 cfy_client.blueprints.upload(vrouter_blueprint_dir +
328 self.util.blueprint_file_name,
329 descriptor.get('name'))
331 self.__logger.info("Get or create flavor for vrouter")
332 flavor_settings = FlavorConfig(
333 name=self.vnf['requirements']['flavor']['name'],
334 ram=self.vnf['requirements']['flavor']['ram_min'],
337 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
338 flavor = flavor_creator.create()
339 self.created_object.append(flavor_creator)
342 glance = glance_utils.glance_client(self.snaps_creds)
343 image = glance_utils.get_image(glance,
345 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
346 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
349 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
350 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
352 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
353 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
354 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
355 self.vnf['inputs'].update(
356 dict(keystone_url=keystone_utils.get_endpoint(
357 self.snaps_creds, 'identity')))
359 self.__logger.info("Create VNF Instance")
360 cfy_client.deployments.create(descriptor.get('name'),
361 descriptor.get('name'),
362 self.vnf.get('inputs'))
364 wait_for_execution(cfy_client,
366 cfy_client, descriptor.get('name')),
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 cfy_client = self.orchestrator['object']
389 credentials = {"snaps_creds": self.snaps_creds,
390 "username": self.snaps_creds.username,
391 "password": self.snaps_creds.password,
392 "auth_url": self.snaps_creds.auth_url,
393 "tenant_name": self.snaps_creds.project_name}
395 self.util_info = {"credentials": credentials,
397 "vnf_data_dir": self.util.vnf_data_dir}
399 start_time = time.time()
401 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
403 duration = time.time() - start_time
406 self.details['test_vnf'].update(status='PASS',
408 full_result=test_result_data,
411 self.details['test_vnf'].update(status='FAIL',
413 full_result=test_result_data,
420 cfy_client = self.orchestrator['object']
421 dep_name = self.vnf['descriptor'].get('name')
422 # kill existing execution
423 self.__logger.info('Deleting the current deployment')
424 exec_list = cfy_client.executions.list(dep_name)
425 for execution in exec_list:
426 if execution['status'] == "started":
428 cfy_client.executions.cancel(execution['id'],
430 except Exception: # pylint: disable=broad-except
431 self.__logger.warn("Can't cancel the current exec")
433 execution = cfy_client.executions.start(
436 parameters=dict(ignore_failure=True))
438 wait_for_execution(cfy_client, execution, self.__logger)
439 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
440 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
441 except Exception: # pylint: disable=broad-except
442 self.__logger.warn("Some issue during the undeployment ..")
443 self.__logger.warn("Tenant clean continue ..")
444 super(CloudifyVrouter, self).clean()
446 def get_vnf_info_list(self, target_vnf_name):
447 return self.util.get_vnf_info_list(
448 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
451 def wait_for_execution(client, execution, logger, timeout=7200, ):
452 """Wait for a workflow execution on Cloudify Manager."""
453 # if execution already ended - return without waiting
454 if execution.status in Execution.END_STATES:
457 if timeout is not None:
458 deadline = time.time() + timeout
460 # Poll for execution status and execution logs, until execution ends
461 # and we receive an event of type in WORKFLOW_END_TYPES
465 execution_ended = False
467 event_list = client.events.list(
468 execution_id=execution.id,
472 sort='@timestamp').items
474 offset = offset + len(event_list)
475 for event in event_list:
476 logger.debug(event.get('message'))
478 if timeout is not None:
479 if time.time() > deadline:
481 'execution of operation {0} for deployment {1} '
482 'timed out'.format(execution.workflow_id,
483 execution.deployment_id))
485 # update the remaining timeout
486 timeout = deadline - time.time()
488 if not execution_ended:
489 execution = client.executions.get(execution.id)
490 execution_ended = execution.status in Execution.END_STATES
500 def get_execution_id(client, deployment_id):
502 Get the execution id of a env preparation.
503 network, security group, fip, VM creation
505 executions = client.executions.list(deployment_id=deployment_id)
506 for execution in executions:
507 if execution.workflow_id == 'create_deployment_environment':
509 raise RuntimeError('Failed to get create_deployment_environment '
510 'workflow execution.'
511 'Available executions: {0}'.format(executions))
514 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
515 """Command to run ssh command with the exit status."""
516 (_, stdout, _) = ssh.exec_command(cmd)
517 if stdout.channel.recv_exit_status() != 0:
518 raise Exception(error_msg)