#!/usr/bin/python

#
# Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
#

#
# log
#
# Query log messages from analytics
#

import sys
import argparse
import json
import datetime
import logging
import logging.handlers
import time
import re
from opserver_util import OpServerUtils
from sandesh_common.vns.ttypes import Module
from sandesh_common.vns.constants import ModuleNames, NodeTypeNames
import sandesh.viz.constants as VizConstants
from pysandesh.gen_py.sandesh.ttypes import SandeshType, SandeshLevel
from pysandesh.sandesh_logger import SandeshLogger
from pysandesh.util import UTCTimestampUsec
import commands
import ast

OBJECT_TYPE_LIST = [table_info.log_query_name for table_info in \
    VizConstants._OBJECT_TABLES.values()]
OBJECT_TABLE_MAP = dict((table_info.log_query_name, table_name) for \
   (table_name, table_info) in VizConstants._OBJECT_TABLES.items())
output_file_handle = None

class LogQuerier(object):

    def __init__(self):
        self._args = None
        self._slogger = None
    # end __init__

    def run(self):
        try:
            if self.parse_args() != 0:
                return
            if self._args.tail:
                start_time = UTCTimestampUsec() - 10*pow(10,6)
                while True:
                    self._start_time = start_time
                    self._end_time = UTCTimestampUsec()
                    start_time = self._end_time + 1
                    time.sleep(3)
                    result = self.query()
                    if result == -1:
                        return
                    self.display(result)
            else:
                start_time = self._args.start_time
                end_time = self._args.end_time
                if not self._args.start_time:
                    start_time = "now-10m"
                if not self._args.end_time:
                    end_time = "now"
                try:
                    self._start_time, self._end_time = \
                        OpServerUtils.parse_start_end_time(
                            start_time = start_time,
                            end_time = end_time,
                            last = self._args.last)
                except:
                    return -1
                result = self.query()
                if result == -1:
                    return
                self.display(result)
        except KeyboardInterrupt:
            return

    # Public functions
    def parse_args(self):
        """
        Eg. python log.py --analytics-api-ip 127.0.0.1
                          --analytics-api-port 8181
                          --source 127.0.0.1
                          --node-type Control
                          --module bgp | cfgm | vnswad
                          --instance-id 0
                          --message-type UveVirtualMachineConfigTrace
                          --category xmpp
                          --level SYS_INFO | SYS_ERROR
                          --object-type virtual-network | virtual-machine
                          --object-id name
                          --object-select-field ObjectLog | SystemLog
                          --reverse
                          --verbose
                          --raw
                          --trace BgpPeerTraceBuf
                          [--start-time now-10m --end-time now] | --last 10m
                          --send-syslog
                          --syslog-server 127.0.0.1
                          --syslog-port 514
                          --keywords comma,seperated,list
        """
        defaults = {
            'analytics_api_ip': '127.0.0.1',
            'analytics_api_port': '8181',
        }

        parser = argparse.ArgumentParser(
            formatter_class=argparse.ArgumentDefaultsHelpFormatter)
        parser.set_defaults(**defaults)
        parser.add_argument("--analytics-api-ip", help="IP address of Analytics API Server")
        parser.add_argument("--analytics-api-port", help="Port of Analytics API Server")
        parser.add_argument(
            "--start-time", help="Logs start time (format now-10m, now-1h)")
        parser.add_argument("--end-time", help="Logs end time")
        parser.add_argument(
            "--last", help="Logs from last time period (format 10m, 1d)")

        parser.add_argument("--source", help="Logs from source address")
        parser.add_argument("--node-type", help="Logs from node type",
            choices=NodeTypeNames.values())
        parser.add_argument(
            "--module", help="Logs from module", choices=ModuleNames.values())
        parser.add_argument("--instance-id", help="Logs from module instance")
        parser.add_argument("--category", help="Logs of category")
        parser.add_argument("--level", help="Logs of level")
        parser.add_argument("--message-type", help="Logs of message type")
        parser.add_argument("--reverse", action="store_true",
                            help="Show logs in reverse chronological order")
        parser.add_argument(
            "--verbose", action="store_true", help="Show internal information")
        parser.add_argument(
            "--raw", action="store_true", help="Show raw XML messages")

        parser.add_argument(
            "--object-type", help="Logs of object type", choices=OBJECT_TYPE_LIST)
        parser.add_argument("--object-values", action="store_true",
            help="Display list of object names")
        parser.add_argument("--object-id", help="Logs of object name")
        parser.add_argument(
            "--object-select-field", help="Select field to filter the log",
            choices=[VizConstants.OBJECT_LOG, VizConstants.SYSTEM_LOG])
        parser.add_argument("--trace", help="Dump trace buffer")
        parser.add_argument("--limit", help="Limit the number of messages")
        parser.add_argument("--send-syslog", action="store_true",
                            help="Send syslog to specified server and port")
        parser.add_argument("--syslog-server",
            help="IP address of syslog server", default='localhost')
        parser.add_argument("--syslog-port", help="Port to send syslog to",
            type=int, default=514)
        parser.add_argument("--tail","-f", help="Tail logs from now", action="store_true")
        parser.add_argument("--keywords", help="comma seperated list of keywords")
        parser.add_argument("--message-types", \
         help="Display list of message type", action="store_true")
        parser.add_argument("--output-file", "-o", help="redirect output to file")
        parser.add_argument("--json", help="Dump output as json", action="store_true")
        parser.add_argument("--all", action="store_true", help=argparse.SUPPRESS)
        parser.add_argument("--admin-user", help="Name of admin user", default="admin")
        parser.add_argument("--admin-password", help="Password of admin user",
            default="contrail123")
        self._args = parser.parse_args()
        return 0
    # end parse_args

    # Public functions
    def query(self):
        if self._args.tail and (self._args.send_syslog or self._args.reverse or
               self._args.start_time or self._args.end_time):
            invalid_combination = " --tail"
            if self._args.send_syslog:
                 invalid_combination += ", --send-syslog"
            if self._args.reverse:
                 invalid_combination += ", --reverse"
            if self._args.start_time:
                 invalid_combination += ", --start-time"
            if self._args.end_time:
                 invalid_combination += ", --end-time"
            print "Combination of options" + invalid_combination + " are not valid."
            return -1
        global output_file_handle
        if self._args.output_file is not None:
            if output_file_handle is None:
               #Open the file for writing
               try:
                   if self._args.tail:
                      output_file_handle = open(self._args.output_file, "a")
                   else:
                      output_file_handle = open(self._args.output_file, "w")
               except Exception as e:
                   print e
                   print "Exception occured when creating/opening file %s" % \
                         self._args.output_file
                   return -1

        start_time, end_time = self._start_time, self._end_time
        if self._args.message_types is True:
            command_str = ("contrail-stats --table FieldNames.fields" +
               " --where name=MessageTable:Messagetype --select name fields.value" +
               " --start-time " + str(start_time) +
               " --end-time " + str(end_time) +
               " --analytics-api-ip " + str(self._args.analytics_api_ip) +
               " --analytics-api-port " + str(self._args.analytics_api_port))
            res = commands.getoutput(command_str)
            res = res.splitlines()
            res = res[1:]
            for r in res:
                print ast.literal_eval(r)['fields.value']
            return None
        messages_url = OpServerUtils.opserver_query_url(
            self._args.analytics_api_ip,
            self._args.analytics_api_port)
        where_msg = []
        where_obj = []
        and_filter = []
        or_filter = []
        if self._args.source is not None:
            if self._args.source.endswith('*'):
                val = self._args.source[:-1]
                oper = OpServerUtils.MatchOp.PREFIX
            else:
                val = self._args.source
                oper = OpServerUtils.MatchOp.EQUAL
            source_match = OpServerUtils.Match(name=VizConstants.SOURCE,
                                               value=val, op=oper)
            where_msg.append(source_match.__dict__)

        if self._args.module is not None:
            module_match = OpServerUtils.Match(name=VizConstants.MODULE,
                                               value=self._args.module,
                                               op=OpServerUtils.MatchOp.EQUAL)
            where_msg.append(module_match.__dict__)

        if self._args.category is not None:
            if self._args.category.endswith('*'):
                val = self._args.category[:-1]
                oper = OpServerUtils.MatchOp.PREFIX
            else:
                val = self._args.category
                oper = OpServerUtils.MatchOp.EQUAL
            category_match = OpServerUtils.Match(
                name=VizConstants.CATEGORY,
                value=val, op=oper)
            where_msg.append(category_match.__dict__)

        if self._args.message_type is not None: