-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathssd.py
executable file
·180 lines (157 loc) · 6.5 KB
/
ssd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/python
import sys, signal, time
import docker
import re
import subprocess
import json
import hashlib
ipv4match = re.compile(
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]).' +
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]).' +
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]).' +
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])'
)
def check_iptables(name, plist):
replace = (':', ',')
ports = []
for port in plist:
for r in replace:
port = port.replace(r, ' ')
p = port.split()
ports.append((p[1], p[3]))
# get the ingress sandbox's docker_gwbridge network IP.
# published ports get DNAT'ed to this IP.
ip = subprocess.check_output(['/usr/bin/nsenter', '--net=/var/run/docker/netns/ingress_sbox', '/bin/bash', '-c', 'ifconfig eth1 | grep \"inet\\ addr\" | cut -d: -f2 | cut -d\" \" -f1'])
ip = ip.rstrip()
for p in ports:
rule = '/sbin/iptables -t nat -C DOCKER-INGRESS -p tcp --dport {0} -j DNAT --to {1}:{2}'.format(p[1], ip, p[1])
try:
subprocess.check_output(["/bin/bash", "-c", rule])
except subprocess.CalledProcessError as e:
print "Service {0}: host iptables DNAT rule for port {1} -> ingress sandbox {2}:{3} missing".format(name, p[1], ip, p[1])
def get_namespaces(data, ingress=False):
if ingress is True:
return {"Ingress":"/var/run/docker/netns/ingress_sbox"}
else:
spaces =[]
for c in data["Containers"]:
sandboxes = {str(c) for c in data["Containers"]}
containers = {}
for s in sandboxes:
spaces.append(str(cli.inspect_container(s)["NetworkSettings"]["SandboxKey"]))
inspect = cli.inspect_container(s)
containers[str(inspect["Name"])] = str(inspect["NetworkSettings"]["SandboxKey"])
return containers
def check_network(nw_name, ingress=False):
print "Verifying LB programming for containers on network %s" % nw_name
data = cli.inspect_network(nw_name, verbose=True)
services = data["Services"]
fwmarks = {str(service): str(svalue["LocalLBIndex"]) for service, svalue in services.items()}
stasks = {}
for service, svalue in services.items():
if service == "":
continue
tasks = []
for task in svalue["Tasks"]:
tasks.append(str(task["EndpointIP"]))
stasks[fwmarks[str(service)]] = tasks
# for services in ingress network verify the iptables rules
# that direct ingress (published port) to backend (target port)
if ingress is True:
check_iptables(service, svalue["Ports"])
containers = get_namespaces(data, ingress)
for container, namespace in containers.items():
print "Verifying container %s..." % container
ipvs = subprocess.check_output(['/usr/bin/nsenter', '--net=%s' % namespace, '/usr/sbin/ipvsadm', '-ln'])
mark = ""
realmark = {}
for line in ipvs.splitlines():
if "FWM" in line:
mark = re.findall("[0-9]+", line)[0]
realmark[str(mark)] = []
elif "->" in line:
if mark == "":
continue
ip = ipv4match.search(line)
if ip is not None:
realmark[mark].append(format(ip.group(0)))
else:
mark = ""
for key in realmark.keys():
if key not in stasks:
print "LB Index %s" % key, "present in IPVS but missing in docker daemon"
del realmark[key]
for key in stasks.keys():
if key not in realmark:
print "LB Index %s" % key, "present in docker daemon but missing in IPVS"
del stasks[key]
for key in realmark:
service = "--Invalid--"
for sname, idx in fwmarks.items():
if key == idx:
service = sname
if len(set(realmark[key])) != len(set(stasks[key])):
print "Incorrect LB Programming for service %s" % service
print "control-plane backend tasks:"
for task in stasks[key]:
print task
print "kernel IPVS backend tasks:"
for task in realmark[key]:
print task
else:
print "service %s... OK" % service
if __name__ == '__main__':
if len(sys.argv) < 2:
print 'Usage: ssd.py network-name [gossip-consistency]'
sys.exit()
cli = docker.APIClient(base_url='unix://var/run/docker.sock', version='auto')
if len(sys.argv) == 3:
command = sys.argv[2]
else:
command = 'default'
if command == 'gossip-consistency':
cspec = docker.types.ContainerSpec(
image='sanimej/ssd',
args=[sys.argv[1], 'gossip-hash'],
mounts=[docker.types.Mount('/var/run/docker.sock', '/var/run/docker.sock', type='bind')]
)
mode = docker.types.ServiceMode(
mode='global'
)
task_template = docker.types.TaskTemplate(cspec)
cli.create_service(task_template, name='gossip-hash', mode=mode)
#TODO change to a deterministic way to check if the service is up.
time.sleep(5)
output = cli.service_logs('gossip-hash', stdout=True, stderr=True, details=True)
for line in output:
print("Node id: %s gossip hash %s" % (line[line.find("=")+1:line.find(",")], line[line.find(" ")+1:]))
if cli.remove_service('gossip-hash') is not True:
print("Deleting gossip-hash service failed")
elif command == 'gossip-hash':
data = cli.inspect_network(sys.argv[1], verbose=True)
services = data["Services"]
md5 = hashlib.md5()
entries = []
for service, value in services.items():
entries.append(service)
entries.append(value["VIP"])
for task in value["Tasks"]:
for key, val in task.items():
if isinstance(val, dict):
for k, v in val.items():
entries.append(v)
else:
entries.append(val)
entries.sort()
for e in entries:
md5.update(e)
print(md5.hexdigest())
sys.stdout.flush()
while True:
signal.pause()
elif command == 'default':
if sys.argv[1] == "ingress":
check_network("ingress", ingress=True)
else:
check_network(sys.argv[1])
check_network("ingress", ingress=True)