/usr/share/pyshared/landscape/manager/shutdownmanager.py is in landscape-common 12.04.3-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | import logging
from twisted.internet.defer import Deferred
from twisted.internet.protocol import ProcessProtocol
from twisted.internet.error import ProcessDone
from landscape.manager.plugin import ManagerPlugin, SUCCEEDED, FAILED
class ShutdownFailedError(Exception):
"""Raised when a call to C{/sbin/shutdown} fails.
@ivar data: The data that the process printed before failing.
"""
def __init__(self, data):
self.data = data
class ShutdownManager(ManagerPlugin):
def __init__(self, process_factory=None):
if process_factory is None:
from twisted.internet import reactor as process_factory
self._process_factory = process_factory
def register(self, registry):
"""Add this plugin to C{registry}.
The shutdown manager handles C{shutdown} activity messages broadcast
from the server.
"""
super(ShutdownManager, self).register(registry)
registry.register_message("shutdown", self.perform_shutdown)
def perform_shutdown(self, message):
"""Request a system restart or shutdown.
If the call to C{/sbin/shutdown} runs without errors the activity
specified in the message will be responded as succeeded. Otherwise,
it will be responded as failed.
"""
operation_id = message["operation-id"]
reboot = message["reboot"]
protocol = ShutdownProcessProtocol()
protocol.set_timeout(self.registry.reactor)
protocol.result.addCallback(self._respond_success, operation_id)
protocol.result.addErrback(self._respond_failure, operation_id)
command, args = self._get_command_and_args(protocol, reboot)
self._process_factory.spawnProcess(protocol, command, args=args)
def _respond_success(self, data, operation_id):
logging.info("Shutdown request succeeded.")
return self._respond(SUCCEEDED, data, operation_id)
def _respond_failure(self, failure, operation_id):
logging.info("Shutdown request failed.")
return self._respond(FAILED, failure.value.data, operation_id)
def _respond(self, status, data, operation_id):
message = {"type": "operation-result",
"status": status,
"result-text": data,
"operation-id": operation_id}
return self.registry.broker.send_message(message, True)
def _get_command_and_args(self, protocol, reboot):
"""
Returns a C{command, args} 2-tuple suitable for use with
L{IReactorProcess.spawnProcess}.
"""
minutes = "+%d" % (protocol.delay // 60,)
if reboot:
args = ["/sbin/shutdown", "-r", minutes,
"Landscape is rebooting the system"]
else:
args = ["/sbin/shutdown", "-h", minutes,
"Landscape is shutting down the system"]
return "/sbin/shutdown", args
class ShutdownProcessProtocol(ProcessProtocol):
"""A ProcessProtocol for calling C{/sbin/shutdown}.
C{shutdown} doesn't return immediately when a time specification is
provided. Failures are reported immediately after it starts and return a
non-zero exit code. The process protocol calls C{shutdown} and waits for
failures for C{timeout} seconds. If no failures are reported it fires
C{result}'s callback with whatever output was received from the process.
If failures are reported C{result}'s errback is fired.
@ivar result: A L{Deferred} fired when C{shutdown} fails or
succeeds.
@ivar reboot: A flag indicating whether a shutdown or reboot should be
performed. Default is C{False}.
@ivar delay: The time in seconds from now to schedule the shutdown.
Default is 240 seconds. The time will be converted to minutes using
integer division when passed to C{shutdown}.
"""
def __init__(self, reboot=False, delay=240):
self.result = Deferred()
self.reboot = reboot
self.delay = delay
self._data = []
self._waiting = True
def get_data(self):
"""Get the data printed by the subprocess."""
return "".join(self._data)
def set_timeout(self, reactor, timeout=10):
"""
Set the error checking timeout, after which C{result}'s callback will
be fired.
"""
reactor.call_later(timeout, self._succeed)
def childDataReceived(self, fd, data):
"""Some data was received from the child.
Add it to our buffer to pass to C{result} when it's fired.
"""
if self._waiting:
self._data.append(data)
def processEnded(self, reason):
"""Fire back the C{result} L{Deferred}.
C{result}'s callback will be fired with the string of data received
from the subprocess, or if the subprocess failed C{result}'s errback
will be fired with the string of data received from the subprocess.
"""
if self._waiting:
if reason.check(ProcessDone):
self._succeed()
else:
self.result.errback(ShutdownFailedError(self.get_data()))
self._waiting = False
def _succeed(self):
"""Fire C{result}'s callback with data accumulated from the process."""
if self._waiting:
self.result.callback(self.get_data())
self._waiting = False
|