#!/usr/bin/env python

import getopt
import getpass
import os
import signal
import string
import sys
import tempfile
import xml.dom.minidom

import isi.config.array as array

try:
    True
except NameError:
    True = 1
    False = 0

def usage(exitCode):
    sys.stderr.write(r'''
Usage: ''' + os.path.basename(sys.argv[0]) + r''' [OPTION]... FORMAT...

Options:
  -a, --array-name ARRAY        Use ARRAY.
  -A, --array-file FILE         Read array information from FILE. The default
                                is to first look for $HOME/.array.xml, and then
                                /etc/ifs/array.xml.
  -D, --diskless                Include diskless nodes only.
  -h, --help                    Print this message and exit.
  -n, --nodes NODES             Include NODES. Can be specified multiple times.
                                Must be a list of either node names or ranges
                                of LNNs. For example: '1,3-5,neal8,10'. If no
                                nodes are explicitly listed, the whole array is
                                used.
  -S, --storage                 Include storage nodes only.
  -L, --local                   Include the local node only.
  -x, --exclude-nodes NODES     Exclude NODES. This argument is specified the
                                same way as the -n option is.

Environment Variables:

  ISI_ARRAY_NAME                Specifies default array NAME.
  ISI_ARRAY_FILE                Specifies default array FILE.
  ISI_ARRAY_NODES               Specifies default NODES.

FORMAT is a printf-like format string, with specialized conversion. For
example, "echo node %{node} is id %{id}, at address %{ip_address}". Valid
conversions:

  The name of the node:         %{name} or %{node}
  External IP address:          %{address} or %{ip_address} or %{external}
  int-a IP address:             %{internal_a} or %{internal_a_address}
  int-b IP address:             %{internal_b} or %{internal_b_address}
  Failover IP address:          %{failover} or %{failover_address}
  Best internal IP address:     %{internal} or %{internal_address}
  Dynamic IP addresses:         %{dynamic} or %{dynamic_address}
  Node Id:                      %{id} or %{devid}
  LNN:                          %{lnn}
  Power control port:           %{powerctl}
''')
    sys.exit(exitCode)

def main():
    # don't treat ^C special
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    arrayFile = None
    arrayName = None
    nodeOps = array.NodeOps()

    # setup defaults from the environment
    for var in os.environ.keys():
        if var == 'ISI_ARRAY_FILE':
            arrayFile = os.environ[var]
        if var == 'ISI_ARRAY_NAME':
            arrayName = os.environ[var]
        if var == 'ISI_ARRAY_NODES':
            nodeOps.defaultTo(os.environ[var])

    # parse options
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'a:A:DhLn:Sx:',
                ['array-name=', 'array-file=', 'diskless', 'exclude-nodes=',
                 'help', 'local', 'storage', 'nodes='])
        for o, a in opts:
            if o in ['-a', '--array-name']:
                arrayName = a
            if o in ['-A', '--array-file']:
                arrayFile = a
            if o in ['-D', '--diskless']:
                nodeOps.defaultToDiskless()
            if o in ['-h', '--help']:
                usage(0)
            if o in ['-L', '--local']:
                nodeOps.defaultToLocal()
            if o in ['-n', '--nodes']:
                nodeOps.include(a)
            if o in ['-S', '--storage']:
                nodeOps.defaultToStorage()
            if o in ['-x', '--exclude-nodes']:
                nodeOps.exclude(a)
    except getopt.GetoptError:
        usage(1)

    if not len(args):
        usage(1)
    else:
        format = string.join(args)

    try:
        # figure out the array.xml file to use
        if arrayFile == None:
            fileNames = [
                '%s/.array.xml' % os.environ['HOME'],
                '/etc/ifs/array.xml',
            ]
            for arrayFile in fileNames:
                if fileExists(arrayFile):
                    break
            else:
                error('no array.xml file found')
        
        # now parse the array.xml file and grab the
        # requested set of nodes
        nodes = array.getNodes(nodeOps, arrayFile, arrayName)
        decorated = [(node.lnn(), node) for node in nodes]
        decorated.sort()
        nodes = [node for (lnn, node) in decorated]

        for node in nodes:
            print substitute(format, node)

    except EnvironmentError, e:
        error(str(e))

def substitute(format, node):
    out = ''
    while True:
        i = format.find('%{')
        if i < 0:
            out += format
            break
        out += format[0:i]
        j = format.find('}', i + 2)
        if j < 0:
            error('specifier "%s" missing "}"' % format[i:]);
        conversion = format[i+2:j]
        format = format[j+1:]
        if conversion in ['name', 'node']:
            out += node.name()
        elif conversion in ['ip_address', 'address', 'external']:
            try:
                out += str(node.external(allowInt=False))
            except array.Error:
                out += "None"
        elif conversion in ['internal_address', 'internal']:
            out += str(node.bestInternalIP())
        elif conversion in ['internal_a_address', 'internal_a']:
            out += str(node.internal_a())
        elif conversion in ['internal_b_address', 'internal_b']:
            out += str(node.internal_b())
        elif conversion in ['failover_address', 'failover']:
            out += str(node.failover())
        elif conversion in ['dynamic_address', 'dynamic']:
            if node.dynamic():
                out += ",".join(node.dynamic())
            else:
                out += 'None'
        elif conversion in ['devid', 'id']:
            out += str(node.id())
        elif conversion in ['lnn']:
            out += str(node.lnn())
        elif conversion in ['powerctl']:
            out += str(node.powerctl())
        else:
            error('unknown specifier "%s"' % conversion)
    return out

def fileExists(fileName):
    try:
        os.stat(fileName)
        return True
    except EnvironmentError, e:
        return False

def error(str):
    print >> sys.stderr, os.path.basename(sys.argv[0]) + ':', str
    sys.exit(1)

if __name__ == '__main__':
    try:
        main()
    except array.Error, e:
        error('%s' % e)
