import re import time import pwd import os from onedns import zone from onedns import resolver from onedns import exception from onedns.clients import one from onedns.logger import log _BAD_CHARS = re.compile('[^-a-zA-Z0-9]') class OneDNS(resolver.DynamicResolver): """ This class provides convenience methods for adding/removing VMs to the DynamicResolver. """ def __init__(self, domain, one_kwargs={}): super(OneDNS, self).__init__(domain) self._one = one.OneClient(**one_kwargs) def _check_for_networks(self, vm): if not hasattr(vm.template, 'nics'): raise exception.NoNetworksError(vm) def _sanitize_name(self, name): name = _BAD_CHARS.sub('-', name) return name.strip('-') def _get_vm_dns_entries(self, vm): self._check_for_networks(vm) entries = {} hostname = self._sanitize_name(vm.name) primary_ip = vm.template.nics[0].ip entries[hostname] = primary_ip for nic in vm.template.nics[1:]: nicname = self._sanitize_name("{hostname}-{id}".format( id=nic.nic_id, hostname=hostname )) entries[nicname] = nic.ip return entries def _check_for_duplicates(self, vm_id, name, ip, zone=None): z = zone or self.zone try: f = z.get_forward(name) raise exception.DuplicateVMError(vm_id, f, ip) except exception.RecordDoesNotExist: pass try: r = z.get_reverse(ip) raise exception.DuplicateVMError(vm_id, ip, r) except exception.RecordDoesNotExist: pass def add_vm(self, vm, zone=None): dns_entries = self._get_vm_dns_entries(vm) log.info("Adding VM {id}: {vm}".format(id=vm.id, vm=vm.name)) for name, ip in dns_entries.items(): self._check_for_duplicates(vm.id, name, ip, zone=zone) self.add_host(name.lower(), ip, zone=zone) def remove_vm(self, vm, zone=None): dns_entries = self._get_vm_dns_entries(vm) log.info("Removing VM {id}: {vm}".format(id=vm.id, vm=vm.name)) for name, ip in dns_entries.items(): self.remove_host(name, ip, zone=zone) def add_vm_by_id(self, vm_id): vm = self._one.get_vm_by_id(vm_id) return self.add_vm(vm) def remove_vm_by_id(self, vm_id): vm = self._one.get_vm_by_id(vm_id) return self.remove_vm(vm) def sync(self, vms=None): z = zone.Zone(self.domain) vms = vms or self._one.vms() vms.sort(key=lambda x: x.id) for vm in vms: try: self.add_vm(vm, zone=z) except exception.NoNetworksError as e: e.log(warn=True) except exception.DuplicateVMError as e: e.log(warn=True) self.load(z) def daemon(self, *args, **kwargs): test = kwargs.pop('test', False) test_vms = kwargs.pop('test_vms', None) user = kwargs.pop('user', 'nobody') sync_interval = kwargs.pop('sync_interval', 5 * 60) if self._udp_server is None or not self._udp_server.isAlive(): self.start(*args, **kwargs) _, _, uid, gid, _, root, shell = pwd.getpwnam(user) os.chdir('/') os.setgroups([]) os.setgid(gid) os.setuid(uid) while self._udp_server.isAlive(): try: self.sync(vms=test_vms) except Exception: log.exception('onedns sync failed:') time.sleep(sync_interval) if test: break