d1882a925ee7591632027951e4609ee33ae15009
[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             # pylint: disable=bad-continuation
70             with open(pkg_resources.resource_filename(
71                     'functest_kubernetes',
72                     'ims/{}-depl.yaml'.format(deployment))) as yfile:
73                 body = yaml.safe_load(yfile)
74                 resp = self.appsv1.create_namespaced_deployment(
75                     body=body, namespace="default")
76                 self.__logger.info("Deployment %s created", resp.metadata.name)
77                 self.__logger.debug(
78                     "create_namespaced_deployment: %s", api_response)
79         for service in self.deployment_list:
80             # pylint: disable=bad-continuation
81             with open(pkg_resources.resource_filename(
82                     'functest_kubernetes',
83                     'ims/{}-svc.yaml'.format(service))) as yfile:
84                 body = yaml.safe_load(yfile)
85                 resp = self.corev1.create_namespaced_service(
86                     body=body, namespace="default")
87                 self.__logger.info("Service %s created", resp.metadata.name)
88                 self.__logger.debug(
89                     "create_namespaced_service: %s", api_response)
90         # pylint: disable=no-member
91         status = self.deployment_list[:]
92         watch_deployment = watch.Watch()
93         for event in watch_deployment.stream(
94                 func=self.appsv1.list_namespaced_deployment,
95                 namespace=self.namespace, timeout_seconds=self.watch_timeout):
96             if event["object"].status.ready_replicas == 1:
97                 if event['object'].metadata.name in status:
98                     status.remove(event['object'].metadata.name)
99                     self.__logger.info(
100                         "%s started in %0.2f sec",
101                         event['object'].metadata.name,
102                         time.time()-self.start_time)
103             if not status:
104                 watch_deployment.stop()
105         self.result = 1/2 * 100
106
107     def test_vnf(self):
108         """Test vIMS as proposed by clearwater-live-test
109
110         It leverages on an unofficial Clearwater docker to allow testing from
111         the Kubernetes cluster.
112
113         See https://github.com/Metaswitch/clearwater-live-test for more details
114         """
115         container = client.V1Container(
116             name=self.test_container_name, image=self.test_image_name)
117         spec = client.V1PodSpec(containers=[container], restart_policy="Never")
118         metadata = client.V1ObjectMeta(name=self.test_container_name)
119         body = client.V1Pod(metadata=metadata, spec=spec)
120         api_response = self.corev1.create_namespaced_pod(self.namespace, body)
121         watch_deployment = watch.Watch()
122         for event in watch_deployment.stream(
123                 func=self.corev1.list_namespaced_pod,
124                 namespace=self.namespace, timeout_seconds=self.watch_timeout):
125             if event["object"].metadata.name == self.test_container_name:
126                 if (event["object"].status.phase == 'Succeeded' or
127                         event["object"].status.phase == 'Error'):
128                     watch_deployment.stop()
129         api_response = self.corev1.read_namespaced_pod_log(
130             name=self.test_container_name, namespace=self.namespace)
131         self.__logger.info(api_response)
132         vims_test_result = {}
133         try:
134             grp = re.search(
135                 r'^(\d+) failures out of (\d+) tests run.*\n'
136                 r'(\d+) tests skipped$', api_response,
137                 re.MULTILINE | re.DOTALL)
138             assert grp
139             vims_test_result["failures"] = int(grp.group(1))
140             vims_test_result["total"] = int(grp.group(2))
141             vims_test_result["skipped"] = int(grp.group(3))
142             vims_test_result['passed'] = (
143                 int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1)))
144             if vims_test_result['total'] - vims_test_result['skipped'] > 0:
145                 vnf_test_rate = vims_test_result['passed'] / (
146                     vims_test_result['total'] - vims_test_result['skipped'])
147             else:
148                 vnf_test_rate = 0
149             self.result += 1/2 * 100 * vnf_test_rate
150         except Exception:  # pylint: disable=broad-except
151             self.__logger.exception("Cannot parse live tests results")
152
153     def run(self, **kwargs):
154         self.start_time = time.time()
155         try:
156             self.deploy_vnf()
157             self.test_vnf()
158         except client.rest.ApiException:
159             self.__logger.exception("Cannot deploy and test vIms")
160         self.stop_time = time.time()
161
162     def clean(self):
163         try:
164             api_response = self.corev1.delete_namespaced_config_map(
165                 name=self.metadata_name, namespace=self.namespace)
166             self.__logger.debug(
167                 "delete_namespaced_config_map: %s", api_response)
168         except client.rest.ApiException:
169             pass
170         try:
171             api_response = self.corev1.delete_namespaced_pod(
172                 name=self.test_container_name, namespace=self.namespace)
173             self.__logger.debug("delete_namespaced_pod: %s", api_response)
174         except client.rest.ApiException:
175             pass
176         for deployment in self.deployment_list:
177             try:
178                 api_response = self.appsv1.delete_namespaced_deployment(
179                     name=deployment, namespace=self.namespace)
180                 self.__logger.debug(
181                     "delete_namespaced_deployment: %s", api_response)
182             except client.rest.ApiException:
183                 pass
184             try:
185                 api_response = self.corev1.delete_namespaced_service(
186                     name=deployment, namespace=self.namespace)
187                 self.__logger.debug(
188                     "delete_namespaced_service: %s", api_response)
189             except client.rest.ApiException:
190                 pass