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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
Python interface to remctl
OVERVIEW
The Python interface to remctl provides Python bindings to the libremctl
client library plus a high-level interface that translates the libremctl
API into something closer to idiomatic Python.
This module provides three interfaces: a low-level interface that
provides a minimal translation from the libremctl API; a simple
interface in Python that performs a single call to a remctl server and
returns the result as an object; and a full interface in Python which
provides more control over the connection, returns individual output
tokens, and allows multiple commands to be sent via the same connection.
REQUIREMENTS
The module has been tested with Python 2.7 and 3.7 and may not work with
older versions of Python, although the only known incompatibility are
with Python 3.0 and with parameters in setup.py that are not supported
prior to Python 2.3.
SIMPLIFIED INTERFACE
remctl.remctl(host, port, principal, command)
Runs a command on the remote system and returns an object containing
the results.
Arguments:
* host (required): str, the host to connect to
* port (optional): int, the port to connect to
* principal (optional): str, authentication identity of host
* command (required): iterable of str or bytes, the command
If port is not given, the library default is used (try 4373 first
and fall back to attempting the legacy 4444 port). If principal is
not given, the library default (host/<host> with the realm
determined by the domain-realm mapping) is used. To use the
defaults, only the host and command may be specified using named
arguments, or None can be passed as the port and principal
arguments.
command should be an iterable or str or bytes. Any str elements
will be encoded in UTF-8 to convert them to bytes. (Currently,
anything that can be converted to str is also supported, but this
support will be dropped in a future version.)
Returns an object of type RemctlSimpleResult with the following
attributes:
* stdout: bytes, the standard output from the command
* stderr: bytes, the standard error from the command
* status: int, the exit status of the command
Exceptions:
* ValueError, TypeError: an invalid argument was supplied
* RemctlProtocolError: an error occurred in the remctl communication
The string value of the RemctlProtocolError exception contains the
string returned by the remctl library. This may be an internal
error (such as a network error) or an error returned by the remote
server (such as an unknown command error).
Here is an example using the simplified interface:
from __future__ import print_function
import remctl
import sys
command = ("test", "test")
try:
result = remctl.remctl(host="foo.example.com", command=command)
except remctl.RemctlProtocolError as e:
print("Error:", str(e))
sys.exit(1)
if result.stdout:
print("stdout:", result.stdout)
if result.stderr:
print("stderr:", result.stderr)
print("exit status:", result.status)
FULL INTERFACE
The full remctl interface requires the user to do more bookkeeping, but
provides more flexibility and visibility into what is happening at a
protocol level. It allows issuing multiple commands on the same
persistant connection (provided that the remote server supports protocol
version two; if it doesn't, the library will transparently fall back to
opening a connection for each command).
To use the full interface, first create a connection object with
remctl.Remctl() (either passing it the connection arguments or then
calling its open() method), and then call the command() method to issue
a command. Read output tokens with output() until a status token has
been received. Then the command is complete and another command can be
issued.
remctl.Remctl(host, port, principal)
The constructor. Create a new Remctl object. The arguments are
optional; if given, the constructor immediately calls open() and
passes those arguments to it. See below for their meaning and
possible exceptions.
All further methods below must be called on a Remctl object as returned
by the constructor.
Remctl.set_ccache(ccache)
Sets the credential cache for outgoing connections to ccache.
Arguments:
* ccache (required): str, the credential cache to use
This is normally the full path to a Kerberos ticket cache, but may
have other valid forms depending on the underlying Kerberos
implementation in use by GSS-API. This method will affect all
subsequent open() calls on the same object, but will have no effect
on connections that are already open.
If the remctl client library was built against a Kerberos library
and the GSS-API library supported gss_krb5_import_cred, this call
affects only this Remctl object. Otherwise, this will affect not
only all subsequent open() calls for the same object, but all
subsequent remctl connections of any kind from the same process, and
even other GSS-API connections from the same process unrelated to
remctl.
Not all GSS-API implementations support setting the credential
cache. If this operation is not supported, a RemctlError exception
will be thrown.
Exceptions:
* RemctlError: setting the credential cache isn't supported
Remctl.set_source_ip(source)
Sets the source IP for future outgoing connections. This will
affect all subsequent open() calls on the same object, but will have
no effect on connections that are already open.
Arguments:
* source (required): str, the source IP address
The source may be either an IPv4 or an IPv6 address (if IPv6 is
supported). It must be an IP address, not a host name.
Exceptions:
* RemctlError: a memory allocation failure occurred
Remctl.set_timeout(timeout)
Sets the timeout for connections and commands. All subsequent
operations on this object will be subject to this timeout, including
open() if called prior to calling open(). If the timeout is 0, this
clears any timeout that was previously set.
The timeout is a timeout on network activity from the server, not on
a complete operation. So, for example, a timeout of ten seconds
just requires that the server send some data every ten seconds. If
the server sends only tiny amounts of data at a time, the complete
operation could take much longer than ten seconds without triggering
the timeout.
Arguments:
* timeout (required): int, the timeout in seconds or 0
Exceptions:
* RemctlError: the timeout was negative
Remctl.open(host, port, principal)
Open a connection to a remote server and authenticate. There is no
return value; an exception is thrown on any error.
Arguments:
* host (required): str, the host to connect to
* port (optional): int, the port to connect to
* principal (optional): str, authentication identity of host
If port is not given, the library default is used (try 4373 first
and fall back to attempting the legacy 4444 port). If principal is
not given, the library default (host/<host> with the realm
determined by the domain-realm mapping) is used.
Exceptions:
* ValueError, TypeError: an invalid argument was supplied
* RemctlError: a network or authentication error occurred
The string value of the RemctlError exception contains the string
returned by the remctl library, similar to what would be returned by
the error() method.
Remctl.command(command)
Send a command to the remote host. There is no return value; an
exception is thrown on any error.
The Remctl object must already be connected. The command may, under
the remctl protocol, contain any character, but be aware that most
remctl servers will reject commands or arguments containing ASCII 0
(NUL). This currently therefore cannot be used for upload of
arbitrary unencoded binary data.
Arguments:
* command (required): iterable of str or bytes, the command
command should be an iterable or str or bytes. Any str elements
will be encoded in UTF-8 to convert them to bytes. (Currently,
anything that can be converted to str is also supported, but this
support will be dropped in a future version.)
Exceptions:
* ValueError, TypeError: an invalid argument was supplied
* RemctlError: a network or authentication error occurred
* RemctlNotOpenedError: no connection currently open
The string value of the RemctlError exception contains the string
returned by the remctl library, the same as would be returned by the
error() method.
Remctl.output()
Reads an output token from the server and returns it as a tuple. A
command will result in either one error token or zero or more output
tokens followed by a status token. The output is complete as soon
as any token other than an output token has been received, but the
module will keep returning done tokens to the caller for as long as
output() is called without another call to command().
The members of the returned tuple are:
* type: str, "output", "status", "error", or "done"
* output: bytes, the returned output or error
* stream: int, 1 for stdout or 2 for stderr for an output token
* status: int, exit status of command for a status token
* error: int, remctl protocol error for an error token
type will always be present. The other members of the tuple may be
None depending on the type of token. Output tokens will have
output and stream information, error tokens will have output and
error information, and status tokens will have status information.
done tokens will return None for all other elements.
For error tokens, error holds the numeric error code (see the remctl
protocol specification), which is the recommended value for programs
to check when looking for specific errors. output will contain an
English text translation of the error code and the exact text may
change.
If the server does not return an output token, output() will throw
an exception.
Exceptions:
* RemctlError: a network or authentication error occurred
* RemctlNotOpenedError: no connection currently open
Remctl.noop()
Send a NOOP message to the server and read the reply. This is
primarily used to keep a connection to a remctl server alive, such
as through a firewall with a session timeout, while waiting to issue
further commands. Returns true on success, false on failure.
The NOOP message requires protocol version 3 support in the server,
so the caller should be prepared for this function to fail,
indicating that the connection could not be kept alive and possibly
that it was closed by the server. In this case, the client will
need to explicitly reopen the connection with open().
Exceptions:
* RemctlError: a network or authentication error occurred
* RemctlNotOpenedError: no connection currently open
Remctl.close()
Close a connection. After calling this method, open() must be
called for this object before sending any further commands. The
connection will also be automatically closed when the object is
destroyed, so calling this method is often not necessary.
Remctl.error()
Returns the error from the last failed Remctl method. This will be
the same string as was returned as the string value of a RemctlError
or RemctlProtocolError exception.
LOW-LEVEL INTERFACE
This module also provides a _remctl module, which exports a low-level
interface with essentially the same API as the C API. It is very
similar to the simplified and full interface above except that the
simplified call returns its results as a tuple, status codes are
returned from functions instead of throwing exceptions (exceptions are
only thrown for things like out-of-memory errors), argument checking
won't be as nice and in some cases relies on the underlying libremctl C
library to do the error checking, and command arguments have to be lists
and not arbitrary sequences or iterators.
The _remctl interface is not currently documented or intended for direct
use. Use at your own risk.
HISTORY
The original implementation was written by Thomas L. Kula
<kula@tproa.net> and was known as pyremctl. As of remctl 2.13 it is
part of the stock remctl distribution and ongoing maintenance is done by
Russ Allbery and Thomas L. Kula.
THANKS
Andrew Mortensen for general code formatting comments and a reminder to
free malloc'd memory.
LICENSE
Copyright 2008 Thomas L. Kula <kula@tproa.net>
Copyright 2008-2009, 2011-2012
The Board of Trustees of the Leland Stanford Junior University
Copyright 2014, 2019 Russ Allbery <eagle@eyrie.org>
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.
SPDX-License-Identifier: FSFAP
|