Overview ======== Gaffer is a set of Python modules and tools to easily maintain and interact with your processes. Depending on your needs you ca simply use the gaffer tools (eventually extend them) or embed the gaffer possibilities in your own apps. Design ------ Gaffer is internally based on an event loop using the `libuv `_ from Joyent via the `pyuv binding `_ All gaffer events are added to the loop and processes asynchronously wich make it pretty performant to handle multiple process and their control. At the lowest level you will find the manager. A manager is responsible of maintaining process alive and manage actions on them: - increase/decrease the number of processes / process template - start/stop processes - add/remove process templates to manage A process template describe the way a process will be launched and how many OS processes you want to handle for this template. This number can be changed dynamically. Current properties of this templates are: - **name**: name of the process - **cmd**: program command, string) - **args**: the arguments for the command to run. Can be a list or a string. If **args** is a string, it's splitted using :func:`shlex.split`. Defaults to None. - **env**: a mapping containing the environment variables the command will run with. Optional - **uid**: int or str, user id - **gid**: int or st, user group id, - **cwd**: working dir - **detach**: the process is launched but won't be monitored and won't exit when the manager is stopped. - **shell**: boolean, run the script in a shell. (UNIX only), - **os_env**: boolean, pass the os environment to the program - **numprocesses**: int the number of OS processes to launch for this description - **flapping**: a FlappingInfo instance or, if flapping detection should be used. flapping parameters are: - **attempts**: maximum number of attempts before we stop the process and set it to retry later - **window**: period in which we are testing the number of retry - **retry_in**: seconds, the time after we restart the process and try to spawn them - **max_retry**: maximum number of retry before we give up and stop the process. - **redirect_output**: list of io to redict (max 2) this is a list of custom labels to use for the redirection. Ex: ["a", "b"] will redirect stdoutt & stderr and stdout events will be labeled "a" - **redirect_input**: Boolean (False is the default). Set it if you want to be able to write to stdin. The manager is also responsible of starting and stopping gaffer applications that you add to he manager to react on different events. A applicaton can fetch informations from the manager and interract with him. Running an application is done like this:: # initialize the controller with the default loop loop = pyuv.Loop.default_loop() manager = Manager(loop=loop) # start the controller manager.start(applications=[HttpHandler()]) .... # do smth manager.stop() # stop the controlller manager.run() # run the event loop The HttpHandler application allows you to interact with gaffer via HTTP. It is used by the gafferd server which is able for now to load process templates via an ini files and maintain an HTTP endpoint which can be configured to be accessible on multiples interfaces and transports (tcp & unix sockets) . .. note:: Only applications instances are used by the manager. It allows you to initialize them with your own settings. Building your own application is easy, basically an application has the following structure:: class MyApplication(object): def __init__(self): # do inti def start(self, loop, manager): # this method is call by the manager to start the controller def stop(self): # method called when the manager stop def restart(self): # methhod called when the manager restart You can use this structure for anything you want, even add an app to the loop. To help you in your work a :doc:`pyuv implementation ` of tornado is integrated and a powerfull :doc:`events ` modules will allows you to manage PUB/SUB events (or anything evented) inside your app. An EventEmitter is a threadsafe class to manage subscriber and publisher of events. It is internally used to broadcast processes and manager events. Watch stats ----------- Stats of a process ca, be monitored continuously (there is a refresh interval of 0.1s to fetch CPU informations) using the following mettod:: manager.monitor(, ) Where `` is the name of the process template. In this case the statistics of all the the OS processes using this template will be emitted. Stats events are collected in the listener callback. Callback signature: ``callback(evtype, msg)``. **evtype** is always "STATS" here and **msg** is a dict:: { "mem_info1: int, "mem_info2: int, "cpu": int, "mem": int, "ctime": int, "pid": int, "username": str, "nicce": int, "cmdline": str, "children": [{ stat dict, ... }] } To unmonitor the process in your app run:: manager.unmonitor(, ) .. note:: Internally a monitor subscribe you to an EventEmitter. A timer is running until there are subscribers to the process stats events. Of course you can monitor directly to a process using the internal pid:: process = manager.running[pid] process.monitor() ... process.unmonitor() IO Events --------- Subscribe to stdout/stderr process stream +++++++++++++++++++++++++++++++++++++++++ You can subscribe to stdout/stderr process stream and even write to stdin if you want. To be able to receive the stdour/stderr streams in your application, you need to create a process with the *redirect_output* setting:: manager.add_process("nameofprocestemplate", cmd, redirect_output["stdout", "stderr"]) .. note:: Name of outputs can be anything, only the order count so if you want to name *stdout* as *a* just replace *stdout* by *a* in the declaration. If you don't want to receive *stderr*, just omit it in the list. Alos if you want to redirect stderr to stdout just use the same name. Then for example, to monitor the stdout output do:: process.monitor_io("stdout", somecallback) Callback signature: ``callback(evtype, msg)``. And to unmonitor:: process.unmonitor_io("stdout", somecallback) .. note:: To subscribe to all process streams replace the stream name by `'.'`` . Write to STDIN ++++++++++++++ Writing to stdin is pretty easy. Just do:: process.write("somedata") or to send multiple lines:: process.writelines(["line", "line"]) You can write lines from multiple publisher and multiple publishers can write at the same time. This method is threadsafe. HTTP API -------- See the :doc:`HTTP api description ` for more informations. Tools ----- Gaffer proposes different tools (and more will come soon) to manage your process without having to code. It can be used like `supervisor `_, `god `_, `runit `_ or other tools around. Speaking of runit a similar controlling will be available in 0.2 . See the :doc:`command-line` documentation for more informations.