60cde887380b1c3e2e64e72b6ae33679da68f461
[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):  # pylint: disable=too-many-instance-attributes
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     watch_timeout = 1200
36     metadata_name = "env-vars"
37     test_image_name = "ollivier/clearwater-live-test:hunter"
38     test_container_name = "live-test"
39
40     __logger = logging.getLogger(__name__)
41
42     deployment_list = [
43         "astaire", "bono", "cassandra", "chronos", "ellis", "etcd", "homer",
44         "homestead", "homestead-prov", "ralf", "sprout"]
45
46     def __init__(self, **kwargs):
47         super(Vims, self).__init__(**kwargs)
48         config.load_kube_config()
49         self.corev1 = client.CoreV1Api()
50         self.appsv1 = client.AppsV1Api()
51         self.output_log_name = 'functest-kubernetes.log'
52         self.output_debug_log_name = 'functest-kubernetes.debug.log'
53         self.namespace = ""
54         self.zone = ""
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         api_response = self.corev1.create_namespace(
65             client.V1Namespace(metadata=client.V1ObjectMeta(
66                 generate_name="ims-")))
67         self.namespace = api_response.metadata.name
68         self.__logger.debug("create_namespace: %s", api_response)
69         metadata = client.V1ObjectMeta(
70             name=self.metadata_name, namespace=self.namespace)
71         self.zone = '{}.svc.cluster.local'.format(self.namespace)
72         body = client.V1ConfigMap(
73             metadata=metadata,
74             data={"ADDITIONAL_SHARED_CONFIG": "", "ZONE": self.zone})
75         api_response = self.corev1.create_namespaced_config_map(
76             self.namespace, body=body)
77         self.__logger.debug("create_namespaced_config_map: %s", api_response)
78         for deployment in self.deployment_list:
79             # pylint: disable=bad-continuation
80             with open(pkg_resources.resource_filename(
81                     'functest_kubernetes',
82                     'ims/{}-depl.yaml'.format(deployment))) as yfile:
83                 body = yaml.safe_load(yfile)
84                 resp = self.appsv1.create_namespaced_deployment(
85                     body=body, namespace=self.namespace)
86                 self.__logger.info("Deployment %s created", resp.metadata.name)
87                 self.__logger.debug(
88                     "create_namespaced_deployment: %s", api_response)
89         for service in self.deployment_list:
90             # pylint: disable=bad-continuation
91             with open(pkg_resources.resource_filename(
92                     'functest_kubernetes',
93                     'ims/{}-svc.yaml'.format(service))) as yfile:
94                 body = yaml.safe_load(yfile)
95                 resp = self.corev1.create_namespaced_service(
96                     body=body, namespace=self.namespace)
97                 self.__logger.info("Service %s created", resp.metadata.name)
98                 self.__logger.debug(
99                     "create_namespaced_service: %s", api_response)
100         # pylint: disable=no-member
101         status = self.deployment_list[:]
102         watch_deployment = watch.Watch()
103         for event in watch_deployment.stream(
104                 func=self.appsv1.list_namespaced_deployment,
105                 namespace=self.namespace, timeout_seconds=self.watch_timeout):
106             self.__logger.debug(event)
107             if event["object"].status.ready_replicas == 1:
108                 if event['object'].metadata.name in status:
109                     status.remove(event['object'].metadata.name)
110                     self.__logger.info(
111                         "%s started in %0.2f sec",
112                         event['object'].metadata.name,
113                         time.time()-self.start_time)
114             if not status:
115                 watch_deployment.stop()
116         self.result = 1/2 * 100
117
118     def test_vnf(self):
119         """Test vIMS as proposed by clearwater-live-test
120
121         It leverages on an unofficial Clearwater docker to allow testing from
122         the Kubernetes cluster.
123
124         See https://github.com/Metaswitch/clearwater-live-test for more details
125         """
126         time.sleep(120)
127         container = client.V1Container(
128             name=self.test_container_name, image=self.test_image_name,
129             command=["rake", "test[{}]".format(self.zone),
130                      "PROXY=bono.{}".format(self.zone),
131                      "ELLIS=ellis.{}".format(self.zone),
132                      "SIGNUP_CODE=secret", "--trace"])
133         spec = client.V1PodSpec(containers=[container], restart_policy="Never")
134         metadata = client.V1ObjectMeta(name=self.test_container_name)
135         body = client.V1Pod(metadata=metadata, spec=spec)
136         api_response = self.corev1.create_namespaced_pod(self.namespace, body)
137         watch_deployment = watch.Watch()
138         for event in watch_deployment.stream(
139                 func=self.corev1.list_namespaced_pod,
140                 namespace=self.namespace, timeout_seconds=self.watch_timeout):
141             self.__logger.debug(event)
142             if event["object"].metadata.name == self.test_container_name:
143                 if (event["object"].status.phase == 'Succeeded' or
144                         event["object"].status.phase == 'Failed'):
145                     watch_deployment.stop()
146         api_response = self.corev1.read_namespaced_pod_log(
147             name=self.test_container_name, namespace=self.namespace)
148         self.__logger.info(api_response)
149         vims_test_result = {}
150         try:
151             grp = re.search(
152                 r'^(\d+) failures out of (\d+) tests run.*\n'
153                 r'(\d+) tests skipped$', api_response,
154                 re.MULTILINE | re.DOTALL)
155             assert grp
156             vims_test_result["failures"] = int(grp.group(1))
157             vims_test_result["total"] = int(grp.group(2))
158             vims_test_result["skipped"] = int(grp.group(3))
159             vims_test_result['passed'] = (
160                 int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1)))
161             if vims_test_result['total'] - vims_test_result['skipped'] > 0:
162                 vnf_test_rate = vims_test_result['passed'] / (
163                     vims_test_result['total'] - vims_test_result['skipped'])
164             else:
165                 vnf_test_rate = 0
166             self.result += 1/2 * 100 * vnf_test_rate
167         except Exception:  # pylint: disable=broad-except
168             self.__logger.exception("Cannot parse live tests results")
169
170     def run(self, **kwargs):
171         self.start_time = time.time()
172         try:
173             self.deploy_vnf()
174             self.test_vnf()
175         except client.rest.ApiException:
176             self.__logger.exception("Cannot deploy and test vIms")
177         self.stop_time = time.time()
178
179     def clean(self):
180         try:
181             api_response = self.corev1.delete_namespaced_config_map(
182                 name=self.metadata_name, namespace=self.namespace)
183             self.__logger.debug(
184                 "delete_namespaced_config_map: %s", api_response)
185         except client.rest.ApiException:
186             pass
187         try:
188             api_response = self.corev1.delete_namespaced_pod(
189                 name=self.test_container_name, namespace=self.namespace)
190             self.__logger.debug("delete_namespaced_pod: %s", api_response)
191         except client.rest.ApiException:
192             pass
193         for deployment in self.deployment_list:
194             try:
195                 api_response = self.appsv1.delete_namespaced_deployment(
196                     name=deployment, namespace=self.namespace)
197                 self.__logger.debug(
198                     "delete_namespaced_deployment: %s", api_response)
199             except client.rest.ApiException:
200                 pass
201             try:
202                 api_response = self.corev1.delete_namespaced_service(
203                     name=deployment, namespace=self.namespace)
204                 self.__logger.debug(
205                     "delete_namespaced_service: %s", api_response)
206             except client.rest.ApiException:
207                 pass
208         try:
209             api_response = self.corev1.delete_namespace(self.namespace)
210             self.__logger.debug("delete_namespace: %s", self.namespace)
211         except client.rest.ApiException:
212             pass