|
|
@@ -61,8 +61,9 @@ def iface_nets(iface):
|
|
|
def get_bridges():
|
|
|
get_docker_client()
|
|
|
reserved_nets = set(map(ipaddress.ip_network, map(lambda c: c['Subnet'], \
|
|
|
- itertools.chain.from_iterable(map(lambda i: i['Config'], filter(lambda i: i['Driver'] != 'net-dhcp', \
|
|
|
- map(lambda n: n.attrs['IPAM'], dockerClient.networks.list())))))))
|
|
|
+ itertools.chain.from_iterable(map(lambda x: [] if x is None else x, \
|
|
|
+ map(lambda i: i['Config'], filter(lambda i: i['Driver'] != 'net-dhcp', \
|
|
|
+ map(lambda n: n.attrs['IPAM'], dockerClient.networks.list()))))))))
|
|
|
|
|
|
return dict(map(lambda i: (i['ifname'], i), filter(lambda i: i['kind'] == 'bridge' and not \
|
|
|
set(iface_nets(i)).intersection(reserved_nets), map(lambda i: interface.GetInterface(LIBRARY, i['ifname']), \
|
|
|
@@ -95,6 +96,7 @@ def endpoint_container_iface(n, e):
|
|
|
}
|
|
|
break
|
|
|
return None
|
|
|
+
|
|
|
def await_endpoint_container_iface(n, e, timeout=5):
|
|
|
start = time.time()
|
|
|
iface = None
|
|
|
@@ -103,6 +105,8 @@ def await_endpoint_container_iface(n, e, timeout=5):
|
|
|
iface = endpoint_container_iface(n, e)
|
|
|
except docker.errors.NotFound:
|
|
|
time.sleep(0.5)
|
|
|
+ except KeyError:
|
|
|
+ time.sleep(0.5)
|
|
|
if not iface:
|
|
|
raise NetDhcpError('Timed out waiting for container to become availabile')
|
|
|
return iface
|
|
|
@@ -114,6 +118,46 @@ def endpoint_container_hostname(n, e):
|
|
|
return dockerClient.containers.get(cid).attrs['Config']['Hostname']
|
|
|
return None
|
|
|
|
|
|
+def endpoint_container_network_gwpriority(n, e):
|
|
|
+ get_docker_client()
|
|
|
+ for cid, info in dockerClient.networks.get(n).attrs['Containers'].items():
|
|
|
+ if info['EndpointID'] == e:
|
|
|
+ networkName = dockerClient.networks.get(n).attrs['Name']
|
|
|
+ if 'GwPriority' in dockerClient.containers.get(cid).attrs['NetworkSettings']['Networks'][networkName]:
|
|
|
+ return dockerClient.containers.get(cid).attrs['NetworkSettings']['Networks'][networkName]['GwPriority']
|
|
|
+ else:
|
|
|
+ return 0
|
|
|
+ return None
|
|
|
+
|
|
|
+def remove_veth(host_ifname):
|
|
|
+ logger.info('Removing veth %s', host_ifname)
|
|
|
+ if LIBRARY == 'NDB':
|
|
|
+ if_host = interface.GetInterface(LIBRARY, if_host)
|
|
|
+ bridge = net_bridge(req['NetworkID'])
|
|
|
+ interface.DelPort(LIBRARY, bridge.ifname, if_host.ifname)
|
|
|
+ interface.RemoveInterface(LIBRARY, if_host.ifname)
|
|
|
+ logger.info('Removed veth for endpoint %s on ()', endpoint)
|
|
|
+ else:
|
|
|
+ logger.info('Deleting veth via shell: %s', host_ifname)
|
|
|
+ try:
|
|
|
+ subprocess.run(
|
|
|
+ ['ip', 'link', 'del', host_ifname],
|
|
|
+ stdout=subprocess.DEVNULL,
|
|
|
+ stderr=subprocess.PIPE,
|
|
|
+ check=True
|
|
|
+ )
|
|
|
+ logger.info('Deleted veth %s', host_ifname)
|
|
|
+
|
|
|
+ except subprocess.CalledProcessError as e:
|
|
|
+ if b'Cannot find device' in e.stderr:
|
|
|
+ logger.info('Veth %s already gone', host_ifname)
|
|
|
+ else:
|
|
|
+ logger.warning(
|
|
|
+ 'Failed to delete veth %s: %s',
|
|
|
+ host_ifname,
|
|
|
+ e.stderr.decode().strip()
|
|
|
+ )
|
|
|
+
|
|
|
@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
|
|
|
def net_get_capabilities():
|
|
|
return jsonify({
|
|
|
@@ -250,18 +294,8 @@ def endpoint_info():
|
|
|
def delete_endpoint():
|
|
|
req = request.get_json(force=True)
|
|
|
|
|
|
- if_host, _if_container = veth_pair(req['EndpointID'])
|
|
|
- if_host = interface.GetInterface(LIBRARY, if_host)
|
|
|
-
|
|
|
- try:
|
|
|
- if LIBRARY == 'NDB':
|
|
|
- bridge = net_bridge(req['NetworkID'])
|
|
|
- interface.DelPort(LIBRARY, bridge.ifname, if_host.ifname)
|
|
|
- else:
|
|
|
- interface.DelPort(LIBRARY, 'dummy0', if_host.ifname)
|
|
|
- interface.RemoveInterface(LIBRARY, if_host.ifname)
|
|
|
- except Exception as e:
|
|
|
- logger.exception(e)
|
|
|
+ if_host, _ = veth_pair(req['EndpointID'])
|
|
|
+ remove_veth(if_host)
|
|
|
|
|
|
return jsonify({})
|
|
|
|
|
|
@@ -335,6 +369,7 @@ class ContainerDHCPManager:
|
|
|
|
|
|
self.dhcp = None
|
|
|
self.dhcp6 = None
|
|
|
+ self.gwPriority = None
|
|
|
self._thread = threading.Thread(target=self.run)
|
|
|
self._thread.start()
|
|
|
|
|
|
@@ -373,7 +408,7 @@ class ContainerDHCPManager:
|
|
|
str(dhcp.iface['ifname'])],
|
|
|
timeout=1, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
|
|
|
|
- if dhcp.gateway:
|
|
|
+ if dhcp.gateway and self.gwPriority >= 0:
|
|
|
logger.info('[dhcp container] Replacing gateway with %s', dhcp.gateway)
|
|
|
subprocess.check_call(['nsenter', f'-n{dhcp.netns}', '--', '/sbin/ip', 'route', 'replace', 'default', 'via',
|
|
|
str(dhcp.gateway)],
|
|
|
@@ -393,8 +428,12 @@ class ContainerDHCPManager:
|
|
|
try:
|
|
|
iface = await_endpoint_container_iface(self.network, self.endpoint)
|
|
|
hostname = endpoint_container_hostname(self.network, self.endpoint)
|
|
|
+ self.gwPriority = endpoint_container_network_gwpriority(self.network, self.endpoint)
|
|
|
|
|
|
- self.dhcp = udhcpc.DHCPClient(iface, event_listener=self._on_event, hostname=hostname)
|
|
|
+ if self.gwPriority >= 0:
|
|
|
+ self.dhcp = udhcpc.DHCPClient(iface, event_listener=self._on_event, hostname=hostname)
|
|
|
+ else:
|
|
|
+ self.dhcp = udhcpc.DHCPClient(iface, event_listener=self._on_event)
|
|
|
logger.info('Starting DHCPv4 client on %s in container namespace %s', iface['ifname'], \
|
|
|
self.dhcp.netns)
|
|
|
|