#!/usr/bin/python

# 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.

"""This script obtains information from the configured region server in the
   cloud environment and uses the information to register the guest with
   the SMT server based on the information provided by the region server.

   The configuration is in ini format and is located in
   etc/regionserverclnt.cfg"""

import ConfigParser
import glob
import logging
import os
import requests
import subprocess
import sys

from lxml import etree

configFile = '/etc/regionserverclnt.cfg'

#==============================================================================
def checkRegistration(smtServerName):
    """Check if the instance is already registerd"""
    credExist = hasCredentials()
    reposExist = hasRepos(smtServerName)
    if reposExist and credExist:
        return 1
    elif reposExist and not credExist:
        removeRepos(smtServerName)
    elif credExist and not reposExist:
        # We might have to rethink this when BYOL comes along and
        # registers with our SMT
        removeCredentials()

    return None

#==============================================================================
def hasCredentials():
    """Check if a credentials file exists."""
    if (os.path.exists('/etc/zypp/credentials.d/NCCcredentials') or
    os.path.exists('/etc/zypp/credentials.d/SCCcredentials')):
        return 1

    return None

#==============================================================================
def hasRepos(smtServerName):
    """Check if repositories exist."""
    repoSrvName = smtServerName.replace('.','_')
    if (glob.glob('/etc/zypp/repos.d/*%s*' %repoSrvName)):
        return 1

    return None

#==============================================================================
def removeCredentials():
    """Remove the server generated credentials"""
    nccCreds = '/etc/zypp/credentials.d/NCCcredentials'
    sccCreds = '/etc/zypp/credentials.d/SCCcredentials'
    if os.path.exists(nccCreds):
        logging.info('Removing credentials: %s' %nccCreds)
        os.unlink(nccCreds)
    if os.path.exists(sccCreds):
        logging.info('Removing credentials: %s' %sccCreds)
        os.unlink(sccCreds)

    return 1

#==============================================================================
def removeRepos(smtServerName):
    """Remove the repositories for the given server"""
    repoSrvName = smtServerName.replace('.','_')
    repos = glob.glob('/etc/zypp/repos.d/*%s*' %repoSrvName)
    for repo in repos:
        logging.info('Removing repo: %s' %repo)
        os.unlink(repo)

    return 1

#==============================================================================
# Support custom logfile with -f command line option
if '-f' in sys.argv:
    idx = sys.argv.index('-f')
    configFile = sys.argv[idx+1]

# Read the configuration
cfg = ConfigParser.RawConfigParser()
try:
    parsed = cfg.read(configFile)
except:
    print 'Could not parse configuration file %s' %configFile
    type, value, tb = sys.exc_info()
    print value.message
    sys.exit(1)
    
if not parsed:
    print 'Error parsing config file: %s' %configFile
    sys.exit(1)

# Set up logging
logFile = '/var/log/cloudregister'
try:
    logging.basicConfig(
        filename=logFile,
        level=logging.INFO,
        format='%(asctime)s %(levelname)s:%(message)s'
        )
except IOError:
    print 'Could not open log file ', logFile, ' for writing.'
    sys.exit(1)

# Get the configured region servers
regionsrvs = cfg.get('server', 'regionsrv')

# Get the location of the cert files for the region servers
certDir =  cfg.get('server', 'certLocation')

# Get the API to use
api = cfg.get('server', 'api')
logging.info('Using API: %s' %api)

# Get the SMT server information from one of the configured region servers
response = None
servers = regionsrvs.split(',')
for srv in servers:
    srvName = srv.strip()
    logging.info('Using region server: %s' %srvName)
    certFile = certDir + '/' + srvName + '.pem'
    if not os.path.isfile(certFile):
        logging.info('No cert found: %s skip this server' %certFile)
        continue
    try:
        response = requests.get('https://%s/%s' %(srvName, api),
                                verify=certFile,
                                timeout=15.0)
        if response.status_code == 200:
            break
        else:
            logging.error('=' *20)
            logging.error('Server returned: %d' %response.status_code)
            logging.error(response.text)
            logging.error('=' *20)
    except:
        logging.error('No response from: %s' %srvName)
        if srv == servers[-1]:
            logging.error('None of the servers responded')
            logging.error('\tAttemted: %s' %servers)
            logging.error('Exiting without registration')
            sys.exit(1)
        continue

if not response.status_code == 200:
    logging.error('Request not answered by any server, exiting')
    sys.exit(1)

# Parse the response to form the arguments for SMT server registration
serverIP = None
serverName = None
severFingerprint = None
testedServers = []
root = etree.fromstring(response.text)
for child in root:
    serverIP = child.attrib['SMTserverIP']
    testedServers.append(serverIP)
    alive = requests.get('http://%s/smt.crt' %serverIP)
    if alive.status_code == 200:
        serverName = child.attrib['SMTserverName']
        severFingerprint = child.attrib['fingerprint']
        # Use the first server that responds
        break

if not severFingerprint:
    logging.error('None response from: %s' %testedServers)
    sys.exit(1)

# Handle hosts file to avoid DNS lookup
knownHosts = open('/etc/hosts', 'r').readlines()
serverKnown = None
updateIP = None
isRegistered = None
for entry in knownHosts:
    if entry.find(serverName) != -1:
        serverKnown = 1
        knownSrvIP = entry.split()[0]
        if knownSrvIP != serverIP:
            # same host name but different IP
            updateIP = knownSrvIP
if not serverKnown:
    hosts = open('/etc/hosts', 'a')
    hosts.write('\n# Added by SMT registration do not remove\n')
    entry = '%s\t%s\t%s' %(serverIP, serverName, serverName.split('.')[0])
    hosts.write(entry)
    hosts.close()
    logging.info('Modified /etc/hosts, added: %s' %entry)
elif serverKnown and updateIP:
    # We know of the server name but the IP has changed
    knownHosts = open('/etc/hosts', 'r').read()
    newContent = knownHosts.replace(updateIP, serverIP)
    hosts = open('/etc/hosts', 'w')
    hosts.write(newContent)
    hosts.close()
    logging.info('Modified /etc/hosts, changed IP for: %s' %serverName)
    isRegistered = checkRegistration(serverName)
elif serverKnown:
    # We know the server, most likely previously registered
    isRegistered = checkRegistration(serverName)

if isRegistered:
    logging.info('Instance is registered, nothing to do')
    sys.exit(0)

# Check if we need to send along any instance data
instDataFile = '/tmp/instanceData.txt'
if cfg.has_section('instance'):
    if cfg.has_option('instance', 'dataProvider'):
        cmdLn = cfg.get('instance', 'dataProvider')
        if cmdLn != 'none':
            cmd = cmdLn.split()[0]
            if cmd[0] != '/':
                try:
                    p = subprocess.Popen(
                        ['which %s' %cmd],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        close_fds=True
                    )
                except:
                    errMsg = 'Could not find configured dataProvider: %s' %cmd
                    logging.error(errMsg)
            if os.access(cmd, os.X_OK):
                # There is nothing secret about the instace data
                # It is expected to be signed such that it cannot be altered
                os.system("%s > %s" %(cmdLn, instDataFile))
            else:
                msg = 'Configured dataProvider "%s" is not executable' %cmd
                logging.error(msg)

cmd = '/usr/lib/suseRegister/bin/clientSetup4SMT.sh '
cmd += '--host %s ' %serverName
cmd += '--fingerprint %s ' %severFingerprint
cmd += '--yes '

if os.path.exists(instDataFile):
    cmd += '--regdata %s' %instDataFile

logging.info('Registration: %s' %cmd)
res = os.system(cmd)

if res:
    logging.error('Registration failed')
