From 621809c1c19b6181417d450b2741644e865178c6 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sat, 23 Jan 2016 15:03:06 +0100 Subject: [PATCH 01/40] Create dhcp python package --- marmoset/dhcp/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 marmoset/dhcp/__init__.py diff --git a/marmoset/dhcp/__init__.py b/marmoset/dhcp/__init__.py new file mode 100644 index 0000000..e69de29 From 2cd5741abaac1d8bf319b4cd092eca556375dbfe Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Sat, 23 Jan 2016 21:04:32 +0100 Subject: [PATCH 02/40] add docs for setting up isc-dhcp + ldap this implements https://github.com/virtapi/marmoset/issues/10 everything is a bit hacky and only for testing! I would not recommend using this ldap config in production. --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index 0b98d5d..5b08cc8 100644 --- a/README.md +++ b/README.md @@ -348,6 +348,49 @@ Remove a VM: 204 on success +## Setup LDAP + isc-dhcpd +We will do all this in a clean Arch nspawn container (login for a new container is always root without password): +```bash +mkdir marmoset_container +sudo pacman -Syu arch-install-scripts +sudo pacstrap -c -d marmoset_container +sudo systemd-nspawn -b -D marmoset_container/ +pacman -Syu git openldap +``` + +Installing the isc-dhcpd is currently a bit tricky because the default package is not linked against ldap. You can get the sources and replace the PKGBUILD with [mine](https://p.bastelfreak.de/U6f5/) or use the package that I [built](https://p.bastelfreak.de/C6An/). The needed data can be found [here](https://p.bastelfreak.de/t5XS/). Please adjust the params `suffix` and `rootdn` for your needs (in /etc/openldap/slapd.conf), we use `dc=example,dc=com` and `cn=root,dc=example,dc=com` in our example. + +```bash +curl -JO https://p.bastelfreak.de/C6An/ +pacman -U dhcp*.pkg.tar.xz +cd /etc/openldap/schema +curl -JO https://raw.githubusercontent.com/dcantrell/ldap-for-dhcp/master/dhcp.schema +cd .. +echo 'include /etc/openldap/schema/dhcp.schema' >> slapd.conf +echo 'index dhcpHWAddress eq' >> slapd.conf +echo 'index dhcpClassData eq' >> slapd.conf +cp DB_CONFIG.example /var/lib/openldap/openldap-data/DB_CONFIG +curl https://p.bastelfreak.de/j63 > initial_data.ldif +systemctl start slapd +ldapadd -x -W -D 'cn=root,dc=example,dc=com' -f initial_data.ldif -c +``` + +Last step, you need to add the following settings to your `/etc/dhcpd.conf` before you start the daemon: +``` +ldap-server "localhost"; +ldap-port 389; +ldap-username "cn=root, dc=example, dc=com"; +ldap-password "secret"; +ldap-base-dn "dc=example, dc=com"; +ldap-method dynamic; +ldap-debug-file "/var/log/dhcp-ldap-startup.log"; +``` + +```bash +systemctl start dhcpd4.service +``` + + ## Issues Find this code at [the git repo](https://www.github.com/virtapi/marmoset/). Find the original code at [the git repo](https://www.aibor.de/cgit/marmoset/). From 862e12260223e2d47ade720936d356dc81803043 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 00:17:29 +0100 Subject: [PATCH 03/40] Added base dhcp config --- marmoset/dhcp/dhcp_config.py | 18 +++++++ marmoset/dhcp/isc_dhcp_ldap_config.py | 2 + marmoset/webserver/dhcp.py | 70 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 marmoset/dhcp/dhcp_config.py create mode 100644 marmoset/dhcp/isc_dhcp_ldap_config.py create mode 100644 marmoset/webserver/dhcp.py diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py new file mode 100644 index 0000000..98474f5 --- /dev/null +++ b/marmoset/dhcp/dhcp_config.py @@ -0,0 +1,18 @@ +from .isc_dhcp_ldap_config import ISCDhcpLdapConfig + + +class DhcpConfig: + def __init__(self, mac, ip_address, gateway, dhcp_hostname): + self.mac = mac + self.ip_address = ip_address + self.gateway = gateway + self.dhcp_hostname = dhcp_hostname + + self.additional_statements = {} + + def add_additional_statement(self, key, value): + self.additional_statements[key] = value + + def create_isc_ldap(self): + isc_dhcp_config = ISCDhcpLdapConfig(self) + isc_dhcp_config.save() diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py new file mode 100644 index 0000000..1c11ea7 --- /dev/null +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -0,0 +1,2 @@ +class isc_dhcp_ldap_config: + \ No newline at end of file diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py new file mode 100644 index 0000000..08bb769 --- /dev/null +++ b/marmoset/webserver/dhcp.py @@ -0,0 +1,70 @@ +from flask import request, make_response +from flask.ext.restful import reqparse, Resource, url_for, abort +from werkzeug.exceptions import NotFound +from .. import dhcp + + +class DhcpCollection(Resource): + def get(self): + return [vars(c) for c in dhcp.DhcpConfig] + + def post(self): + pass + + +class DhcpIpv4Object(Resource): + def get(self, ipv4): + dhcp_config = dhcp.DhcpConfig(ipv4=ipv4) + + if dhcp_config.exists(): + return vars(dhcp_config) + else: + abort(404) + + def put(self, ipv4): + args = parser.parse_args(request) + + dhcp_config = dhcp.DhcpConfig(ipv4=ipv4) + + dhcp_config.create() + + location = url_for('dhcpipv4object', _method='GET', ipv4=dhcp_config.ipv4) + return vars(dhcp_config), 201, {'Location': location} + + def delete(self, ipv4): + dhcp_config = dhcp.DhcpConfig(ipv4=ipv4) + + if dhcp_config.exists(): + dhcp_config.remove() + return '', 204 + else: + abort(404) + + +class DhcpMacObject(Resource): + def get(self, mac): + dhcp_config = dhcp.DhcpConfig(mac=mac) + + if dhcp_config.exists(): + return vars(dhcp_config) + else: + abort(404) + + def put(self, mac): + args = parser.parse_args(request) + + dhcp_config = dhcp.DhcpConfig(mac) + + dhcp_config.create() + + location = url_for('dhcpmacobject', _method='GET', mac=dhcp_config.mac) + return vars(dhcp_config), 201, {'Location': location} + + def delete(self, mac): + dhcp_config = dhcp.DhcpConfig(mac) + + if dhcp_config.exists(): + dhcp_config.remove() + return '', 204 + else: + abort(404) \ No newline at end of file From 0f1392fdc20db0b4c3b6eccf1042732d0e4e6880 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 00:18:37 +0100 Subject: [PATCH 04/40] Some LDAP Testing, but @bastelfreak forced me to push! --- marmoset/dhcp/__init__.py | 1 + marmoset/dhcp/isc_dhcp_ldap_config.py | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/marmoset/dhcp/__init__.py b/marmoset/dhcp/__init__.py index e69de29..b55316b 100644 --- a/marmoset/dhcp/__init__.py +++ b/marmoset/dhcp/__init__.py @@ -0,0 +1 @@ +from .dhcp_config import DhcpConfig diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index 1c11ea7..cd89c70 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -1,2 +1,20 @@ -class isc_dhcp_ldap_config: - \ No newline at end of file +from ldap3 import Server, Connection, ALL +from datetime import datetime + + +class ISCDhcpLdapConfig: + def __init__(self, dhcp_config): + self.dhcp_config = dhcp_config + + def save(self): + server = Server('localhost', get_info=ALL) + conn = Connection(server, 'cn=root,dc=example,dc=com', 'secret', auto_bind=True) + + entry_attributes = {'dhcpHWAddress': "ethernet %s" % self.dhcp_config.mac, + 'dhcpStatements': ["fixed-address %s;" % self.dhcp_config.ip_address, + "option subnet-mask %s;" % '255.255.255.0', + "option routers %s;" % self.dhcp_config.gateway], + 'dhcpComments': "date=%s dhcp-hostname=%s" % (datetime.now().strftime("%Y%m%d_%H%M%S"), self.dhcp_config.dhcp_hostname)} + + conn.add("cn=%s,dc=example,dc=com" % self.dhcp_config.ip_address, 'dhcpHost', entry_attributes) + From 24d45ad18ec9de2ea286fa0c6dc9a6b13f67a73d Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 00:18:53 +0100 Subject: [PATCH 05/40] Add dhcp to webserver --- marmoset/webserver/__init__.py | 12 ++++++++++++ marmoset/webserver/dhcp.py | 24 +++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/marmoset/webserver/__init__.py b/marmoset/webserver/__init__.py index 2f9d48e..bc762a8 100644 --- a/marmoset/webserver/__init__.py +++ b/marmoset/webserver/__init__.py @@ -1,6 +1,7 @@ from flask import Flask, jsonify from flask.ext import restful from .flask import auth +import json API_VERSION = '1' config = None @@ -41,6 +42,17 @@ def app(config): api.add_resource(installimage.InstallimageObject, '/installimage/') api.add_resource(installimage.InstallimageConfigCommand, '/installimage//config') + if config['Modules'].getboolean('DHCP'): + from . import dhcp + + additional_statements_str = config['DHCPConfig'].get('additional_statements') + additional_statements = additional_statements_str.split(',') + dhcp.build_parameters(additional_statements) + + api.add_resource(dhcp.DhcpCollection, '/dhcp') + api.add_resource(dhcp.DhcpIpv4Object, '/dhcp/ipv4/') + api.add_resource(dhcp.DhcpMacObject, '/dhcp/mac/') + @app.errorhandler(404) def not_found(ex): resp = jsonify_nl(message="Route not found.", status=404) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 08bb769..d2c3f01 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -3,13 +3,35 @@ from werkzeug.exceptions import NotFound from .. import dhcp +parser = None + + +def build_parameters(additional_statements): + global parser + + parser = reqparse.RequestParser() + parser.add_argument('mac', type=str, required=True) + parser.add_argument('ip_address', type=str, required=True) + parser.add_argument('gateway', type=str, required=True) + parser.add_argument('dhcp_hostname', type=str, required=True) + + for additional_statement in additional_statements: + parser.add_argument(additional_statement, type=str, required=False) + class DhcpCollection(Resource): def get(self): return [vars(c) for c in dhcp.DhcpConfig] def post(self): - pass + args = parser.parse_args() + dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.dhcp_hostname) + + for args_item in parser.args: + if not args_item.required and args_item.name in args and args[args_item.name] is not None: + dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) + + dhcp_config.create_isc_ldap() class DhcpIpv4Object(Resource): From a87d715f1063d0dc1b925418428757bcda5d2819 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 00:22:49 +0100 Subject: [PATCH 06/40] Added pip requirements --- requirements.txt | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3839de1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,48 @@ +alabaster==0.7.7 +aniso8601==1.1.0 +Babel==2.1.1 +chardet==2.3.0 +configobj==5.0.6 +coverage==4.0.3 +decorator==4.0.6 +docutils==0.12 +Flask==0.10.1 +Flask-RESTful==0.3.5 +glob2==0.4.1 +ipykernel==4.2.2 +ipython==4.0.1 +ipython-genutils==0.1.0 +itsdangerous==0.24 +Jinja2==2.8 +jupyter-client==4.1.1 +jupyter-core==4.0.6 +ldap3==1.0.4 +libvirt-python==1.3.1 +livestreamer==1.12.2 +MarkupSafe==0.23 +ordereddict==1.1 +packaging==15.3 +path.py==8.1.2 +pexpect==4.0.1 +pickleshare==0.5 +ptyprocess==0.5 +pyasn1==0.1.9 +Pygments==2.0.2 +pyserial==2.7 +python-dateutil==2.4.2 +python-mpd==0.3.0 +python-mpd2==0.5.4 +pytz==2015.7 +pyxdg==0.25 +pyzmq==15.2.0 +requests==2.9.1 +simplegeneric==0.8.1 +six==1.10.0 +snowballstemmer==1.2.1 +Sphinx==1.3.4 +sphinx-rtd-theme==0.1.9 +traitlets==4.1.0b1 +urllib3==1.14 +virtualenv==13.1.2 +Werkzeug==0.11.3 +wheel==0.24.0 From 2fcd69c97d0d2837de2467c9fa6b7fdd2e6744d9 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 00:25:39 +0100 Subject: [PATCH 07/40] update pip requirements --- requirements.txt | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3839de1..1b4960e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,48 +1,14 @@ -alabaster==0.7.7 aniso8601==1.1.0 -Babel==2.1.1 -chardet==2.3.0 -configobj==5.0.6 -coverage==4.0.3 -decorator==4.0.6 -docutils==0.12 Flask==0.10.1 Flask-RESTful==0.3.5 -glob2==0.4.1 -ipykernel==4.2.2 -ipython==4.0.1 -ipython-genutils==0.1.0 itsdangerous==0.24 Jinja2==2.8 -jupyter-client==4.1.1 -jupyter-core==4.0.6 ldap3==1.0.4 libvirt-python==1.3.1 -livestreamer==1.12.2 MarkupSafe==0.23 -ordereddict==1.1 -packaging==15.3 -path.py==8.1.2 -pexpect==4.0.1 -pickleshare==0.5 -ptyprocess==0.5 pyasn1==0.1.9 -Pygments==2.0.2 -pyserial==2.7 python-dateutil==2.4.2 -python-mpd==0.3.0 -python-mpd2==0.5.4 pytz==2015.7 -pyxdg==0.25 -pyzmq==15.2.0 -requests==2.9.1 -simplegeneric==0.8.1 six==1.10.0 -snowballstemmer==1.2.1 -Sphinx==1.3.4 -sphinx-rtd-theme==0.1.9 -traitlets==4.1.0b1 -urllib3==1.14 -virtualenv==13.1.2 Werkzeug==0.11.3 wheel==0.24.0 From 1a58d13faef7cf2ce0ff42b3922a9aa563cde5ed Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 21:56:17 +0100 Subject: [PATCH 08/40] Added some validation functions for ip stuff (developed by @bastelfreak) --- marmoset/validation.py | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 marmoset/validation.py diff --git a/marmoset/validation.py b/marmoset/validation.py new file mode 100644 index 0000000..fbf4121 --- /dev/null +++ b/marmoset/validation.py @@ -0,0 +1,53 @@ +import ipaddress +import re + + +def is_mac(mac): + return True if re.match("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", mac) else False + + +def is_ipv4(ip): + try: + ipaddress.IPv4Address(ip) + except: + return False + return True + + +def is_ipv6(ip): + try: + ipaddress.IPv6Address(ip) + except: + return False + return True + + +def is_cidr(cidr): + try: + ipaddress.IPv4Interface(cidr) + except: + return False + return True + + +def get_cidr(cidr): + interface = ipaddress.IPv4Interface(cidr) + gateway = list(interface.network.hosts())[0] + return {'ip': interface.ip, + 'nm': interface.netmask, + 'gw': gateway} + + +def get_ip_from_cidr(cidr): + data = get_cidr(cidr) + return data['ip'] + + +def get_nm_from_cidr(cidr): + data = get_cidr(cidr) + return data['nm'] + + +def get_gw_from_cidr(cidr): + data = get_cidr(cidr) + return data['gw'] From 4a0b4fdf25883172477bbdf867983cbfed8336ee Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 21:57:04 +0100 Subject: [PATCH 09/40] Update Parameters for dhcp creation/update --- marmoset/webserver/dhcp.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index d2c3f01..e7a78bb 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -2,6 +2,7 @@ from flask.ext.restful import reqparse, Resource, url_for, abort from werkzeug.exceptions import NotFound from .. import dhcp +from marmoset import validation parser = None @@ -12,7 +13,8 @@ def build_parameters(additional_statements): parser = reqparse.RequestParser() parser.add_argument('mac', type=str, required=True) parser.add_argument('ip_address', type=str, required=True) - parser.add_argument('gateway', type=str, required=True) + parser.add_argument('gateway', type=str, required=False, default=None) + parser.add_argument('networkmask', type=str, required=False, default=None) parser.add_argument('dhcp_hostname', type=str, required=True) for additional_statement in additional_statements: @@ -25,7 +27,8 @@ def get(self): def post(self): args = parser.parse_args() - dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.dhcp_hostname) + + dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: if not args_item.required and args_item.name in args and args[args_item.name] is not None: From c366f7312b5e33df060c0ab5c8663077d1d6d1e8 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 21:57:56 +0100 Subject: [PATCH 10/40] Use networkmask and addentional statemetns from dhcp_config, not test data anymore --- marmoset/dhcp/isc_dhcp_ldap_config.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index cd89c70..db91336 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -10,10 +10,15 @@ def save(self): server = Server('localhost', get_info=ALL) conn = Connection(server, 'cn=root,dc=example,dc=com', 'secret', auto_bind=True) + dhcpStatements = ["fixed-address %s;" % self.dhcp_config.ip_address, + "option subnet-mask %s;" % self.dhcp_config.networkmask, + "option routers %s;" % self.dhcp_config.gateway] + + for additional_statement in self.dhcp_config.additional_statements: + dhcpStatements.append("%s %s;" % (additional_statement, self.dhcp_config.additional_statements[additional_statement])) + entry_attributes = {'dhcpHWAddress': "ethernet %s" % self.dhcp_config.mac, - 'dhcpStatements': ["fixed-address %s;" % self.dhcp_config.ip_address, - "option subnet-mask %s;" % '255.255.255.0', - "option routers %s;" % self.dhcp_config.gateway], + 'dhcpStatements': dhcpStatements, 'dhcpComments': "date=%s dhcp-hostname=%s" % (datetime.now().strftime("%Y%m%d_%H%M%S"), self.dhcp_config.dhcp_hostname)} conn.add("cn=%s,dc=example,dc=com" % self.dhcp_config.ip_address, 'dhcpHost', entry_attributes) From 75c6195ca95110ea2e527418e4f04949294f39d0 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 21:58:35 +0100 Subject: [PATCH 11/40] Added ip validation and ip, networkmask, gateway export from cidr notation --- marmoset/__init__.py | 2 +- marmoset/dhcp/__init__.py | 1 + marmoset/dhcp/dhcp_config.py | 21 ++++++++++++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/marmoset/__init__.py b/marmoset/__init__.py index 852d2f5..81a0b58 100644 --- a/marmoset/__init__.py +++ b/marmoset/__init__.py @@ -1,4 +1,4 @@ -from . import config, cli +from . import config, cli, validation def run(config_file = None): cfg = config.load(config_file) diff --git a/marmoset/dhcp/__init__.py b/marmoset/dhcp/__init__.py index b55316b..a84d16b 100644 --- a/marmoset/dhcp/__init__.py +++ b/marmoset/dhcp/__init__.py @@ -1 +1,2 @@ from .dhcp_config import DhcpConfig +from .isc_dhcp_ldap_config import ISCDhcpLdapConfig \ No newline at end of file diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py index 98474f5..cdc0922 100644 --- a/marmoset/dhcp/dhcp_config.py +++ b/marmoset/dhcp/dhcp_config.py @@ -1,12 +1,27 @@ from .isc_dhcp_ldap_config import ISCDhcpLdapConfig +from marmoset import validation +import socket class DhcpConfig: - def __init__(self, mac, ip_address, gateway, dhcp_hostname): + def __init__(self, mac, ip_address, gateway=None, networkmask=None): self.mac = mac - self.ip_address = ip_address + self.gateway = gateway - self.dhcp_hostname = dhcp_hostname + self.networkmask = networkmask + + if validation.is_cidr(ip_address): + self.ip_address = validation.get_ip_from_cidr(ip_address) + + if self.networkmask is None: + self.networkmask = validation.get_nm_from_cidr(ip_address) + + if self.gateway is None: + self.gateway = validation.get_gw_from_cidr(ip_address) + else: + self.ip_address = ip_address + + self.dhcp_hostname = socket.getfqdn() self.additional_statements = {} From 6d0f425a99e996b514d302704565f09738c4ce81 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 22:14:14 +0100 Subject: [PATCH 12/40] Remove dhcp hostname parameter The dhcp hostname will be set automated by socket.getfqdn() --- marmoset/webserver/dhcp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index e7a78bb..51b551a 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -15,7 +15,6 @@ def build_parameters(additional_statements): parser.add_argument('ip_address', type=str, required=True) parser.add_argument('gateway', type=str, required=False, default=None) parser.add_argument('networkmask', type=str, required=False, default=None) - parser.add_argument('dhcp_hostname', type=str, required=True) for additional_statement in additional_statements: parser.add_argument(additional_statement, type=str, required=False) From fa0a09498e2550aeb7403ab9f77c2b846ecd872c Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 22:33:15 +0100 Subject: [PATCH 13/40] Make __init__.py PEP8 conform --- marmoset/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/marmoset/__init__.py b/marmoset/__init__.py index 81a0b58..5668f0e 100644 --- a/marmoset/__init__.py +++ b/marmoset/__init__.py @@ -1,6 +1,9 @@ -from . import config, cli, validation +from marmoset import config +from marmoset import cli +from marmoset import validation -def run(config_file = None): + +def run(config_file=None): cfg = config.load(config_file) cli.parse(cfg) From 84af84ab9d0be7bc1fe6c313c38e02771ea06067 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 22:34:13 +0100 Subject: [PATCH 14/40] cleanup imports --- marmoset/webserver/dhcp.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 51b551a..d66fb0a 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -1,8 +1,6 @@ -from flask import request, make_response +from flask import request from flask.ext.restful import reqparse, Resource, url_for, abort -from werkzeug.exceptions import NotFound -from .. import dhcp -from marmoset import validation +from marmoset import dhcp parser = None From 730854f8116cda861af348c3608f5daa6f3c6a57 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Wed, 27 Jan 2016 22:35:46 +0100 Subject: [PATCH 15/40] Make marmoset/webserver/dhcp.py imports PEP8 conform --- marmoset/webserver/dhcp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index d66fb0a..b35d68d 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -1,5 +1,8 @@ from flask import request -from flask.ext.restful import reqparse, Resource, url_for, abort +from flask.ext.restful import reqparse +from flask.ext.restful import Resource +from flask.ext.restful import url_for +from flask.ext.restful import abort from marmoset import dhcp parser = None From 5766b155bf6bc685fd7a01674a4269d70800b38c Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 01:59:10 +0100 Subject: [PATCH 16/40] Added Methods to get LDAP DHCP Information * You can delete LDAP Entries by DN * You can get LDAP DN by IP and by MAC * You can get all objects of class "dhcpHost" --- marmoset/dhcp/isc_dhcp_ldap_config.py | 145 ++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 6 deletions(-) diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index db91336..3818560 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -1,25 +1,158 @@ -from ldap3 import Server, Connection, ALL +from ldap3 import Server, Connection, ALL, SUBTREE, BASE, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES from datetime import datetime +from marmoset import config as config_reader + +import re + +config = config_reader.load() class ISCDhcpLdapConfig: def __init__(self, dhcp_config): self.dhcp_config = dhcp_config + @staticmethod + def __get_server_connection(): + server = Server(config['DHCPConfig'].get('ldap_server'), + port=int(config['DHCPConfig'].get('ldap_port')), + get_info=ALL) + + conn = Connection(server, + config['DHCPConfig'].get('ldap_bind_dn'), + config['DHCPConfig'].get('ldap_passwort'), + auto_bind=True) + + return conn + def save(self): - server = Server('localhost', get_info=ALL) - conn = Connection(server, 'cn=root,dc=example,dc=com', 'secret', auto_bind=True) + conn = self.__get_server_connection() dhcpStatements = ["fixed-address %s;" % self.dhcp_config.ip_address, "option subnet-mask %s;" % self.dhcp_config.networkmask, "option routers %s;" % self.dhcp_config.gateway] for additional_statement in self.dhcp_config.additional_statements: - dhcpStatements.append("%s %s;" % (additional_statement, self.dhcp_config.additional_statements[additional_statement])) + dhcpStatements.append("%s %s;" % (additional_statement, + self.dhcp_config.additional_statements[additional_statement])) entry_attributes = {'dhcpHWAddress': "ethernet %s" % self.dhcp_config.mac, 'dhcpStatements': dhcpStatements, - 'dhcpComments': "date=%s dhcp-hostname=%s" % (datetime.now().strftime("%Y%m%d_%H%M%S"), self.dhcp_config.dhcp_hostname)} + 'dhcpComments': "date=%s dhcp-hostname=%s" % (datetime.now().strftime("%Y%m%d_%H%M%S"), + self.dhcp_config.dhcp_hostname)} + + conn.add("cn=%s,%s" % (self.dhcp_config.ip_address, config['DHCPConfig'].get('ldap_client_base_dn')), + 'dhcpHost', + entry_attributes) + + @staticmethod + def all(): + conn = ISCDhcpLdapConfig.__get_server_connection() + + entry_generator = conn.extend.standard.paged_search(search_base=config['DHCPConfig'].get('ldap_client_base_dn'), + search_filter='(objectClass=dhcpHost)', + search_scope=SUBTREE, + attributes=['cn'], + paged_size=5, + generator=True) + result = [] + for entry in entry_generator: + result.append(ISCDhcpLdapConfig.get_by_ip(entry['attributes']['cn'][0])) + + return result + + @staticmethod + def __get_dn_by_ipv4(ip_address): + conn = ISCDhcpLdapConfig.__get_server_connection() + conn.search(search_base=config['DHCPConfig'].get('ldap_client_base_dn'), + search_filter='(cn=%s)' % ip_address, + search_scope=SUBTREE, + paged_size=5, + attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES]) + + entries = conn.response + + if len(entries) == 0: + return None + + return entries[0]['dn'] + + @staticmethod + def __get_dn_by_mac(mac_address): + conn = ISCDhcpLdapConfig.__get_server_connection() + conn.search(search_base=config['DHCPConfig'].get('ldap_client_base_dn'), + search_filter='(dhcpHWAddress=ethernet %s)' % mac_address, + search_scope=SUBTREE, + paged_size=5, + attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES]) + + entries = conn.response + + if len(entries) == 0: + return None + + return entries[0]['dn'] + + @staticmethod + def __get_dhcp_config(dn): + from marmoset.dhcp import DhcpConfig + + conn = ISCDhcpLdapConfig.__get_server_connection() + conn.search(search_base=dn, + search_filter='(objectClass=dhcpHost)', + search_scope=SUBTREE, + paged_size=5, + attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES]) + + entries = conn.response + + if len(entries) == 0: + return None + + mac_option = str(entries[0]['attributes']['dhcpHWAddress']) + + regex_gateway = 'option routers\s+([0-9]+.[0-9]+.[0-9]+.[0-9]+)' + regex_networkmask = 'option subnet-mask\s+([0-9]+.[0-9]+.[0-9]+.[0-9]+)' + + mac = re.search('(([0-9a-f]{2}:){5}[0-9a-f]{2})', mac_option).group(0) + ip = entries[0]['attributes']['cn'][0] + + gateway = None + networkmask = None + + for dhcpStatement in entries[0]['attributes']['dhcpStatements']: + if re.match(regex_gateway, dhcpStatement): + gateway = re.search(regex_gateway, dhcpStatement).group(1) + + if re.match(regex_networkmask, dhcpStatement): + networkmask = re.search(regex_networkmask, dhcpStatement).group(1) + + dhcp_config = DhcpConfig(mac, ip, gateway, networkmask) + + additional_statements_str = config['DHCPConfig'].get('additional_statements') + additional_statements = additional_statements_str.split(',') + + for ldap_additional_statement in entries[0]['attributes']['dhcpStatements']: + for additional_statement in additional_statements: + regex_additional_statement = '%s\s+(.*);' % additional_statement + + if re.match(regex_additional_statement, ldap_additional_statement): + value = re.search(regex_additional_statement, ldap_additional_statement).group(1) + dhcp_config.add_additional_statement(additional_statement, value) + + return dhcp_config + + @staticmethod + def get_by_ip(ip_address): + return ISCDhcpLdapConfig.__get_dhcp_config(ISCDhcpLdapConfig.__get_dn_by_ipv4(ip_address)) + + @staticmethod + def get_by_mac(mac_address): + return ISCDhcpLdapConfig.__get_dhcp_config(ISCDhcpLdapConfig.__get_dn_by_mac(mac_address)) - conn.add("cn=%s,dc=example,dc=com" % self.dhcp_config.ip_address, 'dhcpHost', entry_attributes) + @staticmethod + def remove(ipv4): + dn = ISCDhcpLdapConfig.__get_dn_by_ipv4(ipv4) + conn = ISCDhcpLdapConfig.__get_server_connection() + conn.delete(dn) + conn.unbind() From a193eaefeb87b2a376ee0b2d448539894cffcef1 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 02:01:06 +0100 Subject: [PATCH 17/40] Added Methods to dhcp_config.py * Added method to check if ipv4/mac configuration exists * Added delete Method * Added get method for ipv4 and mac * Added a all method to get all stored configurations --- marmoset/dhcp/dhcp_config.py | 43 ++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py index cdc0922..a893156 100644 --- a/marmoset/dhcp/dhcp_config.py +++ b/marmoset/dhcp/dhcp_config.py @@ -5,10 +5,24 @@ class DhcpConfig: def __init__(self, mac, ip_address, gateway=None, networkmask=None): + self.additional_statements = {} + + self.mac = None + self.ip_address = None + self.gateway = None + self.networkmask = None + self.dhcp_hostname = None + + self.set_settings(True, mac, ip_address, gateway, networkmask) + + def set_settings(self, set_not_required_if_none=False, mac=None, ip_address=None, gateway=None, networkmask=None): self.mac = mac - self.gateway = gateway - self.networkmask = networkmask + if gateway is not None or set_not_required_if_none: + self.gateway = gateway + + if networkmask is not None or set_not_required_if_none: + self.networkmask = networkmask if validation.is_cidr(ip_address): self.ip_address = validation.get_ip_from_cidr(ip_address) @@ -23,11 +37,32 @@ def __init__(self, mac, ip_address, gateway=None, networkmask=None): self.dhcp_hostname = socket.getfqdn() - self.additional_statements = {} - def add_additional_statement(self, key, value): self.additional_statements[key] = value def create_isc_ldap(self): isc_dhcp_config = ISCDhcpLdapConfig(self) isc_dhcp_config.save() + + def remove(self): + return ISCDhcpLdapConfig.remove(self.ip_address) is not None + + @staticmethod + def all(): + return ISCDhcpLdapConfig.all() + + @staticmethod + def get_by_ip(ip_address): + return ISCDhcpLdapConfig.get_by_ip(ip_address) + + @staticmethod + def get_by_mac(mac): + return ISCDhcpLdapConfig.get_by_mac(mac) + + @staticmethod + def exists_ipv4(ip_address): + return ISCDhcpLdapConfig.get_by_ip(ip_address) is not None + + @staticmethod + def exists_mac(mac_address): + return ISCDhcpLdapConfig.get_by_mac(mac_address) is not None From 4dc70c9c15fa85ea113d403d2aeee4baf7232e59 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 10:14:01 +0100 Subject: [PATCH 18/40] Fix: Check in is_cidr that the given parameter contains a '/' --- marmoset/validation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/marmoset/validation.py b/marmoset/validation.py index fbf4121..1781ce3 100644 --- a/marmoset/validation.py +++ b/marmoset/validation.py @@ -24,6 +24,9 @@ def is_ipv6(ip): def is_cidr(cidr): try: + if "/" not in cidr: + return False + ipaddress.IPv4Interface(cidr) except: return False From 677907e95b27fcb45358e32161238675d408de68 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 10:16:21 +0100 Subject: [PATCH 19/40] Added correct implementation and checks of dhcp endpoint * /dhcp endoint have GET,POST * /dhcp/ipv4/ endpoint have GET,PUT,DELETE * /dhcp/mac/ endpoint have GET,PUT,DELETE * The gateway and networkmask arguments are not requires anymore --- marmoset/webserver/dhcp.py | 105 +++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index b35d68d..b8d32c4 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -4,26 +4,26 @@ from flask.ext.restful import url_for from flask.ext.restful import abort from marmoset import dhcp +from marmoset import config as config_reader -parser = None +config = config_reader.load() +additional_statements_str = config['DHCPConfig'].get('additional_statements') +additional_statements = additional_statements_str.split(',') -def build_parameters(additional_statements): - global parser +parser = reqparse.RequestParser() +parser.add_argument('mac', type=str, required=True) +parser.add_argument('ip_address', type=str, required=True) +parser.add_argument('gateway', type=str, required=False, default=None) +parser.add_argument('networkmask', type=str, required=False, default=None) - parser = reqparse.RequestParser() - parser.add_argument('mac', type=str, required=True) - parser.add_argument('ip_address', type=str, required=True) - parser.add_argument('gateway', type=str, required=False, default=None) - parser.add_argument('networkmask', type=str, required=False, default=None) - - for additional_statement in additional_statements: - parser.add_argument(additional_statement, type=str, required=False) +for additional_statement in additional_statements: + parser.add_argument(additional_statement, type=str, required=False) class DhcpCollection(Resource): def get(self): - return [vars(c) for c in dhcp.DhcpConfig] + return [vars(c) for c in dhcp.DhcpConfig.all()] def post(self): args = parser.parse_args() @@ -31,65 +31,90 @@ def post(self): dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: - if not args_item.required and args_item.name in args and args[args_item.name] is not None: + if not args_item.required and args_item.name in args and args[args_item.name] is not None and args_item.name is not 'gateway' and args_item.name is not 'networkmask': dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) dhcp_config.create_isc_ldap() + location = url_for('dhcpipv4object', _method='GET', ipv4=dhcp_config.ip_address) + return vars(dhcp_config), 201, {'Location': location} + class DhcpIpv4Object(Resource): def get(self, ipv4): - dhcp_config = dhcp.DhcpConfig(ipv4=ipv4) + if not dhcp.DhcpConfig.exists_ipv4(ipv4): + return abort(404) - if dhcp_config.exists(): - return vars(dhcp_config) - else: - abort(404) + dhcp_config = dhcp.DhcpConfig.get_by_ip(ipv4) + + return vars(dhcp_config) def put(self, ipv4): args = parser.parse_args(request) - dhcp_config = dhcp.DhcpConfig(ipv4=ipv4) + if not dhcp.DhcpConfig.exists_ipv4(ipv4): + return abort(404) + + dhcp_config = dhcp.DhcpConfig.get_by_ip(ipv4) + + dhcp_config.set_settings(False, args.mac, args.ip_address, args.gateway, args.networkmask) - dhcp_config.create() + for args_item in parser.args: + if not args_item.required and args_item.name in args and args[args_item.name] is not None and args_item.name is not 'gateway' and args_item.name is not 'networkmask': + dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) + + dhcp_config.remove() + + dhcp_config.create_isc_ldap() - location = url_for('dhcpipv4object', _method='GET', ipv4=dhcp_config.ipv4) + location = url_for('dhcpipv4object', _method='GET', ipv4=dhcp_config.ip_address) return vars(dhcp_config), 201, {'Location': location} def delete(self, ipv4): - dhcp_config = dhcp.DhcpConfig(ipv4=ipv4) + if not dhcp.DhcpConfig.exists_ipv4(ipv4): + return abort(404) - if dhcp_config.exists(): - dhcp_config.remove() - return '', 204 - else: - abort(404) + dhcp_config = dhcp.DhcpConfig.get_by_ip(ipv4) + dhcp_config.remove() + + return '', 204 class DhcpMacObject(Resource): def get(self, mac): - dhcp_config = dhcp.DhcpConfig(mac=mac) + dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) + + if not dhcp.DhcpConfig.exists_mac(mac): + return abort(404) - if dhcp_config.exists(): - return vars(dhcp_config) - else: - abort(404) + return vars(dhcp_config) def put(self, mac): args = parser.parse_args(request) - dhcp_config = dhcp.DhcpConfig(mac) + if not dhcp.DhcpConfig.exists_mac(mac): + return abort(404) + + dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) + + dhcp_config.set_settings(False, args.mac, args.ip_address, args.gateway, args.networkmask) + + for args_item in parser.args: + if not args_item.required and args_item.name in args and args[args_item.name] is not None and args_item.name is not 'gateway' and args_item.name is not 'networkmask': + dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) - dhcp_config.create() + dhcp_config.remove() + + dhcp_config.create_isc_ldap() location = url_for('dhcpmacobject', _method='GET', mac=dhcp_config.mac) return vars(dhcp_config), 201, {'Location': location} def delete(self, mac): - dhcp_config = dhcp.DhcpConfig(mac) + if not dhcp.DhcpConfig.exists_mac(mac): + return abort(404) + + dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) + dhcp_config.remove() - if dhcp_config.exists(): - dhcp_config.remove() - return '', 204 - else: - abort(404) \ No newline at end of file + return '', 204 From 5b28305f464968d91847876315ed7ca77b222f61 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 10:25:54 +0100 Subject: [PATCH 20/40] Remove the additional Statement for dhcp endpoint from webserver __init__.py --- marmoset/webserver/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/marmoset/webserver/__init__.py b/marmoset/webserver/__init__.py index bc762a8..556e1c2 100644 --- a/marmoset/webserver/__init__.py +++ b/marmoset/webserver/__init__.py @@ -45,10 +45,6 @@ def app(config): if config['Modules'].getboolean('DHCP'): from . import dhcp - additional_statements_str = config['DHCPConfig'].get('additional_statements') - additional_statements = additional_statements_str.split(',') - dhcp.build_parameters(additional_statements) - api.add_resource(dhcp.DhcpCollection, '/dhcp') api.add_resource(dhcp.DhcpIpv4Object, '/dhcp/ipv4/') api.add_resource(dhcp.DhcpMacObject, '/dhcp/mac/') From 34ab1f708a704292be44c4e40bce26491629541f Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 10:27:23 +0100 Subject: [PATCH 21/40] Added a variable for the config So we can import this config variable from other files --- marmoset/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/marmoset/app.py b/marmoset/app.py index 375a4ab..a5813fd 100644 --- a/marmoset/app.py +++ b/marmoset/app.py @@ -1,4 +1,6 @@ from . import config, webserver -app = webserver.app(config.load()) +config = config.load() + +app = webserver.app(config) From 53a7ddbbb4b2dfc621aa017e77bae05f7553090f Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 10:53:41 +0100 Subject: [PATCH 22/40] Change aweful variable name 'set_not_required_if_none' to 'allow_none_value_for_not_required_parameter' --- marmoset/dhcp/dhcp_config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py index a893156..ebe288b 100644 --- a/marmoset/dhcp/dhcp_config.py +++ b/marmoset/dhcp/dhcp_config.py @@ -15,13 +15,13 @@ def __init__(self, mac, ip_address, gateway=None, networkmask=None): self.set_settings(True, mac, ip_address, gateway, networkmask) - def set_settings(self, set_not_required_if_none=False, mac=None, ip_address=None, gateway=None, networkmask=None): + def set_settings(self, allow_none_value_for_not_required_parameter=False, mac=None, ip_address=None, gateway=None, networkmask=None): self.mac = mac - if gateway is not None or set_not_required_if_none: + if gateway is not None or allow_none_value_for_not_required_parameter: self.gateway = gateway - if networkmask is not None or set_not_required_if_none: + if networkmask is not None or allow_none_value_for_not_required_parameter: self.networkmask = networkmask if validation.is_cidr(ip_address): From 9b9ec41447c2fe800b1377cad560e6fbd6bd92b9 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Fri, 5 Feb 2016 11:41:50 +0100 Subject: [PATCH 23/40] Add Parameter check for creation of new configurations Now you have to set networkmask and gateway or you have to use CIDR notation for the ip_address parameter --- marmoset/webserver/dhcp.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index b8d32c4..42faf17 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -5,6 +5,7 @@ from flask.ext.restful import abort from marmoset import dhcp from marmoset import config as config_reader +from marmoset import validation config = config_reader.load() @@ -28,6 +29,9 @@ def get(self): def post(self): args = parser.parse_args() + if (args.gateway is None or args.networkmask is None) and not validation.is_cidr(args.ip_address): + return 'missing parameter gateway and networkmask or give an ip address in CIDR notation', 406 + dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: From f9eb97b6f83a85473ffeda3c50dc6c2f251be137 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 02:28:55 +0100 Subject: [PATCH 24/40] Added some validation of parameters For the dhcp endpoints i implemented checks for mac and ipv4 adresses --- marmoset/webserver/dhcp.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 42faf17..2cb4571 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -32,6 +32,12 @@ def post(self): if (args.gateway is None or args.networkmask is None) and not validation.is_cidr(args.ip_address): return 'missing parameter gateway and networkmask or give an ip address in CIDR notation', 406 + if not validation.is_ipv4(args.ipaddress): + return 'please provide a valid ipv4 address', 406 + + if not validation.is_mac(args.mac): + return 'please provide a valid mac address', 406 + dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: @@ -46,6 +52,9 @@ def post(self): class DhcpIpv4Object(Resource): def get(self, ipv4): + if not validation.is_ipv4(ipv4): + return 'please provide a valid ipv4 address', 406 + if not dhcp.DhcpConfig.exists_ipv4(ipv4): return abort(404) @@ -56,6 +65,9 @@ def get(self, ipv4): def put(self, ipv4): args = parser.parse_args(request) + if not validation.is_ipv4(ipv4): + return 'please provide a valid ipv4 address', 406 + if not dhcp.DhcpConfig.exists_ipv4(ipv4): return abort(404) @@ -75,6 +87,9 @@ def put(self, ipv4): return vars(dhcp_config), 201, {'Location': location} def delete(self, ipv4): + if not validation.is_ipv4(ipv4): + return 'please provide a valid ipv4 address', 406 + if not dhcp.DhcpConfig.exists_ipv4(ipv4): return abort(404) @@ -86,6 +101,9 @@ def delete(self, ipv4): class DhcpMacObject(Resource): def get(self, mac): + if not validation.is_mac(mac): + return 'please provide a valid mac address', 406 + dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) if not dhcp.DhcpConfig.exists_mac(mac): @@ -96,6 +114,9 @@ def get(self, mac): def put(self, mac): args = parser.parse_args(request) + if not validation.is_mac(mac): + return 'please provide a valid mac address', 406 + if not dhcp.DhcpConfig.exists_mac(mac): return abort(404) @@ -115,6 +136,9 @@ def put(self, mac): return vars(dhcp_config), 201, {'Location': location} def delete(self, mac): + if not validation.is_mac(mac): + return 'please provide a valid mac address', 406 + if not dhcp.DhcpConfig.exists_mac(mac): return abort(404) From 007c6648e66606305be81195f1a46c5fda967c22 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 02:46:42 +0100 Subject: [PATCH 25/40] Fix conversion of ip objects The IP Objects are not json serializable, so we cast it to string --- marmoset/validation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/marmoset/validation.py b/marmoset/validation.py index 1781ce3..158ed84 100644 --- a/marmoset/validation.py +++ b/marmoset/validation.py @@ -36,9 +36,9 @@ def is_cidr(cidr): def get_cidr(cidr): interface = ipaddress.IPv4Interface(cidr) gateway = list(interface.network.hosts())[0] - return {'ip': interface.ip, - 'nm': interface.netmask, - 'gw': gateway} + return {'ip': str(interface.ip), + 'nm': str(interface.netmask), + 'gw': str(gateway)} def get_ip_from_cidr(cidr): From 8cd88d37b8ac621b709c85b5b3916f3678731991 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 02:49:05 +0100 Subject: [PATCH 26/40] Added [Common] with FQDN option section to config. * You can set in the [Common] Section in the config the FQDN value for the hostname that will be used for configs. (if not provided, it will get by socket.fqdn()) --- marmoset/config.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/marmoset/config.py b/marmoset/config.py index 82ddf04..3840d4e 100644 --- a/marmoset/config.py +++ b/marmoset/config.py @@ -1,11 +1,17 @@ from os import path -import configparser, warnings +import configparser +import warnings +import socket PATH = path.join(path.dirname(__file__), '../marmoset.conf') def default(): config = configparser.ConfigParser() + config['Common'] = dict( + FQDN=socket.getfqdn() + ) + config['Modules'] = dict( Webserver = 'True', PXE = 'True', From b3c514a20325fd971b168c57d58e4f2edebf0086 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 02:50:57 +0100 Subject: [PATCH 27/40] Use the new FQDN Config var for dhcp_hostname in dhcp_config --- marmoset/dhcp/dhcp_config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py index ebe288b..e905170 100644 --- a/marmoset/dhcp/dhcp_config.py +++ b/marmoset/dhcp/dhcp_config.py @@ -1,7 +1,8 @@ from .isc_dhcp_ldap_config import ISCDhcpLdapConfig from marmoset import validation -import socket +from marmoset import config as config_reader +config = config_reader.load() class DhcpConfig: def __init__(self, mac, ip_address, gateway=None, networkmask=None): @@ -35,7 +36,7 @@ def set_settings(self, allow_none_value_for_not_required_parameter=False, mac=No else: self.ip_address = ip_address - self.dhcp_hostname = socket.getfqdn() + self.dhcp_hostname = config['Common'].get('FQDN') def add_additional_statement(self, key, value): self.additional_statements[key] = value From 4bb3d6f336852e562e8402fedd0c360c81c7b4e4 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 02:51:32 +0100 Subject: [PATCH 28/40] Fix validation of ipv4 the validation checks now for ipv4 or ipv4 cidr notation --- marmoset/webserver/dhcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 2cb4571..08674c5 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -32,7 +32,7 @@ def post(self): if (args.gateway is None or args.networkmask is None) and not validation.is_cidr(args.ip_address): return 'missing parameter gateway and networkmask or give an ip address in CIDR notation', 406 - if not validation.is_ipv4(args.ipaddress): + if not validation.is_ipv4(args.ip_address) and not validation.is_cidr(args.ip_address): return 'please provide a valid ipv4 address', 406 if not validation.is_mac(args.mac): From 94aa64762ceeb3c86198d52392646c69908c6999 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 10:19:37 +0100 Subject: [PATCH 29/40] Fix PEP8 issues for DHCP Endpoint stuff --- marmoset/dhcp/dhcp_config.py | 4 +++- marmoset/webserver/dhcp.py | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py index e905170..df50f32 100644 --- a/marmoset/dhcp/dhcp_config.py +++ b/marmoset/dhcp/dhcp_config.py @@ -4,6 +4,7 @@ config = config_reader.load() + class DhcpConfig: def __init__(self, mac, ip_address, gateway=None, networkmask=None): self.additional_statements = {} @@ -16,7 +17,8 @@ def __init__(self, mac, ip_address, gateway=None, networkmask=None): self.set_settings(True, mac, ip_address, gateway, networkmask) - def set_settings(self, allow_none_value_for_not_required_parameter=False, mac=None, ip_address=None, gateway=None, networkmask=None): + def set_settings(self, allow_none_value_for_not_required_parameter=False, mac=None, ip_address=None, + gateway=None, networkmask=None): self.mac = mac if gateway is not None or allow_none_value_for_not_required_parameter: diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 08674c5..44c30af 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -41,7 +41,8 @@ def post(self): dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: - if not args_item.required and args_item.name in args and args[args_item.name] is not None and args_item.name is not 'gateway' and args_item.name is not 'networkmask': + if not args_item.required and args_item.name in args and args[args_item.name] is not None and \ + args_item.name is not 'gateway' and args_item.name is not 'networkmask': dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) dhcp_config.create_isc_ldap() @@ -76,7 +77,8 @@ def put(self, ipv4): dhcp_config.set_settings(False, args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: - if not args_item.required and args_item.name in args and args[args_item.name] is not None and args_item.name is not 'gateway' and args_item.name is not 'networkmask': + if not args_item.required and args_item.name in args and args[args_item.name] is not None and \ + args_item.name is not 'gateway' and args_item.name is not 'networkmask': dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) dhcp_config.remove() @@ -125,7 +127,8 @@ def put(self, mac): dhcp_config.set_settings(False, args.mac, args.ip_address, args.gateway, args.networkmask) for args_item in parser.args: - if not args_item.required and args_item.name in args and args[args_item.name] is not None and args_item.name is not 'gateway' and args_item.name is not 'networkmask': + if not args_item.required and args_item.name in args and args[args_item.name] is not None and \ + args_item.name is not 'gateway' and args_item.name is not 'networkmask': dhcp_config.add_additional_statement(args_item.name, args[args_item.name]) dhcp_config.remove() From f8d9fb78e911e8a87949770590a09f8437c7c563 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 13:13:56 +0100 Subject: [PATCH 30/40] Added check for empty response of ldap --- marmoset/dhcp/isc_dhcp_ldap_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index 3818560..0d8e918 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -71,7 +71,7 @@ def __get_dn_by_ipv4(ip_address): entries = conn.response - if len(entries) == 0: + if entries is None or len(entries) == 0: return None return entries[0]['dn'] @@ -87,7 +87,7 @@ def __get_dn_by_mac(mac_address): entries = conn.response - if len(entries) == 0: + if entries is None or len(entries) == 0: return None return entries[0]['dn'] From 9c3351ec5c7cc4662c6113f4445f4e8c19d6b4fc Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 21:46:06 +0100 Subject: [PATCH 31/40] Fix: Check DN is not none before searching in ldap --- marmoset/dhcp/isc_dhcp_ldap_config.py | 14 ++++++++++++-- marmoset/webserver/dhcp.py | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index 3818560..07abccb 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -143,11 +143,21 @@ def __get_dhcp_config(dn): @staticmethod def get_by_ip(ip_address): - return ISCDhcpLdapConfig.__get_dhcp_config(ISCDhcpLdapConfig.__get_dn_by_ipv4(ip_address)) + dn = ISCDhcpLdapConfig.__get_dn_by_ipv4(ip_address) + + if dn is None: + return None + + return ISCDhcpLdapConfig.__get_dhcp_config(dn) @staticmethod def get_by_mac(mac_address): - return ISCDhcpLdapConfig.__get_dhcp_config(ISCDhcpLdapConfig.__get_dn_by_mac(mac_address)) + dn = ISCDhcpLdapConfig.__get_dn_by_mac(mac_address) + + if dn is None: + return None + + return ISCDhcpLdapConfig.__get_dhcp_config(dn) @staticmethod def remove(ipv4): diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 44c30af..0ec7973 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -106,11 +106,11 @@ def get(self, mac): if not validation.is_mac(mac): return 'please provide a valid mac address', 406 - dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) - if not dhcp.DhcpConfig.exists_mac(mac): return abort(404) + dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) + return vars(dhcp_config) def put(self, mac): From 3775e5e53bac9036321255538d5d7d420fd09d22 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sun, 7 Feb 2016 21:50:33 +0100 Subject: [PATCH 32/40] Fix validation of mac addresses Now it is possible to user upper- or lower- or mixedcase mac addresses --- marmoset/validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marmoset/validation.py b/marmoset/validation.py index 158ed84..5d48516 100644 --- a/marmoset/validation.py +++ b/marmoset/validation.py @@ -3,7 +3,7 @@ def is_mac(mac): - return True if re.match("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", mac) else False + return True if re.match("^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$", mac) else False def is_ipv4(ip): From afc87e1531a603b8cf83fdfa825e0decd7e4afca Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Thu, 11 Feb 2016 11:22:03 +0100 Subject: [PATCH 33/40] add info about our own nspawn image --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 5b08cc8..45ca8d9 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,18 @@ ldap-debug-file "/var/log/dhcp-ldap-startup.log"; systemctl start dhcpd4.service ``` +We also provide a prepacked nspawn container. It has a working openldap + DHCP server, +the openldap is configured to start at boot. Systemd is so awesome that it supports downloading the tar, +so no fiddeling with curl/wget. machinectl will throw the image into a btrfs subvol and requires you to run /var/lib/machines on btrfs: +```bash +pacman -Syu btrfs-progs +modprobe loop +machinectl pull-tar https://p.bastelfreak.de/g5Wocw/ marmoset_container +machinectl start marmoset_container +machinectl login marmoset_container +``` + +If you don't run btrfs you can still download the tar to /var/lib/machines, extract it by hand and then continue with the machinectl commands (or start it oldschool like with systemd-nspawn). ## Issues From b236da1e7e123298f11359b573d27058bd59b30c Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Thu, 11 Feb 2016 11:22:03 +0100 Subject: [PATCH 34/40] update URL to containeupdate URL to containerr --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45ca8d9..b607ece 100644 --- a/README.md +++ b/README.md @@ -396,7 +396,7 @@ so no fiddeling with curl/wget. machinectl will throw the image into a btrfs sub ```bash pacman -Syu btrfs-progs modprobe loop -machinectl pull-tar https://p.bastelfreak.de/g5Wocw/ marmoset_container +machinectl pull-tar https://bastelfreak.de/marmoset_container.tar marmoset_container machinectl start marmoset_container machinectl login marmoset_container ``` From f89aad743db678467780c367efba444b2fd31236 Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Thu, 11 Feb 2016 11:22:03 +0100 Subject: [PATCH 35/40] add --verify=no param to machinectl --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b607ece..45803e9 100644 --- a/README.md +++ b/README.md @@ -396,7 +396,7 @@ so no fiddeling with curl/wget. machinectl will throw the image into a btrfs sub ```bash pacman -Syu btrfs-progs modprobe loop -machinectl pull-tar https://bastelfreak.de/marmoset_container.tar marmoset_container +machinectl --verify=no pull-tar https://bastelfreak.de/marmoset_container.tar marmoset_container machinectl start marmoset_container machinectl login marmoset_container ``` From 7361b01f11a939d132774375d6d99236f0f39107 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sat, 13 Feb 2016 21:55:55 +0100 Subject: [PATCH 36/40] Added multi parameter to get multiple mac/ip DNs --- marmoset/dhcp/isc_dhcp_ldap_config.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index bf2dda5..5783d93 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -61,7 +61,7 @@ def all(): return result @staticmethod - def __get_dn_by_ipv4(ip_address): + def __get_dn_by_ipv4(ip_address, multi=False): conn = ISCDhcpLdapConfig.__get_server_connection() conn.search(search_base=config['DHCPConfig'].get('ldap_client_base_dn'), search_filter='(cn=%s)' % ip_address, @@ -72,12 +72,21 @@ def __get_dn_by_ipv4(ip_address): entries = conn.response if entries is None or len(entries) == 0: + if multi: + return [] return None + if multi: + dn_list = [] + for entry in entries: + dn_list.append(entry['dn']) + + return dn_list + return entries[0]['dn'] @staticmethod - def __get_dn_by_mac(mac_address): + def __get_dn_by_mac(mac_address, multi=False): conn = ISCDhcpLdapConfig.__get_server_connection() conn.search(search_base=config['DHCPConfig'].get('ldap_client_base_dn'), search_filter='(dhcpHWAddress=ethernet %s)' % mac_address, @@ -88,8 +97,17 @@ def __get_dn_by_mac(mac_address): entries = conn.response if entries is None or len(entries) == 0: + if multi: + return [] return None + if multi: + dn_list = [] + for entry in entries: + dn_list.append(entry['dn']) + + return dn_list + return entries[0]['dn'] @staticmethod From 72f7a61721dc1e2c65c637a15f93c8573c957c6b Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sat, 13 Feb 2016 21:56:36 +0100 Subject: [PATCH 37/40] Seperate remove in mac and ip function, also returns the count of founded DNs --- marmoset/dhcp/isc_dhcp_ldap_config.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/marmoset/dhcp/isc_dhcp_ldap_config.py b/marmoset/dhcp/isc_dhcp_ldap_config.py index 5783d93..ec47d11 100644 --- a/marmoset/dhcp/isc_dhcp_ldap_config.py +++ b/marmoset/dhcp/isc_dhcp_ldap_config.py @@ -178,9 +178,23 @@ def get_by_mac(mac_address): return ISCDhcpLdapConfig.__get_dhcp_config(dn) @staticmethod - def remove(ipv4): - dn = ISCDhcpLdapConfig.__get_dn_by_ipv4(ipv4) + def remove_by_ipv4(ipv4): + dn_list = ISCDhcpLdapConfig.__get_dn_by_ipv4(ipv4, multi=True) - conn = ISCDhcpLdapConfig.__get_server_connection() - conn.delete(dn) - conn.unbind() + for dn in dn_list: + conn = ISCDhcpLdapConfig.__get_server_connection() + conn.delete(dn) + conn.unbind() + + return len(dn_list) + + @staticmethod + def remove_by_mac(mac): + dn_list = ISCDhcpLdapConfig.__get_dn_by_mac(mac, multi=True) + + for dn in dn_list: + conn = ISCDhcpLdapConfig.__get_server_connection() + conn.delete(dn) + conn.unbind() + + return len(dn_list) From 89e612626043ba0b6244bd27d631ee7256b7e194 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sat, 13 Feb 2016 21:58:03 +0100 Subject: [PATCH 38/40] Seperate remove in remove_by_ipv4 and remove_by_mac, also added remove_all --- marmoset/dhcp/dhcp_config.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/marmoset/dhcp/dhcp_config.py b/marmoset/dhcp/dhcp_config.py index df50f32..2d33cfb 100644 --- a/marmoset/dhcp/dhcp_config.py +++ b/marmoset/dhcp/dhcp_config.py @@ -47,8 +47,17 @@ def create_isc_ldap(self): isc_dhcp_config = ISCDhcpLdapConfig(self) isc_dhcp_config.save() - def remove(self): - return ISCDhcpLdapConfig.remove(self.ip_address) is not None + def remove_by_ipv4(self): + return ISCDhcpLdapConfig.remove_by_ipv4(self.ip_address) > 0 + + def remove_by_mac(self): + return ISCDhcpLdapConfig.remove_by_mac(self.mac) > 0 + + def remove_all(self): + ipv4_removed_count = ISCDhcpLdapConfig.remove_by_ipv4(self.ip_address) + mac_removed_count = ISCDhcpLdapConfig.remove_by_mac(self.mac) + + return (ipv4_removed_count + mac_removed_count) > 0 @staticmethod def all(): From 6bcc23a63ce6ad17fee05e1fbca8ff8267e90906 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sat, 13 Feb 2016 21:58:31 +0100 Subject: [PATCH 39/40] Fix: macs will now be removed by mac and not by founded ip --- marmoset/webserver/dhcp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index 0ec7973..b23c12c 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -96,7 +96,7 @@ def delete(self, ipv4): return abort(404) dhcp_config = dhcp.DhcpConfig.get_by_ip(ipv4) - dhcp_config.remove() + dhcp_config.remove_by_ipv4() return '', 204 @@ -146,6 +146,6 @@ def delete(self, mac): return abort(404) dhcp_config = dhcp.DhcpConfig.get_by_mac(mac) - dhcp_config.remove() + dhcp_config.remove_by_mac() return '', 204 From dc26635ce57ee418fe46339dfd000c0174ff1c14 Mon Sep 17 00:00:00 2001 From: Sebastian Rakel Date: Sat, 13 Feb 2016 22:03:19 +0100 Subject: [PATCH 40/40] Fix: Return messages now response in json format --- marmoset/webserver/dhcp.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/marmoset/webserver/dhcp.py b/marmoset/webserver/dhcp.py index b23c12c..eb15245 100644 --- a/marmoset/webserver/dhcp.py +++ b/marmoset/webserver/dhcp.py @@ -30,13 +30,13 @@ def post(self): args = parser.parse_args() if (args.gateway is None or args.networkmask is None) and not validation.is_cidr(args.ip_address): - return 'missing parameter gateway and networkmask or give an ip address in CIDR notation', 406 + return {'message': 'missing parameter gateway and networkmask or give an ip address in CIDR notation'}, 406 if not validation.is_ipv4(args.ip_address) and not validation.is_cidr(args.ip_address): - return 'please provide a valid ipv4 address', 406 + return {'message': 'please provide a valid ipv4 address'}, 406 if not validation.is_mac(args.mac): - return 'please provide a valid mac address', 406 + return {'message': 'please provide a valid mac address'}, 406 dhcp_config = dhcp.DhcpConfig(args.mac, args.ip_address, args.gateway, args.networkmask) @@ -54,7 +54,7 @@ def post(self): class DhcpIpv4Object(Resource): def get(self, ipv4): if not validation.is_ipv4(ipv4): - return 'please provide a valid ipv4 address', 406 + return {'message': 'please provide a valid ipv4 address'}, 406 if not dhcp.DhcpConfig.exists_ipv4(ipv4): return abort(404) @@ -67,7 +67,7 @@ def put(self, ipv4): args = parser.parse_args(request) if not validation.is_ipv4(ipv4): - return 'please provide a valid ipv4 address', 406 + return {'message': 'please provide a valid ipv4 address'}, 406 if not dhcp.DhcpConfig.exists_ipv4(ipv4): return abort(404) @@ -90,7 +90,7 @@ def put(self, ipv4): def delete(self, ipv4): if not validation.is_ipv4(ipv4): - return 'please provide a valid ipv4 address', 406 + return {'message': 'please provide a valid ipv4 address'}, 406 if not dhcp.DhcpConfig.exists_ipv4(ipv4): return abort(404) @@ -104,7 +104,7 @@ def delete(self, ipv4): class DhcpMacObject(Resource): def get(self, mac): if not validation.is_mac(mac): - return 'please provide a valid mac address', 406 + return {'message': 'please provide a valid mac address'}, 406 if not dhcp.DhcpConfig.exists_mac(mac): return abort(404) @@ -117,7 +117,7 @@ def put(self, mac): args = parser.parse_args(request) if not validation.is_mac(mac): - return 'please provide a valid mac address', 406 + return {'message': 'please provide a valid mac address'}, 406 if not dhcp.DhcpConfig.exists_mac(mac): return abort(404) @@ -140,7 +140,7 @@ def put(self, mac): def delete(self, mac): if not validation.is_mac(mac): - return 'please provide a valid mac address', 406 + return {'message': 'please provide a valid mac address'}, 406 if not dhcp.DhcpConfig.exists_mac(mac): return abort(404)