source: web/old/remctl-2.14/python/remctl.py @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 5.6 KB
Line 
1# Python interface to remctl.
2#
3# This is the high-level interface that most Python programs that use remctl
4# should be using.  It's a Python wrapper around the _remctl C module, which
5# exposes exactly the libremctl API.
6#
7# Original implementation by Thomas L. Kula <kula@tproa.net>
8# Copyright 2008 Thomas L. Kula <kula@tproa.net>
9# Copyright 2008 Board of Trustees, Leland Stanford Jr. University
10#
11# Permission to use, copy, modify, and distribute this software and its
12# documentation for any purpose and without fee is hereby granted, provided
13# that the above copyright notice appear in all copies and that both that
14# copyright notice and this permission notice appear in supporting
15# documentation, and that the name of Thomas L. Kula not be used in
16# advertising or publicity pertaining to distribution of the software without
17# specific, written prior permission. Thomas L. Kula makes no representations
18# about the suitability of this software for any purpose.  It is provided "as
19# is" without express or implied warranty.
20#
21# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
22# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
23# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
24
25"""Interface to remctl.
26
27   This module is an interface to remctl, a client/server
28   protocol for running single commands on a remote host
29   using Kerberos v5 authentication.
30"""
31
32VERSION = '2.14'
33
34import _remctl
35
36# Exception classes.
37
38class RemctlError(Exception):
39    """The underlying remctl library has returned an error."""
40    def __init__(self, value):
41        self.value = value
42    def __str__(self):
43        return str(self.value)
44
45class RemctlProtocolError(RemctlError):
46    """A remctl protocol error occurred.
47
48    This exception is only used with the remctl.remctl() simple interface;
49    for the full interface, errors are returned as a regular output token.
50    """
51    pass
52
53class RemctlNotOpenedError(RemctlError):
54    """No open connection to a server."""
55    pass
56
57# Simple interface.
58
59class RemctlSimpleResult:
60    """An object holding the results from the simple interface."""
61    def __init__(self):
62        self.stdout = None
63        self.stderr = None
64        self.status = None
65
66def remctl(host, port = None, principal = None, command = []):
67    """Simple interface to remctl.
68
69    Connect to HOST on PORT, using PRINCIPAL as the server principal for
70    authentication, and issue COMMAND.  Returns the result as a
71    RemctlSimpleResult object, which has three attributes.  stdout holds the
72    complete standard output, stderr holds the complete standard error, and
73    status holds the exit status.
74    """
75    if port == None:
76        port = 0
77    else:
78        try:
79            port = int(port)
80        except ValueError:
81            raise TypeError, 'port must be a number: ' + `port`
82    if (port < 0) or (port > 65535):
83        raise ValueError, 'invalid port number: ' + `port`
84    if isinstance(command, (basestring, bool, int, float)):
85        raise TypeError, 'command must be a sequence or iterator'
86
87    # Convert the command to a list of strings.
88    mycommand = []
89    for item in command:
90        mycommand.append(str(item))
91    if len(mycommand) < 1:
92        raise ValueError, 'command must not be empty'
93
94    # At this point, things should be sane.  Call the low-level interface.
95    output = _remctl.remctl(host, port, principal, mycommand)
96    if output[0] != None:
97        raise RemctlProtocolError, output[0]
98    result = RemctlSimpleResult()
99    setattr(result, 'stdout', output[1])
100    setattr(result, 'stderr', output[2])
101    setattr(result, 'status', output[3])
102    return result
103
104# Complex interface.
105
106class Remctl:
107    def __init__(self, host = None, port = None, principal = None):
108        self.r = _remctl.remctl_new()
109        self.opened = False
110
111        if host != None:
112            self.open(host, port, principal)
113
114    def open(self, host, port = None, principal = None):
115        if port == None:
116            port = 0
117        else:
118            try:
119                port = int(port)
120            except ValueError:
121                raise TypeError, 'port must be a number: ' + `port`
122        if (port < 0) or (port > 65535):
123            raise ValueError, 'invalid port number: ' + `port`
124
125        # At this point, things should be sane.  Call the low-level interface.
126        if not _remctl.remctl_open(self.r, host, port, principal):
127            raise RemctlError, self.error()
128        self.opened = True
129
130    def command(self, comm):
131        commlist = []
132        if not self.opened:
133            raise RemctlNotOpenedError, 'no currently open connection'
134        if isinstance(comm, (basestring, bool, int, float)):
135            raise TypeError, 'command must be a sequence or iterator'
136
137        # Convert the command to a list of strings.
138        for item in comm:
139            commlist.append(str(item))
140        if len(commlist) < 1:
141            raise ValueError, 'command must not be empty'
142
143        # At this point, things should be sane.  Call the low-level interface.
144        if not _remctl.remctl_commandv(self.r, commlist):
145            raise RemctlError, self.error()
146
147    def output(self):
148        if not self.opened:
149            raise RemctlNotOpenedError, 'no currently open connection'
150        return _remctl.remctl_output(self.r)
151
152    def close(self):
153        del(self.r)
154        self.r = None
155        self.opened = False
156
157    def error(self):
158        if self.r == None:
159            # We do this instead of throwing an exception so that callers
160            # don't have to handle an exception when they are trying to find
161            # out why an exception occured.
162            return 'no currently open connection'
163        return _remctl.remctl_error(self.r)
Note: See TracBrowser for help on using the repository browser.