Example #1
0
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()
Example #2
0
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()
Example #3
0
    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)
Example #4
0
	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
Example #5
0
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()
Example #6
0
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()
Example #7
0
        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__)
Example #8
0
File: receiver.py Project: jrha/ssm
        # 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__)
Example #9
0
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()
Example #10
0
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()
Example #11
0
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)
Example #12
0
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()
Example #13
0
File: agents.py Project: pjcon/ssm
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)
Example #14
0
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()