#!/usr/bin/env python
from __future__ import print_function
import cloudmaker.digitalocean
import json
import logging
import os.path
import re
import subprocess
import sys
import time

   

def runRemoteQuietly(user, host, *args):
    newargs = ['ssh', '-o', 'StrictHostKeyChecking=no', user + '@' + host] + list(args)
    
    cmd = ' '.join(newargs)
    
    p = subprocess.Popen(newargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = p.communicate()
    if p.returncode == 0:
        logging.info('"' + cmd + '" succeeded')
    
    else:
        msg = '"' + cmd + '" failed with the following output: \n\t' + output[0]
        logging.error(msg)
        raise Exception(msg)

def runRemote(user, host, *args):
    newargs = ['ssh', '-o', 'StrictHostKeyChecking=no', user + '@' + host] + list(args)
    subprocess.check_call(newargs)

   
    
def writeInventory():
    # make a map of domain records by IP address - one for A records
    # and another for AAAA records- key is IP address, value is list of names
    ARecordMap = dict()
    AAAARecordMap = dict()
    for domain in do.listDomains()["domains"]:
        dname = domain['name']
        for drec in do.listDomainRecords(dname)['domain_records']:
            if drec['type'] == 'A':
                if drec['name'] == '@':
                    name = dname
                else:
                    name =  drec['name']  + '.' + dname
                    
                ip = drec['data']
                
                if ip not in ARecordMap:
                    ARecordMap[ip] = []
                    
                ARecordMap[ip].append(name)
            elif drec['type'] == 'AAAA':
                if drec['name'] == '@':
                    name = dname
                else:
                    name =  drec['name']  + '.' + dname
                    
                ip = drec['data'].lower()
                if ip not in AAAARecordMap:
                    AAAARecordMap[ip] = []
                    
                AAAARecordMap[ip].append(name)
                
    #now got through all droplet defs, creating a cloud_maker formatted
    #inventory file
    inventory = dict()
    for droplet in do.listDroplets()['droplets']:
        cloudmakerDroplet =  dict()
        cloudmakerDroplet['region'] = droplet['region']['slug']
        cloudmakerDroplet['image'] = droplet['image']['slug']
        cloudmakerDroplet['size'] = droplet['size_slug']
        cloudmakerDroplet['backups'] = 'backups' in droplet['features']
        cloudmakerDroplet['public_network_interfaces'] = dict()
        cloudmakerDroplet['private_network_interfaces'] = dict()
        cloudmakerDroplet['names'] = []
        for network in droplet['networks']['v4']:
            if network['type'] == 'public':
                cloudmakerDroplet['public_network_interfaces']['ipv4'] = network['ip_address']
                if network['ip_address'] in ARecordMap:
                    for name in ARecordMap[network['ip_address']]:
                        if name not in cloudmakerDroplet['names']:
                            cloudmakerDroplet['names'].append(name)
            else :
                cloudmakerDroplet['private_network_interfaces']['ipv4'] = network['ip_address']
                
        for network in droplet['networks']['v6']:
            if network['type'] == 'public':
                cloudmakerDroplet['public_network_interfaces']['ipv6'] = network['ip_address'].lower()
                if network['ip_address'].lower() in AAAARecordMap:
                    for name in AAAARecordMap[network['ip_address'].lower()]:
                        if name not in cloudmakerDroplet['names']:
                            cloudmakerDroplet['names'].append(name)
            else :
                cloudmakerDroplet['private_network_interfaces']['ipv6'] = network['ip_address']

        inventory[droplet['name']] = cloudmakerDroplet
        
    with open('inventory.json', 'w') as outfile:
        json.dump(inventory,outfile,indent = 3)
    
    print('wrote inventory to file: "inventory.json"', file=sys.stderr)
    
    
def deploy(directory):
    deployArgs = loadDigitalOceanDeployArgs(directory)
    
    dropletDef = do.deploy(deployArgs)
    if 'dnsRecords' in deployArgs:
        for name in deployArgs['dnsRecords']:
            do.createNameRecords(dropletDef,name)

    #install cloudmaker on the remote system
    #use ip address in case the name hasn't propagated
    ipv4 = do.publicAddressIPV4(dropletDef)
    runRemoteQuietly('root',ipv4,'apt-get','update')
    runRemoteQuietly('root',ipv4,'apt-get','install', '-y','python-pip')
    runRemoteQuietly('root',ipv4,'pip','install', 'cloudmaker')
    
    #copy the install resources and setup.py
    setupFileExists = False
    if os.path.isfile(os.path.join(directory,'setup.py')):
        setupFileExists = True

    if directory.endswith('/'):
        directory = directory[:-1]
        
    subprocess.check_call(['rsync', '-avz','--delete', '-e' ,'ssh -o StrictHostKeyChecking=no', directory + '/', 'root@' + ipv4 + ':/tmp/setup'])
    
    if setupFileExists:
        runRemote('root',ipv4,'chmod', '+x', '/tmp/setup/setup.py')
        runRemote('root',ipv4,'/tmp/setup/setup.py')
        
    runRemote('root',ipv4,'rm','-rf','/tmp/setup')
    
def undeploy(directory):
    deployArgs = loadDigitalOceanDeployArgs(directory)
    if 'dnsRecords' in deployArgs:
        for name in deployArgs['dnsRecords']:
            do.removeNameRecords(name)
    
    do.undeploy(deployArgs['name'])
                        
def printUsage():
    print('Usage:',file=sys.stderr)
    print('   cloudmaker inventory              prints inventory to cloudmaker_inventory.json',file=sys.stderr)
    print('   cloudmaker deploy <dir>    deploys server described in <dir>',file=sys.stderr)
    print('   cloudmaker ubdeploy <dir>  undeploys server described in <dir>',file=sys.stderr)
    
    
def loadDigitalOceanDeployArgs(directory):

    configFile = os.path.join(directory,'server.json')
    if not os.path.isfile(configFile):
        sys.exit('required configuration file: {0} is not present'.format(configFile) )
        
    with open(os.path.join(directory,'server.json'), 'r') as cloudFile:
        config = json.load(cloudFile)
        
    if not 'digitalocean' in config:
        sys.exit('{0} must be a json document containing the key: {1}'.format(configFile,'digitalocean'))
        
    if not 'provision' in config['digitalocean']:
        sys.exit('"digitalocean" section of {0} must contain the key: "provision"'.format(configFile))

    return config["digitalocean"]["provision"]
    
    
if __name__ == '__main__':
    logging.basicConfig(level='INFO')
    
    if len(sys.argv) < 2:
        printUsage()
        sys.exit(1)
            
    cmd = sys.argv[1]
    
    if cmd != 'inventory':
        directory = sys.argv[2]
        if not os.path.isdir(directory):
            sys.exit('"{0}" must be a directory'.format(directory))
            
        if not os.path.isfile(os.path.join(directory,'server.json')):
            sys.exit('"{0}" must contain a config file named "server.json"'.format(directory))
            
        with open(os.path.join(directory,'server.json')) as configFile:
            config = json.load(configFile)
            
        if (not 'digital_ocean' in config) or (not 'security' in config['digital_ocean']):
            sys.exit('"{0}" must contain a "digital_ocean/security" section.'.format(os.path.join(directory,'server.json')))
                     
        do = cloudmaker.digitalocean.Context(config['digital_ocean']['security'])
            
    else:
        do = cloudmaker.digitalocean.Context()
    
    
    if cmd == 'inventory':
            writeInventory()
    elif cmd == 'deploy':
        deploy(directory)
    elif cmd == 'undeploy':
        undeploy(directory)
        pass
    else:
        sys.exit('unknown command: ' + cmd)
        
    