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