Avoid killing create_number.py
[functest.git] / functest / opnfv_tests / vnf / ims / heat_ims.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2018 Kontron, Orange 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 """HeatIms testcase implementation."""
11
12 from __future__ import division
13
14 import logging
15 import os
16 import re
17 import time
18 import tempfile
19
20 import paramiko
21 import pkg_resources
22 from xtesting.core import testcase
23
24 from functest.core import singlevm
25 from functest.opnfv_tests.vnf.ims import clearwater
26 from functest.utils import config
27 from functest.utils import env
28 from functest.utils import functest_utils
29
30 __author__ = "Valentin Boucher <valentin.boucher@kontron.com>"
31
32
33 class HeatIms(singlevm.VmReady2):
34     # pylint: disable=too-many-instance-attributes
35     """Clearwater vIMS deployed with Heat Orchestrator Case."""
36
37     __logger = logging.getLogger(__name__)
38
39     filename = ('/home/opnfv/functest/images/'
40                 'ubuntu-14.04-server-cloudimg-amd64-disk1.img')
41
42     flavor_ram = 1024
43     flavor_vcpus = 1
44     flavor_disk = 3
45
46     quota_security_group = 20
47     quota_security_group_rule = 100
48     quota_port = 50
49
50     parameters = {
51         'private_mgmt_net_cidr': '192.168.100.0/24',
52         'private_mgmt_net_gateway': '192.168.100.254',
53         'private_mgmt_net_pool_start': '192.168.100.1',
54         'private_mgmt_net_pool_end': '192.168.100.253'}
55
56     def __init__(self, **kwargs):
57         """Initialize HeatIms testcase object."""
58         if "case_name" not in kwargs:
59             kwargs["case_name"] = "heat_ims"
60         super(HeatIms, self).__init__(**kwargs)
61
62         # Retrieve the configuration
63         try:
64             self.config = getattr(
65                 config.CONF, 'vnf_{}_config'.format(self.case_name))
66         except Exception:
67             raise Exception("VNF config file not found")
68
69         self.case_dir = pkg_resources.resource_filename(
70             'functest', 'opnfv_tests/vnf/ims')
71         config_file = os.path.join(self.case_dir, self.config)
72
73         self.vnf = dict(
74             descriptor=functest_utils.get_parameter_from_yaml(
75                 "vnf.descriptor", config_file),
76             parameters=functest_utils.get_parameter_from_yaml(
77                 "vnf.inputs", config_file)
78         )
79         self.details['vnf'] = dict(
80             descriptor_version=self.vnf['descriptor']['version'],
81             name=functest_utils.get_parameter_from_yaml(
82                 "vnf.name", config_file),
83             version=functest_utils.get_parameter_from_yaml(
84                 "vnf.version", config_file),
85         )
86         self.__logger.debug("VNF configuration: %s", self.vnf)
87         self.keypair = None
88         self.stack = None
89         self.clearwater = None
90         self.role = None
91         (_, self.key_filename) = tempfile.mkstemp()
92
93     def create_network_resources(self):
94         pass
95
96     def execute(self):
97         # pylint: disable=too-many-locals,too-many-statements
98         """
99         Prepare Tenant/User
100
101         network, security group, fip, VM creation
102         """
103         self.orig_cloud.set_network_quotas(
104             self.project.project.name,
105             security_group=self.quota_security_group,
106             security_group_rule=self.quota_security_group_rule,
107             port=self.quota_port)
108         if not self.orig_cloud.get_role("heat_stack_owner"):
109             self.role = self.orig_cloud.create_role("heat_stack_owner")
110         self.orig_cloud.grant_role(
111             "heat_stack_owner", user=self.project.user.id,
112             project=self.project.project.id,
113             domain=self.project.domain.id)
114         self.keypair = self.cloud.create_keypair(
115             '{}-kp_{}'.format(self.case_name, self.guid))
116         self.__logger.info("keypair:\n%s", self.keypair.private_key)
117         with open(self.key_filename, 'w') as private_key_file:
118             private_key_file.write(self.keypair.private_key)
119
120         if self.deploy_vnf() and self.test_vnf():
121             self.result = 100
122             return 0
123         self.result = 1/3 * 100
124         return 1
125
126     def run(self, **kwargs):
127         """Deploy and test clearwater
128
129         Here are the main actions:
130         - deploy clearwater stack via heat
131         - test the vnf instance
132
133         Returns:
134         - TestCase.EX_OK
135         - TestCase.EX_RUN_ERROR on error
136         """
137         status = testcase.TestCase.EX_RUN_ERROR
138         try:
139             assert self.cloud
140             assert super(HeatIms, self).run(
141                 **kwargs) == testcase.TestCase.EX_OK
142             self.result = 0
143             if not self.execute():
144                 self.result = 100
145                 status = testcase.TestCase.EX_OK
146         except Exception:  # pylint: disable=broad-except
147             self.__logger.exception('Cannot run %s', self.case_name)
148         finally:
149             self.stop_time = time.time()
150         return status
151
152     def _monit(self, username="ubuntu", timeout=60):
153         servers = self.cloud.list_servers(detailed=True)
154         self.__logger.debug("servers: %s", servers)
155         for server in servers:
156             if 'ns' in server.name:
157                 break
158             self.__logger.info("server:\n%s", server.name)
159             ssh = paramiko.SSHClient()
160             ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
161             ssh.connect(
162                 server.public_v4, username=username,
163                 key_filename=self.key_filename, timeout=timeout)
164             (_, stdout, _) = ssh.exec_command('sudo monit summary')
165             self.__logger.info("output:\n%s", stdout.read())
166             ssh.close()
167
168     def deploy_vnf(self):
169         """Deploy Clearwater IMS."""
170         start_time = time.time()
171         descriptor = self.vnf['descriptor']
172         parameters = self.vnf['parameters']
173
174         parameters['public_mgmt_net_id'] = self.ext_net.id
175         parameters['flavor'] = self.flavor.name
176         parameters['image'] = self.image.name
177         parameters['key_name'] = self.keypair.name
178         parameters['external_mgmt_dns_ip'] = env.get('NAMESERVER')
179         parameters.update(self.parameters)
180
181         self.__logger.info("Create Heat Stack")
182         self.stack = self.cloud.create_stack(
183             name=descriptor.get('name'),
184             template_file=descriptor.get('file_name'),
185             wait=True, **parameters)
186         self.__logger.debug("stack: %s", self.stack)
187
188         time.sleep(300)
189         self._monit()
190
191         servers = self.cloud.list_servers(detailed=True)
192         self.__logger.debug("servers: %s", servers)
193         for server in servers:
194             if not self.check_regex_in_console(
195                     server.name, regex='Cloud-init .* finished at ', loop=1):
196                 return False
197             if 'ellis' in server.name:
198                 self.__logger.debug("server: %s", server)
199                 ellis_ip = server.public_v4
200
201         assert ellis_ip
202         self.clearwater = clearwater.ClearwaterTesting(self.case_name,
203                                                        ellis_ip)
204         # This call can take time and many retry because Heat is
205         # an infrastructure orchestrator so when Heat say "stack created"
206         # it means that all OpenStack ressources are created but not that
207         # Clearwater are up and ready (Cloud-Init script still running)
208         self.clearwater.availability_check()
209
210         duration = time.time() - start_time
211
212         self.details['vnf'].update(status='PASS', duration=duration)
213         self.result += 1/3 * 100
214
215         return True
216
217     def test_vnf(self):
218         """Run test on clearwater ims instance."""
219         start_time = time.time()
220
221         outputs = self.cloud.get_stack(self.stack.id).outputs
222         self.__logger.debug("stack outputs: %s", outputs)
223         dns_ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', str(outputs))[0]
224
225         if not dns_ip:
226             return False
227
228         short_result = self.clearwater.run_clearwater_live_test(
229             dns_ip=dns_ip,
230             public_domain=self.vnf['parameters']["zone"])
231         duration = time.time() - start_time
232         self.__logger.info(short_result)
233         self.details['test_vnf'] = dict(result=short_result,
234                                         duration=duration)
235         try:
236             vnf_test_rate = short_result['passed'] / (
237                 short_result['total'] - short_result['skipped'])
238             # orchestrator + vnf + test_vnf
239             self.result += vnf_test_rate / 3 * 100
240         except ZeroDivisionError:
241             self.__logger.error("No test has been executed")
242             self.details['test_vnf'].update(status='FAIL')
243             return False
244         except Exception:  # pylint: disable=broad-except
245             self.__logger.exception("Cannot calculate results")
246             self.details['test_vnf'].update(status='FAIL')
247             return False
248         self._monit()
249         return True if vnf_test_rate > 0 else False
250
251     def clean(self):
252         """Clean created objects/functions."""
253         assert self.cloud
254         try:
255             if self.stack:
256                 self.cloud.delete_stack(self.stack.id, wait=True)
257         except TypeError:
258             # shade raises TypeError exceptions when checking stack status
259             pass
260         except Exception:  # pylint: disable=broad-except
261             self.__logger.exception("Cannot clean stack ressources")
262         super(HeatIms, self).clean()
263         if self.role:
264             self.orig_cloud.delete_role(self.role.id)