~ubuntu-branches/ubuntu/lucid/landscape-client/lucid-updates

« back to all changes in this revision

Viewing changes to landscape/manager/scriptexecution.py

  • Committer: Package Import Robot
  • Author(s): Andreas Hasenack
  • Date: 2012-04-10 14:28:48 UTC
  • mfrom: (1.1.27)
  • mto: This revision was merged to the branch mainline in revision 35.
  • Revision ID: package-import@ubuntu.com-20120410142848-7xsy4g2xii7y7ntc
ImportĀ upstreamĀ versionĀ 12.04.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
import shutil
11
11
 
12
12
from twisted.internet.protocol import ProcessProtocol
13
 
from twisted.internet.defer import Deferred, fail, maybeDeferred
 
13
from twisted.internet.defer import (
 
14
    Deferred, fail, inlineCallbacks, returnValue, succeed)
14
15
from twisted.internet.error import ProcessDone
15
16
 
 
17
from landscape import VERSION
 
18
from landscape.constants import UBUNTU_PATH
16
19
from landscape.lib.scriptcontent import build_script
 
20
from landscape.lib.fetch import fetch_async, HTTPCodeError
 
21
from landscape.lib.persist import Persist
17
22
from landscape.manager.plugin import ManagerPlugin, SUCCEEDED, FAILED
18
23
 
19
24
 
20
25
ALL_USERS = object()
21
26
TIMEOUT_RESULT = 102
22
27
PROCESS_FAILED_RESULT = 103
23
 
# The name "UBUNTU" is used in the variable name due to the fact that the path
24
 
# is Ubuntu-specific, taken from /etc/login.defs.
25
 
UBUNTU_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 
28
FETCH_ATTACHMENTS_FAILED_RESULT = 104
26
29
 
27
30
 
28
31
class UnknownUserError(Exception):
86
89
 
87
90
 
88
91
class ScriptRunnerMixin(object):
 
92
    """
 
93
    @param process_factory: The L{IReactorProcess} provider to run the
 
94
        process with.
 
95
    """
89
96
 
90
97
    def __init__(self, process_factory=None):
91
 
        """
92
 
        @param process_factory: The L{IReactorProcess} provider to run the
93
 
            process with.
94
 
        """
95
98
        if process_factory is None:
96
99
            from twisted.internet import reactor as process_factory
97
100
        self.process_factory = process_factory
113
116
        script_file.close()
114
117
 
115
118
    def _run_script(self, filename, uid, gid, path, env, time_limit):
 
119
 
 
120
        if uid == os.getuid():
 
121
            uid = None
 
122
        if gid == os.getgid():
 
123
            gid = None
 
124
 
116
125
        pp = ProcessAccumulationProtocol(
117
126
            self.registry.reactor, self.size_limit)
118
127
        self.process_factory.spawnProcess(
157
166
                    FAILED,
158
167
                    u"Scripts cannot be run as user %s." % (user,),
159
168
                    opid)
 
169
            server_supplied_env = message.get("env", None)
160
170
 
161
171
            d = self.run_script(message["interpreter"], message["code"],
162
 
                                time_limit=message["time-limit"],
163
 
                                user=user, attachments=message["attachments"])
 
172
                                time_limit=message["time-limit"], user=user,
 
173
                                attachments=message["attachments"],
 
174
                                server_supplied_env=server_supplied_env)
164
175
            d.addCallback(self._respond_success, opid)
165
176
            d.addErrback(self._respond_failure, opid)
166
177
            return d
180
191
            code = TIMEOUT_RESULT
181
192
        elif failure.check(ProcessFailedError):
182
193
            code = PROCESS_FAILED_RESULT
 
194
        elif failure.check(HTTPCodeError):
 
195
            code = FETCH_ATTACHMENTS_FAILED_RESULT
 
196
            return self._respond(
 
197
                FAILED, str(failure.value), opid,
 
198
                FETCH_ATTACHMENTS_FAILED_RESULT)
 
199
 
183
200
        if code is not None:
184
201
            return self._respond(FAILED, failure.value.data, opid, code)
185
202
        else:
186
203
            return self._respond(FAILED, str(failure), opid)
187
204
 
 
205
    @inlineCallbacks
 
206
    def _save_attachments(self, attachments, uid, gid, computer_id):
 
207
        root_path = self.registry.config.url.rsplit("/", 1)[0] + "/attachment/"
 
208
        attachment_dir = tempfile.mkdtemp()
 
209
        headers = {"User-Agent": "landscape-client/%s" % VERSION,
 
210
                   "Content-Type": "application/octet-stream",
 
211
                   "X-Computer-ID": computer_id}
 
212
        for filename, attachment_id in attachments.items():
 
213
            if isinstance(attachment_id, str):
 
214
                # Backward compatible behavior
 
215
                data = attachment_id
 
216
                yield succeed(None)
 
217
            else:
 
218
                data = yield fetch_async(
 
219
                    "%s%d" % (root_path, attachment_id),
 
220
                    cainfo=self.registry.config.ssl_public_key,
 
221
                    headers=headers)
 
222
            full_filename = os.path.join(attachment_dir, filename)
 
223
            attachment = file(full_filename, "wb")
 
224
            os.chmod(full_filename, 0600)
 
225
            if uid is not None:
 
226
                os.chown(full_filename, uid, gid)
 
227
            attachment.write(data)
 
228
            attachment.close()
 
229
        os.chmod(attachment_dir, 0700)
 
230
        if uid is not None:
 
231
            os.chown(attachment_dir, uid, gid)
 
232
        returnValue(attachment_dir)
 
233
 
188
234
    def run_script(self, shell, code, user=None, time_limit=None,
189
 
                   attachments=None):
 
235
                   attachments=None, server_supplied_env=None):
190
236
        """
191
237
        Run a script based on a shell and the code.
192
238
 
210
256
        if not os.path.exists(shell.split()[0]):
211
257
            return fail(
212
258
                UnknownInterpreterError(shell))
 
259
 
213
260
        uid, gid, path = get_user_info(user)
 
261
 
214
262
        fd, filename = tempfile.mkstemp()
215
263
        script_file = os.fdopen(fd, "w")
216
 
        self.write_script_file(script_file, filename, shell, code, uid, gid)
 
264
        self.write_script_file(
 
265
            script_file, filename, shell, code, uid, gid)
217
266
 
218
 
        env = {
219
 
            "PATH": UBUNTU_PATH,
220
 
            "USER": user or "",
221
 
            "HOME": path or "",
222
 
            }
 
267
        env = {"PATH": UBUNTU_PATH, "USER": user or "", "HOME": path or ""}
 
268
        if server_supplied_env:
 
269
            env.update(server_supplied_env)
223
270
        old_umask = os.umask(0022)
224
271
 
225
 
        def run_with_attachments():
226
 
            if attachments:
227
 
                attachment_dir = tempfile.mkdtemp()
 
272
        if attachments:
 
273
            persist = Persist(
 
274
                filename=os.path.join(self.registry.config.data_path,
 
275
                                      "broker.bpickle"))
 
276
            persist = persist.root_at("registration")
 
277
            computer_id = persist.get("secure-id")
 
278
            d = self._save_attachments(attachments, uid, gid, computer_id)
 
279
        else:
 
280
            d = succeed(None)
 
281
 
 
282
        def prepare_script(attachment_dir):
 
283
 
 
284
            if attachment_dir is not None:
228
285
                env["LANDSCAPE_ATTACHMENTS"] = attachment_dir
229
 
                for attachment_filename, data in attachments.iteritems():
230
 
                    full_filename = os.path.join(
231
 
                        attachment_dir, attachment_filename)
232
 
                    attachment = file(full_filename, "wb")
233
 
                    os.chmod(full_filename, 0600)
234
 
                    if uid is not None:
235
 
                        os.chown(full_filename, uid, gid)
236
 
                    attachment.write(data)
237
 
                    attachment.close()
238
 
                os.chmod(attachment_dir, 0700)
239
 
                if uid is not None:
240
 
                    os.chown(attachment_dir, uid, gid)
241
286
 
242
287
            return self._run_script(
243
288
                filename, uid, gid, path, env, time_limit)
244
289
 
245
 
        result = maybeDeferred(run_with_attachments)
246
 
        return result.addBoth(self._cleanup, filename, env, old_umask)
 
290
        d.addCallback(prepare_script)
 
291
        return d.addBoth(self._cleanup, filename, env, old_umask)
247
292
 
248
293
    def _cleanup(self, result, filename, env, old_umask):
249
294
        try: