Completely restructure the daemon code to move the 4 discrete daemons into a single daemon that can be run on every hypervisor. Introduce the idea of a static list of "coordinator" nodes which are configured at install time to run Zookeeper and FRR in router mode, and which are allowed to take on client network management duties (gateway, DHCP, DNS, etc.) while also allowing them to run VMs (i.e. no dedicated "router" nodes required).
140 lines
4.2 KiB
Python
Executable File
140 lines
4.2 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import argparse
|
|
import configparser
|
|
import os, sys
|
|
import kazoo.client
|
|
import re
|
|
|
|
#
|
|
# Variables
|
|
#
|
|
#
|
|
# General Functions
|
|
#
|
|
def get_zookeeper_key():
|
|
# Get the interface from environment (passed by dnsmasq)
|
|
try:
|
|
interface = os.environ['DNSMASQ_INTERFACE']
|
|
except:
|
|
exit(1)
|
|
# Get the ID of the interface (the digits)
|
|
network_vni = re.findall('\d+', interface)[0]
|
|
# Create the key
|
|
zookeeper_key = '/networks/{}/dhcp_leases'.format(network_vni)
|
|
return zookeeper_key
|
|
|
|
def get_lease_expiry():
|
|
try:
|
|
expiry = os.environ['DNSMASQ_LEASE_EXPIRES']
|
|
except:
|
|
expiry = '0'
|
|
return expiry
|
|
|
|
def get_client_id():
|
|
try:
|
|
client_id = os.environ['DNSMASQ_CLIENT_ID']
|
|
except:
|
|
client_id = '*'
|
|
return client_id
|
|
|
|
def connect_zookeeper():
|
|
# We expect the environ to contain the config file
|
|
try:
|
|
pvcd_config_file = os.environ['PVCD_CONFIG_FILE']
|
|
except:
|
|
# Default place
|
|
pvcd_config_file = '/etc/pvc/pvcd.conf'
|
|
|
|
o_config = configparser.ConfigParser()
|
|
o_config.read(pvcd_config_file)
|
|
|
|
try:
|
|
zk_host = o_config['default']['coordinators']
|
|
except:
|
|
try:
|
|
zk_host = o_config[socket.gethostname()]['coordinators']
|
|
except:
|
|
exit(1)
|
|
|
|
zk_conn = kazoo.client.KazooClient(hosts=zk_host)
|
|
try:
|
|
zk_conn.start()
|
|
except:
|
|
exit(1)
|
|
|
|
return zk_conn
|
|
|
|
def read_data(zk_conn, key):
|
|
return zk_conn.get(key)[0].decode('ascii')
|
|
|
|
def get_lease(zk_conn, zk_leases_key, macaddr):
|
|
expiry = read_data(zk_conn, '{}/{}/expiry'.format(zk_leases_key, macaddr))
|
|
ipaddr = read_data(zk_conn, '{}/{}/ipaddr'.format(zk_leases_key, macaddr))
|
|
hostname = read_data(zk_conn, '{}/{}/hostname'.format(zk_leases_key, macaddr))
|
|
clientid = read_data(zk_conn, '{}/{}/clientid'.format(zk_leases_key, macaddr))
|
|
return expiry, ipaddr, hostname, clientid
|
|
|
|
#
|
|
# Command Functions
|
|
#
|
|
def read_lease_database(zk_conn, zk_leases_key):
|
|
leases_list = zk_conn.get_children(zk_leases_key)
|
|
output_list = []
|
|
for macaddr in leases_list:
|
|
expiry, ipaddr, hostname, clientid = get_lease(zk_conn, zk_leases_key, macaddr)
|
|
data_string = '{} {} {} {} {}'.format(expiry, macaddr, ipaddr, hostname, clientid)
|
|
print('Reading lease from Zookeeper: {}'.format(data_string), file=sys.stderr)
|
|
output_list.append('{}'.format(data_string))
|
|
|
|
# Output list
|
|
print('\n'.join(output_list))
|
|
|
|
def add_lease(zk_conn, zk_leases_key, expiry, macaddr, ipaddr, hostname, clientid):
|
|
transaction = zk_conn.transaction()
|
|
transaction.create('{}/{}'.format(zk_leases_key, macaddr), ''.encode('ascii'))
|
|
transaction.create('{}/{}/expiry'.format(zk_leases_key, macaddr), expiry.encode('ascii'))
|
|
transaction.create('{}/{}/ipaddr'.format(zk_leases_key, macaddr), ipaddr.encode('ascii'))
|
|
transaction.create('{}/{}/hostname'.format(zk_leases_key, macaddr), hostname.encode('ascii'))
|
|
transaction.create('{}/{}/clientid'.format(zk_leases_key, macaddr), clientid.encode('ascii'))
|
|
transaction.commit()
|
|
|
|
def del_lease(zk_conn, zk_leases_key, macaddr, expiry):
|
|
zk_conn.delete('{}/{}'.format(zk_leases_key, macaddr), recursive=True)
|
|
|
|
#
|
|
# Instantiate the parser
|
|
#
|
|
parser = argparse.ArgumentParser(description='Store or retrieve dnsmasq leases in Zookeeper')
|
|
parser.add_argument('action', type=str, help='Action')
|
|
parser.add_argument('macaddr', type=str, help='MAC Address', nargs='?', default=None)
|
|
parser.add_argument('ipaddr', type=str, help='IP Address', nargs='?', default=None)
|
|
parser.add_argument('hostname', type=str, help='Hostname', nargs='?', default=None)
|
|
args = parser.parse_args()
|
|
|
|
action = args.action
|
|
macaddr = args.macaddr
|
|
ipaddr = args.ipaddr
|
|
hostname = args.hostname
|
|
|
|
zk_conn = connect_zookeeper()
|
|
zk_leases_key = get_zookeeper_key()
|
|
|
|
if action == 'init':
|
|
read_lease_database(zk_conn, zk_leases_key)
|
|
exit(0)
|
|
|
|
expiry = get_lease_expiry()
|
|
clientid = get_client_id()
|
|
|
|
#
|
|
# Choose action
|
|
#
|
|
print('Lease action - {} {} {} {}'.format(action, macaddr, ipaddr, hostname), file=sys.stderr)
|
|
if action == 'add':
|
|
add_lease(zk_conn, zk_leases_key, expiry, macaddr, ipaddr, hostname, clientid)
|
|
elif action == 'del':
|
|
del_lease(zk_conn, zk_leases_key, macaddr, expiry)
|
|
elif action == 'old':
|
|
pass
|