blob: 748cb04326f21448a0f385aa30060a20da321afc [file] [log] [blame]
Artem Panchenko0594cd72017-06-12 13:25:26 +03001# Copyright 2017 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Victor Ryzhenkin8ff3c3f2018-01-17 19:37:05 +040015import os
Vladimir Jigulina6b018b2018-07-18 15:19:01 +040016import requests
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +040017import yaml
Artem Panchenko0594cd72017-06-12 13:25:26 +030018
19from devops.helpers import helpers
Victor Ryzhenkin66d39372017-09-28 19:25:48 +040020from devops.error import DevopsCalledProcessError
Artem Panchenko0594cd72017-06-12 13:25:26 +030021
22from tcp_tests import logger
Victor Ryzhenkin66d39372017-09-28 19:25:48 +040023from tcp_tests.helpers import ext
24from tcp_tests.helpers.utils import retry
Artem Panchenko0594cd72017-06-12 13:25:26 +030025from tcp_tests.managers.execute_commands import ExecuteCommandsMixin
26from tcp_tests.managers.k8s import cluster
Artem Panchenko0594cd72017-06-12 13:25:26 +030027
28LOG = logger.logger
29
30
31class K8SManager(ExecuteCommandsMixin):
32 """docstring for K8SManager"""
33
34 __config = None
35 __underlay = None
36
37 def __init__(self, config, underlay, salt):
38 self.__config = config
39 self.__underlay = underlay
40 self._salt = salt
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040041 self._api = None
42 self.kubectl = K8SKubectlCli(self)
43 self.virtlet = K8SVirtlet(self)
44 super(K8SManager, self).__init__(config=config, underlay=underlay)
Artem Panchenko0594cd72017-06-12 13:25:26 +030045
46 def install(self, commands):
47 self.execute_commands(commands,
48 label='Install Kubernetes services')
49 self.__config.k8s.k8s_installed = True
50 self.__config.k8s.kube_host = self.get_proxy_api()
51
52 def get_proxy_api(self):
53 k8s_proxy_ip_pillars = self._salt.get_pillar(
vrovachev99228d32017-06-08 19:46:10 +040054 tgt='I@haproxy:proxy:enabled:true and I@kubernetes:master',
Artem Panchenko0594cd72017-06-12 13:25:26 +030055 pillar='haproxy:proxy:listen:k8s_secure:binds:address')
vrovachev99228d32017-06-08 19:46:10 +040056 k8s_hosts = self._salt.get_pillar(
57 tgt='I@haproxy:proxy:enabled:true and I@kubernetes:master',
58 pillar='kubernetes:pool:apiserver:host')
Artem Panchenko0594cd72017-06-12 13:25:26 +030059 k8s_proxy_ip = set([ip
60 for item in k8s_proxy_ip_pillars
Dina Belovae6fdffb2017-09-19 13:58:34 -070061 for node, ip in item.items() if ip])
vrovachev99228d32017-06-08 19:46:10 +040062 k8s_hosts = set([ip
Dina Belovae6fdffb2017-09-19 13:58:34 -070063 for item in k8s_hosts
64 for node, ip in item.items() if ip])
vrovachev99228d32017-06-08 19:46:10 +040065 assert len(k8s_hosts) == 1, (
66 "Found more than one Kubernetes API hosts in pillars:{0}, "
67 "expected one!").format(k8s_hosts)
68 k8s_host = k8s_hosts.pop()
69 assert k8s_host in k8s_proxy_ip, (
70 "Kubernetes API host:{0} not found in proxies:{} "
71 "on k8s master nodes. K8s proxies are expected on "
72 "nodes with K8s master").format(k8s_host, k8s_proxy_ip)
73 return k8s_host
Artem Panchenko0594cd72017-06-12 13:25:26 +030074
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040075 def _api_init(self):
76 ca_result = self.controller_check_call(
77 'base64 --wrap=0 /etc/kubernetes/ssl/ca-kubernetes.crt')
78
79 self._api = cluster.K8sCluster(
80 user=self.__config.k8s_deploy.kubernetes_admin_user,
81 password=self.__config.k8s_deploy.kubernetes_admin_password,
82 ca=ca_result['stdout'][0],
83 host=self.__config.k8s.kube_host,
84 port=self.__config.k8s.kube_apiserver_port)
85
Artem Panchenko0594cd72017-06-12 13:25:26 +030086 @property
87 def api(self):
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040088 """
89 :rtype: cluster.K8sCluster
90 """
91 if self._api is None:
92 self._api_init()
93 return self._api
Artem Panchenko0594cd72017-06-12 13:25:26 +030094
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +040095 def get_controllers(self):
96 """ Return list of controllers ssh underlays """
Vladimir Jigulin34dfa942018-07-23 21:05:48 +040097 return [node for node in self.__config.underlay.ssh if
98 ext.UNDERLAY_NODE_ROLES.k8s_controller in node['roles']]
99
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400100 def get_masters(self):
101 """ Return list of kubernetes masters hosts fqdn """
102 masters_fqdn = self._salt.get_pillar(
103 tgt='I@kubernetes:master', pillar='linux:network:fqdn')
104 return [self.__underlay.host_by_node_name(node_name=v)
105 for pillar in masters_fqdn for k, v in pillar.items()]
106
Victor Ryzhenkin66d39372017-09-28 19:25:48 +0400107 @property
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400108 def controller_name(self):
109 """ Return node name of controller node that used for all actions """
110 names = [node['node_name'] for node in self.get_controllers()]
111 # we want to return same controller name every time
112 names.sort()
113 return names[0]
Victor Ryzhenkin66d39372017-09-28 19:25:48 +0400114
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400115 def controller_check_call(self, cmd, **kwargs):
116 """ Run command on controller and return result """
117 LOG.info("running cmd on k8s controller: {}".format(cmd))
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400118 return self.__underlay.check_call(
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400119 cmd=cmd, node_name=self.controller_name, **kwargs)
Artem Panchenko501e67e2017-06-14 14:59:18 +0300120
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400121 def get_keepalived_vip(self):
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400122 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400123 Return k8s VIP IP address
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400124
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400125 :return: str, IP address
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400126 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400127 ctl_vip_pillar = self._salt.get_pillar(
128 tgt="I@kubernetes:control:enabled:True",
129 pillar="_param:cluster_vip_address")[0]
130 return ctl_vip_pillar.values()[0]
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400131
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400132 def run_sample_deployment(self, name, **kwargs):
133 return K8SSampleDeployment(self, name, **kwargs)
Victor Ryzhenkin3ffa2b42017-10-05 16:38:44 +0400134
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400135 def get_pod_ips_from_container(self, pod_name, exclude_local=True,
136 namespace='default'):
137 """ Get ips from container using 'ip a'
138 Required for cni-genie multi-cni cases
139
140 :return: list of IP adresses
Victor Ryzhenkin3ffa2b42017-10-05 16:38:44 +0400141 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400142 cmd = "ip a|grep \"inet \"|awk '{{print $2}}'"
143 result = self.kubectl.cli_exec(namespace, pod_name, cmd)['stdout']
144 ips = [line.strip().split('/')[0] for line in result]
145 if exclude_local:
146 ips = [ip for ip in ips if not ip.startswith("127.")]
147 return ips
Victor Ryzhenkin3ffa2b42017-10-05 16:38:44 +0400148
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400149 def update_k8s_version(self, tag):
Vladimir Jigulin62bcf462018-05-28 18:17:01 +0400150 """
151 Update k8s images tag version in cluster meta and apply required
152 for update states
153
154 :param tag: New version tag of k8s images
155 :return:
156 """
157 master_host = self.__config.salt.salt_master_host
158
159 def update_image_tag_meta(config, image_name):
160 image_old = config.get(image_name)
161 image_base = image_old.split(':')[0]
162 image_new = "{}:{}".format(image_base, tag)
163 LOG.info("Changing k8s '{0}' image cluster meta to '{1}'".format(
164 image_name, image_new))
165
166 with self.__underlay.remote(host=master_host) as r:
167 cmd = "salt-call reclass.cluster_meta_set" \
168 " name={0} value={1}".format(image_name, image_new)
169 r.check_call(cmd)
170 return image_new
171
172 cfg = self.__config
173
174 update_image_tag_meta(cfg.k8s_deploy, "kubernetes_hyperkube_image")
175 update_image_tag_meta(cfg.k8s_deploy, "kubernetes_pause_image")
176 cfg.k8s.k8s_conformance_image = update_image_tag_meta(
177 cfg.k8s, "k8s_conformance_image")
178
179 steps_path = cfg.k8s_deploy.k8s_update_steps_path
180 update_commands = self.__underlay.read_template(steps_path)
181 self.execute_commands(
182 update_commands, label="Updating kubernetes to '{}'".format(tag))
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400183
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400184 def run_conformance(self, timeout=60*60, log_out='k8s_conformance.log',
185 raise_on_err=True, node_name=None,
186 api_server='http://127.0.0.1:8080'):
187 if node_name is None:
188 node_name = self.controller_name
189 cmd = "set -o pipefail; docker run --net=host " \
190 "-e API_SERVER='{api}' {image} | tee '{log}'".format(
191 api=api_server, log=log_out,
192 image=self.__config.k8s.k8s_conformance_image)
193 return self.__underlay.check_call(
194 cmd=cmd, node_name=node_name, timeout=timeout,
195 raise_on_err=raise_on_err)
196
197 def run_virtlet_conformance(self, timeout=60 * 120,
198 log_file='virtlet_conformance.log'):
199 if self.__config.k8s.run_extended_virtlet_conformance:
200 ci_image = "cloud-images.ubuntu.com/xenial/current/" \
201 "xenial-server-cloudimg-amd64-disk1.img"
202 cmd = ("set -o pipefail; "
203 "docker run --net=host {0} /virtlet-e2e-tests "
204 "-include-cloud-init-tests -junitOutput report.xml "
205 "-image {2} -sshuser ubuntu -memoryLimit 1024 "
206 "-alsologtostderr -cluster-url http://127.0.0.1:8080 "
207 "-ginkgo.focus '\[Conformance\]' "
208 "| tee {1}".format(
209 self.__config.k8s_deploy.kubernetes_virtlet_image,
210 log_file, ci_image))
211 else:
212 cmd = ("set -o pipefail; "
213 "docker run --net=host {0} /virtlet-e2e-tests "
214 "-junitOutput report.xml "
215 "-alsologtostderr -cluster-url http://127.0.0.1:8080 "
216 "-ginkgo.focus '\[Conformance\]' "
217 "| tee {1}".format(
218 self.__config.k8s_deploy.kubernetes_virtlet_image,
219 log_file))
220 LOG.info("Executing: {}".format(cmd))
221 with self.__underlay.remote(
222 node_name=self.controller_name) as remote:
223 result = remote.check_call(cmd, timeout=timeout)
224 stderr = result['stderr']
225 stdout = result['stdout']
226 LOG.info("Test results stdout: {}".format(stdout))
227 LOG.info("Test results stderr: {}".format(stderr))
228 return result
229
230 def start_k8s_cncf_verification(self, timeout=60 * 90):
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +0400231 """
232 Build sonobuoy using golang docker image and install it in system
233 Then generate sonobuoy verification manifest using gen command
234 and wait for it to end using status annotation
235 TODO (vjigulin): All sonobuoy methods can be improved if we define
236 correct KUBECONFIG env variable
237 """
238 cncf_cmd =\
239 'docker run --network host --name sonobuoy golang:1.11 bash -c ' \
240 '"go get -d -v github.com/heptio/sonobuoy && ' \
241 'go install -v github.com/heptio/sonobuoy" && ' \
242 'docker cp sonobuoy:/go/bin/sonobuoy /usr/local/bin/ && ' \
243 'docker rm sonobuoy && ' \
244 'sonobuoy gen | kubectl apply -f -'
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400245
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +0400246 self.controller_check_call(cncf_cmd, timeout=900)
247
248 sonobuoy_pod = self.api.pods.get('sonobuoy', 'heptio-sonobuoy')
249 sonobuoy_pod.wait_running()
250
251 def sonobuoy_status():
252 annotations = sonobuoy_pod.read().metadata.annotations
253 json_status = annotations['sonobuoy.hept.io/status']
254 status = yaml.safe_load(json_status)['status']
255 if status != 'running':
256 LOG.info("CNCF status: {}".format(json_status))
257 return yaml.safe_load(json_status)['status']
258
259 LOG.info("Waiting for CNCF to complete")
260 helpers.wait(
261 lambda: sonobuoy_status() == 'complete',
262 interval=30, timeout=timeout,
263 timeout_msg="Timeout for CNCF reached."
264 )
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400265
266 def extract_file_to_node(self, system='docker',
267 container='virtlet',
268 file_path='report.xml',
269 out_dir='.',
270 **kwargs):
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400271 """
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400272 Download file from docker or k8s container to node
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400273
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400274 :param system: docker or k8s
275 :param container: Full name of part of name
276 :param file_path: File path in container
277 :param kwargs: Used to control pod and namespace
278 :param out_dir: Output directory
279 :return:
Vladimir Jigulinee1faa52018-06-25 13:00:51 +0400280 """
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +0400281 with self.__underlay.remote(node_name=self.controller_name) as remote:
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400282 if system is 'docker':
283 cmd = ("docker ps --all | grep \"{0}\" |"
284 " awk '{{print $1}}'".format(container))
285 result = remote.check_call(cmd, raise_on_err=False)
286 if result['stdout']:
287 container_id = result['stdout'][0].strip()
288 else:
289 LOG.info('No container found, skipping extraction...')
290 return
291 cmd = "docker start {}".format(container_id)
292 remote.check_call(cmd, raise_on_err=False)
293 cmd = "docker cp \"{0}:/{1}\" \"{2}\"".format(
294 container_id, file_path, out_dir)
295 remote.check_call(cmd, raise_on_err=False)
296 else:
297 # system is k8s
298 pod_name = kwargs.get('pod_name')
299 pod_namespace = kwargs.get('pod_namespace')
300 cmd = 'kubectl cp \"{0}/{1}:/{2}\" \"{3}\"'.format(
301 pod_namespace, pod_name, file_path, out_dir)
302 remote.check_call(cmd, raise_on_err=False)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400303
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400304 def download_k8s_logs(self, files):
305 """
306 Download JUnit report and conformance logs from cluster
307 :param files:
308 :return:
309 """
310 master_host = self.__config.salt.salt_master_host
311 with self.__underlay.remote(host=master_host) as r:
312 for log_file in files:
313 cmd = "rsync -r \"{0}:/root/{1}\" /root/".format(
314 self.controller_name, log_file)
315 r.check_call(cmd, raise_on_err=False)
316 LOG.info("Downloading the artifact {0}".format(log_file))
317 r.download(destination=log_file, target=os.getcwd())
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400318
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400319 def combine_xunit(self, path, output):
320 """
321 Function to combine multiple xmls with test results to
322 one.
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400323
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400324 :param path: Path where xmls to combine located
325 :param output: Path to xml file where output will stored
326 :return:
327 """
328 with self.__underlay.remote(node_name=self.controller_name) as r:
329 cmd = ("apt-get install python-setuptools -y; "
330 "pip install "
331 "https://github.com/mogaika/xunitmerge/archive/master.zip")
332 LOG.debug('Installing xunitmerge')
333 r.check_call(cmd, raise_on_err=False)
334 LOG.debug('Merging xunit')
335 cmd = ("cd {0}; arg = ''; "
336 "for i in $(ls | grep xml); "
337 "do arg=\"$arg $i\"; done && "
338 "xunitmerge $arg {1}".format(path, output))
339 r.check_call(cmd, raise_on_err=False)
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400340
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400341 def manage_cncf_archive(self):
342 """
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +0400343 Function to untar archive, move files that we are needs to the
344 home folder, prepare it to downloading.
345 Will generate files on controller node:
346 e2e.log, junit_01.xml, cncf_results.tar.gz, version.txt
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400347 :return:
348 """
349
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +0400350 cmd =\
351 "rm -rf cncf_results.tar.gz result && " \
352 "mkdir result && " \
353 "mv *_sonobuoy_*.tar.gz cncf_results.tar.gz && " \
354 "tar -C result -xzf cncf_results.tar.gz && " \
355 "mv result/plugins/e2e/results/e2e.log . ; " \
356 "mv result/plugins/e2e/results/junit_01.xml . ; " \
357 "kubectl version > version.txt"
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400358
Vladimir Jigulin0c8dd5a2018-08-28 05:08:35 +0400359 self.controller_check_call(cmd, raise_on_err=False)
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400360
361 @retry(300, exception=DevopsCalledProcessError)
362 def nslookup(self, host, src):
363 """ Run nslookup on controller and return result """
364 return self.controller_check_call("nslookup {0} {1}".format(host, src))
365
366 @retry(300, exception=DevopsCalledProcessError)
Vladimir Jigulin57ecae92018-09-10 22:51:15 +0400367 def curl(self, url, *args):
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400368 """
369 Run curl on controller and return stdout
370
371 :param url: url to curl
Vladimir Jigulin57ecae92018-09-10 22:51:15 +0400372 :return: list of strings (with /n at end of every line)
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400373 """
Vladimir Jigulin57ecae92018-09-10 22:51:15 +0400374 args = list(args)
375 args.append(url)
376 cmd = "curl -s -S {}".format(
377 " ".join(["'{}'".format(a.replace("'", "\\'")) for a in args]))
378 result = self.controller_check_call(cmd)
379 LOG.debug("{0}\nresult:\n{1}".format(cmd, result['stdout']))
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400380 return result['stdout']
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400381
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400382
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400383class K8SKubectlCli(object):
384 """ Contain kubectl cli commands and api wrappers"""
385 def __init__(self, manager):
386 self._manager = manager
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400387
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400388 def cli_run(self, namespace, name, image, port, replicas=1):
389 cmd = "kubectl -n {0} run {1} --image={2} --port={3} --replicas={4}".\
390 format(namespace, name, image, port, replicas)
391 return self._manager.controller_check_call(cmd)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400392
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400393 def run(self, namespace, name, image, port, replicas=1):
394 self.cli_run(namespace, name, image, port, replicas)
395 return self._manager.api.deployments.get(
396 namespace=namespace, name=name)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400397
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400398 def cli_expose(self, namespace, resource_type, resource_name,
399 service_name=None, port='', service_type='ClusterIP'):
400 cmd = "kubectl -n {0} expose {1} {2} --port={3} --type={4}".format(
401 namespace, resource_type, resource_name, port, service_type)
402 if service_name is not None:
403 cmd += " --name={}".format(service_name)
404 return self._manager.controller_check_call(cmd)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400405
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400406 def expose(self, resource, service_name=None,
407 port='', service_type='ClusterIP'):
408 self.cli_expose(resource.namespace, resource.resource_type,
409 resource.name, service_name=service_name,
410 port=port, service_type=service_type)
411 return self._manager.api.services.get(
412 namespace=resource.namespace, name=service_name or resource.name)
413
414 def cli_exec(self, namespace, pod_name, cmd, container=''):
415 kubectl_cmd = "kubectl -n {0} exec --container={1} {2} -- {3}".format(
416 namespace, container, pod_name, cmd)
417 return self._manager.controller_check_call(kubectl_cmd)
418
419 # def exec(...), except exec is statement in python
420 def execute(self, pod, cmd, container=''):
421 return self.cli_exec(pod.namespace, pod.name, cmd, container=container)
422
423 def cli_annotate(self, namespace, resource_type, resource_name,
424 annotations, overwrite=False):
425 cmd = "kubectl -n {0} annotate {1} {2} {3}".format(
426 namespace, resource_type, resource_name, annotations)
427 if overwrite:
428 cmd += " --overwrite"
429 return self._manager.controller_check_call(cmd)
430
431 def annotate(self, resource, annotations, overwrite=False):
432 return self.cli_annotate(resource.namespace, resource.resource_type,
433 resource.name, annotations,
434 overwrite=overwrite)
435
436
437class K8SVirtlet(object):
438 """ Contain virtlet-related methods"""
439 def __init__(self, manager, namespace='kube-system'):
440 self._manager = manager
441 self._namespace = namespace
442
443 def get_virtlet_node_pod(self, node_name):
444 for pod in self._manager.api.pods.list(
445 namespace=self._namespace, name_prefix='virtlet-'):
446 if pod.read().spec.node_name == node_name:
447 return pod
448 return None
449
450 def get_pod_dom_uuid(self, pod):
451 uuid_name_map = self.virtlet_execute(
452 pod.read().spec.node_name, 'virsh list --uuid --name')['stdout']
453 LOG.info("HEHEHEH {}".format(uuid_name_map))
454 LOG.info("MDAMDMAD {}".format(pod.name))
455 for line in uuid_name_map:
456 if line.rstrip().endswith("-{}".format(pod.name)):
457 return line.split(" ")[0]
458 raise Exception("Cannot detect uuid for pod {}".format(pod.name))
459
460 def virsh_domstats(self, pod):
461 """ get dict of vm stats """
462 uuid = self.get_pod_dom_uuid(pod)
463 result = self.virtlet_execute(
464 pod.read().spec.node_name, 'virsh domstats {}'.format(uuid))
465 stats = dict()
466 for line in result['stdout']:
467 if '=' in line:
468 vals = line.strip().split('=')
469 stats[vals[0]] = vals[1]
470 return stats
471
472 def virtlet_execute(self, node_name, cmd, container='libvirt'):
473 """ execute command inside virtlet container """
474 pod = self.get_virtlet_node_pod(node_name)
475 return self._manager.kubectl.execute(pod, cmd, container)
476
477
478class K8SSampleDeployment(object):
479 """ Wrapper for deployment run=>expose=>check frequent combination """
480 def __init__(self, manager, name,
481 namespace=None,
482 image='gcr.io/google-samples/node-hello:1.0',
483 port=8080,
484 replicas=2):
485 namespace = namespace or manager.api.default_namespace
486
487 self._manager = manager
488 self._port = port
489 self._deployment = \
490 manager.kubectl.run(namespace, name,
491 image=image, port=port, replicas=replicas)
492 self._index = 1 # used to generate svc name
493 self._svc = None # hold last created svc
494
495 def wait_ready(self, timeout=300, interval=5):
496 self._deployment.wait_ready(timeout=timeout, interval=interval)
497 return self
498
499 def svc(self):
500 """ Return the last exposed service"""
501 return self._svc
502
503 def expose(self, service_type='ClusterIP'):
504 service_name = "{0}-s{1}".format(self._deployment.name, self._index)
505 self._svc = self._manager.kubectl.expose(
506 self._deployment, port=self._port,
507 service_name=service_name, service_type=service_type)
508 return self._svc
509
510 def curl(self, svc=None, external=False):
511 if svc is None:
512 svc = self.svc()
513 url = "http://{0}:{1}".format(svc.get_ip(external), self._port)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400514 if external:
515 return requests.get(url).text
516 else:
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400517 return self._manager.curl(url)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400518
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400519 def is_service_available(self, svc=None, external=False):
520 return "Hello Kubernetes!" in self.curl(svc, external=external)