3 # Copyright (c) 2020 Orange and others.
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
10 """Deploy and test Clearwater vIMS using Kubernetes"""
12 from __future__ import division
22 from jinja2 import Template
23 from kubernetes import client
24 from kubernetes import config
25 from kubernetes import watch
27 from xtesting.core import testcase
30 class Vims(testcase.TestCase): # pylint: disable=too-many-instance-attributes
31 """Deploy and test Clearwater vIMS using Kubernetes
33 It leverage on the Python kubernetes client to apply operation proposed by
36 See https://github.com/Metaswitch/clearwater-docker for more details
39 metadata_name = "env-vars"
40 test_image_name = "ollivier/clearwater-live-test:hunter"
41 test_container_name = "live-test"
42 ns_generate_name = "ims-"
43 dockerhub_repo = os.getenv("MIRROR_REPO", "docker.io")
44 quay_repo = os.getenv("MIRROR_REPO", "quay.io")
46 __logger = logging.getLogger(__name__)
49 "astaire", "bono", "cassandra", "chronos", "ellis", "etcd", "homer",
50 "homestead", "homestead-prov", "ralf", "sprout"]
52 def __init__(self, **kwargs):
53 super().__init__(**kwargs)
54 config.load_kube_config()
55 self.corev1 = client.CoreV1Api()
56 self.appsv1 = client.AppsV1Api()
57 self.output_log_name = 'functest-kubernetes.log'
58 self.output_debug_log_name = 'functest-kubernetes.debug.log'
62 def prepare_vnf(self):
63 """Prepare vIMS as proposed by clearwater-live-test
65 It creates a dedicated namespace and the configmap needed.
67 See https://github.com/Metaswitch/clearwater-live-test for more details
69 api_response = self.corev1.create_namespace(
70 client.V1Namespace(metadata=client.V1ObjectMeta(
71 generate_name=self.ns_generate_name)))
72 self.namespace = api_response.metadata.name
73 self.__logger.debug("create_namespace: %s", api_response)
74 self.zone = f'{self.namespace}.svc.cluster.local'
75 metadata = client.V1ObjectMeta(
76 name=self.metadata_name, namespace=self.namespace)
77 body = client.V1ConfigMap(
79 data={"ADDITIONAL_SHARED_CONFIG": "", "ZONE": self.zone})
80 api_response = self.corev1.create_namespaced_config_map(
81 self.namespace, body=body)
82 self.__logger.debug("create_namespaced_config_map: %s", api_response)
86 """Deploy vIMS as proposed by clearwater-docker
88 It must be overriden on purpose.
90 See https://github.com/Metaswitch/clearwater-docker for more details
94 """Wait vIMS is up and running"""
96 status = self.deployment_list.copy()
97 watch_deployment = watch.Watch()
98 for event in watch_deployment.stream(
99 func=self.appsv1.list_namespaced_deployment,
100 namespace=self.namespace, timeout_seconds=self.watch_timeout):
101 self.__logger.debug(event)
102 if event["object"].status.ready_replicas == 1:
103 if event['object'].metadata.name in status:
104 status.remove(event['object'].metadata.name)
106 "%s started in %0.2f sec",
107 event['object'].metadata.name,
108 time.time()-self.start_time)
110 watch_deployment.stop()
112 self.result = 1/2 * 100
114 self.__logger.error("Cannot deploy vIMS")
118 """Test vIMS as proposed by clearwater-live-test
120 It leverages an unofficial Clearwater docker to allow testing from
121 the Kubernetes cluster.
123 See https://github.com/Metaswitch/clearwater-live-test for more details
126 assert self.namespace
128 container = client.V1Container(
129 name=self.test_container_name, image=self.test_image_name,
130 command=["rake", f"test[{self.zone}]",
131 f"PROXY=bono.{self.zone}",
132 f"ELLIS=ellis.{self.zone}",
133 "SIGNUP_CODE=secret", "--trace"])
134 spec = client.V1PodSpec(containers=[container], restart_policy="Never")
135 metadata = client.V1ObjectMeta(name=self.test_container_name)
136 body = client.V1Pod(metadata=metadata, spec=spec)
137 api_response = self.corev1.create_namespaced_pod(self.namespace, body)
138 watch_deployment = watch.Watch()
139 for event in watch_deployment.stream(
140 func=self.corev1.list_namespaced_pod,
141 namespace=self.namespace, timeout_seconds=self.watch_timeout):
142 self.__logger.debug(event)
143 if event["object"].metadata.name == self.test_container_name:
144 if event["object"].status.phase in ('Succeeded', '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 = {}
152 r'^(\d+) failures out of (\d+) tests run.*\n'
153 r'(\d+) tests skipped$', api_response,
154 re.MULTILINE | re.DOTALL)
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'])
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")
170 def run(self, **kwargs):
171 self.start_time = time.time()
177 except client.rest.ApiException:
178 self.__logger.exception("Cannot deploy and test vIms")
179 self.stop_time = time.time()
183 api_response = self.corev1.delete_namespaced_pod(
184 name=self.test_container_name, namespace=self.namespace)
185 self.__logger.debug("delete_namespaced_pod: %s", api_response)
186 except client.rest.ApiException:
189 api_response = self.corev1.delete_namespaced_config_map(
190 name=self.metadata_name, namespace=self.namespace)
192 "delete_namespaced_config_map: %s", api_response)
193 except client.rest.ApiException:
196 api_response = self.corev1.delete_namespace(self.namespace)
197 self.__logger.debug("delete_namespace: %s", self.namespace)
198 except client.rest.ApiException:
203 """Deploy vIMS via kubectl as proposed by clearwater-docker
205 It leverages unofficial Clearwater dockers as proposed in the
208 See https://github.com/Metaswitch/clearwater-docker for more details
211 __logger = logging.getLogger(__name__)
213 def deploy_vnf(self):
214 """Deploy vIMS via kubectl as proposed by clearwater-docker
216 See https://github.com/Metaswitch/clearwater-docker for more details
218 assert self.namespace
219 for deployment in self.deployment_list:
220 with open(pkg_resources.resource_filename(
221 'functest_kubernetes',
222 f'ims/{deployment}-depl.yaml'),
223 encoding='utf-8') as yfile:
224 template = Template(yfile.read())
225 body = yaml.safe_load(template.render(
226 dockerhub_repo=os.getenv(
227 "DOCKERHUB_REPO", self.dockerhub_repo),
229 "QUAY_REPO", self.quay_repo)))
230 resp = self.appsv1.create_namespaced_deployment(
231 body=body, namespace=self.namespace)
232 self.__logger.info("Deployment %s created", resp.metadata.name)
234 "create_namespaced_deployment: %s", resp)
235 for service in self.deployment_list:
236 with open(pkg_resources.resource_filename(
237 'functest_kubernetes', f'ims/{service}-svc.yaml'),
238 encoding='utf-8') as yfile:
239 body = yaml.safe_load(yfile)
240 resp = self.corev1.create_namespaced_service(
241 body=body, namespace=self.namespace)
242 self.__logger.info("Service %s created", resp.metadata.name)
244 "create_namespaced_service: %s", resp)
247 for deployment in self.deployment_list:
249 api_response = self.appsv1.delete_namespaced_deployment(
250 name=deployment, namespace=self.namespace)
252 "delete_namespaced_deployment: %s", api_response)
253 except client.rest.ApiException:
256 api_response = self.corev1.delete_namespaced_service(
257 name=deployment, namespace=self.namespace)
259 "delete_namespaced_service: %s", api_response)
260 except client.rest.ApiException:
265 class HelmVims(Vims):
266 """Deploy vIMS via Helm as proposed by clearwater-docker
268 It leverages unofficial Clearwater dockers as proposed in the
271 See https://github.com/Metaswitch/clearwater-docker for more details
274 __logger = logging.getLogger(__name__)
276 def deploy_vnf(self):
277 """Deploy vIMS via Helm as proposed by clearwater-docker
279 See https://github.com/Metaswitch/clearwater-docker for more details
281 dockerhub_repo = os.getenv("DOCKERHUB_REPO", self.dockerhub_repo)
282 quay_repo = os.getenv("QUAY_REPO", self.quay_repo)
284 "helm", "install", "clearwater", "--set",
285 f"repo.dockerHub={dockerhub_repo},repo.quay={quay_repo}",
286 pkg_resources.resource_filename("functest_kubernetes", "ims/helm"),
287 "-n", self.namespace]
288 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
289 self.__logger.debug(output.decode("utf-8"))
292 cmd = ["helm", "uninstall", "clearwater", "-n", self.namespace]
293 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
294 self.__logger.debug(output.decode("utf-8"))