| # 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. |
| # |
| # $Id$ |
| # |
| |
| import logging |
| import os |
| import signal |
| import socket |
| import subprocess |
| import time |
| #from instancehook import InstanceHook |
| #from tashi.services.ttypes import Instance, NetworkConfiguration |
| #from tashi import boolean |
| from tashi.rpycservices.rpyctypes import Instance |
| |
| |
| class DhcpDns(): |
| def __init__(self, config, verbose=None): |
| self.verbose = verbose |
| self.dnsKeyName = config['dnsKeyName'] |
| self.dnsSecretKey = config['dnsSecretKey'] |
| self.dnsServer = config['dnsServer'] |
| self.dnsDomain = config['dnsDomain'] |
| self.dnsExpire = int(config['dnsExpire']) |
| self.dhcpServer = config['dhcpServer'] |
| self.dhcpKeyName = config['dhcpKeyName'] |
| self.dhcpSecretKey = config['dhcpSecretKey'] |
| self.error = "" |
| |
| self.reverseDns = True |
| |
| def strToIp(self, s): |
| ipNum = -1 |
| try: |
| ipNum = reduce(lambda x, y: x*256+y, map(int, s.split("."))) |
| except: |
| pass |
| return ipNum |
| |
| def ipToStr(self, ip): |
| return "%d.%d.%d.%d" % (ip>>24, (ip>>16)%256, (ip>>8)%256, ip%256) |
| |
| def allocateIP(self, nic): |
| network = nic.network |
| allocatedIP = None |
| requestedIP = self.strToIp(nic.ip) |
| if (requestedIP <= self.ipMax[network] and requestedIP >= self.ipMin[network] and (requestedIP not in self.usedIPs)): |
| allocatedIP = requestedIP |
| while (allocatedIP == None): |
| if (self.currentIP[network] > self.ipMax[network]): |
| self.currentIP[network] = self.ipMin[network] |
| elif (self.currentIP[network] in self.usedIPs): |
| self.currentIP[network] = self.currentIP[network] + 1 |
| else: |
| allocatedIP = self.currentIP[network] |
| ipString = self.ipToStr(allocatedIP) |
| self.usedIPs[allocatedIP] = ipString |
| return ipString |
| |
| def addDhcp(self, name, ipaddr, hwaddr): |
| try: |
| self.removeDhcp(name) |
| self.removeDhcp(name, ipaddr) |
| except: |
| pass |
| cmd = "omshell" |
| (stdin, stdout) = os.popen2(cmd) |
| stdin.write("server %s\n" % (self.dhcpServer)) |
| if (self.dhcpSecretKey != ""): |
| stdin.write("key %s %s\n" % (self.dhcpKeyName, self.dhcpSecretKey)) |
| stdin.write("connect\n") |
| stdin.write("new \"host\"\n") |
| stdin.write("set name = \"%s\"\n" % (name)) |
| stdin.write("set ip-address = %s\n" % (ipaddr)) |
| stdin.write("set hardware-address = %s\n" % (hwaddr)) |
| stdin.write("set hardware-type = 00:00:00:01\n") # Ethernet |
| stdin.write("create\n") |
| stdin.close() |
| output = stdout.read() |
| stdout.close() |
| |
| def removeDhcp(self, name, ipaddr=None): |
| cmd = "omshell" |
| (stdin, stdout) = os.popen2(cmd) |
| stdin.write("server %s\n" % (self.dhcpServer)) |
| if (self.dhcpSecretKey != ""): |
| stdin.write("key %s %s\n" % (self.dhcpKeyName, self.dhcpSecretKey)) |
| stdin.write("connect\n") |
| stdin.write("new \"host\"\n") |
| if (ipaddr == None): |
| stdin.write("set name = \"%s\"\n" % (name)) |
| else: |
| stdin.write("set ip-address = %s\n"%(ipaddr)) |
| stdin.write("open\n") |
| stdin.write("remove\n") |
| stdin.close() |
| output = stdout.read() |
| stdout.close() |
| |
| def addDns(self, name, ip): |
| try: |
| self.removeDns(name) |
| except Exception, e: |
| pass |
| |
| cmd = "nsupdate" |
| child = subprocess.Popen(args=cmd.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
| try: |
| (stdin, stdout) = (child.stdin, child.stdout) |
| stdin.write("server %s\n" % (self.dnsServer)) |
| stdin.write("key %s %s\n" % (self.dnsKeyName, self.dnsSecretKey)) |
| stdin.write("update add %s.%s %d A %s\n" % (name, self.dnsDomain, self.dnsExpire, ip)) |
| stdin.write("\n") |
| if (self.reverseDns): |
| ipSegments = map(int, ip.split(".")) |
| ipSegments.reverse() |
| reverseIpStr = ("%d.%d.%d.%d.in-addr.arpa" % (ipSegments[0], ipSegments[1], ipSegments[2], ipSegments[3])) |
| stdin.write("update add %s %d IN PTR %s.%s.\n" % (reverseIpStr, self.dnsExpire, name, self.dnsDomain)) |
| stdin.write("\n") |
| stdin.close() |
| output = stdout.read() |
| stdout.close() |
| except Exception, e: |
| self.error = e |
| finally: |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| while (pid == 0): |
| time.sleep(0.5) |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| |
| def removeDns(self, name): |
| cmd = "nsupdate" |
| child = subprocess.Popen(args=cmd.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
| try: |
| (stdin, stdout) = (child.stdin, child.stdout) |
| stdin.write("server %s\n" % (self.dnsServer)) |
| stdin.write("key %s %s\n" % (self.dnsKeyName, self.dnsSecretKey)) |
| stdin.write("update delete %s.%s A\n" % (name, self.dnsDomain)) |
| stdin.write("\n") |
| if (self.reverseDns): |
| hostInfo = socket.gethostbyaddr(name) |
| ip = hostInfo[2][0] |
| ipSegments = map(int, ip.split(".")) |
| ipSegments.reverse() |
| reverseIpStr = ("%d.%d.%d.%d.in-addr.arpa" % (ipSegments[0], ipSegments[1], ipSegments[2], ipSegments[3])) |
| stdin.write("update delete %s IN PTR\n" % (reverseIpStr)) |
| stdin.write("\n") |
| stdin.close() |
| output = stdout.read() |
| stdout.close() |
| except Exception, e: |
| self.error = e |
| finally: |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| while (pid == 0): |
| time.sleep(0.5) |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| |
| def addCname(self, name, origName): |
| |
| cmd = "nsupdate" |
| child = subprocess.Popen(args=cmd.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
| try: |
| # Check for existance of hostname |
| #ip = socket.gethostbyname(origName) |
| # add this to make sure we always input the textual name instead of the ip address by mistake |
| hostInfo = socket.gethostbyaddr(origName) |
| hostName = hostInfo[0].split(".")[0] |
| |
| (stdin, stdout) = (child.stdin, child.stdout) |
| stdin.write("server %s\n" % (self.dnsServer)) |
| stdin.write("key %s %s\n" % (self.dnsKeyName, self.dnsSecretKey)) |
| stdin.write("update add %s.%s %d CNAME %s.%s\n" % (name, self.dnsDomain, self.dnsExpire, hostName, self.dnsDomain)) |
| stdin.write("\n") |
| stdin.close() |
| output = stdout.read() |
| stdout.close() |
| |
| except Exception, e: |
| self.error = e |
| |
| finally: |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| while (pid == 0): |
| time.sleep(0.5) |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| |
| def removeCname(self, name): |
| cmd = "nsupdate" |
| child = subprocess.Popen(args=cmd.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
| try: |
| (stdin, stdout) = (child.stdin, child.stdout) |
| stdin.write("server %s\n" % (self.dnsServer)) |
| stdin.write("key %s %s\n" % (self.dnsKeyName, self.dnsSecretKey)) |
| stdin.write("update delete %s.%s CNAME\n" % (name, self.dnsDomain)) |
| stdin.write("\n") |
| stdin.close() |
| output = stdout.read() |
| stdout.close() |
| except Exception, e: |
| self.error = e |
| finally: |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| while (pid == 0): |
| time.sleep(0.5) |
| os.kill(child.pid, signal.SIGTERM) |
| (pid, status) = os.waitpid(child.pid, os.WNOHANG) |
| |
| def doUpdate(self, instance): |
| newInstance = Instance() |
| newInstance.id = instance.id |
| newInstance.nics = instance.nics |
| self.client.vmUpdate(instance.id, newInstance, None) |
| |
| def preCreate(self, instance): |
| if (len(instance.nics) < 1): |
| return |
| for i in range(0, len(instance.nics)): |
| nic = instance.nics[i] |
| ip = self.allocateIP(nic) |
| nic.ip = ip |
| try: |
| if (i == 0): |
| self.log.info("Adding %s:{%s->%s} to DNS" % (instance.name, instance.name, ip)) |
| self.addDns(instance.name, ip) |
| if (i == 0): |
| dhcpName = instance.name |
| else: |
| dhcpName = instance.name + "-nic%d" % (i) |
| self.log.info("Adding %s:{%s->%s} to DHCP" % (dhcpName, nic.mac, ip)) |
| self.addDhcp(dhcpName, ip, nic.mac) |
| except Exception, e: |
| self.log.exception("Failed to add host %s to DHCP/DNS" % (instance.name)) |
| self.doUpdate(instance) |
| |
| def postDestroy(self, instance): |
| if (len(instance.nics) < 1): |
| return |
| self.log.info("Removing %s from DHCP/DNS" % (instance.name)) |
| for i in range(0, len(instance.nics)): |
| nic = instance.nics[i] |
| ip = nic.ip |
| try: |
| ipNum = self.strToIp(ip) |
| del self.usedIPs[ipNum] |
| except Exception, e: |
| self.log.exception("Failed to remove host %s, ip %s from pool of usedIPs" % (instance.name, ip)) |
| try: |
| if (i == 0): |
| dhcpName = instance.name |
| else: |
| dhcpName = instance.name + "-nic%d" % (i) |
| self.removeDhcp(dhcpName) |
| except Exception, e: |
| self.log.exception("Failed to remove host %s from DHCP" % (instance.name)) |
| try: |
| self.removeDns(instance.name) |
| except Exception, e: |
| self.log.exception("Failed to remove host %s from DNS" % (instance.name)) |