Skip to content

Commit

Permalink
Recommended IP when adding new entry to DHCPD and better ip validator
Browse files Browse the repository at this point in the history
  • Loading branch information
ant30 committed Jul 17, 2012
1 parent 0421b24 commit 91dc986
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 9 deletions.
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ DHCPD
-----

1. [X] Simple Validators (valid IP, valid MAC...)
1. [ ] Consistant file generation (IP in valid header range)
1. [ ] Suggest a valid IP.
1. [X] Consistant file generation (IP in valid header range)
1. [X] Suggest a valid IP.


Ninja SysOP
Expand Down
10 changes: 6 additions & 4 deletions ninjasysop/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
from colander import Invalid
import re

RE_IP = re.compile(r"^(?:\d{1,3}\.){3}(?:\d{1,3})$")
import re
from colander import Invalid
from ipaddr import IPv4Address, AddressValueError

RE_NAME = re.compile(r"^([a-z0-9]([-a-z0-9]*[a-z0-9])?\\.)*([a-z0-9]+)$", re.IGNORECASE)

RE_MAC = re.compile(r"([0-9a-f]{2}:){5}[0-9a-f]{2}", re.IGNORECASE)


def ip_validator(node, value):
if RE_IP.match(value) is None:
try:
IPv4Address(value)
except AddressValueError:
raise Invalid(node, "%s is not a valid IP" % value)


Expand Down
44 changes: 41 additions & 3 deletions plugins/dhcpd/dhcpd.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import re
import shutil
import subprocess
from ipaddr import IPv4Network, IPv4Address


from ninjasysop.backends import Backend, BackendApplyChangesException
Expand All @@ -44,7 +45,9 @@
# SERIAL = yyyymmddnn ; serial
PARSER_RE = {
'partition':re.compile(r"(?P<sub>subnet[^\}]*}) *(?P<hosts>.*)$"),
'hosts': re.compile(r"host (?P<hostname>[^ ]*) *{ *hardware ethernet (?P<mac>[^\;]*); *fixed-address (?P<ip>[^\;]*)")
'header': re.compile("subnet (?P<subnet>[\d.]+) netmask (?P<netmask>[\d\.]+)(?: *|)\{(?: *|)range (?P<start>[\d\.]+) (?P<end>[\d\.]+)"),
'hosts': re.compile(r"host (?P<hostname>[^ ]*) *{ *hardware ethernet (?P<mac>[^\;]*); *fixed-address (?P<ip>[^\;]*)"),
'router': re.compile(r"option routers (?P<router>[\d.]+)")
}

MATCH_RE_STR = {
Expand Down Expand Up @@ -85,12 +88,23 @@ def readfile(self):
if not partition:
raise IOError("Bad File Format")
(header, hosts) = partition.groups()

parsed_header = PARSER_RE['header'].search(header)
route_header = PARSER_RE['router'].search(header)
network = dict(network=IPv4Network("%s/%s" % (parsed_header.group('subnet'),
parsed_header.group('netmask'),
)),
start=IPv4Address(parsed_header.group('start')),
end=IPv4Address(parsed_header.group('end')),
router=IPv4Address(route_header.group('router')),
)

parsed_hosts = PARSER_RE['hosts'].findall(hosts)
for (name, mac, ip) in parsed_hosts:
item = DhcpHost(name, mac, ip)
items[name] = item

return items
return (network, items)

def __str_item(self, item):
itemstr = ''
Expand Down Expand Up @@ -151,7 +165,7 @@ class Dhcpd(Backend):
def __init__(self, name, filename):
super(Dhcpd, self).__init__(name, filename)
self.networkfile = NetworkFile(filename)
self.items = self.networkfile.readfile()
(self.network, self.items) = self.networkfile.readfile()

def del_item(self, name):
self.networkfile.remove_item(self.items[name])
Expand Down Expand Up @@ -192,6 +206,22 @@ def filter_name_exact(r):
else:
return self.items.values()

def get_free_ip(self):
# A free IP is:
# * Not asigned IP
# * Not in DHCPD start/end range (not ip collisions)
# * A IP in network (subnet/netmask) range
start_ip = self.network['network'].ip
ip = start_ip + 1
while (not (ip > self.network['start'] and ip < self.network['start']) and
not (self.get_items(ip=ip.exploded)) and
ip in self.network['network']):
ip += 1

if ip not in self.network['network']:
return ""
else:
return ip.exploded

def add_item(self, obj):
item = DhcpHost(name=obj['name'],
Expand Down Expand Up @@ -221,6 +251,14 @@ def get_add_schema(self):
for field in schema.children:
if field.name == 'name':
field.widget = deform.widget.TextInputWidget()
if field.name == 'ip':
free_ip = self.get_free_ip()
if free_ip:
field.default = self.get_free_ip()
field.description = "You can use %s as available IP" % free_ip
else:
field.description = "There aren't available IPs"

return schema


Expand Down
8 changes: 8 additions & 0 deletions plugins/dhcpd/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

from ninjasysop.validators import name_validator, ip_validator, mac_validator

from ipaddr import IPv4Address


class HostSchema(colander.MappingSchema):
name = colander.SchemaNode(
Expand Down Expand Up @@ -80,3 +82,9 @@ def __call__(self, form, value):
raise exc

## TODO: verify IP is in correct range taken from header file
if IPv4Address(item.ip) not in self.group.network['network']:
exc = colander.Invalid(form, '%s is not a valid IP' % (item.ip))
exc['ip'] = colander.Invalid(
form, "This IP is not a valid IP")
raise exc

1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'deform_bootstrap',
'passlib',
'webhelpers',
'ipaddr',
'paste',
]

Expand Down

0 comments on commit 91dc986

Please sign in to comment.