#!/usr/bin/python

"""SUSENebula Node Registration Service."""

# Author: Robert Schweikert
# License: GPL v2
# Copyright: SUSE

import SocketServer
import os
import re
import signal
import sys
import syslog

ipPattern = r'\d{1,3}?\.\d{1,3}?\.\d{1,3}?\.\d{1,3}?'
ipPat = re.compile(ipPattern)

base = r'\d{0,2}?[A-F]{0,2}?:'
macPattern = 5*base + r'\d{0,2}?[A-F]{0,2}?'
macPat = re.compile(macPattern)

#------------------------------------------------------------------------------
class RegistrationHandler(SocketServer.BaseRequestHandler):
    """Cloud head node registration request handler."""
    def handle(self):
        syslog.openlog('[NOD_REG]',syslog.LOG_PID, syslog.LOG_DAEMON)
        syslog.syslog(syslog.LOG_INFO, 'Cloud node registration\n')
        # self.request is the TCP socket connected to the client
        macAddr, ipAddr, name = self.request.recv(1024).strip().split(';')
        # Verify proper MAC Address format
        if not macPat.match(macAddr):
            msg = 'Received improper data for MAC Address '
            msg += macAddr
            msg += ' -> skip registration\n'
            syslog.syslog(syslog.LOG_ERR, msg)
            syslog.closelog()
            return
        # Verify the IP address format
        if not ipPat.match(ipAddr):
             msg = 'Received improper data for IP Adress '
             msg += ipAddr
             msg += ' -> skip registration\n'
             syslog.syslog(syslog.LOG_ERR, msg)
             syslog.closelog()
             return
        syslog.syslog(syslog.LOG_INFO, 'MAC Address: %s\n' %macAddr)
        syslog.syslog(syslog.LOG_INFO, 'IP Address: %s\n' %ipAddr)
        syslog.syslog(syslog.LOG_INFO, 'Node name: %s\n' %name)
        # Setup the dhcp server to make sure the node being registered
        # always gets the same IP address
        lines = open('/etc/dhcpd.conf', 'r').readlines()
        processBlock = None
        for ln in lines:
            if ln.find(name) != -1:
                processBlock = 1
                continue
            if processBlock and ln.find('fixed-address') != -1:
                setIP = ln.split()[-1].strip()[:-1]
                if setIP != ipAddr:
                    msg = 'Host with name "%s" already registered with ' %name
                    msg += 'IP "%s". Cannot register node.\n' %ipAddr
                    syslog.syslog(syslog.LOG_INFO, msg)
                    syslog.closelog()
                    return
        dhconf = open('/etc/dhcpd.conf', 'a')
        dhconf.write('\n')
        dhconf.write('host %s {\n' %name)
        dhconf.write('    hardware ethernet %s;\n' %macAddr)
        dhconf.write('    fixed-address %s;\n' %ipAddr)
        dhconf.write('}\n')
        dhconf.close()
        # Restart the dhcp server as we just change the config
        os.system('/bin/systemctl restart dhcpd.service >& /dev/null')
        # Create hosts entry
        hosts = open('/etc/hosts', 'a')
        hosts.write('%s    %s\n' %(ipAddr, name))
        hosts.close()
        # Register node with Nebula infrastructure
        cmd = '/usr/bin/sudo env ONE_AUTH=/var/lib/one/.one/one_auth '
        cmd += '/usr/bin/onehost create %s -i im_kvm -v vmm_kvm ' %ipAddr
        cmd += '-n vnm_kvm'
        res = os.system(cmd)
        if res:
            msg = 'Registration failed, onehost command with non zero status\n'
            syslog.syslog(syslog.LOG_ERR, msg)
            syslog.closelog()
            return
        syslog.syslog(syslog.LOG_ERR, 'Node registration succesful.\n')
        syslog.closelog()

#------------------------------------------------------------------------------
def sigHandler(signum, frame):
    """Signal handler for termination"""
    syslog.openlog('[NOD_REG]',syslog.LOG_PID,syslog.LOG_DAEMON)
    syslog.syslog(syslog.LOG_INFO, 'Terminating server on signal: %d' %signum)
    syslog.closelog()
    os.remove('/var/run/suseNebula/regSrv.pid')
    sys.exit(0)

#------------------------------------------------------------------------------
if __name__ == "__main__":
    syslog.openlog('[NOD_REG]', syslog.LOG_PID, syslog.LOG_DAEMON)
    syslog.syslog(syslog.LOG_INFO, 'Start node registration service\n')
    # Determine the IP address of this host
    inetInfo = os.popen('/sbin/ifconfig').readlines()
    ipAddr = None
    # Use the first IPv4 address found
    for item in inetInfo:
        if item.find('inet addr') != -1:
            # Extract the IP address from line expected in following format
            # inet addr:192.168.1.6  Bcast:192.168.1.255  Mask:255.255.255.0
            ipAddr = item.strip().split()[1].split(':')[-1]
            if ipPat.match(ipAddr):
                break

    if not ipAddr:
        # No IP address found. Print a message to syslog and exit
        syslog.syslog(syslog.LOG_ERR,
                      'Could not determine IP address, exiting\n')
        syslog.closelog()
        sys.exit(1)
        
    server = SocketServer.TCPServer((ipAddr, 8445), RegistrationHandler)

    if not server:
        syslog.syslog(syslog.LOG_ERR, 'Could not create server, exiting\n')
        syslog.closelog()
        sys.exit(1)

    # Write pid to a file
    pid = os.getpid()
    if not os.path.exists('/var/run/suseNebula'):
        os.mkdir('/var/run/suseNebula')
    pidFl = open('/var/run/suseNebula/regSrv.pid', 'w');
    pidFl.write('%d\n' %pid)
    pidFl.close()

    # Register signal handler
    signal.signal(signal.SIGHUP, sigHandler)
    signal.signal(signal.SIGTERM, sigHandler)
    signal.signal(signal.SIGINT, sigHandler)

    # No additional message will be printed, close the log
    syslog.syslog(syslog.LOG_INFO, 'Cloud registration service  running\n')
    syslog.closelog()
    server.serve_forever()
        
