summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobey Pointer <robey@lag.net>2006-06-27 21:59:19 -0700
committerRobey Pointer <robey@lag.net>2006-06-27 21:59:19 -0700
commitc22df4490027a62268f1d46073d9513aad83ef2a (patch)
treeb66db4f24ea3364b65af6ad4a7faf985a15b4016
parent4fa4fdee4b85a74be63cb016955169eaac48daeb (diff)
[project @ robey@lag.net-20060628045919-ffac82c51c51b3df]
make HostKeys use odict to ensure order is preserved, and add HostKeys.save()
-rw-r--r--paramiko/hostkeys.py27
-rw-r--r--paramiko/odict.py63
2 files changed, 87 insertions, 3 deletions
diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py
index 5d880842..7cc8d40c 100644
--- a/paramiko/hostkeys.py
+++ b/paramiko/hostkeys.py
@@ -26,6 +26,7 @@ import UserDict
from paramiko.common import *
from paramiko.dsskey import DSSKey
+from paramiko.odict import odict
from paramiko.rsakey import RSAKey
@@ -50,7 +51,7 @@ class HostKeys (UserDict.DictMixin):
@type filename: str
"""
# hostname -> keytype -> PKey
- self._keys = {}
+ self._keys = odict()
self.contains_hashes = False
if filename is not None:
self.load(filename)
@@ -68,7 +69,7 @@ class HostKeys (UserDict.DictMixin):
@type key: L{PKey}
"""
if not hostname in self._keys:
- self._keys[hostname] = {}
+ self._keys[hostname] = odict()
if hostname.startswith('|1|'):
self.contains_hashes = True
self._keys[hostname][keytype] = key
@@ -89,7 +90,7 @@ class HostKeys (UserDict.DictMixin):
@raise IOError: if there was an error reading the file
"""
- f = file(filename, 'r')
+ f = open(filename, 'r')
for line in f:
line = line.strip()
if (len(line) == 0) or (line[0] == '#'):
@@ -105,6 +106,26 @@ class HostKeys (UserDict.DictMixin):
elif keytype == 'ssh-dss':
self.add(host, keytype, DSSKey(data=base64.decodestring(key)))
f.close()
+
+ def save(self, filename):
+ """
+ Save host keys into a file, in the format used by openssh. The order of
+ keys in the file will be preserved when possible (if these keys were
+ loaded from a file originally). The single exception is that combined
+ lines will be split into individual key lines, which is arguably a bug.
+
+ @param filename: name of the file to write
+ @type filename: str
+
+ @raise IOError: if there was an error writing the file
+
+ @since: 1.6.1
+ """
+ f = open(filename, 'w')
+ for hostname, d in self._keys.iteritems():
+ for keytype, key in d.iteritems():
+ f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
+ f.close()
def lookup(self, hostname):
"""
diff --git a/paramiko/odict.py b/paramiko/odict.py
new file mode 100644
index 00000000..103e52f1
--- /dev/null
+++ b/paramiko/odict.py
@@ -0,0 +1,63 @@
+#
+# This file and source code are in the public domain.
+#
+
+class odict (dict):
+ """
+ A dictionary with ordered keys. Based on the cookbook recipe at:
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
+ """
+
+ def __init__(self, *larg, **kwarg):
+ self._keys = []
+ dict.__init__(self, *larg, **kwarg)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self._keys.remove(key)
+
+ def __setitem__(self, key, item):
+ dict.__setitem__(self, key, item)
+ if key not in self._keys:
+ self._keys.append(key)
+
+ def clear(self):
+ dict.clear(self)
+ self._keys = []
+
+ def copy(self):
+ od = odict(self)
+ return od
+
+ def items(self):
+ return zip(self._keys, self.values())
+
+ def iteritems(self):
+ for k in self._keys:
+ yield k, self[k]
+
+ def keys(self):
+ return self._keys[:]
+
+ def popitem(self):
+ try:
+ key = self._keys[-1]
+ except IndexError:
+ raise KeyError('dictionary is empty')
+
+ val = self[key]
+ del self[key]
+
+ return (key, val)
+
+ def setdefault(self, key, failobj=None):
+ if key not in self._keys:
+ self._keys.append(key)
+ dict.setdefault(self, key, failobj)
+
+ def update(self, d):
+ for key, item in d.items():
+ self.__setitem__(key, item)
+
+ def values(self):
+ return map(self.get, self._keys)