#!/usr/bin/python

"""SUSENebula configuration 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 CloudConfigService(SocketServer.StreamRequestHandler):
        """Cloud head node information provider."""
        def __init__(self, request, client_address, server):
                self.srvInfo = {}
                # Collect the information we want to make propagate to the cloud node
                # Keyboard layout
                os.system('/sbin/yast2 keyboard summary >& /tmp/.kdb.info')
                info = open('/tmp/.kdb.info', 'r').read()
                os.remove('/tmp/.kdb.info')
                self.srvInfo['keyLayout'] = info.split(':')[-1].strip()
                # Timezone and HW clock setting (assume HW clock on nodes is set the
                # same by default
                os.system('/sbin/yast2 timezone summary >& /tmp/.tz.info')
                info = open('/tmp/.tz.info', 'r').readlines()
                os.remove('/tmp/.tz.info')
                timezone = None
                hwClockSet = None
                for ln in info:
                        if ln.find('Current Time Zone') != -1:
                                timezone = ln.split(':')[-1].strip()
                        if ln.find('Hardware Clock') != -1:
                                hwClockSet = ln.split(':')[-1].strip()
                self.srvInfo['timezone'] = timezone
                self.srvInfo['hwclock'] = hwClockSet 
                # Root passwd
                lines = open('/etc/shadow', 'r').readlines()
                passWd = None
                for ln in lines:
                        if ln[0:4] == 'root':
                                passWd = ln.split(':')[1]
                self.srvInfo['rootpass'] = passWd
                # IP address
                lines = open('/etc/sysconfig/network/ifcfg-br0', 'r').readlines()
                ipAddr = None
                for ln in lines:
                        if ln[0:7] == 'IPADDR=':
                                # [1:] at end removes leading quote
                                ipAddr = ln.split('=')[-1].split('/')[0].strip()[1:]
                self.srvInfo['serverIP'] = ipAddr
                # DHCP feature identifier
                lines = open('/etc/dhcpd.conf','r').readlines()
                dhcpFeature = None
                domainname = None
                for ln in lines:
                        if ln.find('domain-name') != -1:
                                # [1:-2] remove leading and trailing quotes and ; 
                                domainname = ln.split()[-1].strip()[1:-2]
                        # 239 is the assigned ID for the feature, hard coded in the YaST
                        # configuration module
                        if ln.find('code 239') != -1:
                                dhcpFeature = ln.split()[1].strip()
                self.srvInfo['dhcpFeature'] = dhcpFeature
                self.srvInfo['domainname'] = domainname
                # Name resolution
                self.srvInfo['resolve'] = open('/etc/resolv.conf', 'r').read()
                # The configured routes
                routes = open('/etc/sysconfig/network/routes', 'r').read()
                self.srvInfo['routes'] = routes
                # The one use ID
                lines = open('/etc/passwd', 'r').readlines()
                for ln in lines:
                        if ln.find('oneadmin') != -1:
                                self.srvInfo['userID'] = ('oneadmin', ln.split(':')[2],
                                                                                    ln.split(':')[1])
                                break
                # The cloud group ID
                lines = open('/etc/group', 'r').readlines()
                for ln in lines:
                        if ln.find('cloud') != -1:
                                self.srvInfo['groupID'] = ('cloud', ln.split(':')[2])
                                break
                SocketServer.StreamRequestHandler.__init__(self, request,
                                                                                                     client_address, server)
                
        #----------------------------------------------------------------------
        def generateHostName(self):
                """Generate a new host name"""
                # Create a host name for the host requesting the information
                while (os.path.exists('/var/lock/nebula-hostname.lock')):
                        # We have to wait another cloud node is being configured
                        pass
                os.system('touch /var/lock/nebula-hostname.lock')
                cntr = None
                if not os.path.exists('/var/lib/one/.nodecnt'):
                        cntr = 1
                        cntFl = open('/var/lib/one/.nodecnt', 'w')
                        cntFl.write('%d' %cntr)
                        cntFl.close()
                if not cntr:
                        cntr = open('/var/lib/one/.nodecnt', 'r').read()
                        cntr = eval(cntr)
                        cntr += 1
                        cntFl = open('/var/lib/one/.nodecnt', 'w')
                        cntFl.write('%d' %cntr)
                        cntFl.close()
                hostname = self.srvInfo['dhcpFeature'] + '-%d' %cntr
                os.remove('/var/lock/nebula-hostname.lock')
                return hostname

        #----------------------------------------------------------------------
        def handle(self):
                """Handle the information request"""
                requestType = self.request.recv(512).strip()
                if requestType == 'PROVIDE_CONFIG':
                        syslog.openlog('[CLOUD_INFO_SRV]',syslog.LOG_PID,syslog.LOG_DAEMON)
                        syslog.syslog(syslog.LOG_INFO, 'Provide cloud config info\n')
                        self.srvInfo['hostname'] = self.generateHostName()
                        for key in self.srvInfo.keys():
                                # Send a key value pair for each entry
                                self.request.send('%s=%s;' %(key,self.srvInfo[key]))
                        self.request.send('COMPLETE=finished')
                syslog.closelog()

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

#---------------------------------------------------------------------------
if __name__ == "__main__":
        syslog.openlog('[CLOUD_INFO_SRV]', syslog.LOG_PID, syslog.LOG_DAEMON)
        syslog.syslog(syslog.LOG_INFO, 'Cloud Info Server start\n')

        # Consistency check for pid file
        if not os.path.exists('/var/run/suseNebula'):
                os.system('mkdir -p /var/run/suseNebula')
        if os.path.exists('/var/run/suseNebula/infoSrv.pid'):
                msg = 'Found pid file, another server is running, not starting a '
                msg += 'new one.'
                syslog.syslog(syslog.LOG_ERR, msg)
                syslog.closelog()
                sys.exit(1)
     
        server = SocketServer.TCPServer(('169.254.1.1',  8445), CloudConfigService)

        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/infoSrv.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 Info Server running\n')
        syslog.closelog()
        server.serve_forever()
        
