From 272eea0d555fb989223c46517d129bba7ff61557 Mon Sep 17 00:00:00 2001 From: Jack O'Sullivan Date: Fri, 23 Aug 2019 14:26:58 +0100 Subject: [PATCH] Remove IPAM driver and implement `CreateNetwork` / `DeleteNetwork` --- Makefile | 4 +-- config.json | 8 +++--- net-dhcp/__init__.py | 2 +- net-dhcp/ipam.py | 58 ------------------------------------------- net-dhcp/network.py | 59 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 65 insertions(+), 66 deletions(-) delete mode 100644 net-dhcp/ipam.py diff --git a/Makefile b/Makefile index ad40617..48b147b 100644 --- a/Makefile +++ b/Makefile @@ -30,10 +30,10 @@ debug: @docker run --rm -ti --cap-add CAP_SYS_ADMIN --network host --volume /run/docker/plugins:/run/docker/plugins \ --volume /run/docker.sock:/run/docker.sock ${PLUGIN_NAME}:rootfs -enable: +enable: @echo "### enable plugin ${PLUGIN_NAME}:${PLUGIN_TAG}" @docker plugin enable ${PLUGIN_NAME}:${PLUGIN_TAG} -push: clean rootfs create enable +push: clean rootfs create enable @echo "### push plugin ${PLUGIN_NAME}:${PLUGIN_TAG}" @docker plugin push ${PLUGIN_NAME}:${PLUGIN_TAG} diff --git a/config.json b/config.json index 7c6e6bf..5755ec8 100644 --- a/config.json +++ b/config.json @@ -3,8 +3,7 @@ "interface": { "socket": "net-dhcp.sock", "types": [ - "docker.networkdriver/1.0", - "docker.ipamdriver/1.0" + "docker.networkdriver/1.0" ] }, "entrypoint": [ "/opt/plugin/launch.sh" ], @@ -16,7 +15,10 @@ { "source": "/var/run/docker.sock", "destination": "/run/docker.sock", - "type": "bind" + "type": "bind", + "options": [ + "bind" + ] } ], "linux": { diff --git a/net-dhcp/__init__.py b/net-dhcp/__init__.py index 67345d2..731a37b 100644 --- a/net-dhcp/__init__.py +++ b/net-dhcp/__init__.py @@ -2,7 +2,7 @@ from flask import Flask, jsonify app = Flask(__name__) -from . import network, ipam +from . import network @app.errorhandler(404) def err_not_found(e): diff --git a/net-dhcp/ipam.py b/net-dhcp/ipam.py deleted file mode 100644 index 59da90c..0000000 --- a/net-dhcp/ipam.py +++ /dev/null @@ -1,58 +0,0 @@ -import itertools -import ipaddress -from os import path -import atexit - -import pyroute2 -import docker -from flask import jsonify - -from . import app - -ipdb = pyroute2.IPDB() -@atexit.register -def close_ipdb(): - ipdb.release() - -client = docker.from_env() -@atexit.register -def close_docker(): - client.close() - -def iface_addrs(iface): - return list(map(ipaddress.ip_interface, iface.ipaddr.ipv4)) -def iface_nets(iface): - return list(map(lambda n: n.network, map(ipaddress.ip_interface, iface.ipaddr.ipv4))) - -def get_bridges(): - 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'], client.networks.list()))))))) - - return list(filter(lambda i: i.kind == 'bridge' and not set(iface_nets(i)).intersection(reserved_nets), \ - map(lambda i: ipdb.interfaces[i], filter(lambda k: isinstance(k, str), ipdb.interfaces.keys())))) - -@app.route('/IpamDriver.GetCapabilities') -def ipam_get_capabilities(): - return jsonify({ - 'RequiresMACAddress': True, - 'RequiresRequestReplay': False - }) - -@app.route('/IpamDriver.GetDefaultAddressSpace') -def get_default_addr_space(): - bridges = get_bridges() - if not bridges: - return jsonify({'Err': 'No bridges available'}), 404 - - first = None - for b in bridges: - if b.ipaddr.ipv4: - first = b - if not first: - return jsonify({'Err': 'No bridges with addresses available'}), 404 - - return jsonify({ - 'LocalDefaultAddressSpace': f'{first.ifname}-{iface_addrs(first)[0].network}', - 'GlobalDefaultAddressSpace': None - }) diff --git a/net-dhcp/network.py b/net-dhcp/network.py index 620d0cf..18178f6 100644 --- a/net-dhcp/network.py +++ b/net-dhcp/network.py @@ -1,10 +1,65 @@ -from flask import jsonify +import itertools +import ipaddress +from os import path +import logging +import atexit + +import pyroute2 +import docker +from flask import request, jsonify from . import app -@app.route('/NetworkDriver.GetCapabilities') +BRIDGE_OPT = 'devplayer0.net-dhcp.bridge' + +logger = logging.getLogger('gunicorn.error') + +ipdb = pyroute2.IPDB() +@atexit.register +def close_ipdb(): + ipdb.release() + +client = docker.from_env() +@atexit.register +def close_docker(): + client.close() + +def iface_addrs(iface): + return list(map(ipaddress.ip_interface, iface.ipaddr.ipv4)) +def iface_nets(iface): + return list(map(lambda n: n.network, map(ipaddress.ip_interface, iface.ipaddr.ipv4))) + +def get_bridges(): + 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'], client.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: ipdb.interfaces[i], \ + filter(lambda k: isinstance(k, str), ipdb.interfaces.keys()))))) + +@app.route('/NetworkDriver.GetCapabilities', methods=['POST']) def net_get_capabilities(): return jsonify({ 'Scope': 'local', 'ConnectivityScope': 'global' }) + +@app.route('/NetworkDriver.CreateNetwork', methods=['POST']) +def create_net(): + if BRIDGE_OPT not in request.json['Options']: + return jsonify({'Err': 'No bridge provided'}), 400 + + desired = request.json[BRIDGE_OPT] + bridges = get_bridges() + if desired not in bridges: + return jsonify({'Err': f'Bridge "{desired}" not found (or the specified bridge is already used by Docker)'}), 400 + + if request.json['IPv6Data']: + return jsonify({'Err': 'IPv6 is currently unsupported'}), 400 + + return jsonify({}) + +@app.route('/NetworkDriver.DeleteNetwork', methods=['POST']) +def delete_net(): + return jsonify({}) \ No newline at end of file