blob: c83cac4db30494acc548aaa3ffd8df9290dabf02 [file] [log] [blame]
Artem Panchenko501e67e2017-06-14 14:59:18 +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
15import json
16import requests
17
18from devops.helpers import helpers
19from k8sclient.client import rest
20
21from tcp_tests import logger
22from tcp_tests.helpers import utils
23
24
25LOG = logger.logger
26
27
28NETCHECKER_SERVICE_NAME = "netchecker-service"
29NETCHECKER_CONTAINER_PORT = NETCHECKER_SERVICE_PORT = 8081
30NETCHECKER_NODE_PORT = 31081
31NETCHECKER_REPORT_INTERVAL = 30
32NETCHECKER_SERVER_REPLICAS = 1
33NETCHECKER_PROBEURLS = "http://ipinfo.io"
34
35NETCHECKER_SVC_CFG = {
36 "apiVersion": "v1",
37 "kind": "Service",
38 "metadata": {
39 "name": NETCHECKER_SERVICE_NAME
40 },
41 "spec": {
42 "ports": [
43 {
44 "nodePort": NETCHECKER_NODE_PORT,
45 "port": NETCHECKER_SERVICE_PORT,
46 "protocol": "TCP",
47 "targetPort": NETCHECKER_CONTAINER_PORT
48 }
49 ],
50 "selector": {
51 "app": "netchecker-server"
52 },
53 "type": "NodePort"
54 }
55}
56
57NETCHECKER_DEPLOYMENT_CFG = {
58 "kind": "Deployment",
59 "spec": {
60 "template": {
61 "spec": {
62 "containers": [
63 {
64 "name": "netchecker-server",
65 "env": None,
66 "imagePullPolicy": "IfNotPresent",
67 "image": "mirantis/k8s-netchecker-server:latest",
68 "args": [
69 "-v=5",
70 "-logtostderr",
71 "-kubeproxyinit",
72 "-endpoint=0.0.0.0:{0}".format(
73 NETCHECKER_CONTAINER_PORT)
74 ],
75 "ports": [
76 {
77 "containerPort": NETCHECKER_CONTAINER_PORT,
78 "hostPort": NETCHECKER_NODE_PORT
79 }
80 ]
81 }
82 ]
83 },
84 "metadata": {
85 "labels": {
86 "app": "netchecker-server"
87 },
88 "name": "netchecker-server"
89 }
90 },
91 "replicas": NETCHECKER_SERVER_REPLICAS
92 },
93 "apiVersion": "extensions/v1beta1",
94 "metadata": {
95 "name": "netchecker-server"
96 }
97}
98
99NETCHECKER_DS_CFG = [
100 {
101 "apiVersion": "extensions/v1beta1",
102 "kind": "DaemonSet",
103 "metadata": {
104 "labels": {
105 "app": "netchecker-agent"
106 },
107 "name": "netchecker-agent"
108 },
109 "spec": {
110 "template": {
111 "metadata": {
112 "labels": {
113 "app": "netchecker-agent"
114 },
115 "name": "netchecker-agent"
116 },
117 "spec": {
118 "tolerations": [
119 {
120 "key": "node-role.kubernetes.io/master",
121 "effect": "NoSchedule"
122 }
123 ],
124 "containers": [
125 {
126 "env": [
127 {
128 "name": "MY_POD_NAME",
129 "valueFrom": {
130 "fieldRef": {
131 "fieldPath": "metadata.name"
132 }
133 }
134 },
135 {
136 "name": "MY_NODE_NAME",
137 "valueFrom": {
138 "fieldRef": {
139 "fieldPath": "spec.nodeName"
140 }
141 }
142 },
143 {
144 "name": "REPORT_INTERVAL",
145 "value": str(NETCHECKER_REPORT_INTERVAL)
146 },
147 ],
148 "image": "mirantis/k8s-netchecker-agent:latest",
149 "imagePullPolicy": "IfNotPresent",
150 "name": "netchecker-agent",
151 "command": ["netchecker-agent"],
152 "args": [
153 "-v=5",
154 "-logtostderr",
155 "-probeurls={0}".format(NETCHECKER_PROBEURLS)
156 ]
157 }
158 ],
159 }
160 },
161 "updateStrategy": {
162 "type": "RollingUpdate"
163 }
164 }
165 },
166 {
167 "apiVersion": "extensions/v1beta1",
168 "kind": "DaemonSet",
169 "metadata": {
170 "labels": {
171 "app": "netchecker-agent-hostnet"
172 },
173 "name": "netchecker-agent-hostnet"
174 },
175 "spec": {
176 "template": {
177 "metadata": {
178 "labels": {
179 "app": "netchecker-agent-hostnet"
180 },
181 "name": "netchecker-agent-hostnet"
182 },
183 "spec": {
184 "tolerations": [
185 {
186 "key": "node-role.kubernetes.io/master",
187 "effect": "NoSchedule"
188 }
189 ],
190 "containers": [
191 {
192 "env": [
193 {
194 "name": "MY_POD_NAME",
195 "valueFrom": {
196 "fieldRef": {
197 "fieldPath": "metadata.name"
198 }
199 }
200 },
201 {
202 "name": "MY_NODE_NAME",
203 "valueFrom": {
204 "fieldRef": {
205 "fieldPath": "spec.nodeName"
206 }
207 }
208 },
209 {
210 "name": "REPORT_INTERVAL",
211 "value": str(NETCHECKER_REPORT_INTERVAL)
212 },
213 ],
214 "image": "mirantis/k8s-netchecker-agent:latest",
215 "imagePullPolicy": "IfNotPresent",
216 "name": "netchecker-agent",
217 "command": ["netchecker-agent"],
218 "args": [
219 "-v=5",
220 "-logtostderr",
221 "-probeurls={0}".format(NETCHECKER_PROBEURLS)
222 ]
223 }
224 ],
225 "hostNetwork": True,
226 "dnsPolicy": "ClusterFirstWithHostNet",
227 "updateStrategy": {
228 "type": "RollingUpdate"
229 }
230 }
231 },
232 "updateStrategy": {
233 "type": "RollingUpdate"
234 }
235 }
236 }
237]
238
239NETCHECKER_BLOCK_POLICY = {
240 "kind": "policy",
241 "spec": {
242 "ingress": [
243 {
244 "action": "allow"
245 },
246 {
247 "action": "deny",
248 "destination": {
249 "ports": [
250 NETCHECKER_SERVICE_PORT
251 ]
252 },
253 "protocol": "tcp"
254 }
255 ]
256 },
257 "apiVersion": "v1",
258 "metadata": {
259 "name": "deny-netchecker"
260 }
261}
262
263
264def start_server(k8s, config, namespace=None,
265 deploy_spec=NETCHECKER_DEPLOYMENT_CFG,
266 svc_spec=NETCHECKER_SVC_CFG):
267 """Start netchecker server in k8s cluster
268
269 :param k8s: K8SManager
270 :param config: fixture provides oslo.config
271 :param namespace: str
272 :param deploy_spec: dict
273 :param svc_spec: dict
274 :return: None
275 """
276 for container in deploy_spec['spec']['template']['spec']['containers']:
277 if container['name'] == 'netchecker-server':
278 container['image'] = \
279 config.k8s_deploy.kubernetes_netchecker_server_image
280 try:
281 if k8s.api.deployments.get(name=deploy_spec['metadata']['name'],
282 namespace=namespace):
283 LOG.debug('Network checker server deployment "{}" '
284 'already exists! Skipping resource '
285 'creation'.format(deploy_spec['metadata']['name']))
286 except rest.ApiException as e:
287 if e.status == 404:
288 n = k8s.check_deploy_create(body=deploy_spec, namespace=namespace)
289 k8s.wait_deploy_ready(n.name, namespace=namespace)
290 else:
291 raise e
292 try:
293 if k8s.api.services.get(name=svc_spec['metadata']['name']):
294 LOG.debug('Network checker server service {} is '
295 'already running! Skipping resource creation'
296 '.'.format(svc_spec['metadata']['name']))
297 except rest.ApiException as e:
298 if e.status == 404:
299 k8s.check_service_create(body=svc_spec, namespace=namespace)
300 else:
301 raise e
302
303
304def start_agent(k8s, config, namespace=None, ds_spec=NETCHECKER_DS_CFG,
305 service_namespace=None):
306 """Start netchecker agent in k8s cluster
307
308 :param k8s: K8SManager
309 :param config: fixture provides oslo.config
310 :param namespace: str
311 :param ds_spec: str
312 :return: None
313 """
314 for ds in ds_spec:
315 for container in ds['spec']['template']['spec']['containers']:
316 if container['name'] == 'netchecker-agent':
317 container['image'] = \
318 config.k8s_deploy.kubernetes_netchecker_agent_image
319 if service_namespace is not None:
320 container['args'].append(
321 "-serverendpoint={0}.{1}.svc.cluster.local:{2}".format(
322 NETCHECKER_SERVICE_NAME,
323 service_namespace,
324 NETCHECKER_SERVICE_PORT))
325 k8s.check_ds_create(body=ds, namespace=namespace)
326 k8s.wait_ds_ready(dsname=ds['metadata']['name'], namespace=namespace)
327 k8s.wait_pods_phase(pods=[pod for pod in k8s.api.pods.list()
328 if 'netchecker-agent' in pod.name],
329 phase='Running',
330 timeout=600)
331
332
333@utils.retry(3, requests.exceptions.RequestException)
Dina Belovae6fdffb2017-09-19 13:58:34 -0700334def get_connectivity_status(k8sclient,
335 netchecker_pod_port=NETCHECKER_NODE_PORT,
336 pod_name='netchecker-server', namespace='default'):
Artem Panchenko501e67e2017-06-14 14:59:18 +0300337
338 netchecker_srv_pod_names = [pod.name for pod in
339 k8sclient.pods.list(namespace=namespace)
340 if pod_name in pod.name]
341
342 assert len(netchecker_srv_pod_names) > 0, \
343 "No netchecker-server pods found!"
344
345 netchecker_srv_pod = k8sclient.pods.get(name=netchecker_srv_pod_names[0],
346 namespace=namespace)
347 kube_host_ip = netchecker_srv_pod.status.host_ip
348 net_status_url = 'http://{0}:{1}/api/v1/connectivity_check'.format(
349 kube_host_ip, netchecker_pod_port)
350 response = requests.get(net_status_url, timeout=5)
351 LOG.debug('Connectivity check status: [{0}] {1}'.format(
352 response.status_code, response.text.strip()))
353 return response
354
355
Tatyana Leontovichf00b2342017-07-04 18:26:25 +0300356@utils.retry(3, requests.exceptions.RequestException)
357def get_netchecker_pod_status(k8s,
358 pod_name='netchecker-server',
359 namespace='default'):
360
361 k8s.wait_pods_phase(
362 pods=[pod for pod in k8s.api.pods.list(namespace=namespace)
363 if pod_name in pod.name], phase='Running', timeout=600)
364
365
Dina Belovae6fdffb2017-09-19 13:58:34 -0700366def check_network(k8sclient, netchecker_pod_port,
Tatyana Leontovichf00b2342017-07-04 18:26:25 +0300367 namespace='default', works=True):
Artem Panchenko501e67e2017-06-14 14:59:18 +0300368 if works:
Tatyana Leontovichf00b2342017-07-04 18:26:25 +0300369 assert get_connectivity_status(
370 k8sclient, namespace=namespace,
371 netchecker_pod_port=netchecker_pod_port).status_code in (200, 204)
Artem Panchenko501e67e2017-06-14 14:59:18 +0300372 else:
Tatyana Leontovichf00b2342017-07-04 18:26:25 +0300373 assert get_connectivity_status(
374 k8sclient, namespace=namespace,
375 netchecker_pod_port=netchecker_pod_port).status_code == 400
Artem Panchenko501e67e2017-06-14 14:59:18 +0300376
377
378def wait_check_network(k8sclient, namespace='default', works=True, timeout=120,
Tatyana Leontovichf00b2342017-07-04 18:26:25 +0300379 interval=5, netchecker_pod_port=NETCHECKER_NODE_PORT):
Dina Belovae6fdffb2017-09-19 13:58:34 -0700380 helpers.wait_pass(
381 lambda: check_network(
382 k8sclient, netchecker_pod_port=netchecker_pod_port,
383 namespace=namespace,
384 works=works),
385 timeout=timeout,
386 interval=interval)
Artem Panchenko501e67e2017-06-14 14:59:18 +0300387
388
389def calico_block_traffic_on_node(underlay, target_node):
390 cmd = "echo '{0}' | calicoctl create -f -".format(NETCHECKER_BLOCK_POLICY)
391 underlay.sudo_check_call(cmd, node_name=target_node)
392 LOG.info('Blocked traffic to the network checker service from '
393 'containers on node "{}".'.format(target_node))
394
395
396def calico_unblock_traffic_on_node(underlay, target_node):
397 cmd = "echo '{0}' | calicoctl delete -f -".format(NETCHECKER_BLOCK_POLICY)
398
399 underlay.sudo_check_call(cmd, node_name=target_node)
400 LOG.info('Unblocked traffic to the network checker service from '
401 'containers on node "{}".'.format(target_node))
402
403
404def calico_get_version(underlay, target_node):
405 raw_version = underlay.sudo_check_call('calicoctl version',
406 node_name=target_node)
407
408 assert raw_version['exit_code'] == 0 and len(raw_version['stdout']) > 0, \
409 "Unable to get calico version!"
410
411 if len(raw_version['stdout']) > 1:
412 ctl_version = raw_version['stdout'][0].split()[1].strip()
413 else:
414 ctl_version = raw_version['stdout'][0].strip()
415
416 LOG.debug("Calico (calicoctl) version on '{0}': '{1}'".format(target_node,
417 ctl_version))
418 return ctl_version
419
420
421def kubernetes_block_traffic_namespace(underlay, kube_host_ip, namespace):
422 # TODO(apanchenko): do annotation using kubernetes API
423 cmd = ('kubectl annotate ns {0} \'net.beta.kubernetes.io/'
424 'network-policy={{"ingress": {{"isolation":'
425 ' "DefaultDeny"}}}}\'').format(namespace)
426 underlay.sudo_check_call(cmd=cmd, host=kube_host_ip)
427
428
429def calico_allow_netchecker_connections(underlay, kube_ssh_ip, kube_host_ip,
430 namespace):
431 calico_policy = {"kind": "policy",
432 "spec": {
433 "ingress": [
434 {
435 "action": "allow",
436 "source": {
437 "net": "{0}/24".format(kube_host_ip)
438 },
439 "destination": {
440 "selector": ("calico/k8s_ns =="
441 " \"{0}\"").format(namespace)
442 },
443 "protocol": "tcp"
444 }
445 ],
446 "order": 500,
447 "selector": "has(calico/k8s_ns)"
448 },
449 "apiVersion": "v1",
450 "metadata": {
451 "name": "netchecker.allow-host-connections"}
452 }
453
454 cmd = "echo '{0}' | calicoctl apply -f -".format(
455 json.dumps(calico_policy))
456 underlay.sudo_check_call(cmd=cmd, host=kube_ssh_ip)
457
458
459def kubernetes_allow_traffic_from_agents(underlay, kube_host_ip, namespace):
460 # TODO(apanchenko): add network policies using kubernetes API
461 label_namespace_cmd = "kubectl label namespace default name=default"
462 underlay.sudo_check_call(cmd=label_namespace_cmd, host=kube_host_ip)
463 kubernetes_policy = {
464 "apiVersion": "extensions/v1beta1",
465 "kind": "NetworkPolicy",
466 "metadata": {
467 "name": "access-netchecker",
468 "namespace": namespace,
469 },
470 "spec": {
471 "ingress": [
472 {
473 "from": [
474 {
475 "namespaceSelector": {
476 "matchLabels": {
477 "name": "default"
478 }
479 }
480 },
481 {
482 "podSelector": {
483 "matchLabels": {
484 "app": "netchecker-agent"
485 }
486 }
487 }
488 ]
489 }
490 ],
491 "podSelector": {
492 "matchLabels": {
493 "app": "netchecker-server"
494 }
495 }
496 }
497 }
498
499 kubernetes_policy_hostnet = {
500 "apiVersion": "extensions/v1beta1",
501 "kind": "NetworkPolicy",
502 "metadata": {
503 "name": "access-netchecker-hostnet",
504 "namespace": namespace,
505 },
506 "spec": {
507 "ingress": [
508 {
509 "from": [
510 {
511 "namespaceSelector": {
512 "matchLabels": {
513 "name": "default"
514 }
515 }
516 },
517 {
518 "podSelector": {
519 "matchLabels": {
520 "app": "netchecker-agent-hostnet"
521 }
522 }
523 }
524 ]
525 }
526 ],
527 "podSelector": {
528 "matchLabels": {
529 "app": "netchecker-server"
530 }
531 }
532 }
533 }
534 cmd_add_policy = "echo '{0}' | kubectl create -f -".format(
535 json.dumps(kubernetes_policy))
536 underlay.sudo_check_call(cmd=cmd_add_policy, host=kube_host_ip)
537 cmd_add_policy_hostnet = "echo '{0}' | kubectl create -f -".format(
538 json.dumps(kubernetes_policy_hostnet))
539 underlay.sudo_check_call(cmd=cmd_add_policy_hostnet, host=kube_host_ip)
Tatyana Leontovichf00b2342017-07-04 18:26:25 +0300540
541
542@utils.retry(3, requests.exceptions.RequestException)
543def get_metric(k8sclient, netchecker_pod_port,
544 pod_name='netchecker-server', namespace='default'):
545
546 netchecker_srv_pod_names = [pod.name for pod in
547 k8sclient.pods.list(namespace=namespace)
548 if pod_name in pod.name]
549
550 assert len(netchecker_srv_pod_names) > 0, \
551 "No netchecker-server pods found!"
552 netchecker_srv_pod = k8sclient.pods.get(name=netchecker_srv_pod_names[0],
553 namespace=namespace)
554
555 kube_host_ip = netchecker_srv_pod.status.host_ip
556 metrics_url = 'http://{0}:{1}/metrics'.format(
557 kube_host_ip, netchecker_pod_port)
558 response = requests.get(metrics_url, timeout=30)
559 LOG.debug('Metrics: [{0}] {1}'.format(
560 response.status_code, response.text.strip()))
561 return response
Tatyana Leontovich09b7b012017-07-10 12:53:45 +0300562
563
564def get_service_port(k8sclient, service_name='netchecker',
Dina Belovae6fdffb2017-09-19 13:58:34 -0700565 namespace='netchecker'):
Tatyana Leontovich09b7b012017-07-10 12:53:45 +0300566 full_service_name = [service.name for service
567 in k8sclient.services.list(namespace=namespace)
568 if service_name in service.name]
569 assert len(full_service_name) > 0, "No netchecker service run"
570
571 service_details = k8sclient.services.get(name=full_service_name[0],
572 namespace=namespace)
573
574 LOG.debug('Necthcecker service details {0}'.format(service_details))
575 netchecker_port = service_details.spec.ports[0].node_port
576 return netchecker_port