# 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. import CsHelper import logging from netaddr import * from random import randint from CsGuestNetwork import CsGuestNetwork from cs.CsDatabag import CsDataBag from cs.CsFile import CsFile LEASES = "/var/lib/misc/dnsmasq.leases" DHCP_HOSTS = "/etc/dhcphosts.txt" DHCP_OPTS = "/etc/dhcpopts.txt" CLOUD_CONF = "/etc/dnsmasq.d/cloud.conf" class CsDhcp(CsDataBag): """ Manage dhcp entries """ def process(self): self.hosts = {} self.changed = [] self.devinfo = CsHelper.get_device_info() self.preseed() self.cloud = CsFile(DHCP_HOSTS) self.dhcp_opts = CsFile(DHCP_OPTS) self.conf = CsFile(CLOUD_CONF) self.cloud.repopulate() self.dhcp_opts.repopulate() for item in self.dbag: if item == "id": continue self.add(self.dbag[item]) self.write_hosts() self.configure_server() restart_dnsmasq = False if self.conf.commit(): restart_dnsmasq = True if self.cloud.commit(): restart_dnsmasq = True self.dhcp_opts.commit() if restart_dnsmasq: self.delete_leases() if not self.cl.is_redundant() or self.cl.is_master(): if restart_dnsmasq: CsHelper.service("dnsmasq", "restart") else: CsHelper.start_if_stopped("dnsmasq") CsHelper.service("dnsmasq", "reload") def configure_server(self): # self.conf.addeq("dhcp-hostsfile=%s" % DHCP_HOSTS) idx = 0 for i in self.devinfo: if not i['dnsmasq']: continue device = i['dev'] ip = i['ip'].split('/')[0] sline = "dhcp-range=set:interface-%s-%s" % (device, idx) line = "dhcp-range=set:interface-%s-%s,%s,static" % (device, idx, ip) self.conf.search(sline, line) gn = CsGuestNetwork(device, self.config) sline = "dhcp-option=tag:interface-%s-%s,15" % (device, idx) line = "dhcp-option=tag:interface-%s-%s,15,%s" % (device, idx, gn.get_domain()) self.conf.search(sline, line) # DNS search order if gn.get_dns() and device: sline = "dhcp-option=tag:interface-%s-%s,6" % (device, idx) dns_list = [x for x in gn.get_dns() if x] line = "dhcp-option=tag:interface-%s-%s,6,%s" % (device, idx, ','.join(dns_list)) self.conf.search(sline, line) # Gateway gateway = '' if self.config.is_vpc(): gateway = gn.get_gateway() else: gateway = i['gateway'] if gateway != '0.0.0.0': sline = "dhcp-option=tag:interface-%s-%s,3," % (device, idx) line = "dhcp-option=tag:interface-%s-%s,3,%s" % (device, idx, gateway) self.conf.search(sline, line) # Netmask netmask = '' if self.config.is_vpc(): netmask = gn.get_netmask() else: netmask = str(i['network'].netmask) sline = "dhcp-option=tag:interface-%s-%s,1," % (device, idx) line = "dhcp-option=tag:interface-%s-%s,1,%s" % (device, idx, netmask) self.conf.search(sline, line) idx += 1 def delete_leases(self): try: open(LEASES, 'w').close() except IOError: return def preseed(self): self.add_host("127.0.0.1", "localhost %s" % CsHelper.get_hostname()) 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_router(): self.add_host(self.config.address().get_guest_ip(), "%s data-server" % CsHelper.get_hostname()) def write_hosts(self): file = CsFile("/etc/hosts") file.repopulate() for ip in self.hosts: file.add("%s\t%s" % (ip, self.hosts[ip])) if file.is_changed(): file.commit() logging.info("Updated hosts file") else: logging.debug("Hosts file unchanged") def add(self, entry): self.add_host(entry['ipv4_address'], entry['host_name']) # lease time boils down to once a month # with a splay of 60 hours to prevent storms lease = randint(700, 760) if entry['default_entry']: self.cloud.add("%s,%s,%s,%sh" % (entry['mac_address'], entry['ipv4_address'], entry['host_name'], lease)) else: tag = entry['ipv4_address'].replace(".", "_") self.cloud.add("%s,set:%s,%s,%s,%sh" % (entry['mac_address'], tag, entry['ipv4_address'], entry['host_name'], lease)) self.dhcp_opts.add("%s,%s" % (tag, 3)) self.dhcp_opts.add("%s,%s" % (tag, 6)) self.dhcp_opts.add("%s,%s" % (tag, 15)) i = IPAddress(entry['ipv4_address']) # 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_host(self, ip, hosts): self.hosts[ip] = hosts