def main(argv=None): ''' Main function for the collector start-up. Called with command-line arguments: * --config *<file>* * --section *<section>* * --verbose Where: *<file>* specifies the path to the configuration file. *<section>* specifies the section within that config file. *verbose* generates more information in the log files. The process listens for REST API invocations and checks them. Errors are displayed to stdout and logged. ''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = 'v{0}'.format(__version__) program_build_date = str(__updated__) program_version_message = '%%(prog)s {0} ({1})'.format(program_version, program_build_date) if (__import__('__main__').__doc__ is not None): program_shortdesc = __import__('__main__').__doc__.split('\n')[1] else: program_shortdesc = 'Running in test harness' program_license = '''{0} Created on {1}. Copyright 2015 Metaswitch Networks Ltd. All rights reserved. Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE '''.format(program_shortdesc, str(__date__)) try: #---------------------------------------------------------------------- # Setup argument parser so we can parse the command-line. #---------------------------------------------------------------------- parser = ArgumentParser(description=program_license, formatter_class=ArgumentDefaultsHelpFormatter) parser.add_argument('-v', '--verbose', dest='verbose', action='count', help='set verbosity level') parser.add_argument('-V', '--version', action='version', version=program_version_message, help='Display version information') parser.add_argument('-a', '--api-version', dest='api_version', default='5', help='set API version') parser.add_argument('-c', '--config', dest='config', default='/etc/opt/att/collector.conf', help='Use this config file.', metavar='<file>') parser.add_argument('-s', '--section', dest='section', default='default', metavar='<section>', help='section to use in the config file') #---------------------------------------------------------------------- # Process arguments received. #---------------------------------------------------------------------- args = parser.parse_args() verbose = args.verbose api_version = args.api_version config_file = args.config config_section = args.section #---------------------------------------------------------------------- # Now read the config file, using command-line supplied values as # overrides. #---------------------------------------------------------------------- defaults = {'log_file': 'collector.log', 'vel_port': '12233', 'vel_path': '', 'vel_topic_name': '', 'transport_prot': 'http' } overrides = {} config = ConfigParser.SafeConfigParser(defaults) config.read(config_file) #---------------------------------------------------------------------- # extract the values we want. #---------------------------------------------------------------------- log_file = config.get(config_section, 'log_file', vars=overrides) vel_port = config.get(config_section, 'vel_port', vars=overrides) vel_path = config.get(config_section, 'vel_path', vars=overrides) transport_prot = config.get(config_section, 'protocol', vars=overrides) vel_topic_name = config.get(config_section, 'vel_topic_name', vars=overrides) if (transport_prot.lower() != 'http' and transport_prot.lower() != 'https' ): logger.error('Invalid Transport must be http or https ({0}) ' 'specified'.format(transport_prot)) raise RuntimeError('Invalid Transport protcol specified ({0}) ' 'specified'.format(transport_prot)) global vel_username global vel_password vel_username = config.get(config_section, 'vel_username', vars=overrides) vel_password = config.get(config_section, 'vel_password', vars=overrides) vel_schema_file = config.get(config_section, 'schema_file', vars=overrides) base_schema_file = config.get(config_section, 'base_schema_file', vars=overrides) throttle_schema_file = config.get(config_section, 'throttle_schema_file', vars=overrides) test_control_schema_file = config.get(config_section, 'test_control_schema_file', vars=overrides) #---------------------------------------------------------------------- # Finally we have enough info to start a proper flow trace. #---------------------------------------------------------------------- global logger print('Logfile: {0}'.format(log_file)) logger = logging.getLogger('collector') if verbose > 0: print('Verbose mode on') logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=1000000, backupCount=10) if (transport_prot.lower() == 'https' ): transport_prot = transport_prot.lower() ca_file = config.get(config_section, 'ca_file', vars=overrides) cert_file = config.get(config_section, 'cert_file', vars=overrides) key_file = config.get(config_section, 'key_file', vars=overrides) if not os.path.exists(ca_file): logger.error('Event Listener SSL CA File ({0}) not found. ' 'No validation will be undertaken.'.format(ca_file)) raise RuntimeError('Invalid CA file ({0}) ' 'specified'.format(ca_file)) if not os.path.exists(cert_file): logger.error('Event Listener SSL Certificate File ({0}) not found. ' 'No validation will be undertaken.'.format(cert_file)) raise RuntimeError('Invalid Certificate file ({0}) ' 'specified'.format(cert_file)) if not os.path.exists(key_file): logger.error('Event Listener SSL Key File ({0}) not found. ' 'No validation will be undertaken.'.format(key_file)) raise RuntimeError('Invalid Key file ({0}) ' 'specified'.format(key_file)) if (platform.system() == 'Windows'): date_format = '%Y-%m-%d %H:%M:%S' else: date_format = '%Y-%m-%d %H:%M:%S.%f %z' formatter = logging.Formatter('%(asctime)s %(name)s - ' '%(levelname)s - %(message)s', date_format) handler.setFormatter(formatter) logger.addHandler(handler) logger.info('Started') #---------------------------------------------------------------------- # Log the details of the configuration. #---------------------------------------------------------------------- logger.debug('Log file = {0}'.format(log_file)) logger.debug('Event Listener Transport = {0}'.format(transport_prot)) logger.debug('Event Listener Port = {0}'.format(vel_port)) logger.debug('Event Listener Path = {0}'.format(vel_path)) logger.debug('Event Listener Topic = {0}'.format(vel_topic_name)) logger.debug('Event Listener Username = {0}'.format(vel_username)) # logger.debug('Event Listener Password = {0}'.format(vel_password)) logger.debug('Event Listener JSON Schema File = {0}'.format( vel_schema_file)) logger.debug('Base JSON Schema File = {0}'.format(base_schema_file)) logger.debug('Throttle JSON Schema File = {0}'.format( throttle_schema_file)) logger.debug('Test Control JSON Schema File = {0}'.format( test_control_schema_file)) #---------------------------------------------------------------------- # Perform some basic error checking on the config. #---------------------------------------------------------------------- if (int(vel_port) < 1024 or int(vel_port) > 65535): logger.error('Invalid Vendor Event Listener port ({0}) ' 'specified'.format(vel_port)) raise RuntimeError('Invalid Vendor Event Listener port ({0}) ' 'specified'.format(vel_port)) if (len(vel_path) > 0 and vel_path[-1] != '/'): logger.warning('Event Listener Path ({0}) should have terminating ' '"/"! Adding one on to configured string.'.format( vel_path)) vel_path += '/' #---------------------------------------------------------------------- # Load up the vel_schema, if it exists. #---------------------------------------------------------------------- if not os.path.exists(vel_schema_file): logger.warning('Event Listener Schema File ({0}) not found. ' 'No validation will be undertaken.'.format( vel_schema_file)) else: global vel_schema global throttle_schema global test_control_schema vel_schema = json.load(open(vel_schema_file, 'r')) logger.debug('Loaded the JSON schema file') #------------------------------------------------------------------ # Load up the throttle_schema, if it exists. #------------------------------------------------------------------ if (os.path.exists(throttle_schema_file)): logger.debug('Loading throttle schema') throttle_fragment = json.load(open(throttle_schema_file, 'r')) throttle_schema = {} throttle_schema.update(vel_schema) throttle_schema.update(throttle_fragment) logger.debug('Loaded the throttle schema') #------------------------------------------------------------------ # Load up the test control _schema, if it exists. #------------------------------------------------------------------ if (os.path.exists(test_control_schema_file)): logger.debug('Loading test control schema') test_control_fragment = json.load( open(test_control_schema_file, 'r')) test_control_schema = {} test_control_schema.update(vel_schema) test_control_schema.update(test_control_fragment) logger.debug('Loaded the test control schema') #------------------------------------------------------------------ # Load up the base_schema, if it exists. #------------------------------------------------------------------ if (os.path.exists(base_schema_file)): logger.debug('Updating the schema with base definition') base_schema = json.load(open(base_schema_file, 'r')) vel_schema.update(base_schema) logger.debug('Updated the JSON schema file') #---------------------------------------------------------------------- # We are now ready to get started with processing. Start-up the various # components of the system in order: # # 1) Create the dispatcher. # 2) Register the functions for the URLs of interest. # 3) Run the webserver. #---------------------------------------------------------------------- root_url = '/{0}eventListener/v{1}{2}'.\ format(vel_path, api_version, '/' + vel_topic_name if len(vel_topic_name) > 0 else '') throttle_url = '/{0}eventListener/v{1}/clientThrottlingState'.\ format(vel_path, api_version) batch_url = '/{0}eventListener/v{1}/eventBatch'.\ format(vel_path, api_version) set_404_content(root_url) dispatcher = PathDispatcher() vendor_event_listener = partial(listener, schema = vel_schema) dispatcher.register('GET', root_url, vendor_event_listener) dispatcher.register('POST', root_url, vendor_event_listener) batch_event_listener = partial(listener, schema = vel_schema) dispatcher.register('GET', batch_url, batch_event_listener) dispatcher.register('POST', batch_url, batch_event_listener) vendor_throttle_listener = partial(listener, schema = throttle_schema) dispatcher.register('GET', throttle_url, vendor_throttle_listener) dispatcher.register('POST', throttle_url, vendor_throttle_listener) #---------------------------------------------------------------------- # We also add a POST-only mechanism for test control, so that we can # send commands to a single attached client. #---------------------------------------------------------------------- test_control_url = '/testControl/v{0}/commandList'.format(api_version) test_control_listener = partial(test_listener, schema = test_control_schema) dispatcher.register('POST', test_control_url, test_control_listener) dispatcher.register('GET', test_control_url, test_control_listener) httpd = make_server('', int(vel_port), dispatcher) if (transport_prot == 'https' ): #httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, ca_certs = "../../../sslcerts/test.ca.pem", certfile="../../../sslcerts/www.testsite.com.crt", keyfile="../../../sslcerts/www.testsite.com.key", cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1_2) logger.debug('Invoking HTTP Secure mode : ca file {0} cert file {1} key file {2} '.format(ca_file,cert_file,key_file)) httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, ca_certs=ca_file, certfile=cert_file, keyfile=key_file, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1_2) print('Serving on port {0}...'.format(vel_port)) httpd.serve_forever() logger.error('Main loop exited unexpectedly!') return 0 except KeyboardInterrupt: #---------------------------------------------------------------------- # handle keyboard interrupt #---------------------------------------------------------------------- logger.info('Exiting on keyboard interrupt!') return 0 except Exception as e: #---------------------------------------------------------------------- # Handle unexpected exceptions. #---------------------------------------------------------------------- if DEBUG or TESTRUN: raise(e) indent = len(program_name) * ' ' sys.stderr.write(program_name + ': ' + repr(e) + '\n') sys.stderr.write(indent + ' for help use --help\n') sys.stderr.write(traceback.format_exc()) logger.critical('Exiting because of exception: {0}'.format(e)) logger.critical(traceback.format_exc()) return 2
def main(argv=None): ''' Main function for the collector start-up. Called with command-line arguments: * --config *<file>* * --section *<section>* * --verbose Where: *<file>* specifies the path to the configuration file. *<section>* specifies the section within that config file. *verbose* generates more information in the log files. The process listens for REST API invocations and checks them. Errors are displayed to stdout and logged. ''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = 'v{0}'.format(__version__) program_build_date = str(__updated__) program_version_message = '%%(prog)s {0} ({1})'.format(program_version, program_build_date) if (__import__('__main__').__doc__ is not None): program_shortdesc = __import__('__main__').__doc__.split('\n')[1] else: program_shortdesc = 'Running in test harness' program_license = '''{0} Created on {1}. Copyright 2015 Metaswitch Networks Ltd. All rights reserved. Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE '''.format(program_shortdesc, str(__date__)) try: #---------------------------------------------------------------------- # Setup argument parser so we can parse the command-line. #---------------------------------------------------------------------- parser = ArgumentParser(description=program_license, formatter_class=ArgumentDefaultsHelpFormatter) parser.add_argument('-v', '--verbose', dest='verbose', action='count', help='set verbosity level') parser.add_argument('-V', '--version', action='version', version=program_version_message, help='Display version information') parser.add_argument('-c', '--config', dest='config', default='/etc/opt/att/collector.conf', help='Use this config file.', metavar='<file>') parser.add_argument('-s', '--section', dest='section', default='default', metavar='<section>', help='section to use in the config file') #---------------------------------------------------------------------- # Process arguments received. #---------------------------------------------------------------------- args = parser.parse_args() verbose = args.verbose config_file = args.config config_section = args.section #---------------------------------------------------------------------- # Now read the config file, using command-line supplied values as # overrides. #---------------------------------------------------------------------- defaults = {'log_file': 'collector.log', 'vel_port': '12233', 'vel_path': '', 'vel_topic_name': '' } overrides = {} config = ConfigParser.SafeConfigParser(defaults) config.read(config_file) #---------------------------------------------------------------------- # extract the values we want. #---------------------------------------------------------------------- log_file = config.get(config_section, 'log_file', vars=overrides) vel_port = config.get(config_section, 'vel_port', vars=overrides) vel_path = config.get(config_section, 'vel_path', vars=overrides) vel_topic_name = config.get(config_section, 'vel_topic_name', vars=overrides) global vel_username global vel_password vel_username = config.get(config_section, 'vel_username', vars=overrides) vel_password = config.get(config_section, 'vel_password', vars=overrides) vel_schema_file = config.get(config_section, 'schema_file', vars=overrides) base_schema_file = config.get(config_section, 'base_schema_file', vars=overrides) #---------------------------------------------------------------------- # Finally we have enough info to start a proper flow trace. #---------------------------------------------------------------------- global logger print('Logfile: {0}'.format(log_file)) logger = logging.getLogger('collector') if verbose > 0: print('Verbose mode on') logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=1000000, backupCount=10) if (platform.system() == 'Windows'): date_format = '%Y-%m-%d %H:%M:%S' else: date_format = '%Y-%m-%d %H:%M:%S.%f %z' formatter = logging.Formatter('%(asctime)s %(name)s - ' '%(levelname)s - %(message)s', date_format) handler.setFormatter(formatter) logger.addHandler(handler) logger.info('Started') #---------------------------------------------------------------------- # Log the details of the configuration. #---------------------------------------------------------------------- logger.debug('Log file = {0}'.format(log_file)) logger.debug('Event Listener Port = {0}'.format(vel_port)) logger.debug('Event Listener Path = {0}'.format(vel_path)) logger.debug('Event Listener Topic = {0}'.format(vel_topic_name)) logger.debug('Event Listener Username = {0}'.format(vel_username)) # logger.debug('Event Listener Password = {0}'.format(vel_password)) logger.debug('Event Listener JSON Schema File = {0}'.format( vel_schema_file)) logger.debug('Base JSON Schema File = {0}'.format(base_schema_file)) #---------------------------------------------------------------------- # Perform some basic error checking on the config. #---------------------------------------------------------------------- if (int(vel_port) < 1024 or int(vel_port) > 65535): logger.error('Invalid Vendor Event Listener port ({0}) ' 'specified'.format(vel_port)) raise RuntimeError('Invalid Vendor Event Listener port ({0}) ' 'specified'.format(vel_port)) if (len(vel_path) > 0 and vel_path[-1] != '/'): logger.warning('Event Listener Path ({0}) should have terminating ' '"/"! Adding one on to configured string.'.format( vel_path)) vel_path += '/' #---------------------------------------------------------------------- # Load up the vel_schema and base_schema, if they exist. #---------------------------------------------------------------------- if (os.path.exists(vel_schema_file)): global vel_schema vel_schema = json.load(open(vel_schema_file, 'r')) logger.debug('Loaded the JSON schema file') if (os.path.exists(base_schema_file)): logger.debug('Updating the schema with base definition') base_schema = json.load(open(base_schema_file, 'r')) vel_schema.update(base_schema) logger.debug('Updated the JSON schema file') else: logger.warning('Event Listener Schema File ({0}) not found. ' 'No validation will be undertaken.'.format( vel_schema_file)) #---------------------------------------------------------------------- # We are now ready to get started with processing. Start-up the various # components of the system in order: # # 1) Create the dispatcher. # 2) Register the functions for the URLs of interest. # 3) Run the webserver. #---------------------------------------------------------------------- root_url = '/{0}eventListener/v{1}{2}'.format(vel_path, API_VERSION, '/' + vel_topic_name if len(vel_topic_name) > 0 else '') set_404_content(root_url) dispatcher = PathDispatcher() dispatcher.register('GET', root_url, vendor_event_listener) dispatcher.register('POST', root_url, vendor_event_listener) httpd = make_server('', int(vel_port), dispatcher) print('Serving on port {0}...'.format(vel_port)) httpd.serve_forever() logger.error('Main loop exited unexpectedly!') return 0 except KeyboardInterrupt: #---------------------------------------------------------------------- # handle keyboard interrupt #---------------------------------------------------------------------- logger.info('Exiting on keyboard interrupt!') return 0 except Exception as e: #---------------------------------------------------------------------- # Handle unexpected exceptions. #---------------------------------------------------------------------- if DEBUG or TESTRUN: raise(e) indent = len(program_name) * ' ' sys.stderr.write(program_name + ': ' + repr(e) + '\n') sys.stderr.write(indent + ' for help use --help\n') sys.stderr.write(traceback.format_exc()) logger.critical('Exiting because of exception: {0}'.format(e)) logger.critical(traceback.format_exc()) return 2