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