Support py3 in uncovered packages
[functest.git] / functest / opnfv_tests / vnf / router / cloudify_vrouter.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2017 Okinawa Open Laboratory and others.
4 #
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
9
10 # pylint: disable=missing-docstring
11
12 """vrouter testcase implementation."""
13
14 import logging
15 import os
16 import time
17 import uuid
18
19 from cloudify_rest_client import CloudifyClient
20 from cloudify_rest_client.executions import Execution
21 from scp import SCPClient
22
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
29
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
39
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
48
49 import snaps.openstack.utils.glance_utils as glance_utils
50 from snaps.openstack.utils import keystone_utils
51
52
53 import six
54
55
56 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
57
58
59 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
60     # pylint: disable=too-many-instance-attributes
61     """vrouter testcase deployed with Cloudify Orchestrator."""
62
63     __logger = logging.getLogger(__name__)
64     name = __name__
65
66     def __init__(self, **kwargs):
67         if "case_name" not in kwargs:
68             kwargs["case_name"] = "vyos_vrouter"
69         super(CloudifyVrouter, self).__init__(**kwargs)
70
71         # Retrieve the configuration
72         try:
73             self.config = getattr(
74                 config.CONF, 'vnf_{}_config'.format(self.case_name))
75         except Exception:
76             raise Exception("VNF config file not found")
77
78         self.cfy_manager_ip = ''
79         self.deployment_name = ''
80
81         config_file = os.path.join(self.case_dir, self.config)
82         self.orchestrator = dict(
83             requirements=functest_utils.get_parameter_from_yaml(
84                 "orchestrator.requirements", config_file),
85         )
86         self.details['orchestrator'] = dict(
87             name=functest_utils.get_parameter_from_yaml(
88                 "orchestrator.name", config_file),
89             version=functest_utils.get_parameter_from_yaml(
90                 "orchestrator.version", config_file),
91             status='ERROR',
92             result=''
93         )
94         self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
95         self.__logger.debug("name = %s", self.name)
96         self.vnf = dict(
97             descriptor=functest_utils.get_parameter_from_yaml(
98                 "vnf.descriptor", config_file),
99             inputs=functest_utils.get_parameter_from_yaml(
100                 "vnf.inputs", config_file),
101             requirements=functest_utils.get_parameter_from_yaml(
102                 "vnf.requirements", config_file)
103         )
104         self.details['vnf'] = dict(
105             descriptor_version=self.vnf['descriptor']['version'],
106             name=functest_utils.get_parameter_from_yaml(
107                 "vnf.name", config_file),
108             version=functest_utils.get_parameter_from_yaml(
109                 "vnf.version", config_file),
110         )
111         self.__logger.debug("VNF configuration: %s", self.vnf)
112
113         self.util = Utilvnf()
114
115         self.details['test_vnf'] = dict(
116             name=functest_utils.get_parameter_from_yaml(
117                 "vnf_test_suite.name", config_file),
118             version=functest_utils.get_parameter_from_yaml(
119                 "vnf_test_suite.version", config_file)
120         )
121         self.images = functest_utils.get_parameter_from_yaml(
122             "tenant_images", config_file)
123         self.__logger.info("Images needed for vrouter: %s", self.images)
124
125     @staticmethod
126     def run_blocking_ssh_command(ssh, cmd,
127                                  error_msg="Unable to run this command"):
128         """Command to run ssh command with the exit status."""
129         (_, stdout, stderr) = ssh.exec_command(cmd)
130         CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
131         if stdout.channel.recv_exit_status() != 0:
132             CloudifyVrouter.__logger.error(
133                 "SSH %s stderr: %s", cmd, stderr.read())
134             raise Exception(error_msg)
135
136     def prepare(self):
137         super(CloudifyVrouter, self).prepare()
138         self.__logger.info("Additional pre-configuration steps")
139         self.util.set_credentials(self.snaps_creds)
140
141     def deploy_orchestrator(self):
142         # pylint: disable=too-many-locals,too-many-statements
143         """
144         Deploy Cloudify Manager.
145         network, security group, fip, VM creation
146         """
147         # network creation
148         start_time = time.time()
149
150         # orchestrator VM flavor
151         self.__logger.info("Get or create flavor for cloudify manager vm ...")
152         flavor_settings = FlavorConfig(
153             name="{}-{}".format(
154                 self.orchestrator['requirements']['flavor']['name'],
155                 self.uuid),
156             ram=self.orchestrator['requirements']['flavor']['ram_min'],
157             disk=50, vcpus=2)
158         flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
159         flavor_creator.create()
160         self.created_object.append(flavor_creator)
161
162         user_creator = OpenStackUser(
163             self.snaps_creds,
164             UserConfig(
165                 name='cloudify_network_bug-{}'.format(self.uuid),
166                 password=str(uuid.uuid4()),
167                 project_name=self.tenant_name,
168                 domain_name=self.snaps_creds.user_domain_name,
169                 roles={'_member_': self.tenant_name}))
170         user_creator.create()
171         self.created_object.append(user_creator)
172
173         snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
174         self.__logger.debug("snaps creds: %s", snaps_creds)
175
176         self.__logger.info("Creating keypair ...")
177         kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
178         keypair_settings = KeypairConfig(
179             name='cloudify_vrouter_kp-{}'.format(self.uuid),
180             private_filepath=kp_file)
181         keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
182         keypair_creator.create()
183         self.created_object.append(keypair_creator)
184
185         self.__logger.info("Upload some OS images if it doesn't exist")
186         for image_name, image_file in six.iteritems(self.images):
187             self.__logger.info("image: %s, file: %s", image_name, image_file)
188             if image_file and image_name:
189                 image_creator = OpenStackImage(
190                     snaps_creds,
191                     ImageConfig(
192                         name=image_name, image_user='cloud',
193                         img_format='qcow2', image_file=image_file))
194                 image_creator.create()
195                 self.created_object.append(image_creator)
196
197         self.__logger.info("Creating full network ...")
198         subnet_settings = SubnetConfig(
199             name='cloudify_vrouter_subnet-{}'.format(self.uuid),
200             cidr='10.67.79.0/24',
201             dns_nameservers=[env.get('NAMESERVER')])
202         network_settings = NetworkConfig(
203             name='cloudify_vrouter_network-{}'.format(self.uuid),
204             subnet_settings=[subnet_settings])
205         network_creator = OpenStackNetwork(snaps_creds, network_settings)
206         network_creator.create()
207         self.created_object.append(network_creator)
208         ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
209         router_creator = OpenStackRouter(
210             snaps_creds,
211             RouterConfig(
212                 name='cloudify_vrouter_router-{}'.format(self.uuid),
213                 external_gateway=ext_net_name,
214                 internal_subnets=[subnet_settings.name]))
215         router_creator.create()
216         self.created_object.append(router_creator)
217
218         # security group creation
219         self.__logger.info("Creating security group for cloudify manager vm")
220         sg_rules = list()
221         sg_rules.append(
222             SecurityGroupRuleConfig(
223                 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
224                 direction=Direction.ingress,
225                 protocol=Protocol.tcp, port_range_min=1,
226                 port_range_max=65535))
227         sg_rules.append(
228             SecurityGroupRuleConfig(
229                 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
230                 direction=Direction.ingress,
231                 protocol=Protocol.udp, port_range_min=1,
232                 port_range_max=65535))
233         security_group_creator = OpenStackSecurityGroup(
234             snaps_creds,
235             SecurityGroupConfig(
236                 name="sg-cloudify-manager-{}".format(self.uuid),
237                 rule_settings=sg_rules))
238         security_group_creator.create()
239         self.created_object.append(security_group_creator)
240
241         image_settings = ImageConfig(
242             name=self.orchestrator['requirements']['os_image'],
243             image_user='centos', exists=True)
244         port_settings = PortConfig(
245             name='cloudify_manager_port-{}'.format(self.uuid),
246             network_name=network_settings.name)
247         manager_settings = VmInstanceConfig(
248             name='cloudify_manager-{}'.format(self.uuid),
249             flavor=flavor_settings.name,
250             port_settings=[port_settings],
251             security_group_names=[
252                 security_group_creator.sec_grp_settings.name],
253             floating_ip_settings=[FloatingIpConfig(
254                 name='cloudify_manager_fip-{}'.format(self.uuid),
255                 port_name=port_settings.name,
256                 router_name=router_creator.router_settings.name)])
257         manager_creator = OpenStackVmInstance(
258             snaps_creds, manager_settings, image_settings,
259             keypair_settings)
260
261         self.__logger.info("Creating cloudify manager VM")
262         manager_creator.create()
263         self.created_object.append(manager_creator)
264
265         cfy_client = CloudifyClient(
266             host=manager_creator.get_floating_ip().ip,
267             username='admin', password='admin', tenant='default_tenant',
268             api_version='v3')
269
270         self.orchestrator['object'] = cfy_client
271
272         self.cfy_manager_ip = manager_creator.get_floating_ip().ip
273
274         self.__logger.info("Attemps running status of the Manager")
275         for loop in range(10):
276             try:
277                 self.__logger.debug(
278                     "status %s", cfy_client.manager.get_status())
279                 cfy_status = cfy_client.manager.get_status()['status']
280                 self.__logger.info(
281                     "The current manager status is %s", cfy_status)
282                 if str(cfy_status) != 'running':
283                     raise Exception("Cloudify Manager isn't up and running")
284                 break
285             except Exception:  # pylint: disable=broad-except
286                 self.logger.info(
287                     "try %s: Cloudify Manager isn't up and running", loop + 1)
288                 time.sleep(30)
289         else:
290             self.logger.error("Cloudify Manager isn't up and running")
291             return False
292
293         duration = time.time() - start_time
294
295         self.__logger.info("Put private keypair in manager")
296         if manager_creator.vm_ssh_active(block=True):
297             ssh = manager_creator.ssh_client()
298             scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
299             scp.put(kp_file, '~/')
300             cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
301             self.run_blocking_ssh_command(ssh, cmd)
302             cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
303             self.run_blocking_ssh_command(ssh, cmd)
304             # cmd2 is badly unpinned by Cloudify
305             cmd = "sudo yum install -y gcc python-devel python-cmd2"
306             self.run_blocking_ssh_command(
307                 ssh, cmd, "Unable to install packages on manager")
308         else:
309             self.__logger.error("Cannot connect to manager")
310             return False
311
312         self.details['orchestrator'].update(status='PASS', duration=duration)
313
314         self.__logger.info("Get or create flavor for vrouter")
315         flavor_settings = FlavorConfig(
316             name="{}-{}".format(
317                 self.vnf['requirements']['flavor']['name'],
318                 self.uuid),
319             ram=self.vnf['requirements']['flavor']['ram_min'],
320             disk=25, vcpus=1)
321         flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
322         flavor = flavor_creator.create()
323         self.created_object.append(flavor_creator)
324
325         # set image name
326         glance = glance_utils.glance_client(snaps_creds)
327         image = glance_utils.get_image(glance, "vyos1.1.7")
328         self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
329         self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
330         self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
331         self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
332         self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
333         self.vnf['inputs'].update(dict(
334             keystone_username=snaps_creds.username))
335         self.vnf['inputs'].update(dict(
336             keystone_password=snaps_creds.password))
337         self.vnf['inputs'].update(dict(
338             keystone_tenant_name=snaps_creds.project_name))
339         self.vnf['inputs'].update(dict(
340             keystone_user_domain_name=snaps_creds.user_domain_name))
341         self.vnf['inputs'].update(dict(
342             keystone_project_domain_name=snaps_creds.project_domain_name))
343         self.vnf['inputs'].update(dict(
344             region=snaps_creds.region_name))
345         self.vnf['inputs'].update(dict(
346             keystone_url=keystone_utils.get_endpoint(
347                 snaps_creds, 'identity')))
348
349         credentials = {"snaps_creds": snaps_creds}
350         self.util_info = {"credentials": credentials,
351                           "cfy": cfy_client,
352                           "vnf_data_dir": self.util.vnf_data_dir}
353
354         return True
355
356     def deploy_vnf(self):
357         start_time = time.time()
358
359         self.__logger.info("Upload VNFD")
360         cfy_client = self.orchestrator['object']
361         descriptor = self.vnf['descriptor']
362         self.deployment_name = descriptor.get('name')
363
364         cfy_client.blueprints.upload(
365             descriptor.get('file_name'), descriptor.get('name'))
366
367         self.__logger.info("Create VNF Instance")
368         cfy_client.deployments.create(
369             descriptor.get('name'), descriptor.get('name'),
370             self.vnf.get('inputs'))
371
372         wait_for_execution(
373             cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
374             self.__logger, timeout=7200)
375
376         self.__logger.info("Start the VNF Instance deployment")
377         execution = cfy_client.executions.start(descriptor.get('name'),
378                                                 'install')
379         # Show execution log
380         execution = wait_for_execution(cfy_client, execution, self.__logger)
381
382         duration = time.time() - start_time
383
384         self.__logger.info(execution)
385         if execution.status == 'terminated':
386             self.details['vnf'].update(status='PASS', duration=duration)
387             result = True
388         else:
389             self.details['vnf'].update(status='FAIL', duration=duration)
390             result = False
391         return result
392
393     def test_vnf(self):
394         start_time = time.time()
395         result, test_result_data = super(CloudifyVrouter, self).test_vnf()
396         duration = time.time() - start_time
397         if result:
398             self.details['test_vnf'].update(
399                 status='PASS', result='OK', full_result=test_result_data,
400                 duration=duration)
401         else:
402             self.details['test_vnf'].update(
403                 status='FAIL', result='NG', full_result=test_result_data,
404                 duration=duration)
405         return True
406
407     def clean(self):
408         try:
409             cfy_client = self.orchestrator['object']
410             dep_name = self.vnf['descriptor'].get('name')
411             # kill existing execution
412             self.__logger.info('Deleting the current deployment')
413             exec_list = cfy_client.executions.list(dep_name)
414             for execution in exec_list:
415                 if execution['status'] == "started":
416                     try:
417                         cfy_client.executions.cancel(
418                             execution['id'], force=True)
419                     except Exception:  # pylint: disable=broad-except
420                         self.__logger.warn("Can't cancel the current exec")
421
422             execution = cfy_client.executions.start(
423                 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
424
425             wait_for_execution(cfy_client, execution, self.__logger)
426             cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
427             cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
428         except Exception:  # pylint: disable=broad-except
429             self.__logger.exception("Some issue during the undeployment ..")
430
431         super(CloudifyVrouter, self).clean()
432
433     def get_vnf_info_list(self, target_vnf_name):
434         return self.util.get_vnf_info_list(
435             self.cfy_manager_ip, self.deployment_name, target_vnf_name)
436
437
438 def wait_for_execution(client, execution, logger, timeout=7200, ):
439     """Wait for a workflow execution on Cloudify Manager."""
440     # if execution already ended - return without waiting
441     if execution.status in Execution.END_STATES:
442         return execution
443
444     if timeout is not None:
445         deadline = time.time() + timeout
446
447     # Poll for execution status and execution logs, until execution ends
448     # and we receive an event of type in WORKFLOW_END_TYPES
449     offset = 0
450     batch_size = 50
451     event_list = []
452     execution_ended = False
453     while True:
454         event_list = client.events.list(
455             execution_id=execution.id, _offset=offset, _size=batch_size,
456             include_logs=True, sort='@timestamp').items
457
458         offset = offset + len(event_list)
459         for event in event_list:
460             logger.debug(event.get('message'))
461
462         if timeout is not None:
463             if time.time() > deadline:
464                 raise RuntimeError(
465                     'execution of operation {0} for deployment {1} '
466                     'timed out'.format(execution.workflow_id,
467                                        execution.deployment_id))
468             else:
469                 # update the remaining timeout
470                 timeout = deadline - time.time()
471
472         if not execution_ended:
473             execution = client.executions.get(execution.id)
474             execution_ended = execution.status in Execution.END_STATES
475
476         if execution_ended:
477             break
478
479         time.sleep(5)
480
481     return execution
482
483
484 def get_execution_id(client, deployment_id):
485     """
486     Get the execution id of a env preparation.
487     network, security group, fip, VM creation
488     """
489     executions = client.executions.list(deployment_id=deployment_id)
490     for execution in executions:
491         if execution.workflow_id == 'create_deployment_environment':
492             return execution
493     raise RuntimeError('Failed to get create_deployment_environment '
494                        'workflow execution.'
495                        'Available executions: {0}'.format(executions))