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
21 from kubernetes import client
22 from kubernetes import config
23 from kubernetes import watch
26 from xtesting.core import testcase
29 class Vims(testcase.TestCase): # pylint: disable=too-many-instance-attributes
30 """Deploy and test Clearwater vIMS using Kubernetes
32 It leverage on the Python kubernetes client to apply operation proposed by
35 See https://github.com/Metaswitch/clearwater-docker for more details
38 metadata_name = "env-vars"
39 test_image_name = "ollivier/clearwater-live-test:hunter"
40 test_container_name = "live-test"
42 __logger = logging.getLogger(__name__)
45 "astaire", "bono", "cassandra", "chronos", "ellis", "etcd", "homer",
46 "homestead", "homestead-prov", "ralf", "sprout"]
48 def __init__(self, **kwargs):
49 super(Vims, self).__init__(**kwargs)
50 config.load_kube_config()
51 self.corev1 = client.CoreV1Api()
52 self.appsv1 = client.AppsV1Api()
53 self.output_log_name = 'functest-kubernetes.log'
54 self.output_debug_log_name = 'functest-kubernetes.debug.log'
58 def prepare_vnf(self):
59 """Prepare vIMS as proposed by clearwater-live-test
61 It creates a dedicated namespace and the configmap needed.
63 See https://github.com/Metaswitch/clearwater-live-test for more details
65 api_response = self.corev1.create_namespace(
66 client.V1Namespace(metadata=client.V1ObjectMeta(
67 generate_name="ims-")))
68 self.namespace = api_response.metadata.name
69 self.__logger.debug("create_namespace: %s", api_response)
70 self.zone = '{}.svc.cluster.local'.format(self.namespace)
71 metadata = client.V1ObjectMeta(
72 name=self.metadata_name, namespace=self.namespace)
73 body = client.V1ConfigMap(
75 data={"ADDITIONAL_SHARED_CONFIG": "", "ZONE": self.zone})
76 api_response = self.corev1.create_namespaced_config_map(
77 self.namespace, body=body)
78 self.__logger.debug("create_namespaced_config_map: %s", api_response)
82 """Deploy vIMS as proposed by clearwater-docker
84 It must be overriden on purpose.
86 See https://github.com/Metaswitch/clearwater-docker for more details
90 """Wait vIMS is up and running"""
92 status = self.deployment_list.copy()
93 watch_deployment = watch.Watch()
94 for event in watch_deployment.stream(
95 func=self.appsv1.list_namespaced_deployment,
96 namespace=self.namespace, timeout_seconds=self.watch_timeout):
97 self.__logger.debug(event)
98 if event["object"].status.ready_replicas == 1:
99 if event['object'].metadata.name in status:
100 status.remove(event['object'].metadata.name)
102 "%s started in %0.2f sec",
103 event['object'].metadata.name,
104 time.time()-self.start_time)
106 watch_deployment.stop()
108 self.result = 1/2 * 100
110 self.__logger.error("Cannot deploy vIMS")
114 """Test vIMS as proposed by clearwater-live-test
116 It leverages an unofficial Clearwater docker to allow testing from
117 the Kubernetes cluster.
119 See https://github.com/Metaswitch/clearwater-live-test for more details
122 assert self.namespace
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 = {}
149 r'^(\d+) failures out of (\d+) tests run.*\n'
150 r'(\d+) tests skipped$', api_response,
151 re.MULTILINE | re.DOTALL)
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'])
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")
167 def run(self, **kwargs):
168 self.start_time = time.time()
174 except client.rest.ApiException:
175 self.__logger.exception("Cannot deploy and test vIms")
176 self.stop_time = time.time()
180 api_response = self.corev1.delete_namespaced_pod(
181 name=self.test_container_name, namespace=self.namespace)
182 self.__logger.debug("delete_namespaced_pod: %s", api_response)
183 except client.rest.ApiException:
186 api_response = self.corev1.delete_namespaced_config_map(
187 name=self.metadata_name, namespace=self.namespace)
189 "delete_namespaced_config_map: %s", api_response)
190 except client.rest.ApiException:
193 api_response = self.corev1.delete_namespace(self.namespace)
194 self.__logger.debug("delete_namespace: %s", self.namespace)
195 except client.rest.ApiException:
200 """Deploy vIMS via kubectl as proposed by clearwater-docker
202 It leverages unofficial Clearwater dockers as proposed in the
205 See https://github.com/Metaswitch/clearwater-docker for more details
208 __logger = logging.getLogger(__name__)
210 def deploy_vnf(self):
211 """Deploy vIMS via kubectl as proposed by clearwater-docker
213 See https://github.com/Metaswitch/clearwater-docker for more details
215 assert self.namespace
216 for deployment in self.deployment_list:
217 with open(pkg_resources.resource_filename(
218 'functest_kubernetes',
219 'ims/{}-depl.yaml'.format(deployment))) as yfile:
220 body = yaml.safe_load(yfile)
221 resp = self.appsv1.create_namespaced_deployment(
222 body=body, namespace=self.namespace)
223 self.__logger.info("Deployment %s created", resp.metadata.name)
225 "create_namespaced_deployment: %s", resp)
226 for service in self.deployment_list:
227 with open(pkg_resources.resource_filename(
228 'functest_kubernetes',
229 'ims/{}-svc.yaml'.format(service))) as yfile:
230 body = yaml.safe_load(yfile)
231 resp = self.corev1.create_namespaced_service(
232 body=body, namespace=self.namespace)
233 self.__logger.info("Service %s created", resp.metadata.name)
235 "create_namespaced_service: %s", resp)
238 for deployment in self.deployment_list:
240 api_response = self.appsv1.delete_namespaced_deployment(
241 name=deployment, namespace=self.namespace)
243 "delete_namespaced_deployment: %s", api_response)
244 except client.rest.ApiException:
247 api_response = self.corev1.delete_namespaced_service(
248 name=deployment, namespace=self.namespace)
250 "delete_namespaced_service: %s", api_response)
251 except client.rest.ApiException:
253 super(K8sVims, self).clean()
256 class HelmVims(Vims):
257 """Deploy vIMS via Helm as proposed by clearwater-docker
259 It leverages unofficial Clearwater dockers as proposed in the
262 See https://github.com/Metaswitch/clearwater-docker for more details
265 __logger = logging.getLogger(__name__)
267 def deploy_vnf(self):
268 """Deploy vIMS via Helm as proposed by clearwater-docker
270 See https://github.com/Metaswitch/clearwater-docker for more details
273 "helm", "install", "clearwater",
274 pkg_resources.resource_filename("functest_kubernetes", "ims/helm"),
275 "-n", self.namespace]
276 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
277 self.__logger.debug(output.decode("utf-8"))
280 cmd = ["helm", "uninstall", "clearwater", "-n", self.namespace]
281 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
282 self.__logger.debug(output.decode("utf-8"))
283 super(HelmVims, self).clean()