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
28 from xtesting.core import testcase
31 class Vims(testcase.TestCase): # pylint: disable=too-many-instance-attributes
32 """Deploy and test Clearwater vIMS using Kubernetes
34 It leverage on the Python kubernetes client to apply operation proposed by
37 See https://github.com/Metaswitch/clearwater-docker for more details
40 metadata_name = "env-vars"
41 test_image_name = "ollivier/clearwater-live-test:hunter"
42 test_container_name = "live-test"
43 ns_generate_name = "ims-"
44 dockerhub_repo = os.getenv("MIRROR_REPO", "docker.io")
45 quay_repo = os.getenv("MIRROR_REPO", "quay.io")
47 __logger = logging.getLogger(__name__)
50 "astaire", "bono", "cassandra", "chronos", "ellis", "etcd", "homer",
51 "homestead", "homestead-prov", "ralf", "sprout"]
53 def __init__(self, **kwargs):
54 super(Vims, self).__init__(**kwargs)
55 config.load_kube_config()
56 self.corev1 = client.CoreV1Api()
57 self.appsv1 = client.AppsV1Api()
58 self.output_log_name = 'functest-kubernetes.log'
59 self.output_debug_log_name = 'functest-kubernetes.debug.log'
63 def prepare_vnf(self):
64 """Prepare vIMS as proposed by clearwater-live-test
66 It creates a dedicated namespace and the configmap needed.
68 See https://github.com/Metaswitch/clearwater-live-test for more details
70 api_response = self.corev1.create_namespace(
71 client.V1Namespace(metadata=client.V1ObjectMeta(
72 generate_name=self.ns_generate_name)))
73 self.namespace = api_response.metadata.name
74 self.__logger.debug("create_namespace: %s", api_response)
75 self.zone = '{}.svc.cluster.local'.format(self.namespace)
76 metadata = client.V1ObjectMeta(
77 name=self.metadata_name, namespace=self.namespace)
78 body = client.V1ConfigMap(
80 data={"ADDITIONAL_SHARED_CONFIG": "", "ZONE": self.zone})
81 api_response = self.corev1.create_namespaced_config_map(
82 self.namespace, body=body)
83 self.__logger.debug("create_namespaced_config_map: %s", api_response)
87 """Deploy vIMS as proposed by clearwater-docker
89 It must be overriden on purpose.
91 See https://github.com/Metaswitch/clearwater-docker for more details
95 """Wait vIMS is up and running"""
97 status = self.deployment_list.copy()
98 watch_deployment = watch.Watch()
99 for event in watch_deployment.stream(
100 func=self.appsv1.list_namespaced_deployment,
101 namespace=self.namespace, timeout_seconds=self.watch_timeout):
102 self.__logger.debug(event)
103 if event["object"].status.ready_replicas == 1:
104 if event['object'].metadata.name in status:
105 status.remove(event['object'].metadata.name)
107 "%s started in %0.2f sec",
108 event['object'].metadata.name,
109 time.time()-self.start_time)
111 watch_deployment.stop()
113 self.result = 1/2 * 100
115 self.__logger.error("Cannot deploy vIMS")
119 """Test vIMS as proposed by clearwater-live-test
121 It leverages an unofficial Clearwater docker to allow testing from
122 the Kubernetes cluster.
124 See https://github.com/Metaswitch/clearwater-live-test for more details
127 assert self.namespace
129 container = client.V1Container(
130 name=self.test_container_name, image=self.test_image_name,
131 command=["rake", "test[{}]".format(self.zone),
132 "PROXY=bono.{}".format(self.zone),
133 "ELLIS=ellis.{}".format(self.zone),
134 "SIGNUP_CODE=secret", "--trace"])
135 spec = client.V1PodSpec(containers=[container], restart_policy="Never")
136 metadata = client.V1ObjectMeta(name=self.test_container_name)
137 body = client.V1Pod(metadata=metadata, spec=spec)
138 api_response = self.corev1.create_namespaced_pod(self.namespace, body)
139 watch_deployment = watch.Watch()
140 for event in watch_deployment.stream(
141 func=self.corev1.list_namespaced_pod,
142 namespace=self.namespace, timeout_seconds=self.watch_timeout):
143 self.__logger.debug(event)
144 if event["object"].metadata.name == self.test_container_name:
145 if (event["object"].status.phase == 'Succeeded'
146 or event["object"].status.phase == 'Failed'):
147 watch_deployment.stop()
148 api_response = self.corev1.read_namespaced_pod_log(
149 name=self.test_container_name, namespace=self.namespace)
150 self.__logger.info(api_response)
151 vims_test_result = {}
154 r'^(\d+) failures out of (\d+) tests run.*\n'
155 r'(\d+) tests skipped$', api_response,
156 re.MULTILINE | re.DOTALL)
158 vims_test_result["failures"] = int(grp.group(1))
159 vims_test_result["total"] = int(grp.group(2))
160 vims_test_result["skipped"] = int(grp.group(3))
161 vims_test_result['passed'] = (
162 int(grp.group(2)) - int(grp.group(3)) - int(grp.group(1)))
163 if vims_test_result['total'] - vims_test_result['skipped'] > 0:
164 vnf_test_rate = vims_test_result['passed'] / (
165 vims_test_result['total'] - vims_test_result['skipped'])
168 self.result += 1/2 * 100 * vnf_test_rate
169 except Exception: # pylint: disable=broad-except
170 self.__logger.exception("Cannot parse live tests results")
172 def run(self, **kwargs):
173 self.start_time = time.time()
179 except client.rest.ApiException:
180 self.__logger.exception("Cannot deploy and test vIms")
181 self.stop_time = time.time()
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:
191 api_response = self.corev1.delete_namespaced_config_map(
192 name=self.metadata_name, namespace=self.namespace)
194 "delete_namespaced_config_map: %s", api_response)
195 except client.rest.ApiException:
198 api_response = self.corev1.delete_namespace(self.namespace)
199 self.__logger.debug("delete_namespace: %s", self.namespace)
200 except client.rest.ApiException:
205 """Deploy vIMS via kubectl as proposed by clearwater-docker
207 It leverages unofficial Clearwater dockers as proposed in the
210 See https://github.com/Metaswitch/clearwater-docker for more details
213 __logger = logging.getLogger(__name__)
215 def deploy_vnf(self):
216 """Deploy vIMS via kubectl as proposed by clearwater-docker
218 See https://github.com/Metaswitch/clearwater-docker for more details
220 assert self.namespace
221 for deployment in self.deployment_list:
222 with open(pkg_resources.resource_filename(
223 'functest_kubernetes',
224 'ims/{}-depl.yaml'.format(deployment))) as yfile:
225 template = Template(yfile.read())
226 body = yaml.safe_load(template.render(
227 dockerhub_repo=os.getenv(
228 "DOCKERHUB_REPO", self.dockerhub_repo),
230 "QUAY_REPO", self.quay_repo)))
231 resp = self.appsv1.create_namespaced_deployment(
232 body=body, namespace=self.namespace)
233 self.__logger.info("Deployment %s created", resp.metadata.name)
235 "create_namespaced_deployment: %s", resp)
236 for service in self.deployment_list:
237 with open(pkg_resources.resource_filename(
238 'functest_kubernetes',
239 'ims/{}-svc.yaml'.format(service))) as yfile:
240 body = yaml.safe_load(yfile)
241 resp = self.corev1.create_namespaced_service(
242 body=body, namespace=self.namespace)
243 self.__logger.info("Service %s created", resp.metadata.name)
245 "create_namespaced_service: %s", resp)
248 for deployment in self.deployment_list:
250 api_response = self.appsv1.delete_namespaced_deployment(
251 name=deployment, namespace=self.namespace)
253 "delete_namespaced_deployment: %s", api_response)
254 except client.rest.ApiException:
257 api_response = self.corev1.delete_namespaced_service(
258 name=deployment, namespace=self.namespace)
260 "delete_namespaced_service: %s", api_response)
261 except client.rest.ApiException:
263 super(K8sVims, self).clean()
266 class HelmVims(Vims):
267 """Deploy vIMS via Helm as proposed by clearwater-docker
269 It leverages unofficial Clearwater dockers as proposed in the
272 See https://github.com/Metaswitch/clearwater-docker for more details
275 __logger = logging.getLogger(__name__)
277 def deploy_vnf(self):
278 """Deploy vIMS via Helm as proposed by clearwater-docker
280 See https://github.com/Metaswitch/clearwater-docker for more details
282 dockerhub_repo = os.getenv("DOCKERHUB_REPO", self.dockerhub_repo)
283 quay_repo = os.getenv("QUAY_REPO", self.quay_repo)
285 "helm", "install", "clearwater", "--set",
286 "repo.dockerHub={},repo.quay={}".format(dockerhub_repo, quay_repo),
287 pkg_resources.resource_filename("functest_kubernetes", "ims/helm"),
288 "-n", self.namespace]
289 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
290 self.__logger.debug(output.decode("utf-8"))
293 cmd = ["helm", "uninstall", "clearwater", "-n", self.namespace]
294 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
295 self.__logger.debug(output.decode("utf-8"))
296 super(HelmVims, self).clean()