def __init__(self, daemon, address, serializer): BaseServer.__init__(self, daemon, address, serializer) host, port = socket.gethostbyname(address[0]), address[1] self.sock = nicos_zmq_ctx.socket(zmq.ROUTER) self.sock.bind('tcp://%s:%s' % (host, port)) self.event_queue = queue.Queue() self.event_sock = nicos_zmq_ctx.socket(zmq.PUB) self.event_sock.bind('tcp://%s:%s' % (host, port + 1)) self.handlers = {} self.handler_ident = 0 self.handler_lock = threading.Lock() self._stoprequest = False
def __init__(self, client_id, server): self.client_id = client_id self.ident = server.handler_ident # only for logging purposes self.unregister = server.clear_handler self.command_queue = queue.Queue() self.reply_sender = nicos_zmq_ctx.socket(zmq.PUSH) self.reply_sender.connect('inproc://daemon_reply') self.serializer = server.serializer self.clientnames = [] ConnectionHandler.__init__(self, server.daemon)
def start(self, interval): createThread('daemon event sender', self.event_sender) # TODO: # * clean up unused handlers (when?) # * more zmq inproc sockets and proxies instead of queues? # * useful and comprehensive error handling reply_collect = nicos_zmq_ctx.socket(zmq.PULL) reply_collect.bind('inproc://daemon_reply') poller = zmq.Poller() poller.register(self.sock, zmq.POLLIN) poller.register(reply_collect, zmq.POLLIN) # ZeroMQ expects a poll timeout given in msec. interval is passed # through in seconds. interval_ms = interval * 1000 while not self._stoprequest: for (sock, _) in poller.poll(interval_ms): # reply? pass it through if sock is reply_collect: self.sock.send_multipart(reply_collect.recv_multipart()) continue # otherwise, must be message from a client msg = self.sock.recv_multipart() client_id = msg[0] if client_id in self.handlers: self.handlers[client_id].command_queue.put(msg) elif msg[2] == b'getbanner': # new connection, create a handler self.handler_ident += 1 handler = ServerTransport(client_id, self) with self.handler_lock: self.handlers[client_id] = handler createThread('handler %d' % self.handler_ident, handler.handle_loop) else: # all other messages: client must initiate connection first self.sock.send_multipart([client_id, b'', b'error', b'', b'"session expired"'])
def _target(self, sandbox, uuid, code, setups, user, emitter, args, quiet): socket = nicos_zmq_ctx.socket(zmq.DEALER) poller = zmq.Poller() poller.register(socket, zmq.POLLIN) if sandbox: # create a new temporary directory for the sandbox helper to # mount the filesystem tempdir = tempfile.mkdtemp() rootdir = path.join(tempdir, 'root') os.mkdir(rootdir) # since the sandbox does not have TCP connection, use a Unix socket sockname = 'ipc://' + path.join(tempdir, 'sock') socket.bind(sockname) prefixargs = [sandbox, rootdir, str(os.getuid()), str(os.getgid())] else: port = socket.bind_to_random_port('tcp://127.0.0.1') sockname = 'tcp://127.0.0.1:%s' % port prefixargs = [] scriptname = path.join(config.nicos_root, 'bin', 'nicos-simulate') userstr = '%s,%d' % (user.name, user.level) if quiet: args.append('--quiet') if config.sandbox_simulation_debug: args.append('--debug') proc = createSubprocess(prefixargs + [ sys.executable, scriptname, sockname, uuid, ','.join(setups), userstr, code ] + args) if sandbox: if not session.current_sysconfig.get('cache'): raise NicosError('no cache is configured') socket.send(pickle.dumps(session.cache.get_values())) else: # let the subprocess connect to the cache socket.send(b'') while True: res = poller.poll(500) if not res: if proc.poll() is not None: if emitter: request = emitter.current_script() if request.reqid == uuid: request.setSimstate('failed') request.emitETA(emitter._controller) if not quiet: session.log.warning('Dry run has terminated ' 'prematurely') return continue msgtype, msg = unserialize(socket.recv()) if msgtype == SIM_MESSAGE: if emitter: emitter.emit_event('simmessage', msg) else: record = logging.LogRecord(msg[0], msg[2], '', 0, msg[3], (), None) record.message = msg[3].rstrip() session.log.handle(record) elif msgtype == SIM_BLOCK_RES: if emitter: block, duration, uuid = msg request = emitter.current_script() if request.reqid == uuid: request.updateRuntime(block, duration) elif msgtype == SIM_END_RES: if emitter: if not quiet: emitter.emit_event('simresult', msg) request = emitter.current_script() if request.reqid == uuid: request.setSimstate('success') request.emitETA(emitter._controller) # In the console session, the summary is printed by the # sim() command. socket.close() break # wait for the process, but only for 5 seconds after the result # has arrived wait_start = time.time() try: # Python 3.x has a timeout argument for poll()... while time.time() < wait_start + 5: if proc.poll() is not None: break else: raise Exception('did not terminate within 5 seconds') except Exception: session.log.exception('Error waiting for dry run process') if sandbox: try: os.rmdir(rootdir) os.rmdir(tempdir) except Exception: pass
def run(cls, sock, uuid, setups, user, code, quiet=False, debug=False): session.__class__ = cls session._is_sandboxed = sock.startswith('ipc://') session._debug_log = debug socket = nicos_zmq_ctx.socket(zmq.DEALER) socket.connect(sock) # we either get an empty message (retrieve cache data ourselves) # or a pickled key-value database data = socket.recv() db = pickle.loads(data) if data else None # send log messages back to daemon if requested session.log_sender = SimLogSender(socket, session, uuid, quiet) username, level = user.rsplit(',', 1) session._user = User(username, int(level)) try: session.__init__(SIMULATION) except Exception as err: try: session.log.exception('Fatal error while initializing') finally: print('Fatal error while initializing:', err, file=sys.stderr) return 1 # Give a sign of life and then tell the log handler to only log # errors during setup. session.log.info('setting up dry run...') session.begin_setup() # Handle "print" statements in the script. sys.stdout = LoggingStdout(sys.stdout) try: # Initialize the session in simulation mode. session._mode = SIMULATION # Load the setups from the original system, this should give the # information about the cache address. session.log.info('loading simulation mode setups: %s', ', '.join(setups)) session.loadSetup(setups, allow_startupcode=False) # Synchronize setups and cache values. session.log.info('synchronizing to master session') session.simulationSync(db) # Set session to always abort on errors. session.experiment.errorbehavior = 'abort' except: # really *all* exceptions -- pylint: disable=W0702 session.log.exception('Exception in dry run setup') session.log_sender.finish() session.shutdown() return 1 # Set up log handlers to output everything. session.log_sender.begin_exec() # Execute the script code. exception = False try: last_clock = session.clock.time code, _ = parseScript(code) for i, c in enumerate(code): exec_(c, session.namespace) time = session.clock.time - last_clock last_clock = session.clock.time session.log_sender.send_block_result(i, time) except Abort: session.log.info('Dry run finished by abort()') except: # pylint: disable=W0702 session.log.exception('Exception in dry run') exception = True else: session.log.info('Dry run finished') finally: session.log_sender.finish(exception) # Shut down. session.shutdown()