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