def start_server(config): os.umask(0o022) weblogger = logging.getLogger('plight_httpd') weblogger.setLevel(config['web_log_level']) if weblogger.handlers == []: weblogging_handler = RotatingFileHandler(config['web_log_file'], mode='a', maxBytes=config[ 'web_log_filesize'], backupCount=config[ 'web_log_rotation_count']) weblogger.addHandler(weblogging_handler) applogger = logging.getLogger('plight') applogger.setLevel(config['log_level']) if applogger.handlers == []: applogging_handler = RotatingFileHandler( config['log_file'], mode='a', maxBytes=config['log_filesize'], backupCount=config['log_rotation_count']) applogger.addHandler(applogging_handler) # if pidfile is locked, do not start another process if PID.is_locked(): sys.stderr.write('Plight is already running\n') sys.exit(1) context = DaemonContext(pidfile=PID, uid=pwd.getpwnam(config['user']).pw_uid, gid=grp.getgrnam(config['group']).gr_gid, files_preserve=[ weblogging_handler.stream, applogging_handler.stream, ], umask=0o022,) context.stdout = applogging_handler.stream context.stderr = applogging_handler.stream context.open() try: try: log_message('Plight is starting...') server_class = BaseHTTPServer.HTTPServer http = server_class((config['host'], config['port']), plight.StatusHTTPRequestHandler) http.serve_forever() except SystemExit as sysexit: log_message("Stopping... " + str(sysexit)) except Exception as ex: log_message("ERROR: " + str(ex)) finally: log_message('Plight has stopped...') context.close()
def start_server(config, node): weblogger = logging.getLogger('plight_httpd') weblogger.setLevel(config['web_log_level']) if weblogger.handlers == []: weblogging_handler = RotatingFileHandler(config['web_log_file'], mode='a', maxBytes=config[ 'web_log_filesize'], backupCount=config[ 'web_log_rotation_count']) weblogger.addHandler(weblogging_handler) applogger = logging.getLogger('plight') applogger.setLevel(config['log_level']) if applogger.handlers == []: applogging_handler = RotatingFileHandler( config['log_file'], mode='a', maxBytes=config['log_filesize'], backupCount=config['log_rotation_count']) applogger.addHandler(applogging_handler) # if pidfile is locked, do not start another process if PID.is_locked(): sys.stderr.write('Plight is already running\n') sys.exit(1) context = DaemonContext(pidfile=PID, uid=pwd.getpwnam(config['user']).pw_uid, gid=grp.getgrnam(config['group']).gr_gid, umask=0o022, files_preserve=[ weblogging_handler.stream, applogging_handler.stream, ],) context.stdout = applogging_handler.stream context.stderr = applogging_handler.stream context.open() os.umask(0o022) try: try: log_message('Plight is starting...') server_class = BaseHTTPServer.HTTPServer http = server_class((config['host'], config['port']), plight.StatusHTTPRequestHandler) http.RequestHandlerClass._node_status = node http.serve_forever() except SystemExit as sysexit: log_message("Stopping... " + str(sysexit)) except Exception as ex: log_message("ERROR: " + str(ex)) finally: log_message('Plight has stopped...') context.close()
def close(self): # We might get called more than once, or before worker exists if self.is_open and self.worker and self.worker.is_alive(): self.logger.info('Stopping worker...') self.worker.should_stop = True self.worker.join(5) # Wait up to 5 seconds if self.worker.is_alive(): self.logger.warn( 'Error stopping worker. Shutting down uncleanly.' ) self.logger.info('Stopped.') DaemonContext.close(self)
def start(self, detachProcess=True): pidFile = TimeoutPIDLockFile(self._pidFile) context = DaemonContext( working_directory=self._runDir, umask=0o002, pidfile=pidFile, detach_process=detachProcess, ) context.signal_map = { signal.SIGTERM: 'terminate', signal.SIGHUP: 'terminate', signal.SIGUSR1: 'terminate', } if self._isRunningAndBreak(pidFile): raise AlreadyRunning("PID file locked and process not stale") self._context = context try: context.open() self._setupLogging() except: if self.logger is None: self._setupLogging() self.logger.warn("Exception while entering context", exc_info=True) try: context.close() except: pass return try: self.run() except Exception as e: self.logger.error("Exception in run()", exc_info=e) finally: self.logger.debug("Shutting down daemon") self.shutdown() try: self._fHandler.close() except: pass try: context.close() except: pass
class SerialDaemon(): """A wrapper class for Serial and DaemonContext with inet socket support. Creates a DaemonContext object and a Serial object using the passed arguments. Some arguments do not take effect after the daemon is started and hence can be altered anytime after initialization until it is started by calling <daemon>.start() Communication with the daemon is done on a port number specified during initialization (or anytime before start), defaulting to 57001. Data is decoded using the user specified encoding (default is UTF-8) and must be either exactly 'device', in which case the daemon will reply with the full path to the device file it is communicating with (serial_context.port), or it must be in the following format: <0-F><0-F0-F...0-F><data to be sent to device> where the first byte of data always signifies how many (base 16) bytes following it give the the number (base 16) of bytes that are to be read from device after the data is sent to it. 0 bytes are read if data[0] is 0 or is not a valid hex number. <data[0] number of bytes> + 1 are always discarded from the beginning of data. For example: 22F8921 sends 8921 to device and reads 2F (47) bytes of data X78192 sends 78192 to device and dos not read a reply 3A2G9130 sends 9130 to device BUT does not read a reply since A2G is not a valid hex number. Note that these 3 bytes are still discarded This class does not inherit from either DaemonContext or Serial. The only export method is start() used to run the daemon. Accepted options for the constructor: name :Default: ``None`` The name of the daemon, used for syslog and the default name of the pidfile and configuration files. Changes to this value after the daemon has started will only be reflected in syslog. config_file :Default: ``'/etc/<name>.conf'`` Configuration file used to set some of the hereby listed parameters. All but name, daemon_context and serial_context are supported. Reloading of the configuration without restarting is done by sending SIGHUP to the daemon but please note that changes to pidfile_path, socket_path and log_file require restart to take effect. The format is as follows: <option_name> = <option value> Option names are case-INsensitive and ``_`` may be replaced by ``-``. Spaces around ``=`` are optional and have no effect. Option values may optionally be enclosed in either single or double quotes. # and any text following it on the line are ignored. log_file :Default: ``/tmp/<name>.log`` Log file used to log exceptions during daemon run. pidfile_path :Default: ``'/var/run/<name>.pid'`` Path to the pidfile. A pidfile lock is created and passed to DaemonContext. Alternatively, you may pass a pid lockfile directly by setting <daemon>.daemon_context.pidfile to the lockfile after initialization but before start. Changing either of them after the daemon is started requires a restart. socket_path :Default: ``'/var/run/<name>.socket'`` Path for the unix daemon socket which the daemon will be listening on. Changing this after the daemon is started requires a restart. Also see documentation for serial.py. data_length :Default: ``1024`` Number of bytes to be read from the socket. This MUST be at least the number of bytes that have been sent, otherwise the remainder is read afterwards and is confused for a new packet. See above for details on the data format. data_encoding :Default: ``'utf-8'`` Valid encoding (accepted by the str.decode() and bytes() methods) for the data read from and sent to the socket. reply_length_strict :Default: ``False`` If True daemon will not send data read from device unless the length matches the expected reply length given as part of the data sent over the socket. See above for details on the data format. daemon_context :Default: ``None`` If this is not None, it must be a DaemonContext object and is used instead of creating a new one. All options relating to DaemoonContext are then ignored. serial_context :Default: ``None`` If this is not None, it must be a Serial object and is used instead of creating a new one. All options relating to Serial are then ignored. In addition to the above arguments, SerialDaemon accepts all arguments valid for DaemonContext and Serial and uses them to create the corresponding objects (unless daemon_context or serial_context are given) """ def __init__( self, name = 'seriald', config_file = 0, log_file = None, pidfile_path = 0, socket_path = 0, reply_length_strict = False, data_length = 1024, data_encoding = 'utf-8', daemon_context = None, serial_context = None, **kwargs ): self.name = name self.config_file = config_file if self.config_file == 0: self.config_file = '/etc/{name}.conf'.format(name = self.name) self.log_file = log_file # log file will be used even if user specified None if self.log_file is None: self.log_file = os.path.join( tempfile.gettempdir(), '{name}.log'.format( name = self.name)) self.pidfile_path = pidfile_path if self.pidfile_path == 0: self.pidfile_path = '/var/run/{name}.pid'.format(name = self.name) self.socket_path = socket_path if self.socket_path == 0: self.socket_path = '/var/run/{name}.socket'.format(name = self.name) self.reply_length_strict = reply_length_strict self.data_length = data_length self.data_encoding = data_encoding self.daemon_context = daemon_context if self.daemon_context is None: self.daemon_context = DaemonContext( signal_map = { signal.SIGHUP: self.__accept_signal, signal.SIGINT: self.__accept_signal, signal.SIGQUIT: self.__accept_signal, signal.SIGTERM: self.__accept_signal, } ) for attr in filter(lambda s: not s.startswith('_'), dir(self.daemon_context)): if kwargs.get(attr) is not None: setattr(self.daemon_context, attr, kwargs.get(attr)) self.serial_context = serial_context if self.serial_context is None: self.serial_context = Serial() for attr in filter(lambda s: not s.startswith('_'), dir(self.serial_context)): if kwargs.get(attr) is not None: setattr(self.serial_context, attr, kwargs.get(attr)) def __run(self): with open(self.log_file, 'a') as log_file: try: soc = None # flush doesn't work in daemon mode for ttyS? # close and reopen instead device = self.serial_context.port is_ttyS = True try: # is it a number? Serial defaults to /dev/ttyS? if so device += 0 except TypeError: # not a number, assume string try: if not device.startswith('/dev/ttyS'): raise AttributeError except AttributeError: # not a string or not a ttyS? device # assume flushing works is_ttyS = False else: device = '/dev/ttyS{num}'.format(num = device) while True: if not soc or soc.fileno() < 0: logsyslog(LOG_INFO, 'Waiting for connection') soc, soc_addr = self.socket.accept() logsyslog(LOG_INFO, ('Connected to {addr}').format( addr = soc_addr)) data = soc.recv(self.data_length).decode( self.data_encoding) if data == '': logsyslog(LOG_INFO, 'Closing connection') soc.close() continue elif data == 'device': logsyslog(LOG_INFO, 'Device path requested') try: soc.sendall(device.encode(self.data_encoding)) except ConnectionResetError: soc.close() continue logsyslog(LOG_INFO, 'Read from socket: {data}'.format( data = data)) reply_length_byte_length = 0 try: reply_length_byte_length = int(data[0], 16) reply_length = int( data[1 : reply_length_byte_length + 1], 16) except ValueError: reply_length = 0 data = data[reply_length_byte_length + 1:] if not self.serial_context.isOpen(): # first time in the loop logsyslog(LOG_INFO, 'Opening serial port') self.serial_context.open() logsyslog(LOG_INFO, 'Sending {data}'.format( data = data)) # discard any input or output self.serial_context.flushOutput() self.serial_context.flushInput() self.serial_context.write(data.encode(self.data_encoding)) if is_ttyS: self.serial_context.close() self.serial_context.open() else: self.serial_context.flush() logsyslog(LOG_INFO, ('Will read {length} bytes').format( length = reply_length)) if reply_length > 0: reply = self.serial_context.read(reply_length) reply_decoded = reply.decode(self.data_encoding) logsyslog(LOG_INFO, 'Received {data}'.format( data = reply_decoded)) if len(reply_decoded) == reply_length \ or not self.reply_length_strict: try: soc.sendall(reply) except ConnectionResetError: soc.close() continue except: traceback.print_exc(file = log_file) self.__stop() def __load_config(self): def reset_invalid_value(opt): logsyslog(LOG_ERR, ('{conf}: Invalid value for ' + '{option}').format( conf = self.config_file, option = opt)) return getattr(self, opt) conf = _openfile(self.config_file, 'r') if conf is not None: with conf: regex_pat = regex.compile(r"""\s* (?| (?P<option> reply_length_strict | data[-_]length | data[-_]encoding | log[-_]file | pidfile[-_]path | socket[-_]path ) \s* (?: =\s* )? (?| " (?P<value> [^"]+ ) " | ' (?P<value> [^']+ ) ' | (?P<value> [^#\r\n]+ ) ) ) """, regex.X|regex.I) line_num = 0 for line in conf: line_num += 1 if line.startswith('#'): continue match = regex_pat.match(line.strip()) if match: # translate the option name to the object's attribute opt = match.group('option').lower().replace('-', '_') if opt.endswith(('file', 'dir', 'path', 'encoding')): # value is a string val = match.group('value') elif opt == 'reply_length_strict': # value must be a boolean if val.lower() not in ['0', '1', 'false', 'true']: val = reset_invalid_value(opt) elif val.lower() in ['0', 'false']: val = False else: val = True else: # value must be numeric and positive val = int(match.group('value')) if val <= 0: val = reset_invalid_value(opt) setattr(self, opt, val) else: logsyslog(LOG_ERR, ('{conf}: Invalid syntax at line ' + '{line}').format( conf = self.config_file, line = line_num)) logsyslog(LOG_INFO, 'Loaded configuration from {conf}'.format( conf = self.config_file)) def start(self): """ Load config, daemonize, connect to serial port, listen on socket """ opensyslog(ident = self.name, facility = LOG_DAEMON) self.__load_config() if self.pidfile_path is not None: self.daemon_context.pidfile = lockfile.FileLock(self.pidfile_path) if _pidfile_isbusy(self.daemon_context.pidfile): logsyslog(LOG_ERR, 'Already running (pidfile is locked)') closesyslog() return if _socket_isbusy(self.socket_path): logsyslog(LOG_ERR, 'Already running (socket is in use)') closesyslog() return self.daemon_context.open() with _openfile(self.daemon_context.pidfile.path, 'w', fail = self.__stop) as file: file.write('{pid}'.format(pid = os.getpid())) # opening the serial port here doesn't work # open it in __run instead # self.serial_context.open() logsyslog(LOG_INFO, 'Started') self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.socket.bind(self.socket_path) self.socket.listen(1) logsyslog(LOG_INFO, ('Listening on socket {socket}').format( socket = self.socket_path)) self.__run() def __stop(self): pid = _get_pid(self.daemon_context.pidfile.path) if pid is None: return logsyslog(LOG_INFO, 'Stopping') os.remove(self.socket.getsockname()) os.remove(self.daemon_context.pidfile.path) self.socket.close() if self.serial_context.isOpen(): self.serial_context.close() self.daemon_context.close() try: os.kill(pid, signal.SIGKILL) except OSError: logsyslog(LOG_ERR, 'Could not stop process id {pid}'.format( pid = pid)) closesyslog() def __accept_signal(self, sig, frame): if sig == signal.SIGHUP: self.__load_config() else: logsyslog(LOG_INFO, 'Caught signal {sig}'.format(sig = sig)) self.__stop()
def main(): # Logger _logger = logger.Logger('middleware') get_logger = _logger.getLogger() # Workaround for development modpath = os.path.realpath(os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', )) if modpath not in sys.path: sys.path.insert(0, modpath) parser = argparse.ArgumentParser() parser.add_argument('restart', nargs='?') parser.add_argument('--foreground', '-f', action='store_true') parser.add_argument('--debug-level', default='DEBUG', choices=[ 'DEBUG', 'INFO', 'WARN', 'ERROR', ]) parser.add_argument('--log-handler', choices=[ 'console', 'file', ]) args = parser.parse_args() if args.log_handler: log_handlers = [args.log_handler] else: log_handlers = ['console' if args.foreground else 'file'] pidpath = '/var/run/middlewared.pid' if args.restart: if os.path.exists(pidpath): with open(pidpath, 'r') as f: pid = int(f.read().strip()) os.kill(pid, 15) if not args.foreground: _logger.configure_logging('file') daemonc = DaemonContext( pidfile=TimeoutPIDLockFile(pidpath), detach_process=True, stdout=logger.LoggerStream(get_logger), stderr=logger.LoggerStream(get_logger), ) daemonc.open() elif 'file' in log_handlers: _logger.configure_logging('file') sys.stdout = logger.LoggerStream(get_logger) sys.stderr = logger.LoggerStream(get_logger) elif 'console' in log_handlers: _logger.configure_logging('console') else: _logger.configure_logging('file') setproctitle.setproctitle('middlewared') # Workaround to tell django to not set up logging on its own os.environ['MIDDLEWARED'] = str(os.getpid()) Middleware().run() if not args.foreground: daemonc.close()
while True: time.sleep(1) if i % REFRESH_DNS == 0: log.info('Refreshing the valid DNs.') dns = get_dns(options.dn_file) ssm.set_dns(dns) try: log.info('Sending ping.') ssm.send_ping() except NotConnectedException: log.error('Connection lost.') ssm.shutdown() dc.close() log.info("Waiting for 10 minutes before restarting...") time.sleep(10 * 60) log.info('Restarting SSM.') dc.open() ssm.startup() i += 1 except SystemExit, e: log.info('Received the shutdown signal: %s', e) ssm.shutdown() dc.close() except Exception, e: log.error('Unexpected exception: %s', e) log.error('Exception type: %s', e.__class__)
# The message listening loop. while True: time.sleep(1) if i % REFRESH_DNS == 0: log.info('Refreshing valid DNs and then sending ping.') dns = get_dns(options.dn_file) ssm.set_dns(dns) try: ssm.send_ping() except NotConnectedException: log.warn('Connection lost.') ssm.shutdown() dc.close() log.info("Waiting for 10 minutes before restarting...") time.sleep(10 * 60) log.info('Restarting SSM.') dc.open() ssm.startup() i += 1 except SystemExit, e: log.info('Received the shutdown signal: %s', e) ssm.shutdown() dc.close() except Exception, e: log.error('Unexpected exception: %s', e) log.error('Exception type: %s', e.__class__)
def main(): # Workaround for development modpath = os.path.realpath(os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', )) if modpath not in sys.path: sys.path.insert(0, modpath) parser = argparse.ArgumentParser() parser.add_argument('restart', nargs='?') parser.add_argument('--foreground', '-f', action='store_true') parser.add_argument('--disable-loop-monitor', '-L', action='store_true') parser.add_argument('--plugins-dirs', '-p', action='append') parser.add_argument('--debug-level', choices=[ 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', ]) parser.add_argument('--log-handler', choices=[ 'console', 'file', ]) args = parser.parse_args() # Logger if args.log_handler: log_handlers = [args.log_handler] else: log_handlers = ['console' if args.foreground else 'file'] if args.debug_level is None and args.foreground: debug_level = 'TRACE' else: debug_level = args.debug_level or 'DEBUG' _logger = logger.Logger('middleware', debug_level) get_logger = _logger.getLogger() pidpath = '/var/run/middlewared.pid' if args.restart: if os.path.exists(pidpath): with open(pidpath, 'r') as f: pid = int(f.read().strip()) try: os.kill(pid, 15) except ProcessLookupError as e: if e.errno != errno.ESRCH: raise if not args.foreground: _logger.configure_logging('file') daemonc = DaemonContext( pidfile=TimeoutPIDLockFile(pidpath), detach_process=True, stdout=logger.LoggerStream(get_logger), stderr=logger.LoggerStream(get_logger), ) daemonc.open() elif 'file' in log_handlers: _logger.configure_logging('file') sys.stdout = sys.stderr = _logger.stream() elif 'console' in log_handlers: _logger.configure_logging('console') else: _logger.configure_logging('file') setproctitle.setproctitle('middlewared') # Workaround to tell django to not set up logging on its own os.environ['MIDDLEWARED'] = str(os.getpid()) if args.foreground: with open(pidpath, "w") as _pidfile: _pidfile.write(f"{str(os.getpid())}\n") Middleware( loop_monitor=not args.disable_loop_monitor, plugins_dirs=args.plugins_dirs, debug_level=debug_level, ).run() if not args.foreground: daemonc.close()
def main(): # Workaround for development modpath = os.path.realpath(os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', )) if modpath not in sys.path: sys.path.insert(0, modpath) parser = argparse.ArgumentParser() parser.add_argument('restart', nargs='?') parser.add_argument('--foregound', '-f', action='store_true') parser.add_argument('--debug-level', default='DEBUG', choices=[ 'DEBUG', 'INFO', 'WARN', 'ERROR', ]) parser.add_argument('--log-handler', choices=[ 'console', 'file', ]) args = parser.parse_args() if args.log_handler: log_handlers = [args.log_handler] else: log_handlers = ['console' if args.foregound else 'file'] pidpath = '/var/run/middlewared.pid' if args.restart: if os.path.exists(pidpath): with open(pidpath, 'r') as f: pid = int(f.read().strip()) os.kill(pid, 15) try: logging.config.dictConfig({ 'version': 1, 'formatters': { 'simple': { 'format': '[%(asctime)s %(filename)s:%(lineno)s] (%(levelname)s) %(message)s' }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple', }, 'file': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', 'filename': '/var/log/middlewared.log', 'formatter': 'simple', } }, 'loggers': { '': { 'handlers': log_handlers, 'level': args.debug_level, 'propagate': True, }, } }) if not args.foregound: daemonc = DaemonContext( pidfile=TimeoutPIDLockFile(pidpath), detach_process=True, stdout=logging._handlers['file'].stream, stderr=logging._handlers['file'].stream, files_preserve=[logging._handlers['file'].stream], ) daemonc.open() elif 'file' in log_handlers: sys.stdout = logging._handlers['file'].stream sys.stderr = logging._handlers['file'].stream setproctitle.setproctitle('middlewared') # Workaround to tell django to not set up logging on its own os.environ['MIDDLEWARED'] = str(os.getpid()) Middleware().run() finally: if not args.foregound: daemonc.close()
def main(): """Set up connection, and listen for messages.""" ver = "SSM %s.%s.%s" % __version__ op = OptionParser(description=__doc__, version=ver) op.add_option('-c', '--config', help='location of config file', default='/etc/apel/receiver.cfg') op.add_option('-l', '--log_config', help='location of logging config file (optional)', default='/etc/apel/logging.cfg') op.add_option('-d', '--dn_file', help='location of the file containing valid DNs', default='/etc/apel/dns') (options, unused_args) = op.parse_args() cp = ConfigParser.ConfigParser({'use_ssl': 'true'}) cp.read(options.config) # Check for pidfile pidfile = cp.get('daemon', 'pidfile') if os.path.exists(pidfile): print('Cannot start SSM. Pidfile %s already exists.' % pidfile) sys.exit(1) # set up logging try: if os.path.exists(options.log_config): logging.config.fileConfig(options.log_config) else: set_up_logging(cp.get('logging', 'logfile'), cp.get('logging', 'level'), cp.getboolean('logging', 'console')) except (ConfigParser.Error, ValueError, IOError) as err: print('Error configuring logging: %s' % err) print('SSM will exit.') sys.exit(1) global log log = logging.getLogger('ssmreceive') log.info(LOG_BREAK) log.info('Starting receiving SSM version %s.%s.%s.', *__version__) # Determine the protocol for the SSM to use. try: protocol = cp.get('receiver', 'protocol') except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): # If the newer configuration setting 'protocol' is not set, use 'STOMP' # for backwards compatability. protocol = Ssm2.STOMP_MESSAGING log.debug("No option set for 'protocol'. Defaulting to %s.", protocol) log.info('Setting up SSM with protocol: %s', protocol) if protocol == Ssm2.STOMP_MESSAGING: # Set defaults for AMS variables that Ssm2 constructor requires below. project = None token = '' use_ssl = cp.getboolean('broker', 'use_ssl') if use_ssl: service = STOMP_SSL_SERVICE else: service = STOMP_SERVICE # If we can't get a broker to connect to, we have to give up. try: bg = StompBrokerGetter(cp.get('broker', 'bdii')) brokers = bg.get_broker_hosts_and_ports(service, cp.get('broker', 'network')) except ConfigParser.NoOptionError as e: try: host = cp.get('broker', 'host') port = cp.get('broker', 'port') brokers = [(host, int(port))] except ConfigParser.NoOptionError: log.error('Options incorrectly supplied for either single ' 'broker or broker network. ' 'Please check configuration') log.error('System will exit.') log.info(LOG_BREAK) sys.exit(1) except ldap.SERVER_DOWN as e: log.error('Could not connect to LDAP server: %s', e) log.error('System will exit.') log.info(LOG_BREAK) sys.exit(1) elif protocol == Ssm2.AMS_MESSAGING: # Then we are setting up an SSM to connect to a AMS. # 'use_ssl' isn't checked when using AMS (SSL is always used), but it # is needed for the call to the Ssm2 constructor below. use_ssl = None try: # We only need a hostname, not a port host = cp.get('broker', 'host') # Use brokers variable so subsequent code is not dependant on # the exact destination type. brokers = [host] except ConfigParser.NoOptionError: log.error('The host must be specified when connecting to AMS, ' 'please check your configuration') log.error('System will exit.') log.info(LOG_BREAK) print('SSM failed to start. See log file for details.') sys.exit(1) # Attempt to configure AMS specific variables. try: token = cp.get('messaging', 'token') project = cp.get('messaging', 'ams_project') except (ConfigParser.Error, ValueError, IOError) as err: # A token and project are needed to successfully receive from an # AMS instance, so log and then exit on an error. log.error('Error configuring AMS values: %s', err) log.error('SSM will exit.') print('SSM failed to start. See log file for details.') sys.exit(1) if len(brokers) == 0: log.error('No brokers available.') log.error('System will exit.') log.info(LOG_BREAK) sys.exit(1) log.info('The SSM will run as a daemon.') # We need to preserve the file descriptor for any log files. rootlog = logging.getLogger() log_files = [x.stream for x in rootlog.handlers] dc = DaemonContext(files_preserve=log_files) try: ssm = Ssm2(brokers, cp.get('messaging', 'path'), cert=cp.get('certificates', 'certificate'), key=cp.get('certificates', 'key'), listen=cp.get('messaging', 'destination'), use_ssl=use_ssl, capath=cp.get('certificates', 'capath'), check_crls=cp.getboolean('certificates', 'check_crls'), pidfile=pidfile, protocol=protocol, project=project, token=token) log.info('Fetching valid DNs.') dns = get_dns(options.dn_file) ssm.set_dns(dns) except Exception as e: log.fatal('Failed to initialise SSM: %s', e) log.info(LOG_BREAK) sys.exit(1) try: # Note: because we need to be compatible with python 2.4, we can't use # with dc: # here - we need to call the open() and close() methods # manually. dc.open() ssm.startup() i = 0 # The message listening loop. while True: try: time.sleep(0.1) if protocol == Ssm2.AMS_MESSAGING: # We need to pull down messages as part of # this loop when using AMS. ssm.pull_msg_ams() if i % (REFRESH_DNS * 10) == 0: log.info('Refreshing valid DNs and then sending ping.') dns = get_dns(options.dn_file) ssm.set_dns(dns) if protocol == Ssm2.STOMP_MESSAGING: ssm.send_ping() except (NotConnectedException, AmsConnectionException) as error: log.warn('Connection lost.') log.debug(error) ssm.shutdown() dc.close() log.info("Waiting for 10 minutes before restarting...") time.sleep(10 * 60) log.info('Restarting SSM.') dc.open() ssm.startup() i += 1 except SystemExit as e: log.info('Received the shutdown signal: %s', e) ssm.shutdown() dc.close() except Exception as e: log.error('Unexpected exception: %s', e) log.error('Exception type: %s', e.__class__) log.error('The SSM will exit.') ssm.shutdown() dc.close() log.info('Receiving SSM has shut down.') log.info(LOG_BREAK)
def main(): # Logger _logger = logger.Logger('middleware') get_logger = _logger.getLogger() # Workaround for development modpath = os.path.realpath(os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', )) if modpath not in sys.path: sys.path.insert(0, modpath) parser = argparse.ArgumentParser() parser.add_argument('restart', nargs='?') parser.add_argument('--foreground', '-f', action='store_true') parser.add_argument('--disable-loop-monitor', '-L', action='store_true') parser.add_argument('--plugins-dirs', '-p', action='append') parser.add_argument('--debug-level', default='DEBUG', choices=[ 'DEBUG', 'INFO', 'WARN', 'ERROR', ]) parser.add_argument('--log-handler', choices=[ 'console', 'file', ]) args = parser.parse_args() if args.log_handler: log_handlers = [args.log_handler] else: log_handlers = ['console' if args.foreground else 'file'] pidpath = '/var/run/middlewared.pid' if args.restart: if os.path.exists(pidpath): with open(pidpath, 'r') as f: pid = int(f.read().strip()) os.kill(pid, 15) if not args.foreground: _logger.configure_logging('file') daemonc = DaemonContext( pidfile=TimeoutPIDLockFile(pidpath), detach_process=True, stdout=logger.LoggerStream(get_logger), stderr=logger.LoggerStream(get_logger), ) daemonc.open() elif 'file' in log_handlers: _logger.configure_logging('file') sys.stdout = logger.LoggerStream(get_logger) sys.stderr = logger.LoggerStream(get_logger) elif 'console' in log_handlers: _logger.configure_logging('console') else: _logger.configure_logging('file') setproctitle.setproctitle('middlewared') # Workaround to tell django to not set up logging on its own os.environ['MIDDLEWARED'] = str(os.getpid()) Middleware( loop_monitor=not args.disable_loop_monitor, plugins_dirs=args.plugins_dirs, ).run() if not args.foreground: daemonc.close()
def run_receiver(protocol, brokers, project, token, cp, log, dn_file): """Run Ssm2 as a receiver daemon.""" log.info('The SSM will run as a daemon.') # We need to preserve the file descriptor for any log files. rootlog = logging.getLogger() log_files = [x.stream for x in rootlog.handlers] dc = DaemonContext(files_preserve=log_files) try: ssm = Ssm2(brokers, cp.get('messaging', 'path'), cert=cp.get('certificates', 'certificate'), key=cp.get('certificates', 'key'), listen=cp.get('messaging', 'destination'), use_ssl=cp.getboolean('broker', 'use_ssl'), capath=cp.get('certificates', 'capath'), check_crls=cp.getboolean('certificates', 'check_crls'), pidfile=cp.get('daemon', 'pidfile'), protocol=protocol, project=project, token=token) log.info('Fetching valid DNs.') dns = get_dns(dn_file, log) ssm.set_dns(dns) except Exception as e: log.fatal('Failed to initialise SSM: %s', e) log.info(LOG_BREAK) sys.exit(1) try: # Note: because we need to be compatible with python 2.4, we can't use # with dc: # here - we need to call the open() and close() methods # manually. dc.open() ssm.startup() i = 0 # The message listening loop. while True: try: time.sleep(0.1) if protocol == Ssm2.AMS_MESSAGING: # We need to pull down messages as part of # this loop when using AMS. ssm.pull_msg_ams() if i % (REFRESH_DNS * 10) == 0: log.info('Refreshing valid DNs and then sending ping.') dns = get_dns(dn_file, log) ssm.set_dns(dns) if protocol == Ssm2.STOMP_MESSAGING: ssm.send_ping() except (NotConnectedException, AmsConnectionException) as error: log.warn('Connection lost.') log.debug(error) ssm.shutdown() dc.close() log.info("Waiting for 10 minutes before restarting...") time.sleep(10 * 60) log.info('Restarting SSM.') dc.open() ssm.startup() i += 1 except SystemExit as e: log.info('Received the shutdown signal: %s', e) ssm.shutdown() dc.close() except Exception as e: log.error('Unexpected exception: %s', e) log.error('Exception type: %s', e.__class__) log.error('The SSM will exit.') ssm.shutdown() dc.close() log.info('Receiving SSM has shut down.') log.info(LOG_BREAK)