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
19 from kubernetes import client
20 from kubernetes import config
21 from kubernetes import watch
24 from xtesting.core import testcase
27 class Vims(testcase.TestCase): # pylint: disable=too-many-instance-attributes
28 """Deploy and test Clearwater vIMS using Kubernetes
30 It leverage on the Python kubernetes client to apply operation proposed by
33 See https://github.com/Metaswitch/clearwater-docker for more details
36 metadata_name = "env-vars"
37 test_image_name = "ollivier/clearwater-live-test:hunter"
38 test_container_name = "live-test"
40 __logger = logging.getLogger(__name__)
43 "astaire", "bono", "cassandra", "chronos", "ellis", "etcd", "homer",
44 "homestead", "homestead-prov", "ralf", "sprout"]
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'
57 """Deploy vIMS as proposed by clearwater-docker
59 It leverages on unofficial Clearwater dockers as proposed in the
62 See https://github.com/Metaswitch/clearwater-docker for more details
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(
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 # pylint: disable=bad-continuation
80 with open(pkg_resources.resource_filename(
81 'functest_kubernetes',
82 'ims/{}-depl.yaml'.format(deployment))) as yfile:
83 body = yaml.safe_load(yfile)
84 resp = self.appsv1.create_namespaced_deployment(
85 body=body, namespace=self.namespace)
86 self.__logger.info("Deployment %s created", resp.metadata.name)
88 "create_namespaced_deployment: %s", api_response)
89 for service in self.deployment_list:
90 # pylint: disable=bad-continuation
91 with open(pkg_resources.resource_filename(
92 'functest_kubernetes',
93 'ims/{}-svc.yaml'.format(service))) as yfile:
94 body = yaml.safe_load(yfile)
95 resp = self.corev1.create_namespaced_service(
96 body=body, namespace=self.namespace)
97 self.__logger.info("Service %s created", resp.metadata.name)
99 "create_namespaced_service: %s", api_response)
100 # pylint: disable=no-member
101 status = self.deployment_list[:]
102 watch_deployment = watch.Watch()
103 for event in watch_deployment.stream(
104 func=self.appsv1.list_namespaced_deployment,
105 namespace=self.namespace, timeout_seconds=self.watch_timeout):
106 self.__logger.debug(event)
107 if event["object"].status.ready_replicas == 1:
108 if event['object'].metadata.name in status:
109 status.remove(event['object'].metadata.name)
111 "%s started in %0.2f sec",
112 event['object'].metadata.name,
113 time.time()-self.start_time)
115 watch_deployment.stop()
116 self.result = 1/2 * 100
119 """Test vIMS as proposed by clearwater-live-test
121 It leverages on 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 container = client.V1Container(
128 name=self.test_container_name, image=self.test_image_name,
129 command=["rake", "test[{}]".format(self.zone),
130 "PROXY=bono.{}".format(self.zone),
131 "ELLIS=ellis.{}".format(self.zone),
132 "SIGNUP_CODE=secret", "--trace"])
133 spec = client.V1PodSpec(containers=[container], restart_policy="Never")
134 metadata = client.V1ObjectMeta(name=self.test_container_name)
135 body = client.V1Pod(metadata=metadata, spec=spec)
136 api_response = self.corev1.create_namespaced_pod(self.namespace, body)
137 watch_deployment = watch.Watch()
138 for event in watch_deployment.stream(
139 func=self.corev1.list_namespaced_pod,
140 namespace=self.namespace, timeout_seconds=self.watch_timeout):
141 self.__logger.debug(event)
142 if event["object"].metadata.name == self.test_container_name:
143 if (event["object"].status.phase == 'Succeeded' or
144 event["object"].status.phase == '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()
175 except client.rest.ApiException:
176 self.__logger.exception("Cannot deploy and test vIms")
177 self.stop_time = time.time()
181 api_response = self.corev1.delete_namespaced_config_map(
182 name=self.metadata_name, namespace=self.namespace)
184 "delete_namespaced_config_map: %s", api_response)
185 except client.rest.ApiException:
188 api_response = self.corev1.delete_namespaced_pod(
189 name=self.test_container_name, namespace=self.namespace)
190 self.__logger.debug("delete_namespaced_pod: %s", api_response)
191 except client.rest.ApiException:
193 for deployment in self.deployment_list:
195 api_response = self.appsv1.delete_namespaced_deployment(
196 name=deployment, namespace=self.namespace)
198 "delete_namespaced_deployment: %s", api_response)
199 except client.rest.ApiException:
202 api_response = self.corev1.delete_namespaced_service(
203 name=deployment, namespace=self.namespace)
205 "delete_namespaced_service: %s", api_response)
206 except client.rest.ApiException:
209 api_response = self.corev1.delete_namespace(self.namespace)
210 self.__logger.debug("delete_namespace: %s", self.namespace)
211 except client.rest.ApiException: