Added docstrings, updated README file.

This commit is contained in:
Carlo Contavalli
2013-02-06 00:22:35 -08:00
parent 3a8625c659
commit bd384ed430
2 changed files with 149 additions and 2 deletions
+69
View File
@@ -107,34 +107,83 @@ CLASSES
Config
class AgentManager(__builtin__.object)
| Manages the ssh-agent for one identity.
|
| Methods defined here:
|
| FindUnloadedKeys(self, keys)
| Determines which keys have not been loaded yet.
|
| Args:
| keys: dict as returned by FindKeys.
|
| Returns:
| iterable of strings, paths to private key files to load.
|
| GetLoadedKeys(self)
| Returns an iterable of strings, each the fingerprint of a loaded key.
|
| LoadKeyFiles(self, keys)
| Load all specified keys.
|
| Args:
| keys: iterable of strings, each string a path to a key to load.
|
| LoadUnloadedKeys(self, keys)
| Loads all the keys specified that are not loaded.
|
| Args:
| keys: dict as returned by FindKeys.
|
| RunSSH(self, argv)
| Execs ssh with the specified arguments.
|
| __init__(self, identity, config)
| Initializes an AgentManager object.
|
| Args:
| identity: string, identity the ssh-agent managed by this instance of
| an AgentManager will control.
| config: object implementing the Config interface, allows access to
| the user configuration parameters.
|
| Attributes:
| identity: same as above.
| config: same as above.
| agents_path: directory where the config of all agents is kept.
| agent_file: the config of the agent corresponding to this identity.
|
| Parameters:
| DIR_AGENTS: used to compute agents_path.
| BINARY_SSH: path to the ssh binary.
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| EscapeShellArguments(argv)
| Escapes all arguments to the shell, returns a string.
|
| GetAgentFile(path, identity)
| Returns the path to an agent config file.
|
| Args:
| path: string, the path where agent config files are kept.
| identity: string, identity for which to load the agent.
|
| Returns:
| string, path to the agent file.
|
| GetPublicKeyFingerprint(key)
| Returns the fingerprint of a public key as a string.
|
| IsAgentFileValid(agentfile)
| Returns true if the specified agentfile refers to a running agent.
|
| RunShellCommand(command)
| Runs a shell command, returns (status, stdout), (int, string).
|
| RunShellCommandInAgent(agentfile, command)
| Runs a shell command with an agent configured in the environment.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
@@ -146,11 +195,15 @@ CLASSES
| list of weak references to the object (if defined)
class Config(__builtin__.object)
| Holds and loads users configurations.
|
| Methods defined here:
|
| Get(self, parameter)
| Returns the value of a parameter, or causes the script to exit.
|
| Load(self)
| Load configurations from the default user file.
|
| __init__(self)
|
@@ -158,6 +211,7 @@ CLASSES
| Static methods defined here:
|
| Expand(value)
| Expand environment variables or ~ in string parameters.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
@@ -198,6 +252,21 @@ FUNCTIONS
matching the first element in elements.
FindKeys(identity, config)
Finds all the private and public keys associated with an identity.
Args:
identity: string, name of the identity to load strings of.
config: object implementing the Config interface, providing configurations
for the user.
Returns:
dict, {"key name": {"pub": "/path/to/public/key", "priv":
"/path/to/private/key"}}, for each key found, the path of the public
key and private key. The key name is just a string representing the
key. Note that for a given key, it is not guaranteed that both the
public and private key will be found.
The return value is affected by DIR_IDENTITIES and PATTERN_KEYS
configuration parameters.
main(argv)
+80 -2
View File
@@ -107,6 +107,8 @@ import collections
class Config(object):
"""Holds and loads users configurations."""
defaults = {
# Where to find the per-user configuration.
"FILE_USER_CONFIG": "$HOME/.ssh-ident",
@@ -135,23 +137,28 @@ class Config(object):
self.values = {}
def Load(self):
"""Load configurations from the default user file."""
path = self.Get("FILE_USER_CONFIG")
variables = {}
try:
execfile(path, {}, variables)
except IOError:
print >>sys.stderr, "Warning: could not load config '%s', you might as well be using plain ssh." % path
print >>sys.stderr, (
"Warning: could not load config '%s', "
"you might as well be using plain ssh." % path)
return self
self.values = variables
return self
@staticmethod
def Expand(value):
"""Expand environment variables or ~ in string parameters."""
if isinstance(value, str):
return os.path.expanduser(os.path.expandvars(value))
return value
def Get(self, parameter):
"""Returns the value of a parameter, or causes the script to exit."""
if parameter in self.values:
return self.Expand(self.values[parameter])
if parameter in self.defaults:
@@ -198,6 +205,22 @@ def FindIdentity(argv, config):
config.Get("DEFAULT_IDENTITY"))
def FindKeys(identity, config):
"""Finds all the private and public keys associated with an identity.
Args:
identity: string, name of the identity to load strings of.
config: object implementing the Config interface, providing configurations
for the user.
Returns:
dict, {"key name": {"pub": "/path/to/public/key", "priv":
"/path/to/private/key"}}, for each key found, the path of the public
key and private key. The key name is just a string representing the
key. Note that for a given key, it is not guaranteed that both the
public and private key will be found.
The return value is affected by DIR_IDENTITIES and PATTERN_KEYS
configuration parameters.
"""
keyfiles = glob.glob(os.path.join(
config.Get("DIR_IDENTITIES"), identity, config.Get("PATTERN_KEYS")))
@@ -218,13 +241,38 @@ def FindKeys(identity, config):
return found
class AgentManager(object):
"""Manages the ssh-agent for one identity."""
def __init__(self, identity, config):
"""Initializes an AgentManager object.
Args:
identity: string, identity the ssh-agent managed by this instance of
an AgentManager will control.
config: object implementing the Config interface, allows access to
the user configuration parameters.
Attributes:
identity: same as above.
config: same as above.
agents_path: directory where the config of all agents is kept.
agent_file: the config of the agent corresponding to this identity.
Parameters:
DIR_AGENTS: used to compute agents_path.
BINARY_SSH: path to the ssh binary.
"""
self.identity = identity
self.config = config
self.agents_path = os.path.abspath(config.Get("DIR_AGENTS"))
self.agent_file = self.GetAgentFile(self.agents_path, self.identity)
def LoadUnloadedKeys(self, keys):
"""Loads all the keys specified that are not loaded.
Args:
keys: dict as returned by FindKeys.
"""
toload = self.FindUnloadedKeys(keys)
if toload:
print "Loading keys:\n %s" % "\n ".join(toload)
@@ -233,6 +281,14 @@ class AgentManager(object):
print "All keys already loaded"
def FindUnloadedKeys(self, keys):
"""Determines which keys have not been loaded yet.
Args:
keys: dict as returned by FindKeys.
Returns:
iterable of strings, paths to private key files to load.
"""
loaded = set(self.GetLoadedKeys())
toload = set()
for key, config in keys.iteritems():
@@ -249,10 +305,16 @@ class AgentManager(object):
return toload
def LoadKeyFiles(self, keys):
"""Load all specified keys.
Args:
keys: iterable of strings, each string a path to a key to load.
"""
keys = " ".join(keys)
self.RunShellCommandInAgent(self.agent_file, "ssh-add %s" % keys)
def GetLoadedKeys(self):
"""Returns an iterable of strings, each the fingerprint of a loaded key."""
retval, stdout = self.RunShellCommandInAgent(self.agent_file, "ssh-add -l")
if retval != 0:
return []
@@ -268,6 +330,7 @@ class AgentManager(object):
@staticmethod
def GetPublicKeyFingerprint(key):
"""Returns the fingerprint of a public key as a string."""
retval, stdout = AgentManager.RunShellCommand("ssh-keygen -l -f %s |tr -s ' '" % key)
if retval:
return None
@@ -280,6 +343,15 @@ class AgentManager(object):
@staticmethod
def GetAgentFile(path, identity):
"""Returns the path to an agent config file.
Args:
path: string, the path where agent config files are kept.
identity: string, identity for which to load the agent.
Returns:
string, path to the agent file.
"""
# Use the hostname as part of the path just in case this is on NFS.
agentfile = os.path.join(path, "agent-%s-%s" % (identity, socket.gethostname()))
if os.access(agentfile, os.R_OK) and AgentManager.IsAgentFileValid(agentfile):
@@ -293,6 +365,7 @@ class AgentManager(object):
@staticmethod
def IsAgentFileValid(agentfile):
"""Returns true if the specified agentfile refers to a running agent."""
retval, output = AgentManager.RunShellCommandInAgent(agentfile, "ssh-add -l >/dev/null 2>/dev/null")
if retval & 0xff not in [0, 1]:
print >>sys.stderr, "Agent in %s not running" % agentfile
@@ -301,6 +374,7 @@ class AgentManager(object):
@staticmethod
def RunShellCommand(command):
"""Runs a shell command, returns (status, stdout), (int, string)."""
command = ["/bin/sh", "-c", command]
process = subprocess.Popen(command, stdout=subprocess.PIPE)
stdout, stderr = process.communicate()
@@ -308,19 +382,23 @@ class AgentManager(object):
@staticmethod
def RunShellCommandInAgent(agentfile, command):
command = ["/usr/bin/env", "-i", "/bin/sh", "-c", ". %s >/dev/null 2>/dev/null; %s" % (agentfile, command)]
"""Runs a shell command with an agent configured in the environment."""
command = ["/usr/bin/env", "-i", "/bin/sh", "-c",
". %s >/dev/null 2>/dev/null; %s" % (agentfile, command)]
process = subprocess.Popen(command, stdout=subprocess.PIPE)
stdout, stderr = process.communicate()
return process.wait(), stdout
@staticmethod
def EscapeShellArguments(argv):
"""Escapes all arguments to the shell, returns a string."""
escaped = []
for arg in argv:
escaped.append("'%s'" % arg.replace("'", "'\"'\"'"))
return " ".join(escaped)
def RunSSH(self, argv):
"""Execs ssh with the specified arguments."""
command = ["/bin/sh", "-c", ". %s >/dev/null 2>/dev/null; exec %s %s" % (
self.agent_file, self.config.Get("BINARY_SSH"),
self.EscapeShellArguments(argv))]