def __init__(self): BuiltinCore.__init__(self) #: A dict of child name -> one end of the #: :class:`multiprocessing.Pipe` object used to communicate #: with that child. (The child is given the other end of the #: Pipe.) self.pipes = dict() #: A queue that keeps track of which children are available to #: render a configuration. A child is popped from the queue #: when it starts to render a config, then it's pushed back on #: when it's done. This lets us use a blocking call to #: :func:`Queue.Queue.get` when waiting for an available #: child. self.available_children = \ Queue(maxsize=Bcfg2.Options.setup.core_children) #: The flag that indicates when to stop child threads and #: processes self.terminate = DualEvent(threading_event=self.terminate) #: A :class:`Bcfg2.Server.MultiprocessingCore.RPCQueue` object #: used to send or publish commands to children. self.rpc_q = RPCQueue() #: A list of children that will be cycled through self._all_children = [] #: An iterator that each child will be taken from in sequence, #: to provide a round-robin distribution of render requests self.children = None
def shutdown(self): BuiltinCore.shutdown(self) self.logger.info("Closing RPC command queues") self.rpc_q.close() def term_children(): """ Terminate all remaining multiprocessing children. """ for child in multiprocessing.active_children(): self.logger.error("Waited %s seconds to shut down %s, " "terminating" % (self.shutdown_timeout, child.name)) child.terminate() timer = threading.Timer(self.shutdown_timeout, term_children) timer.start() while len(multiprocessing.active_children()): self.logger.info("Waiting for %s child(ren): %s" % (len(multiprocessing.active_children()), [c.name for c in multiprocessing.active_children()])) time.sleep(1) timer.cancel() self.logger.info("All children shut down") while len(threading.enumerate()) > 1: threads = [t for t in threading.enumerate() if t != threading.current_thread()] self.logger.info("Waiting for %s thread(s): %s" % (len(threads), [t.name for t in threads])) time.sleep(1) self.logger.info("Shutdown complete")
def shutdown(self): BuiltinCore.shutdown(self) self.logger.info("Closing RPC command queues") self.rpc_q.close() def term_children(): """ Terminate all remaining multiprocessing children. """ for child in multiprocessing.active_children(): self.logger.error("Waited %s seconds to shut down %s, " "terminating" % (self.shutdown_timeout, child.name)) child.terminate() timer = threading.Timer(self.shutdown_timeout, term_children) timer.start() while len(multiprocessing.active_children()): self.logger.info( "Waiting for %s child(ren): %s" % (len(multiprocessing.active_children()), [c.name for c in multiprocessing.active_children()])) time.sleep(1) timer.cancel() self.logger.info("All children shut down") while len(threading.enumerate()) > 1: threads = [ t for t in threading.enumerate() if t != threading.current_thread() ] self.logger.info("Waiting for %s thread(s): %s" % (len(threads), [t.name for t in threads])) time.sleep(1) self.logger.info("Shutdown complete")
def _run(self): for cnum in range(Bcfg2.Options.setup.core_children): name = "Child-%s" % cnum self.logger.debug("Starting child %s" % name) child_q = self.rpc_q.add_subscriber(name) childcore = ChildCore(name, child_q, self.terminate) child = multiprocessing.Process(target=childcore.run, name=name) child.start() self.logger.debug("Child %s started with PID %s" % (name, child.pid)) self._all_children.append(name) self.logger.debug("Started %s children: %s" % (len(self._all_children), self._all_children)) self.children = cycle(self._all_children) return BuiltinCore._run(self)
def _get_rmi(self): child_rmi = dict() for pname, pinst in self._get_rmi_objects().items(): for crmi in pinst.__child_rmi__: if isinstance(crmi, tuple): parentname, childname = crmi else: parentname = childname = crmi child_rmi["%s.%s" % (pname, parentname)] = \ "%s.%s" % (pname, childname) rmi = BuiltinCore._get_rmi(self) for method in rmi.keys(): if method in child_rmi: rmi[method] = self._child_rmi_wrapper(method, rmi[method], child_rmi[method]) return rmi
def _run(self): for cnum in range(Bcfg2.Options.setup.core_children): name = "Child-%s" % cnum self.logger.debug("Starting child %s" % name) child_q = self.rpc_q.add_subscriber(name) childcore = ChildCore(name, child_q, self.terminate) child = multiprocessing.Process(target=childcore.run, name=name) child.start() self.logger.debug("Child %s started with PID %s" % (name, child.pid)) self._all_children.append(name) self.logger.debug("Started %s children: %s" % (len(self._all_children), self._all_children)) self.children = cycle(self._all_children) Bcfg2.Server.Cache.add_expire_hook(self.cache_dispatch) return BuiltinCore._run(self)
def get_statistics(self, address): stats = dict() def _aggregate_statistics(newstats, prefix=None): """ Aggregate a set of statistics from a child or parent server core. This adds the statistics to the overall statistics dict (optionally prepending a prefix, such as "Child-1", to uniquely identify this set of statistics), and aggregates it with the set of running totals that are kept from all cores. """ for statname, vals in newstats.items(): if statname.startswith("ChildCore:"): statname = statname[5:] if prefix: prettyname = "%s:%s" % (prefix, statname) else: prettyname = statname stats[prettyname] = vals totalname = "Total:%s" % statname if totalname not in stats: stats[totalname] = vals else: newmin = min(stats[totalname][0], vals[0]) newmax = max(stats[totalname][1], vals[1]) newcount = stats[totalname][3] + vals[3] newmean = ((stats[totalname][2] * stats[totalname][3]) + (vals[2] * vals[3])) / newcount stats[totalname] = (newmin, newmax, newmean, newcount) stats = dict() for childname in self._all_children: _aggregate_statistics(self.rpc_q.rpc(childname, "get_statistics", args=[address]), prefix=childname) _aggregate_statistics(BuiltinCore.get_statistics(self, address)) return stats
def get_statistics(self, address): stats = dict() def _aggregate_statistics(newstats, prefix=None): """ Aggregate a set of statistics from a child or parent server core. This adds the statistics to the overall statistics dict (optionally prepending a prefix, such as "Child-1", to uniquely identify this set of statistics), and aggregates it with the set of running totals that are kept from all cores. """ for statname, vals in newstats.items(): if statname.startswith("ChildCore:"): statname = statname[5:] if prefix: prettyname = "%s:%s" % (prefix, statname) else: prettyname = statname stats[prettyname] = vals totalname = "Total:%s" % statname if totalname not in stats: stats[totalname] = vals else: newmin = min(stats[totalname][0], vals[0]) newmax = max(stats[totalname][1], vals[1]) newcount = stats[totalname][3] + vals[3] newmean = ((stats[totalname][2] * stats[totalname][3]) + (vals[2] * vals[3])) / newcount stats[totalname] = (newmin, newmax, newmean, newcount) stats = dict() for childname in self._all_children: _aggregate_statistics( self.rpc_q.rpc(childname, "get_statistics", args=[address]), prefix=childname) _aggregate_statistics(BuiltinCore.get_statistics(self, address)) return stats
def set_debug(self, address, debug): self.rpc_q.set_debug(debug) self.rpc_q.publish("set_debug", args=[address, debug]) return BuiltinCore.set_debug(self, address, debug)