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