mirror of https://github.com/apache/cloudstack.git
Dhcp refactor
Loads of tiny bugs squashed and some big ones Tested with domR needs VPC testing now TODO: Unit tests CsDhcp
This commit is contained in:
parent
31266d354f
commit
8a6a407114
|
|
@ -141,18 +141,19 @@ class CsAcl(CsDataBag):
|
|||
class AclDevice():
|
||||
""" A little class for each list of acls per device """
|
||||
|
||||
def __init__(self, obj, fw):
|
||||
def __init__(self, obj, config):
|
||||
self.ingess = []
|
||||
self.egress = []
|
||||
self.device = obj['device']
|
||||
self.ip = obj['nic_ip']
|
||||
self.netmask = obj['nic_netmask']
|
||||
self.config = config
|
||||
self.cidr = "%s/%s" % (self.ip, self.netmask)
|
||||
if "ingress_rules" in obj.keys():
|
||||
self.ingress = obj['ingress_rules']
|
||||
if "egress_rules" in obj.keys():
|
||||
self.egress = obj['egress_rules']
|
||||
self.fw = fw
|
||||
self.fw = config.get_fw()
|
||||
|
||||
def create(self):
|
||||
self.process("ingress", self.ingress)
|
||||
|
|
@ -166,10 +167,8 @@ class CsAcl(CsDataBag):
|
|||
class AclRule():
|
||||
|
||||
def __init__(self, direction, acl, rule, config):
|
||||
if config_is_vpc():
|
||||
self.init_vpc(self, direction, acl, rule, config)
|
||||
else:
|
||||
self.init_vr(self, direction, acl, rule, config)
|
||||
if config.is_vpc():
|
||||
self.init_vpc(direction, acl, rule, config)
|
||||
|
||||
def init_vpc(self, direction, acl, rule, config):
|
||||
self.table = ""
|
||||
|
|
@ -179,7 +178,7 @@ class CsAcl(CsDataBag):
|
|||
self.dest = "-s %s" % rule['cidr']
|
||||
if direction == "egress":
|
||||
self.table = config.get_egress_table()
|
||||
self.chain = config.get_egress_chain(self.device, ip)
|
||||
self.chain = config.get_egress_chain(self.device, acl.ip)
|
||||
self.dest = "-d %s" % rule['cidr']
|
||||
self.type = ""
|
||||
self.type = rule['type']
|
||||
|
|
@ -217,9 +216,9 @@ class CsAcl(CsDataBag):
|
|||
if item == "id":
|
||||
continue
|
||||
if self.config.is_vpc():
|
||||
dev_obj = self.AclDevice(self.dbag[item], self.fw).create()
|
||||
dev_obj = self.AclDevice(self.dbag[item], self.config).create()
|
||||
else:
|
||||
self.AclIP(self.dbag[item], self.fw).create()
|
||||
self.AclIP(self.dbag[item], self.config).create()
|
||||
|
||||
|
||||
class CsVmMetadata(CsDataBag):
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ from CsApp import CsApache, CsDnsmasq, CsPasswdSvc
|
|||
import CsHelper
|
||||
import logging
|
||||
import CsHelper
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
from CsRoute import CsRoute
|
||||
from CsRule import CsRule
|
||||
|
||||
|
|
@ -53,6 +55,26 @@ class CsAddress(CsDataBag):
|
|||
return ip.get_ip()
|
||||
return None
|
||||
|
||||
def get_guest_gateway(self):
|
||||
"""
|
||||
Return the gateway of the first guest interface
|
||||
For use with routers not vpcrouters
|
||||
"""
|
||||
for ip in self.get_ips():
|
||||
if ip.is_guest():
|
||||
return ip.get_gateway()
|
||||
return None
|
||||
|
||||
def get_guest_netmask(self):
|
||||
"""
|
||||
Return the gateway of the first guest interface
|
||||
For use with routers not vpcrouters
|
||||
"""
|
||||
for ip in self.get_ips():
|
||||
if ip.is_guest():
|
||||
return ip.get_netmask()
|
||||
return None
|
||||
|
||||
def needs_vrrp(self, o):
|
||||
"""
|
||||
Returns if the ip needs to be managed by keepalived or not
|
||||
|
|
@ -128,6 +150,12 @@ class CsInterface:
|
|||
def get_ip(self):
|
||||
return self.get_attr("public_ip")
|
||||
|
||||
def get_netmask(self):
|
||||
return self.get_attr("netmask")
|
||||
|
||||
def get_gateway(self):
|
||||
return self.get_attr("gateway")
|
||||
|
||||
def get_device(self):
|
||||
return self.get_attr("device")
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ class CsConfig(object):
|
|||
def get_domain(self):
|
||||
return self.cl.get_domain()
|
||||
|
||||
def get_dns(self):
|
||||
return self.get_cmdline().get_dns()
|
||||
|
||||
def get_format(self):
|
||||
return self.__LOG_FORMAT
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@ class CsCmdLine(CsDataBag):
|
|||
else:
|
||||
return "unloved-router"
|
||||
|
||||
def get_dns(self):
|
||||
dns = []
|
||||
names = "dns1 dns2"
|
||||
for name in names:
|
||||
if name in self.dbag['config']:
|
||||
dns.append(self.dbag['config'][name])
|
||||
return dns
|
||||
|
||||
def get_type(self):
|
||||
if "type" in self.dbag['config']:
|
||||
return self.dbag['config']['type']
|
||||
|
|
|
|||
|
|
@ -19,12 +19,10 @@ import logging
|
|||
from netaddr import *
|
||||
from CsGuestNetwork import CsGuestNetwork
|
||||
from cs.CsDatabag import CsDataBag
|
||||
from cs.CsFile import CsFile
|
||||
|
||||
NO_PRELOAD = False
|
||||
LEASES = "/var/lib/misc/dnsmasq.leases"
|
||||
DHCP_HOSTS = "/etc/dhcphosts.txt"
|
||||
DHCP_OPTS = "/etc/dhcpopts.txt"
|
||||
DNSMASQ_CONF = "/etc/dnsmasq.conf"
|
||||
CLOUD_CONF = "/etc/dnsmasq.d/cloud.conf"
|
||||
|
||||
|
||||
|
|
@ -32,50 +30,68 @@ class CsDhcp(CsDataBag):
|
|||
""" Manage dhcp entries """
|
||||
|
||||
def process(self):
|
||||
dnsmasq = CsDnsMasq(self.config)
|
||||
self.hosts = {}
|
||||
self.changed = []
|
||||
self.devinfo = CsHelper.get_device_info()
|
||||
self.preseed()
|
||||
self.cloud = CsFile(DHCP_HOSTS)
|
||||
self.conf = CsFile(CLOUD_CONF)
|
||||
length = len(self.conf)
|
||||
for item in self.dbag:
|
||||
if item == "id":
|
||||
continue
|
||||
dnsmasq.add(self.dbag[item])
|
||||
dnsmasqb4 = CsDnsMasq(self.config, NO_PRELOAD)
|
||||
dnsmasqb4.parse_hosts()
|
||||
dnsmasqb4.parse_dnsmasq()
|
||||
if not dnsmasq.compare_hosts(dnsmasqb4):
|
||||
logging.info("Updating hosts file")
|
||||
dnsmasq.write_hosts()
|
||||
else:
|
||||
logging.debug("Hosts file is up to date")
|
||||
diff = dnsmasq.compare_dnsmasq(dnsmasqb4)
|
||||
if len(diff) > 0:
|
||||
dnsmasq.updated = True
|
||||
dnsmasq.delete_leases(diff)
|
||||
dnsmasq.write_dnsmasq()
|
||||
dnsmasq.first_host = dnsmasqb4.first_host
|
||||
dnsmasq.configure_server()
|
||||
self.add(self.dbag[item])
|
||||
self.write_hosts()
|
||||
if self.cloud.is_changed():
|
||||
self.delete_leases()
|
||||
self.configure_server()
|
||||
self.conf.commit()
|
||||
self.cloud.commit()
|
||||
if self.cloud.is_changed():
|
||||
if length < 2:
|
||||
CsHelper.service("dnsmasq", "restart")
|
||||
else:
|
||||
CsHelper.hup_dnsmasq("dnsmasq", "dnsmasq")
|
||||
|
||||
def configure_server(self):
|
||||
#self.conf.addeq("dhcp-hostsfile=%s" % DHCP_HOSTS)
|
||||
for i in self.devinfo:
|
||||
if not i['dnsmasq']:
|
||||
continue
|
||||
device = i['dev']
|
||||
ip = i['ip'].split('/')[0]
|
||||
sline = "dhcp-range=interface:%s,set:interface" % (device)
|
||||
line = "dhcp-range=interface:%s,set:interface-%s,%s,static" % (device, device, ip)
|
||||
self.conf.search(sline, line)
|
||||
gn = CsGuestNetwork(device, self.config)
|
||||
sline = "dhcp-option=tag:interface-%s,15" % device
|
||||
line = "dhcp-option=tag:interface-%s,15,%s" % (device, gn.get_domain())
|
||||
self.conf.search(sline, line)
|
||||
# DNS search order
|
||||
sline = "dhcp-option=tag:interface-%s,119" % device
|
||||
line = "dhcp-option=tag:interface-%s,119,%s" % (device, ','.join(gn.get_dns()))
|
||||
self.conf.search(sline, line)
|
||||
# Gateway
|
||||
gateway = ''
|
||||
if self.config.is_vpc():
|
||||
gateway = gn.get_gateway()
|
||||
else:
|
||||
gateway = i['gateway']
|
||||
sline = "dhcp-option=tag:interface-%s,3," % device
|
||||
line = "dhcp-option=tag:interface-%s,3,%s" % (device, gateway)
|
||||
self.conf.search(sline, line)
|
||||
# Netmask
|
||||
netmask = ''
|
||||
if self.config.is_vpc():
|
||||
netmask = gn.get_netmask()
|
||||
else:
|
||||
netmask = self.config.address().get_guest_netmask()
|
||||
sline = "dhcp-option=tag:interface-%s,1," % device
|
||||
line = "dhcp-option=tag:interface-%s,1,%s" % (device, netmask)
|
||||
self.conf.search(sline, line)
|
||||
|
||||
class CsDnsMasq(object):
|
||||
|
||||
def __init__(self, config, preload=True):
|
||||
self.list = []
|
||||
self.hosts = []
|
||||
self.leases = []
|
||||
self.config = config
|
||||
self.updated = False
|
||||
self.devinfo = CsHelper.get_device_info()
|
||||
self.devs = []
|
||||
self.first_host = False
|
||||
if preload:
|
||||
self.add_host("127.0.0.1", "localhost")
|
||||
self.add_host("::1", "localhost ip6-localhost ip6-loopback")
|
||||
self.add_host("ff02::1", "ip6-allnodes")
|
||||
self.add_host("ff02::2", "ip6-allrouters")
|
||||
if config.is_vpc():
|
||||
self.add_host("127.0.0.1", CsHelper.get_hostname())
|
||||
if config.is_router():
|
||||
self.add_host(self.config.address().get_guest_ip(), "%s data-server" % CsHelper.get_hostname())
|
||||
|
||||
def delete_leases(self, clist):
|
||||
def delete_leases(self):
|
||||
leases = []
|
||||
try:
|
||||
for line in open(LEASES):
|
||||
bits = line.strip().split(' ')
|
||||
|
|
@ -85,95 +101,55 @@ class CsDnsMasq(object):
|
|||
"host": bits[3],
|
||||
"del": False
|
||||
}
|
||||
for l in clist:
|
||||
lbits = l.split(',')
|
||||
if lbits[0] == to['mac'] or \
|
||||
lbits[1] == to['ip']:
|
||||
to['del'] is True
|
||||
break
|
||||
self.leases.append(to)
|
||||
for o in self.leases:
|
||||
for v in changed:
|
||||
if v['mac'] == to['mac'] or v['ip'] == to['ip'] or v['host'] == to['host']:
|
||||
to['del'] = True
|
||||
leases.append(to)
|
||||
|
||||
for o in leases:
|
||||
if o['del']:
|
||||
cmd = "dhcp_release %s %s %s" % (o.device, o.ip, o.mac)
|
||||
cmd = "dhcp_release eth%s %s %s" % (o.device, o.ip, o.mac)
|
||||
logging.info(cmd)
|
||||
CsHelper.execute(cmd)
|
||||
# Finally add the new lease
|
||||
except IOError:
|
||||
return
|
||||
|
||||
def configure_server(self):
|
||||
self.updated = self.updated | CsHelper.addifmissing(DNSMASQ_CONF, "dhcp-hostsfile=/etc/dhcphosts.txt")
|
||||
# self.updated = self.updated | CsHelper.addifmissing(DNSMASQ_CONF, "dhcp-optsfile=%s:" % DHCP_OPTS)
|
||||
for i in self.devinfo:
|
||||
if not i['dnsmasq']:
|
||||
continue
|
||||
device = i['dev']
|
||||
ip = i['ip'].split('/')[0]
|
||||
line = "dhcp-range=interface:%s,set:interface-%s,%s,static" % (device, device, ip)
|
||||
self.updated = self.updated | CsHelper.addifmissing(CLOUD_CONF, line)
|
||||
# Next add the domain
|
||||
# if this is a guest network get it there otherwise use the value in resolv.conf
|
||||
gn = CsGuestNetwork(device, self.config)
|
||||
line = "dhcp-option=tag:interface-%s,15,%s" % (device, gn.get_domain())
|
||||
self.updated = self.updated | CsHelper.addifmissing(CLOUD_CONF, line)
|
||||
if self.updated:
|
||||
if self.first_host:
|
||||
CsHelper.service("dnsmasq", "restart")
|
||||
else:
|
||||
CsHelper.hup_dnsmasq("dnsmasq", "dnsmasq")
|
||||
|
||||
def parse_dnsmasq(self):
|
||||
self.first_host = False
|
||||
try:
|
||||
for line in open(DHCP_HOSTS):
|
||||
self.list.append(line.strip())
|
||||
if len(self.list) == 0:
|
||||
self.first_host = True
|
||||
except IOError:
|
||||
self.first_host = True
|
||||
|
||||
def parse_hosts(self):
|
||||
for line in open("/etc/hosts"):
|
||||
line = line.rstrip().lstrip()
|
||||
if line == '':
|
||||
continue
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
bits = ' '.join(line.split()).split(' ', 1)
|
||||
self.add_host(bits[0], bits[1])
|
||||
|
||||
def compare_hosts(self, obj):
|
||||
return set(self.hosts) == set(obj.hosts)
|
||||
|
||||
def compare_dnsmasq(self, obj):
|
||||
return list(set(self.list).symmetric_difference(set(obj.list)))
|
||||
def preseed(self):
|
||||
self.add_host("127.0.0.1", "localhost")
|
||||
self.add_host("::1", "localhost ip6-localhost ip6-loopback")
|
||||
self.add_host("ff02::1", "ip6-allnodes")
|
||||
self.add_host("ff02::2", "ip6-allrouters")
|
||||
if self.config.is_vpc():
|
||||
self.add_host("127.0.0.1", CsHelper.get_hostname())
|
||||
if self.config.is_router():
|
||||
self.add_host(self.config.address().get_guest_ip(), "%s data-server" % CsHelper.get_hostname())
|
||||
|
||||
def write_hosts(self):
|
||||
logging.debug("Updating hosts file")
|
||||
handle = open("/etc/hosts", 'w+')
|
||||
for line in self.hosts:
|
||||
handle.write("%s\n" % line)
|
||||
handle.close()
|
||||
|
||||
def write_dnsmasq(self):
|
||||
logging.debug("Updating %s", DHCP_HOSTS)
|
||||
handle = open(DHCP_HOSTS, 'w+')
|
||||
for line in self.list:
|
||||
handle.write("%s\n" % line)
|
||||
b = line.split(',')
|
||||
handle.close()
|
||||
file = CsFile("/etc/hosts")
|
||||
for ip in self.hosts:
|
||||
file.search("%s" % ip, "%s\t%s" % (ip, self.hosts[ip]))
|
||||
file.commit()
|
||||
if file.is_changed():
|
||||
logging.info("Updated hosts file")
|
||||
else:
|
||||
logging.debug("Hosts file unchanged")
|
||||
|
||||
def add(self, entry):
|
||||
self.add_host(entry['ipv4_adress'], entry['host_name'])
|
||||
self.add_dnsmasq(entry['ipv4_adress'], entry['host_name'], entry['mac_address'])
|
||||
if self.cloud.search("%s," % entry['mac_address'],
|
||||
"%s,%s,%s,infinite" % (entry['mac_address'],
|
||||
entry['ipv4_adress'],
|
||||
entry['host_name'])):
|
||||
self.changed.append({'mac': entry['mac_address'],
|
||||
'ip4': entry['ipv4_adress'],
|
||||
'host': entry['host_name']})
|
||||
i = IPAddress(entry['ipv4_adress'])
|
||||
# Calculate the device
|
||||
for v in self.devinfo:
|
||||
if i > v['network'].network and i < v['network'].broadcast:
|
||||
v['dnsmasq'] = True
|
||||
# Virtual Router
|
||||
v['gateway'] = entry['default_gateway']
|
||||
|
||||
def add_dnsmasq(self, ip, host, mac):
|
||||
self.list.append("%s,%s,%s,infinite" % (mac, ip, host))
|
||||
|
||||
def add_host(self, ip, host):
|
||||
self.hosts.append("%s\t%s" % (ip, host))
|
||||
def add_host(self, ip, hosts):
|
||||
self.hosts.update({ip: hosts})
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ class CsFile:
|
|||
else:
|
||||
return False
|
||||
|
||||
def __len__(self):
|
||||
return len(self.config)
|
||||
|
||||
def empty(self):
|
||||
self.config = []
|
||||
self.new_config = []
|
||||
|
|
@ -80,11 +83,12 @@ class CsFile:
|
|||
def add(self, string, where=-1):
|
||||
for index, line in enumerate(self.new_config):
|
||||
if line.strip() == string:
|
||||
return
|
||||
return False
|
||||
if where == -1:
|
||||
self.new_config.append("%s\n" % string)
|
||||
else:
|
||||
self.new_config.insert(where, "%s\n" % string)
|
||||
return True
|
||||
|
||||
def section(self, start, end, content):
|
||||
sind = -1
|
||||
|
|
@ -114,6 +118,8 @@ class CsFile:
|
|||
self.new_config[index] = replace + "\n"
|
||||
if not found:
|
||||
self.new_config.append(replace + "\n")
|
||||
return True
|
||||
return False
|
||||
|
||||
def compare(self, o):
|
||||
return (isinstance(o, self.__class__) and set(self.config) == set(o.new_config))
|
||||
|
|
|
|||
Loading…
Reference in New Issue