Created
May 3, 2018 19:09
-
-
Save madsc13ntist/d1594fccf681f103b0b5577e37aafb62 to your computer and use it in GitHub Desktop.
A generic script for sending (arbitrary) Syslog messages with Python.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python2.7 | |
| # -*- coding: utf-8 -*- | |
| ### Import Modules | |
| import os | |
| import socket | |
| import hashlib | |
| import logging | |
| import argparse | |
| from argparse import RawTextHelpFormatter | |
| import ConfigParser | |
| import warnings | |
| warnings.filterwarnings("ignore", category=DeprecationWarning) | |
| __version__ = "0.0.1" | |
| __date__ = "05.03.2018" | |
| __author__ = "Joseph Zeranski" | |
| __maintainer__ = "Joseph Zeranski" | |
| __email__ = "madsc13ntist@gmail.com" | |
| __copyright__ = "Copyright (c) 2018 " + __author__ | |
| __credits__ = [""] | |
| __description__= "Send a Syslog message to listening syslog servers/connectors." | |
| __build__ = "" | |
| '''''''''' Setup additional formatted usage info ''''''''''' | |
| if not __build__: | |
| __build__ = hashlib.md5(open(__file__, 'rb').read()).hexdigest() | |
| EPILOG = "" | |
| if __description__ not in ["", [""], None, False]: | |
| EPILOG += __description__ + "\n" | |
| EPILOG += "version {0!s}, build {1!s}\n".format(__version__, __build__) | |
| EPILOG += "{0!s} <{1!s}>".format(__copyright__, __email__) | |
| if __credits__ not in ["", [""], None, False]: | |
| EPILOG += "\nThanks go out to " | |
| if isinstance(__credits__, str): | |
| EPILOG += __credits__ + "." | |
| elif isinstance(__credits__, list): | |
| if len(__credits__) == 1: | |
| EPILOG += __credits__[0] + "." | |
| else: | |
| EPILOG += "{0!s} and {1!s}.".format(', '.join(__credits__[:-1]), __credits__[-1]) | |
| '''''''''' setup usage/command line options ''''''''''' | |
| parser = argparse.ArgumentParser(description=EPILOG, formatter_class=RawTextHelpFormatter) | |
| parser.add_argument('-s', '--server', | |
| dest = "syslog_server", | |
| action = "append", | |
| default=[], | |
| required = True, | |
| help='The IP Address of the Syslog Server/Connector.') | |
| parser.add_argument('-p', '--port', | |
| dest = "syslog_port", | |
| action = "store", | |
| default="514", | |
| help='The port number that the Syslog Server/Connector is listening on. [default: 514]') | |
| parser.add_argument('-P', '--protocol', | |
| dest = "syslog_protocol", | |
| action = "store", | |
| default="udp", | |
| help='The protocol to send Syslog messages with. (either tcp or udp). [default: udp]') | |
| parser.add_argument("-c", "--config", | |
| dest="config", | |
| action = "store", | |
| default = os.path.realpath(__file__).replace('.py', '.conf'), | |
| help = "The configuration file to use. [default: {0!s}]".format(os.path.realpath(__file__).replace('.py', '.conf'))) | |
| group = parser.add_mutually_exclusive_group(required=True) | |
| group.add_argument('-m', '--message', | |
| dest = "message", | |
| action = "append", | |
| default=[], | |
| help='The message/text to send to the listening Syslog Server(s)/Connector(s).') | |
| group.add_argument('-f', '--file', | |
| dest = "text_file", | |
| action = "append", | |
| default=[], | |
| help='Use the lines of a text file for the Syslog Message. (one message per line)') | |
| parser.add_argument("-v", "--verbose", | |
| dest = "verbose", | |
| action = "store_true", | |
| default = False, | |
| help = "Print additional information.") | |
| parser.add_argument("-vv", "--debug", | |
| dest="debug", | |
| action = "store_true", | |
| default = False, | |
| help = "Print debug information to the screen.") | |
| parser.add_argument("-l", "--log", | |
| dest="log", | |
| action = "store", | |
| default = False, | |
| help = "The path to a log file of results.") | |
| '''''''''' Parse arguments from user ''''''''''' | |
| args, unknown = parser.parse_known_args() | |
| '''''''''' Define Functions ''''''''''' | |
| def syslog(data, host, port=514, proto='udp'): | |
| """ | |
| Send data over a TCP connection. | |
| :param data: the syslog message to send. | |
| :param host: The Syslog server's ip address. | |
| :param port: the port number to send the syslog to. | |
| :return: returns True on success and error message on failure. | |
| """ | |
| if isinstance(port, str): | |
| port = int(port) | |
| try: | |
| s = False | |
| if proto.lower() == 'tcp': | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP Connection | |
| elif proto.lower() == 'udp': | |
| s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP Connection | |
| s.connect((host,port)) | |
| s.send("{0!s}".format(data)) | |
| s.close() | |
| logging.info("Syslog sent to {0!s} ({1!s}/{2!s}): {3!s}".format(host, port, proto, data)) | |
| return True | |
| except Exception as e: | |
| logging.exception("Syslog failed to send to {0!s} ({1!s}/{2!s}): {3!s}".format(host, port, proto, data)) | |
| logging.error(str(e)) | |
| return str(e) | |
| def lines(filename): | |
| """ | |
| Read in a file and return a generator object. slices are each newline. (without reading the whole file into memory). | |
| :param filename: The text file to read through line by line. | |
| :return: lines of text. (generator object) | |
| """ | |
| line_count = 0 | |
| with open(filename) as fp: | |
| logging.debug("opened '{0!s}' for reading.".format(filename)) | |
| stream = fp.read().replace('\r\n','\n').replace('\r','\n') #Handling for files with various newlines. | |
| for line in stream.split('\n'): | |
| line_count += 1 | |
| yield line | |
| logging.debug("closing '{0!s}' for reading. (read {1:,} lines)".format(filename, line_count)) | |
| def main(): | |
| """ | |
| MAIN FUNCTION IS MAIN | |
| :return: | |
| """ | |
| '''''''''' Log arguments and parameters passed to script (for Debug) ''''''''''' | |
| logging.debug("Running {0!s} version {1!s} (build {2!s})".format(os.path.basename(__file__), __version__, __build__)) | |
| for arg in [x for x in dir(args) if not x.startswith('_')]: | |
| logging.debug("args.{0!s} = {1!s}".format(arg, getattr(args, arg))) | |
| '''''''''' Syslog message(s) off to listening syslog Server(s)/Connector(s) ''''''''''' | |
| for syslog_server in sorted(args.syslog_server): | |
| if args.text_file not in [[], False, ""]: | |
| for text_file in sorted(args.text_file): | |
| if not os.path.isfile(text_file): | |
| logging.error("File not found: {0!s}".format(os.path.realpath(text_file))) | |
| else: | |
| logging.info("Reading '{0!s}'".format(os.path.realpath(text_file))) | |
| for line in lines(text_file): | |
| syslog(str(line), str(syslog_server), int(args.syslog_port), str(args.syslog_protocol)) | |
| else: | |
| for message in sorted(args.message): | |
| syslog(str(message), str(syslog_server), int(args.syslog_port), str(args.syslog_protocol)) | |
| # If the script is being executed (not imported). | |
| if __name__ == "__main__": | |
| try: | |
| # '''''''''' check for config file with static parameters '''''''''''''''''''' | |
| if os.path.isfile(args.config): | |
| args.config = os.path.abspath(args.config) | |
| config = ConfigParser.ConfigParser() | |
| config.read(args.config) | |
| required_args = [] # <------- add required args here! | |
| for arg in [x for x in dir(args) if not x.startswith('_')]: | |
| for section in config.sections(): | |
| if section in ["settings"]: | |
| if config.has_option(section, arg): | |
| setattr(args, arg, eval(config.get(section, arg))) | |
| if arg in required_args: | |
| if getattr(args, arg) in ['', None]: | |
| logging.critical("Missing required option: {0!s}".format(arg)) | |
| exit(1) | |
| # '''''''''' Setup More Robust Logging '''''''''''''''''''' | |
| # Set log level | |
| LOG_LEVEL = logging.WARN | |
| if args.verbose: | |
| LOG_LEVEL = logging.INFO | |
| if args.debug: | |
| LOG_LEVEL = logging.DEBUG | |
| if not args.log: | |
| logging.basicConfig(level=LOG_LEVEL, format='[%(levelname)s] %(funcName)s: %(message)s') | |
| else: | |
| log_file_level = logging.INFO | |
| if LOG_LEVEL == logging.DEBUG: | |
| log_file_level = logging.DEBUG | |
| logging.basicConfig(level=log_file_level, | |
| format='%(asctime)s:%(levelname)s:%(filename)s:%(funcName)s:%(message)s', | |
| datefmt='%Y-%m-%d %H:%M:%S', | |
| filename=args.log, | |
| filemode='a') # This can be set to "w" to over write everytime the script runs. saves space. | |
| # create logging handler taylored for the user. | |
| console = logging.StreamHandler() | |
| console.setLevel(LOG_LEVEL) | |
| formatter = logging.Formatter('[%(levelname)s] %(funcName)s: %(message)s') | |
| console.setFormatter(formatter) | |
| logging.getLogger('').addHandler(console) | |
| # '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' | |
| ''''''''''''' Execute Main ''''''''''''''' | |
| main() # Execute the main section | |
| except Exception as e: | |
| logging.exception("Exception: {0!s}".format(e)) | |
| exit(1) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
it prints usage info if you use "-h".