blob: a72f2f1af0cd99c9d50c41cf12143fe823c831d9 [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)
367 def curl(self, url):
368 """
369 Run curl on controller and return stdout
370
371 :param url: url to curl
372 :return: response string
373 """
374 result = self.controller_check_call("curl -s -S \"{}\"".format(url))
375 LOG.debug("curl \"{0}\" result: {1}".format(url, result['stdout']))
376 return result['stdout']
Vladimir Jigulin34dfa942018-07-23 21:05:48 +0400377
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400378
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400379class K8SKubectlCli(object):
380 """ Contain kubectl cli commands and api wrappers"""
381 def __init__(self, manager):
382 self._manager = manager
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400383
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400384 def cli_run(self, namespace, name, image, port, replicas=1):
385 cmd = "kubectl -n {0} run {1} --image={2} --port={3} --replicas={4}".\
386 format(namespace, name, image, port, replicas)
387 return self._manager.controller_check_call(cmd)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400388
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400389 def run(self, namespace, name, image, port, replicas=1):
390 self.cli_run(namespace, name, image, port, replicas)
391 return self._manager.api.deployments.get(
392 namespace=namespace, name=name)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400393
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400394 def cli_expose(self, namespace, resource_type, resource_name,
395 service_name=None, port='', service_type='ClusterIP'):
396 cmd = "kubectl -n {0} expose {1} {2} --port={3} --type={4}".format(
397 namespace, resource_type, resource_name, port, service_type)
398 if service_name is not None:
399 cmd += " --name={}".format(service_name)
400 return self._manager.controller_check_call(cmd)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400401
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400402 def expose(self, resource, service_name=None,
403 port='', service_type='ClusterIP'):
404 self.cli_expose(resource.namespace, resource.resource_type,
405 resource.name, service_name=service_name,
406 port=port, service_type=service_type)
407 return self._manager.api.services.get(
408 namespace=resource.namespace, name=service_name or resource.name)
409
410 def cli_exec(self, namespace, pod_name, cmd, container=''):
411 kubectl_cmd = "kubectl -n {0} exec --container={1} {2} -- {3}".format(
412 namespace, container, pod_name, cmd)
413 return self._manager.controller_check_call(kubectl_cmd)
414
415 # def exec(...), except exec is statement in python
416 def execute(self, pod, cmd, container=''):
417 return self.cli_exec(pod.namespace, pod.name, cmd, container=container)
418
419 def cli_annotate(self, namespace, resource_type, resource_name,
420 annotations, overwrite=False):
421 cmd = "kubectl -n {0} annotate {1} {2} {3}".format(
422 namespace, resource_type, resource_name, annotations)
423 if overwrite:
424 cmd += " --overwrite"
425 return self._manager.controller_check_call(cmd)
426
427 def annotate(self, resource, annotations, overwrite=False):
428 return self.cli_annotate(resource.namespace, resource.resource_type,
429 resource.name, annotations,
430 overwrite=overwrite)
431
432
433class K8SVirtlet(object):
434 """ Contain virtlet-related methods"""
435 def __init__(self, manager, namespace='kube-system'):
436 self._manager = manager
437 self._namespace = namespace
438
439 def get_virtlet_node_pod(self, node_name):
440 for pod in self._manager.api.pods.list(
441 namespace=self._namespace, name_prefix='virtlet-'):
442 if pod.read().spec.node_name == node_name:
443 return pod
444 return None
445
446 def get_pod_dom_uuid(self, pod):
447 uuid_name_map = self.virtlet_execute(
448 pod.read().spec.node_name, 'virsh list --uuid --name')['stdout']
449 LOG.info("HEHEHEH {}".format(uuid_name_map))
450 LOG.info("MDAMDMAD {}".format(pod.name))
451 for line in uuid_name_map:
452 if line.rstrip().endswith("-{}".format(pod.name)):
453 return line.split(" ")[0]
454 raise Exception("Cannot detect uuid for pod {}".format(pod.name))
455
456 def virsh_domstats(self, pod):
457 """ get dict of vm stats """
458 uuid = self.get_pod_dom_uuid(pod)
459 result = self.virtlet_execute(
460 pod.read().spec.node_name, 'virsh domstats {}'.format(uuid))
461 stats = dict()
462 for line in result['stdout']:
463 if '=' in line:
464 vals = line.strip().split('=')
465 stats[vals[0]] = vals[1]
466 return stats
467
468 def virtlet_execute(self, node_name, cmd, container='libvirt'):
469 """ execute command inside virtlet container """
470 pod = self.get_virtlet_node_pod(node_name)
471 return self._manager.kubectl.execute(pod, cmd, container)
472
473
474class K8SSampleDeployment(object):
475 """ Wrapper for deployment run=>expose=>check frequent combination """
476 def __init__(self, manager, name,
477 namespace=None,
478 image='gcr.io/google-samples/node-hello:1.0',
479 port=8080,
480 replicas=2):
481 namespace = namespace or manager.api.default_namespace
482
483 self._manager = manager
484 self._port = port
485 self._deployment = \
486 manager.kubectl.run(namespace, name,
487 image=image, port=port, replicas=replicas)
488 self._index = 1 # used to generate svc name
489 self._svc = None # hold last created svc
490
491 def wait_ready(self, timeout=300, interval=5):
492 self._deployment.wait_ready(timeout=timeout, interval=interval)
493 return self
494
495 def svc(self):
496 """ Return the last exposed service"""
497 return self._svc
498
499 def expose(self, service_type='ClusterIP'):
500 service_name = "{0}-s{1}".format(self._deployment.name, self._index)
501 self._svc = self._manager.kubectl.expose(
502 self._deployment, port=self._port,
503 service_name=service_name, service_type=service_type)
504 return self._svc
505
506 def curl(self, svc=None, external=False):
507 if svc is None:
508 svc = self.svc()
509 url = "http://{0}:{1}".format(svc.get_ip(external), self._port)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400510 if external:
511 return requests.get(url).text
512 else:
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400513 return self._manager.curl(url)
Vladimir Jigulina6b018b2018-07-18 15:19:01 +0400514
Vladimir Jigulin4ad52a82018-08-12 05:51:30 +0400515 def is_service_available(self, svc=None, external=False):
516 return "Hello Kubernetes!" in self.curl(svc, external=external)