diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsDhcp.py b/systemvm/patches/debian/config/opt/cloud/bin/CsDhcp.py index a821c332d89..0a1769d6e22 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/CsDhcp.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsDhcp.py @@ -15,24 +15,17 @@ # specific language governing permissions and limitations # under the License. import CsHelper -from pprint import pprint import logging +from netaddr import * +from CsGuestNetwork import CsGuestNetwork 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" -""" - "172.16.1.102": { - "default_entry": true, - "default_gateway": "172.16.1.1", - "host_name": "VM-58976c22-0832-451e-9ab2-039e9f27e415", - "ipv4_adress": "172.16.1.102", - "ipv6_duid": "00:03:00:01:02:00:26:c3:00:02", - "mac_address": "02:00:26:c3:00:02", - "type": "dhcpentry" - }, -""" class CsDhcp(object): """ Manage dhcp entries """ @@ -42,15 +35,19 @@ class CsDhcp(object): if item == "id": continue dnsmasq.add(dbag[item]) - dnsmasq.collect_leases() - dnsmasq.to_str() dnsmasqb4 = CsDnsMasq(NO_PRELOAD) dnsmasqb4.parse_hosts() + dnsmasqb4.parse_dnsmasq() if not dnsmasq.compare_hosts(dnsmasqb4): dnsmasq.write_hosts() else: logging.debug("Hosts file is up to date") - + diff = dnsmasq.compare_dnsmasq(dnsmasqb4) + if len(diff) > 0: + self.updated = True + dnsmasq.delete_leases(diff) + dnsmasq.write_dnsmasq() + dnsmasq.configure_server() class CsDnsMasq(object): @@ -58,6 +55,9 @@ class CsDnsMasq(object): self.list = [] self.hosts = [] self.leases = [] + self.updated = False + self.devinfo = CsHelper.get_device_info() + self.devs = [] if preload: self.add_host("127.0.0.1", "localhost") self.add_host("::1", "localhost ip6-localhost ip6-loopback") @@ -65,18 +65,55 @@ class CsDnsMasq(object): self.add_host("ff02::2", "ip6-allrouters") self.add_host("127.0.0.1", CsHelper.get_hostname()) - def collect_leases(self): - # Format is - # 0 02:00:56:aa:00:03 10.1.1.18 pup * + def delete_leases(self, clist): try: for line in open(LEASES): bits = line.strip().split(' ') to = { "device" : bits[0], "mac" : bits[1], "ip" : bits[2], - "host" : bits[3] + "host" : bits[3], + "del" : False } + for l in clist: + lbits = l.split(',') + if lbits[0] == to['mac'] or \ + lbits[1] == to['ip']: + to['del'] == True + break self.leases.append(to) + for o in self.leases: + if o['del']: + cmd = "dhcp_release %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 or CsHelper.addifmissing(CLOUD_CONF, "dhcp-hostsfile=/etc/dhcphosts.txt") + self.updated = self.updated or 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 or 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) + line = "dhcp-option=tag:interface-%s,15,%s" % (device,gn.get_domain()) + self.updated = self.updated or CsHelper.addifmissing(CLOUD_CONF, line) + if self.updated: + CsHelper.hup_dnsmasq("dnsmasq", "dnsmasq") + + def parse_dnsmasq(self): + try: + for line in open(DHCP_HOSTS): + self.list.append(line.strip()) except IOError: pass @@ -93,23 +130,35 @@ class CsDnsMasq(object): 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 write_hosts(self): - logging.debug("Updating hosts file with new entry") + 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() + 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']) - + 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 + 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 to_str(self): - pprint(self.hosts) - pprint(self.list) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsGuestNetwork.py b/systemvm/patches/debian/config/opt/cloud/bin/CsGuestNetwork.py new file mode 100644 index 00000000000..8f19969ea82 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsGuestNetwork.py @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +from merge import dataBag +import CsHelper + +class CsGuestNetwork: + def __init__(self, device): + self.data = {} + self.guest = True + db = dataBag() + db.setKey("guestnetwork") + db.load() + dbag = db.getDataBag() + if device in dbag.keys() and len(dbag[device]) != 0: + self.data = dbag[device][0] + else: + self.guest = False + + def is_guestnetwork(self): + return self.guest + + def get_domain(self): + domain = "cloudnine.internal" + if not self.guest: + return CsHelper.get_domain() + + if 'domain_name' in self.data: + return self.data['domain_name'] + + return domain diff --git a/systemvm/patches/debian/config/opt/cloud/bin/CsHelper.py b/systemvm/patches/debian/config/opt/cloud/bin/CsHelper.py index e2efc5d0d37..10efe678efe 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/CsHelper.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/CsHelper.py @@ -7,6 +7,8 @@ import logging import os.path import re import shutil +from netaddr import * +from pprint import pprint def updatefile(filename, val, mode): """ add val to file """ @@ -18,6 +20,36 @@ def updatefile(filename, val, mode): handle.write(val) handle.close() +def get_device_info(): + """ Returns all devices on system with their ipv4 ip netmask """ + list = [] + for i in execute("ip addr show"): + vals = i.strip().lstrip().rstrip().split() + if vals[0] == "inet": + to = {} + to['ip'] = vals[1] + to['dev'] = vals[-1] + to['network'] = IPNetwork(to['ip']) + to['dnsmasq'] = False + list.append(to) + return list + +def get_domain(): + for line in open("/etc/resolv.conf"): + vals = line.lstrip().split() + if vals[0] == "domain": + return vals[1] + return "cloudnine.internal" + +def get_ip(device): + """ Return first ip on an interface """ + cmd = "ip addr show dev %s" % device + for i in execute(cmd): + vals = i.lstrip().split() + if (vals[0] == 'inet'): + return vals[1] + return "" + def definedinfile(filename, val): """ Check if val is defined in the file """ for line in open(filename): @@ -31,6 +63,8 @@ def addifmissing(filename, val): if not definedinfile(filename, val): updatefile(filename, val + "\n", "a") logging.debug("Added %s to file %s" % (val, filename)) + return True + return False def get_hostname(): for line in open("/etc/hostname"): @@ -46,6 +80,18 @@ def service(name, op): execute("service %s %s" % (name, op)) logging.info("Service %s %s" % (name, op)) +def hup_dnsmasq(name, user): + pid = "" + for i in execute("ps -ef | grep %s" % name): + vals = i.lstrip().split() + if (vals[0] == user): + pid = vals[1] + if pid: + logging.info("Sent hup to %s", name) + execute("kill -HUP %s" % pid) + else: + service("dnsmasq", "start") + def copy_if_needed(src, dest): """ Copy a file if the destination does not already exist """ diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index 5f8c09e3362..e17e4355a21 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -206,11 +206,7 @@ class CsApp: def __init__(self, ip): self.dev = ip.getDevice() self.ip = ip.get_ip_address() - self.domain = "domain.local" self.type = ip.get_type() - if self.type == "guest": - gn = CsGuestNetwork(self.dev) - self.domain = gn.get_domain() global fw class CsPasswdSvc(CsApp): @@ -274,39 +270,6 @@ class CsDnsmasq(CsApp): "-A INPUT -i %s -d %s/32 -p tcp -m tcp --dport 53 -j ACCEPT" % ( self.dev, self.ip ) ]) - def configure_server(self, method = "add"): - file = CsFile("/etc/dnsmasq.d/cloud.conf") - file.search("dhcp-range=interface:%s" % self.dev, \ - "dhcp-range=interface:%s,set:interface-%s,%s,static" % (self.dev, self.dev, self.ip)) - file.search("dhcp-option=tag:interface-%s," % self.dev, \ - "dhcp-option=tag:interface-%s,15,%s" % (self.dev, self.domain)) - file.commit() - - if file.is_changed(): - CsHelper.service("dnsmasq", "restart") - - -class CsGuestNetwork: - def __init__(self, device): - self.data = {} - db = dataBag() - db.setKey("guestnetwork") - db.load() - dbag = db.getDataBag() - for dev in dbag: - if dev == "id": - continue - if len(dbag[dev]) == 0: - continue - if dev == device: - self.data = dbag[dev][0] - - def get_domain(self): - if 'domain_name' in self.data: - return self.data['domain_name'] - else: - return "cloudnine.internal" - class CsDevice: """ Configure Network Devices """ def __init__(self, dev): @@ -441,7 +404,6 @@ class CsIP: ]) dns = CsDnsmasq(self) dns.add_firewall_rules() - dns.configure_server() app = CsApache(self) app.setup() pwdsvc = CsPasswdSvc(self).setup() @@ -795,8 +757,8 @@ def main(argv): nf = CsNetfilters() nf.compare(fw) - acls = CsDataBag("dhcpentry") - dhcp = CsDhcp(acls.get_bag(), cl) + dh = CsDataBag("dhcpentry") + dhcp = CsDhcp(dh.get_bag(), cl) if __name__ == "__main__": main(sys.argv)