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
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.constants import CONST
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=get_config("orchestrator.requirements", config_file),
84 self.details['orchestrator'] = dict(
85 name=get_config("orchestrator.name", config_file),
86 version=get_config("orchestrator.version", config_file),
90 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
91 self.__logger.debug("name = %s", self.name)
93 descriptor=get_config("vnf.descriptor", config_file),
94 inputs=get_config("vnf.inputs", config_file),
95 requirements=get_config("vnf.requirements", config_file)
97 self.details['vnf'] = dict(
98 descriptor_version=self.vnf['descriptor']['version'],
99 name=get_config("vnf.name", config_file),
100 version=get_config("vnf.version", config_file),
102 self.__logger.debug("VNF configuration: %s", self.vnf)
104 self.util = Utilvnf()
106 self.details['test_vnf'] = dict(
107 name=get_config("vnf_test_suite.name", config_file),
108 version=get_config("vnf_test_suite.version", config_file)
110 self.images = get_config("tenant_images", config_file)
111 self.__logger.info("Images needed for vrouter: %s", self.images)
114 super(CloudifyVrouter, self).prepare()
116 self.__logger.info("Additional pre-configuration steps")
118 self.util.set_credentials(self.snaps_creds)
121 self.__logger.info("Upload some OS images if it doesn't exist")
122 for image_name, image_file in self.images.iteritems():
123 self.__logger.info("image: %s, file: %s", image_name, image_file)
124 if image_file and image_name:
125 image_creator = OpenStackImage(
127 ImageConfig(name=image_name,
130 image_file=image_file))
131 image_creator.create()
132 self.created_object.append(image_creator)
134 def deploy_orchestrator(self):
135 # pylint: disable=too-many-locals,too-many-statements
137 Deploy Cloudify Manager.
138 network, security group, fip, VM creation
142 start_time = time.time()
143 self.__logger.info("Creating keypair ...")
144 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
145 keypair_settings = KeypairConfig(name='cloudify_vrouter_kp',
146 private_filepath=kp_file)
147 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
148 keypair_creator.create()
149 self.created_object.append(keypair_creator)
151 self.__logger.info("Creating full network ...")
152 subnet_settings = SubnetConfig(name='cloudify_vrouter_subnet',
153 cidr='10.67.79.0/24')
154 network_settings = NetworkConfig(name='cloudify_vrouter_network',
155 subnet_settings=[subnet_settings])
156 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
157 network_creator.create()
158 self.created_object.append(network_creator)
159 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
160 router_creator = OpenStackRouter(
163 name='cloudify_vrouter_router',
164 external_gateway=ext_net_name,
165 internal_subnets=[subnet_settings.name]))
166 router_creator.create()
167 self.created_object.append(router_creator)
169 # security group creation
170 self.__logger.info("Creating security group for cloudify manager vm")
173 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
174 direction=Direction.ingress,
175 protocol=Protocol.tcp, port_range_min=1,
176 port_range_max=65535))
178 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
179 direction=Direction.ingress,
180 protocol=Protocol.udp, port_range_min=1,
181 port_range_max=65535))
183 security_group_creator = OpenStackSecurityGroup(
186 name="sg-cloudify-manager",
187 rule_settings=sg_rules))
189 security_group_creator.create()
190 self.created_object.append(security_group_creator)
192 # orchestrator VM flavor
193 self.__logger.info("Get or create flavor for cloudify manager vm ...")
195 flavor_settings = FlavorConfig(
196 name=self.orchestrator['requirements']['flavor']['name'],
197 ram=self.orchestrator['requirements']['flavor']['ram_min'],
200 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
201 flavor_creator.create()
202 self.created_object.append(flavor_creator)
203 image_settings = ImageConfig(
204 name=self.orchestrator['requirements']['os_image'],
208 port_settings = PortConfig(name='cloudify_manager_port',
209 network_name=network_settings.name)
211 manager_settings = VmInstanceConfig(
212 name='cloudify_manager',
213 flavor=flavor_settings.name,
214 port_settings=[port_settings],
215 security_group_names=[
216 security_group_creator.sec_grp_settings.name],
217 floating_ip_settings=[FloatingIpConfig(
218 name='cloudify_manager_fip',
219 port_name=port_settings.name,
220 router_name=router_creator.router_settings.name)])
222 manager_creator = OpenStackVmInstance(self.snaps_creds,
227 self.__logger.info("Creating cloudify manager VM")
228 manager_creator.create()
229 self.created_object.append(manager_creator)
231 public_auth_url = keystone_utils.get_endpoint(
232 self.snaps_creds, 'identity')
234 self.__logger.info("Set creds for cloudify manager")
235 cfy_creds = dict(keystone_username=self.tenant_name,
236 keystone_password=self.tenant_name,
237 keystone_tenant_name=self.tenant_name,
238 keystone_url=public_auth_url)
240 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
243 tenant='default_tenant')
245 self.orchestrator['object'] = cfy_client
247 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
249 self.__logger.info("Attemps running status of the Manager")
252 while str(cfy_status) != 'running' and retry:
254 cfy_status = cfy_client.manager.get_status()['status']
255 self.__logger.debug("The current manager status is %s",
257 except Exception: # pylint: disable=broad-except
258 self.__logger.warning("Cloudify Manager isn't " +
259 "up and running. Retrying ...")
263 if str(cfy_status) == 'running':
264 self.__logger.info("Cloudify Manager is up and running")
266 raise Exception("Cloudify Manager isn't up and running")
268 self.__logger.info("Put OpenStack creds in manager")
269 secrets_list = cfy_client.secrets.list()
270 for k, val in cfy_creds.iteritems():
271 if not any(d.get('key', None) == k for d in secrets_list):
272 cfy_client.secrets.create(k, val)
274 cfy_client.secrets.update(k, val)
276 duration = time.time() - start_time
278 self.__logger.info("Put private keypair in manager")
279 if manager_creator.vm_ssh_active(block=True):
280 ssh = manager_creator.ssh_client()
281 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
282 scp.put(kp_file, '~/')
283 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
284 run_blocking_ssh_command(ssh, cmd)
285 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
286 run_blocking_ssh_command(ssh, cmd)
287 cmd = "sudo yum install -y gcc python-devel"
288 run_blocking_ssh_command(
289 ssh, cmd, "Unable to install packages on manager")
291 self.details['orchestrator'].update(status='PASS', duration=duration)
293 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
297 def deploy_vnf(self):
298 start_time = time.time()
300 self.__logger.info("Upload VNFD")
301 cfy_client = self.orchestrator['object']
302 descriptor = self.vnf['descriptor']
303 self.deployment_name = descriptor.get('name')
305 vrouter_blueprint_dir = os.path.join(self.data_dir,
306 self.util.blueprint_dir)
307 if not os.path.exists(vrouter_blueprint_dir):
308 Repo.clone_from(descriptor.get('url'),
309 vrouter_blueprint_dir,
310 branch=descriptor.get('version'))
312 cfy_client.blueprints.upload(vrouter_blueprint_dir +
313 self.util.blueprint_file_name,
314 descriptor.get('name'))
316 self.__logger.info("Get or create flavor for vrouter")
317 flavor_settings = FlavorConfig(
318 name=self.vnf['requirements']['flavor']['name'],
319 ram=self.vnf['requirements']['flavor']['ram_min'],
322 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
323 flavor = flavor_creator.create()
324 self.created_object.append(flavor_creator)
327 glance = glance_utils.glance_client(self.snaps_creds)
328 image = glance_utils.get_image(glance,
330 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
331 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
334 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
335 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
337 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
338 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
339 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
340 self.vnf['inputs'].update(
341 dict(keystone_url=keystone_utils.get_endpoint(
342 self.snaps_creds, 'identity')))
344 self.__logger.info("Create VNF Instance")
345 cfy_client.deployments.create(descriptor.get('name'),
346 descriptor.get('name'),
347 self.vnf.get('inputs'))
349 wait_for_execution(cfy_client,
351 cfy_client, descriptor.get('name')),
355 self.__logger.info("Start the VNF Instance deployment")
356 execution = cfy_client.executions.start(descriptor.get('name'),
359 execution = wait_for_execution(cfy_client, execution, self.__logger)
361 duration = time.time() - start_time
363 self.__logger.info(execution)
364 if execution.status == 'terminated':
365 self.details['vnf'].update(status='PASS', duration=duration)
368 self.details['vnf'].update(status='FAIL', duration=duration)
373 cfy_client = self.orchestrator['object']
374 credentials = {"snaps_creds": self.snaps_creds,
375 "username": self.snaps_creds.username,
376 "password": self.snaps_creds.password,
377 "auth_url": self.snaps_creds.auth_url,
378 "tenant_name": self.snaps_creds.project_name}
380 self.util_info = {"credentials": credentials,
382 "vnf_data_dir": self.util.vnf_data_dir}
384 start_time = time.time()
386 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
388 duration = time.time() - start_time
391 self.details['test_vnf'].update(status='PASS',
393 full_result=test_result_data,
396 self.details['test_vnf'].update(status='FAIL',
398 full_result=test_result_data,
405 cfy_client = self.orchestrator['object']
406 dep_name = self.vnf['descriptor'].get('name')
407 # kill existing execution
408 self.__logger.info('Deleting the current deployment')
409 exec_list = cfy_client.executions.list(dep_name)
410 for execution in exec_list:
411 if execution['status'] == "started":
413 cfy_client.executions.cancel(execution['id'],
415 except Exception: # pylint: disable=broad-except
416 self.__logger.warn("Can't cancel the current exec")
418 execution = cfy_client.executions.start(
421 parameters=dict(ignore_failure=True))
423 wait_for_execution(cfy_client, execution, self.__logger)
424 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
425 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
426 except Exception: # pylint: disable=broad-except
427 self.__logger.warn("Some issue during the undeployment ..")
428 self.__logger.warn("Tenant clean continue ..")
429 super(CloudifyVrouter, self).clean()
431 def get_vnf_info_list(self, target_vnf_name):
432 return self.util.get_vnf_info_list(
433 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
436 # ----------------------------------------------------------
440 # -----------------------------------------------------------
441 def get_config(parameter, file_path):
443 Get config parameter.
444 Returns the value of a given parameter in file.yaml
445 parameter must be given in string format with dots
446 Example: general.openstack.image_name
448 with open(file_path) as config_file:
449 file_yaml = yaml.safe_load(config_file)
452 for element in parameter.split("."):
453 value = value.get(element)
455 raise ValueError("The parameter %s is not defined in"
456 " reporting.yaml" % parameter)
460 def wait_for_execution(client, execution, logger, timeout=7200, ):
461 """Wait for a workflow execution on Cloudify Manager."""
462 # if execution already ended - return without waiting
463 if execution.status in Execution.END_STATES:
466 if timeout is not None:
467 deadline = time.time() + timeout
469 # Poll for execution status and execution logs, until execution ends
470 # and we receive an event of type in WORKFLOW_END_TYPES
474 execution_ended = False
476 event_list = client.events.list(
477 execution_id=execution.id,
481 sort='@timestamp').items
483 offset = offset + len(event_list)
484 for event in event_list:
485 logger.debug(event.get('message'))
487 if timeout is not None:
488 if time.time() > deadline:
490 'execution of operation {0} for deployment {1} '
491 'timed out'.format(execution.workflow_id,
492 execution.deployment_id))
494 # update the remaining timeout
495 timeout = deadline - time.time()
497 if not execution_ended:
498 execution = client.executions.get(execution.id)
499 execution_ended = execution.status in Execution.END_STATES
509 def get_execution_id(client, deployment_id):
511 Get the execution id of a env preparation.
512 network, security group, fip, VM creation
514 executions = client.executions.list(deployment_id=deployment_id)
515 for execution in executions:
516 if execution.workflow_id == 'create_deployment_environment':
518 raise RuntimeError('Failed to get create_deployment_environment '
519 'workflow execution.'
520 'Available executions: {0}'.format(executions))
523 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
524 """Command to run ssh command with the exit status."""
525 (_, stdout, _) = ssh.exec_command(cmd)
526 if stdout.channel.recv_exit_status() != 0:
527 raise Exception(error_msg)