Ease modifying the test list in E2E testing
[functest-kubernetes.git] / functest_kubernetes / ims / ims.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2020 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 """Deploy and test Clearwater vIMS using Kubernetes"""
11
12 from __future__ import division
13
14 import logging
15 import time
16 import re
17 import yaml
18
19 from kubernetes import client
20 from kubernetes import config
21 from kubernetes import watch
22 import pkg_resources
23
24 from xtesting.core import testcase
25
26
27 class Vims(testcase.TestCase):
28     """Deploy and test Clearwater vIMS using Kubernetes
29
30     It leverage on the Python kubernetes client to apply operation proposed by
31     clearwater-docker.
32
33     See https://github.com/Metaswitch/clearwater-docker for more details
34     """
35     namespace = 'default'
36     zone = 'default.svc.cluster.local'
37     watch_timeout = 1200
38     metadata_name = "env-vars"
39     test_image_name = "ollivier/clearwater-live-test:latest"
40     test_container_name = "live-test"
41
42     __logger = logging.getLogger(__name__)
43
44     deployment_list = [
45         "astaire", "bono", "cassandra", "chronos", "ellis", "etcd", "homer",
46         "homestead", "homestead-prov", "ralf", "sprout"]
47
48     def __init__(self, **kwargs):
49         super(Vims, self).__init__(**kwargs)
50         config.load_kube_config()
51         self.corev1 = client.CoreV1Api()
52         self.appsv1 = client.AppsV1Api()
53         self.output_log_name = 'functest-kubernetes.log'
54         self.output_debug_log_name = 'functest-kubernetes.debug.log'
55
56     def deploy_vnf(self):
57         """Deploy vIMS as proposed by clearwater-docker
58
59         It leverages on unofficial Clearwater dockers as proposed in the
60         documentation.
61
62         See https://github.com/Metaswitch/clearwater-docker for more details
63         """
64         metadata = client.V1ObjectMeta(
65             name=self.metadata_name, namespace=self.namespace)
66         body = client.V1ConfigMap(
67             metadata=metadata,
68             data={"ADDITIONAL_SHARED_CONFIG": "", "ZONE": self.zone})
69         api_response = self.corev1.create_namespaced_config_map(
70             self.namespace, body=body)
71         self.__logger.debug("create_namespaced_config_map: %s", api_response)
72         for deployment in self.deployment_list:
73             with open(pkg_resources.resource_filename(
74                     'functest_kubernetes',
75                     'ims/{}-depl.yaml'.format(deployment))) as yfile:
76                 body = yaml.safe_load(yfile)
77                 resp = self.appsv1.create_namespaced_deployment(
78                     body=body, namespace="default")
79                 self.__logger.info("Deployment %s created", resp.metadata.name)
80                 self.__logger.debug(
81                     "create_namespaced_deployment: %s", api_response)
82         for service in self.deployment_list:
83             with open(pkg_resources.resource_filename(
84                     'functest_kubernetes',
85                     'ims/{}-svc.yaml'.format(service))) as yfile:
86                 body = yaml.safe_load(yfile)
87                 resp = self.corev1.create_namespaced_service(
88                     body=body, namespace="default")
89                 self.__logger.info("Service %s created", resp.metadata.name)
90                 self.__logger.debug(
91                     "create_namespaced_service: %s", api_response)
92         status = self.deployment_list.copy()
93         watch_deployment = watch.Watch()
94         for event in watch_deployment.stream(
95                 func=self.appsv1.list_namespaced_deployment,
96                 namespace=self.namespace, timeout_seconds=self.watch_timeout):
97             self.__logger.debug(event)
98             if event["object"].status.ready_replicas == 1:
99                 if event['object'].metadata.name in status:
100                     status.remove(event['object'].metadata.name)
101                     self.__logger.info(
102                         "%s started in %0.2f sec",
103                         event['object'].metadata.name,
104                         time.time()-self.start_time)
105             if not status:
106                 watch_deployment.stop()
107         self.result = 1/2 * 100
108
109     def test_vnf(self):
110         """Test vIMS as proposed by clearwater-live-test
111
112         It leverages on an unofficial Clearwater docker to allow testing from
113         the Kubernetes cluster.
114
115         See https://github.com/Metaswitch/clearwater-live-test for more details
116         """
117         container = client.V1Container(
118             name=self.test_container_name, image=self.test_image_name)
119         spec = client.V1PodSpec(containers=[container], restart_policy="Never")
120         metadata = client.V1ObjectMeta(name=self.test_container_name)
121         body = client.V1Pod(metadata=metadata, spec=spec)
122         api_response = self.corev1.create_namespaced_pod(self.namespace, body)
123         watch_deployment = watch.Watch()
124         for event in watch_deployment.stream(
125                 func=self.corev1.list_namespaced_pod,
126                 namespace=self.namespace, timeout_seconds=self.watch_timeout):
127             self.__logger.debug(event)
128             if event["object"].metadata.name == self.test_container_name:
129                 if (event["object"].status.phase == 'Succeeded' or
130                         event["object"].status.phase == 'Failed'):
131                     watch_deployment.stop()
132         api_response = self.corev1.read_namespaced_pod_log(
133             name=self.test_container_name, namespace=self.namespace)
134         self.__logger.info(api_response)
135         vims_test_result = {}
136         try:
137             grp = re.search(
138                 r'^(\d+) failures out of (\d+) tests run.*\n'
139                 r'(\d+) tests skipped$', api_response,
140                 re.MULTILINE | re.DOTALL)
141             assert grp
142             vims_test_result["failures"] = int(grp.group(1))
143             vims_test_result["total"] = int(grp.group(2))
144             vims_test_result["skipped"] = int(grp.group(3))
145             vims_test_result['passed'] = (
146                 int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1)))
147             if vims_test_result['total'] - vims_test_result['skipped'] > 0:
148                 vnf_test_rate = vims_test_result['passed'] / (
149                     vims_test_result['total'] - vims_test_result['skipped'])
150             else:
151                 vnf_test_rate = 0
152             self.result += 1/2 * 100 * vnf_test_rate
153         except Exception:  # pylint: disable=broad-except
154             self.__logger.exception("Cannot parse live tests results")
155
156     def run(self, **kwargs):
157         self.start_time = time.time()
158         try:
159             self.deploy_vnf()
160             self.test_vnf()
161         except client.rest.ApiException:
162             self.__logger.exception("Cannot deploy and test vIms")
163         self.stop_time = time.time()
164
165     def clean(self):
166         try:
167             api_response = self.corev1.delete_namespaced_config_map(
168                 name=self.metadata_name, namespace=self.namespace)
169             self.__logger.debug(
170                 "delete_namespaced_config_map: %s", api_response)
171         except client.rest.ApiException:
172             pass
173         try:
174             api_response = self.corev1.delete_namespaced_pod(
175                 name=self.test_container_name, namespace=self.namespace)
176             self.__logger.debug("delete_namespaced_pod: %s", api_response)
177         except client.rest.ApiException:
178             pass
179         for deployment in self.deployment_list:
180             try:
181                 api_response = self.appsv1.delete_namespaced_deployment(
182                     name=deployment, namespace=self.namespace)
183                 self.__logger.debug(
184                     "delete_namespaced_deployment: %s", api_response)
185             except client.rest.ApiException:
186                 pass
187             try:
188                 api_response = self.corev1.delete_namespaced_service(
189                     name=deployment, namespace=self.namespace)
190                 self.__logger.debug(
191                     "delete_namespaced_service: %s", api_response)
192             except client.rest.ApiException:
193                 pass