From a616342e2e6643f8f41530686c7909ba74e2975e Mon Sep 17 00:00:00 2001 From: Jack O'Sullivan Date: Fri, 23 Aug 2019 12:55:35 +0100 Subject: [PATCH] Implement IpamDriver.GetDefaultAddressSpace --- Makefile | 10 ++++++++-- config.json | 7 +++++++ net-dhcp/ipam.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 ++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4519aa3..ad40617 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,17 @@ PLUGIN_NAME = devplayer0/net-dhcp PLUGIN_TAG ?= latest -all: clean rootfs create +all: clean rootfs build create clean: @echo "### rm ./plugin" @rm -rf ./plugin -rootfs: +build: @echo "### docker build: rootfs image with net-dhcp" @docker build -t ${PLUGIN_NAME}:rootfs . + +rootfs: @echo "### create rootfs directory in ./plugin/rootfs" @mkdir -p ./plugin/rootfs @docker create --name tmp ${PLUGIN_NAME}:rootfs @@ -24,6 +26,10 @@ create: @echo "### create new plugin ${PLUGIN_NAME}:${PLUGIN_TAG} from ./plugin" @docker plugin create ${PLUGIN_NAME}:${PLUGIN_TAG} ./plugin +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: @echo "### enable plugin ${PLUGIN_NAME}:${PLUGIN_TAG}" @docker plugin enable ${PLUGIN_NAME}:${PLUGIN_TAG} diff --git a/config.json b/config.json index 6b8f5c2..7c6e6bf 100644 --- a/config.json +++ b/config.json @@ -12,6 +12,13 @@ "network": { "type": "host" }, + "mounts": [ + { + "source": "/var/run/docker.sock", + "destination": "/run/docker.sock", + "type": "bind" + } + ], "linux": { "capabilities": [ "CAP_SYS_ADMIN" diff --git a/net-dhcp/ipam.py b/net-dhcp/ipam.py index 1f10bdf..3b91fde 100644 --- a/net-dhcp/ipam.py +++ b/net-dhcp/ipam.py @@ -1,10 +1,58 @@ +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 }) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f2ab4a0..5d73c45 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ flask==1.1.1 gunicorn==19.9.0 +pyroute2==0.5.6 +docker==4.0.2 \ No newline at end of file