#!/usr/bin/python
#
# $Id: clients.py,v 1.2 2009-12-02 14:46:44 jloden Exp $
#

import re

# Traverse module local imports
from pytraverse import NameSpace
from pytraverse.debug import tvdebug
from pytraverse.error import *
from pytraverse.SimpleServerClient import SimpleServerClient

class Provisioning(SimpleServerClient):
    """
    Traverse Provisioning API interface.  Constructor is inhereted from
    Traverse.SocketServer and takes the following form (all parameters are
    optional):

    BVE = new Traverse.Provisioning(
        [host     = hostname_or_ip,]
        [port     = tcp_port,]
        [timeout  = timeout,]
        [debug    = <True|False>]
    )

    No connection is made to the remote host when this method is called.  Only
    the object is created with remote host address and port information.  The
    new object returned is given the following defaults in the absence of
    corresponding named arguments:

        The default host is "localhost"

        The default port is 7661

        The default timeout is 30 seconds

        debug is set to False
    """

    def _get_cmd_name(self, method_name):
        """
        Given a method name, return a BVE command or raise execption on
        failure to parse or if an invalid command is specified.

        Example: "ListDevice" becomes device.list

        Return cmd string, or raise InvalidCommand error if parsing fails.
        """

        action = None
        object = None
        membersre = re.compile(r'^Get(\w+)Members\s*(.*)$')
        statusre = re.compile(r'^Get(\w+)Status\s*(.*)$')
        actionre = re.compile(r'^(Create|Delete|Update|List|Suspend|Resume|Represent|Suppress)(.*)$')
        actionObj = actionre.search(method_name)
        statusObj = statusre.search(method_name)
        membersObj = membersre.search(method_name)
        if actionObj:
            action = actionObj.group(1)
            object = actionObj.group(2)
        elif statusObj:
            action = "Status"
            object = statusObj.group(1)
        elif membersObj:
            action = "Members"
            object = membersObj.group(1)

        else:
            tvdebug.debug("requested command %s does not match the expected format" % (method_name))
            self.ERRMSG = "unknown command %s" % (method_name)
            raise InvalidCommand("unknown command %s" % (method_name))

        if object.lower() in NameSpace.TRAVERSE_ALIASES_NQP.keys():
            newobject = NameSpace.TRAVERSE_ALIASES_NQP[object.lower()]
            tvdebug.debug("translating requested object name %s to %s" % (object, newobject))
            object = newobject

        if object.lower() not in NameSpace.TRAVERSE_BVEAPI_OBJECTS:
            raise InvalidCommand("unknown object type \'%s\' for command %s.%s" % (object, object, action))

        return "%s.%s" % (object, action)


class Message(SimpleServerClient):
    """
    Traverse Socket/Input Stream Monitor API interface.  Constructor is
    inhereted from Traverse.SocketServer and takes the following form (all
    parameters are optional):

    BVE = new Traverse.Message(
        [host     = hostname_or_ip,]
        [port     = tcp_port,]
        [timeout  = timeout,]
        [debug    = <True|False>]
    )

    No connection is made to the remote host when this method is called.  Only
    the object is created with remote host address and port information.  The
    new object returned is given the following defaults in the absence of
    corresponding named arguments:

        The default host is "localhost"

        The default port is 7659

        The default timeout is 30 seconds

        debug is set to False
    """

    def __init__(self, host="localhost", port=7659, timeout=30, debug=False):
        SimpleServerClient.__init__(self, host, port, timeout, debug)

    def _get_cmd_name(self, method_name):
        """
        ISM API only implements one command, message.insert, so this is
        implemented as a static method InsertMessage(). Any other methods
        are invalid so we raise InvalidCommand if anything else is called.
        """
        raise InvalidCommand("invalid method %s" % method_name)


    def InsertMessage(self, devicename, address, severity, message, testname="", testserial="", time="0"):
        """
        Insert a message via the Input Stream Monitor using the provided
        parameters. devicename, address, severity, and message are required.
        """

        # message.insert device_name | device_addr | test_name | test_serial | date_time | severity | message
        msg_insert_cmd = "message.insert %s | %s | %s | %s | %s | %s | %s" % (
            devicename,
            address,
            testname,
            testserial,
            time,
            severity.upper(),
            message
        )

        self._api_send(msg_insert_cmd)
        return self.GetSuccess()

    def InsertRawMessage(self, msg):
        """
        Insert a message via a socket interface, with no processing of text.
        Use InsertRawMessage when not using the ISM-specific pipe-delimited
        format.
        """
        self._api_send(msg)
        return self.GetSuccess()


class ExternalData(SimpleServerClient):
    """
    Traverse External Data Feed API interface.  Constructor is inhereted from
    Traverse.SocketServer and takes the following form (all parameters are
    optional):

    BVE = new Traverse.ExternalData(
        [host     = hostname_or_ip,]
        [port     = tcp_port,]
        [timeout  = timeout,]
        [debug    = <True|False>]
    )

    No connection is made to the remote host when this method is called.  Only
    the object is created with remote host address and port information.  The
    new object returned is given the following defaults in the absence of
    corresponding named arguments:

        The default host is "localhost"

        The default port is 7657

        The default timeout is 30 seconds

        debug is set to False
    """

    def __init__(self, host="localhost", port=7657, timeout=30, debug=False):
        SimpleServerClient.__init__(self, host, port, timeout, debug)

    def _get_cmd_name(self, method_name):
        """
        ISM API only implements one command, message.insert, so this is
        implemented as a static method InsertResult(). Any other methods
        are invalid so we raise InvalidCommand if anything else is called.
        """
        raise InvalidCommand("invalid method %s" % method_name)


    #  [deviceName => $device_name,]
    #  [deviceAddr => $device_fqdn_or_ip,]
    #  [testName=  => $test_name,]
    #  [testSerial => $test_serial_num,]
    #  [dateTime   => time(),]
    #  [result     => $value_to_insert,
    #  [extraInfo  => $additional_info])
    def InsertResult(self, devicename, address, testname, testserial, result, time="0"):
        """
        Insert a message via the Input Stream Monitor using the provided
        parameters. All parameters are required except for time. At least one of
        testname or testserial must be also be provided. If testname is given,
        then testserial can be passed as an empty string, or if testserial is
        given, then testname can be an empty string, but at least one must be
        passed as a non-empty string.
        """

        # Result.insert device_name | device_addr | test_name | test_serial | date_time | result_value
        insert_cmd = "result.insert %s | %s | %s | %s | %s | %s" % (
            devicename,
            address,
            testname,
            testserial,
            time,
            result,
        )

        self._api_send(insert_cmd)
        return self.GetSuccess()


# unit tests
if __name__ == "__main__":
    pass
