Complete DHCP server setup including ZK leases database
This commit is contained in:
parent
505a951a7d
commit
9dbeaf8524
|
@ -39,6 +39,8 @@ import select
|
||||||
import ipaddress
|
import ipaddress
|
||||||
from socket import *
|
from socket import *
|
||||||
|
|
||||||
|
import daemon_lib.zkhandler as zkhandler
|
||||||
|
|
||||||
# see https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
|
# see https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
|
||||||
# section DHCP options
|
# section DHCP options
|
||||||
|
|
||||||
|
@ -500,26 +502,25 @@ class Transaction(object):
|
||||||
self.server.client_has_chosen(inform)
|
self.server.client_has_chosen(inform)
|
||||||
|
|
||||||
class DHCPServerConfiguration(object):
|
class DHCPServerConfiguration(object):
|
||||||
def __init__(self, configuration):
|
|
||||||
|
def __init__(self, zk_conn, ipaddr, iface, vni, network, router, dns_servers):
|
||||||
self.dhcp_offer_after_seconds = 1
|
self.dhcp_offer_after_seconds = 1
|
||||||
self.dhcp_acknowledge_after_seconds = 1
|
self.dhcp_acknowledge_after_seconds = 1
|
||||||
self.length_of_transaction = 60
|
self.length_of_transaction = 60
|
||||||
|
|
||||||
print(configuration)
|
self.zk_conn = zk_conn
|
||||||
if not configuration:
|
|
||||||
print('ERROR: Invalid DHCP configuration!')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
self.ipaddr = configuration['ipaddr']
|
self.ipaddr = ipaddr
|
||||||
self.iface = configuration['iface']
|
self.iface = iface
|
||||||
|
self.vni = vni
|
||||||
|
|
||||||
network_cidr = ipaddress.IPv4Network(configuration['network'], False)
|
network_cidr = ipaddress.IPv4Network(network, False)
|
||||||
self.network = network_cidr.network_address
|
self.network = str(network_cidr.network_address)
|
||||||
self.broadcast_address = network_cidr.broadcast_address
|
self.broadcast_address = str(network_cidr.broadcast_address)
|
||||||
self.subnet_mask = network_cidr.netmask
|
self.subnet_mask = str(network_cidr.netmask)
|
||||||
|
|
||||||
self.router = configuration['router']
|
self.router = router
|
||||||
self.domain_name_server = configuration['dns_servers']
|
self.domain_name_server = dns_servers
|
||||||
|
|
||||||
# 1 day is 86400
|
# 1 day is 86400
|
||||||
self.ip_address_lease_time = 300 # seconds
|
self.ip_address_lease_time = 300 # seconds
|
||||||
|
@ -561,7 +562,6 @@ class GREATER(object):
|
||||||
class NETWORK(object):
|
class NETWORK(object):
|
||||||
def __init__(self, network, subnet_mask):
|
def __init__(self, network, subnet_mask):
|
||||||
self.subnet_mask = struct.unpack('>I', inet_aton(subnet_mask))[0]
|
self.subnet_mask = struct.unpack('>I', inet_aton(subnet_mask))[0]
|
||||||
print(self.subnet_mask)
|
|
||||||
self.network = struct.unpack('>I', inet_aton(network))[0]
|
self.network = struct.unpack('>I', inet_aton(network))[0]
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
ip = struct.unpack('>I', inet_aton(other))[0]
|
ip = struct.unpack('>I', inet_aton(other))[0]
|
||||||
|
@ -606,6 +606,53 @@ class CSVDatabase(object):
|
||||||
with self.file() as f:
|
with self.file() as f:
|
||||||
return [list(line.strip().split(self.delimiter)) for line in f]
|
return [list(line.strip().split(self.delimiter)) for line in f]
|
||||||
|
|
||||||
|
|
||||||
|
class ZKDatabase(object):
|
||||||
|
|
||||||
|
# Store DHCP leases in zookeeper
|
||||||
|
# /networks/<VNI>/dhcp_leases/<MAC>:<timestamp>/{ipaddr,hostname}
|
||||||
|
# Line:
|
||||||
|
# ['52:54:00:21:34:11', '10.10.10.6', 'test1', '1538287572']
|
||||||
|
|
||||||
|
def __init__(self, zk_conn, key):
|
||||||
|
self.zk_conn = zk_conn
|
||||||
|
self.key = key
|
||||||
|
zkhandler.writedata(self.zk_conn, { self.key: '' }) # Create base key
|
||||||
|
|
||||||
|
def get(self, pattern):
|
||||||
|
pattern = list(pattern)
|
||||||
|
return [line for line in self.all() if pattern == line]
|
||||||
|
|
||||||
|
def add(self, line):
|
||||||
|
macaddr = line[0]
|
||||||
|
ipaddr = line[1]
|
||||||
|
hostname = line[2]
|
||||||
|
timestamp = line[3]
|
||||||
|
|
||||||
|
zkhandler.writedata(self.zk_conn, {
|
||||||
|
'{}/{}'.format(self.key, macaddr): timestamp,
|
||||||
|
'{}/{}/ipaddr'.format(self.key, macaddr): ipaddr,
|
||||||
|
'{}/{}/hostname'.format(self.key, macaddr): hostname
|
||||||
|
})
|
||||||
|
|
||||||
|
def delete(self, pattern):
|
||||||
|
macaddr = pattern[0]
|
||||||
|
try:
|
||||||
|
zkhandler.delete(self.zk_conn, '{}/{}'.format(self.key, macaddr))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
leases = []
|
||||||
|
mac_list = zkhandler.listchildren(self.zk_conn, self.key)
|
||||||
|
for macaddr in mac_list:
|
||||||
|
timestamp = zkhandler.readdata(self.zk_conn, '{}/{}'.format(self.key, macaddr))
|
||||||
|
ipaddr = zkhandler.readdata(self.zk_conn, '{}/{}/ipaddr'.format(self.key, macaddr))
|
||||||
|
hostname = zkhandler.readdata(self.zk_conn, '{}/{}/hostname'.format(self.key, macaddr))
|
||||||
|
leases.append([macaddr, ipaddr, hostname, timestamp])
|
||||||
|
return leases
|
||||||
|
|
||||||
|
|
||||||
class Host(object):
|
class Host(object):
|
||||||
|
|
||||||
def __init__(self, mac, ip, hostname, last_used):
|
def __init__(self, mac, ip, hostname, last_used):
|
||||||
|
@ -648,8 +695,8 @@ class Host(object):
|
||||||
|
|
||||||
|
|
||||||
class HostDatabase(object):
|
class HostDatabase(object):
|
||||||
def __init__(self, file_name):
|
def __init__(self, zk_conn, vni):
|
||||||
self.db = CSVDatabase(file_name)
|
self.db = ZKDatabase(zk_conn, '/networks/{}/dhcp_leases'.format(vni))
|
||||||
|
|
||||||
def get(self, **kw):
|
def get(self, **kw):
|
||||||
pattern = Host.get_pattern(**kw)
|
pattern = Host.get_pattern(**kw)
|
||||||
|
@ -685,11 +732,10 @@ class DHCPServer(object):
|
||||||
self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||||
self.socket.setsockopt(SOL_SOCKET, 25, self.configuration.iface.encode('ascii'))
|
self.socket.setsockopt(SOL_SOCKET, 25, self.configuration.iface.encode('ascii'))
|
||||||
self.socket.bind(('<broadcast>', 67))
|
self.socket.bind(('<broadcast>', 67))
|
||||||
print(self.socket)
|
|
||||||
self.delay_worker = DelayWorker()
|
self.delay_worker = DelayWorker()
|
||||||
self.closed = False
|
self.closed = False
|
||||||
self.transactions = collections.defaultdict(lambda: Transaction(self)) # id: transaction
|
self.transactions = collections.defaultdict(lambda: Transaction(self)) # id: transaction
|
||||||
self.hosts = HostDatabase(self.configuration.host_file)
|
self.hosts = HostDatabase(self.configuration.zk_conn, self.configuration.vni)
|
||||||
self.time_started = time.time()
|
self.time_started = time.time()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
|
@ -166,14 +166,15 @@ class VXNetworkInstance():
|
||||||
'',
|
'',
|
||||||
'o'
|
'o'
|
||||||
)
|
)
|
||||||
config = {
|
dhcp_configuration = DHCPServerConfiguration(
|
||||||
'ipaddr': self.ip_gateway,
|
zk_conn=self.zk_conn,
|
||||||
'iface': self.bridge_nic,
|
ipaddr=self.ip_gateway,
|
||||||
'network': self.ip_network,
|
iface=self.bridge_nic,
|
||||||
'router': [self.ip_gateway],
|
vni=self.vni,
|
||||||
'dns_servers': [self.ip_gateway]
|
network=self.ip_network,
|
||||||
}
|
router=[self.ip_gateway],
|
||||||
dhcp_configuration = DHCPServerConfiguration(config)
|
dns_servers=[self.ip_gateway]
|
||||||
|
)
|
||||||
dhcp_configuration.debug = print
|
dhcp_configuration.debug = print
|
||||||
self.dhcp_server = DHCPServer.DHCPServer(dhcp_configuration)
|
self.dhcp_server = DHCPServer.DHCPServer(dhcp_configuration)
|
||||||
self.dhcp_server.start()
|
self.dhcp_server.start()
|
||||||
|
|
Loading…
Reference in New Issue