Statistics
| Branch: | Tag: | Revision:

root / tools / setup-ssh @ 2c9cf6bb

History | View | Annotate | Download (11.4 kB)

1 05cd934d René Nussbaumer
#!/usr/bin/python
2 05cd934d René Nussbaumer
#
3 05cd934d René Nussbaumer
4 05cd934d René Nussbaumer
# Copyright (C) 2010 Google Inc.
5 05cd934d René Nussbaumer
#
6 05cd934d René Nussbaumer
# This program is free software; you can redistribute it and/or modify
7 05cd934d René Nussbaumer
# it under the terms of the GNU General Public License as published by
8 05cd934d René Nussbaumer
# the Free Software Foundation; either version 2 of the License, or
9 05cd934d René Nussbaumer
# (at your option) any later version.
10 05cd934d René Nussbaumer
#
11 05cd934d René Nussbaumer
# This program is distributed in the hope that it will be useful, but
12 05cd934d René Nussbaumer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 05cd934d René Nussbaumer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 05cd934d René Nussbaumer
# General Public License for more details.
15 05cd934d René Nussbaumer
#
16 05cd934d René Nussbaumer
# You should have received a copy of the GNU General Public License
17 05cd934d René Nussbaumer
# along with this program; if not, write to the Free Software
18 05cd934d René Nussbaumer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 05cd934d René Nussbaumer
# 02110-1301, USA.
20 05cd934d René Nussbaumer
21 05cd934d René Nussbaumer
"""Tool to setup the SSH configuration on a remote node.
22 05cd934d René Nussbaumer
23 05cd934d René Nussbaumer
This is needed before we can join the node into the cluster.
24 05cd934d René Nussbaumer
25 05cd934d René Nussbaumer
"""
26 05cd934d René Nussbaumer
27 c9a4a662 Manuel Franceschini
# pylint: disable-msg=C0103
28 c9a4a662 Manuel Franceschini
# C0103: Invalid name setup-ssh
29 c9a4a662 Manuel Franceschini
30 05cd934d René Nussbaumer
import getpass
31 05cd934d René Nussbaumer
import logging
32 05cd934d René Nussbaumer
import paramiko
33 05cd934d René Nussbaumer
import os.path
34 05cd934d René Nussbaumer
import optparse
35 05cd934d René Nussbaumer
import sys
36 05cd934d René Nussbaumer
37 05cd934d René Nussbaumer
from ganeti import cli
38 05cd934d René Nussbaumer
from ganeti import constants
39 05cd934d René Nussbaumer
from ganeti import errors
40 05cd934d René Nussbaumer
from ganeti import netutils
41 05cd934d René Nussbaumer
from ganeti import ssh
42 05cd934d René Nussbaumer
from ganeti import utils
43 05cd934d René Nussbaumer
44 05cd934d René Nussbaumer
45 05cd934d René Nussbaumer
class RemoteCommandError(errors.GenericError):
46 05cd934d René Nussbaumer
  """Exception if remote command was not successful.
47 05cd934d René Nussbaumer
48 05cd934d René Nussbaumer
  """
49 05cd934d René Nussbaumer
50 05cd934d René Nussbaumer
51 05cd934d René Nussbaumer
def _RunRemoteCommand(transport, command):
52 05cd934d René Nussbaumer
  """Invokes and wait for the command over SSH.
53 05cd934d René Nussbaumer
54 05cd934d René Nussbaumer
  @param transport: The paramiko transport instance
55 05cd934d René Nussbaumer
  @param command: The command to be executed
56 05cd934d René Nussbaumer
57 05cd934d René Nussbaumer
  """
58 05cd934d René Nussbaumer
  chan = transport.open_session()
59 05cd934d René Nussbaumer
  chan.set_combine_stderr(True)
60 05cd934d René Nussbaumer
  output_handler = chan.makefile("r")
61 05cd934d René Nussbaumer
  chan.exec_command(command)
62 05cd934d René Nussbaumer
63 05cd934d René Nussbaumer
  result = chan.recv_exit_status()
64 05cd934d René Nussbaumer
  msg = output_handler.read()
65 05cd934d René Nussbaumer
66 05cd934d René Nussbaumer
  out_msg = "'%s' exited with status code %s, output %r" % (command, result,
67 05cd934d René Nussbaumer
                                                            msg)
68 05cd934d René Nussbaumer
69 05cd934d René Nussbaumer
  # If result is -1 (no exit status provided) we assume it was not successful
70 05cd934d René Nussbaumer
  if result:
71 05cd934d René Nussbaumer
    raise RemoteCommandError(out_msg)
72 05cd934d René Nussbaumer
73 05cd934d René Nussbaumer
  if msg:
74 05cd934d René Nussbaumer
    logging.info(out_msg)
75 05cd934d René Nussbaumer
76 05cd934d René Nussbaumer
77 05cd934d René Nussbaumer
def _InvokeDaemonUtil(transport, command):
78 05cd934d René Nussbaumer
  """Invokes daemon-util on the remote side.
79 05cd934d René Nussbaumer
80 05cd934d René Nussbaumer
  @param transport: The paramiko transport instance
81 05cd934d René Nussbaumer
  @param command: The daemon-util command to be run
82 05cd934d René Nussbaumer
83 05cd934d René Nussbaumer
  """
84 05cd934d René Nussbaumer
  _RunRemoteCommand(transport, "%s %s" % (constants.DAEMON_UTIL, command))
85 05cd934d René Nussbaumer
86 05cd934d René Nussbaumer
87 05cd934d René Nussbaumer
def _WriteSftpFile(sftp, name, perm, data):
88 05cd934d René Nussbaumer
  """SFTPs data to a remote file.
89 05cd934d René Nussbaumer
90 05cd934d René Nussbaumer
  @param sftp: A open paramiko SFTP client
91 05cd934d René Nussbaumer
  @param name: The remote file name
92 05cd934d René Nussbaumer
  @param perm: The remote file permission
93 05cd934d René Nussbaumer
  @param data: The data to write
94 05cd934d René Nussbaumer
95 05cd934d René Nussbaumer
  """
96 05cd934d René Nussbaumer
  remote_file = sftp.open(name, "w")
97 05cd934d René Nussbaumer
  try:
98 05cd934d René Nussbaumer
    sftp.chmod(name, perm)
99 05cd934d René Nussbaumer
    remote_file.write(data)
100 05cd934d René Nussbaumer
  finally:
101 05cd934d René Nussbaumer
    remote_file.close()
102 05cd934d René Nussbaumer
103 05cd934d René Nussbaumer
104 05cd934d René Nussbaumer
def SetupSSH(transport):
105 05cd934d René Nussbaumer
  """Sets the SSH up on the other side.
106 05cd934d René Nussbaumer
107 05cd934d René Nussbaumer
  @param transport: The paramiko transport instance
108 05cd934d René Nussbaumer
109 05cd934d René Nussbaumer
  """
110 05cd934d René Nussbaumer
  priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
111 05cd934d René Nussbaumer
  keyfiles = [
112 05cd934d René Nussbaumer
    (constants.SSH_HOST_DSA_PRIV, 0600),
113 05cd934d René Nussbaumer
    (constants.SSH_HOST_DSA_PUB, 0644),
114 05cd934d René Nussbaumer
    (constants.SSH_HOST_RSA_PRIV, 0600),
115 05cd934d René Nussbaumer
    (constants.SSH_HOST_RSA_PUB, 0644),
116 05cd934d René Nussbaumer
    (priv_key, 0600),
117 05cd934d René Nussbaumer
    (pub_key, 0644),
118 05cd934d René Nussbaumer
    ]
119 05cd934d René Nussbaumer
120 05cd934d René Nussbaumer
  sftp = transport.open_sftp_client()
121 05cd934d René Nussbaumer
122 05cd934d René Nussbaumer
  filemap = dict((name, (utils.ReadFile(name), perm))
123 05cd934d René Nussbaumer
                 for (name, perm) in keyfiles)
124 05cd934d René Nussbaumer
125 05cd934d René Nussbaumer
  auth_path = os.path.dirname(auth_keys)
126 05cd934d René Nussbaumer
127 05cd934d René Nussbaumer
  try:
128 05cd934d René Nussbaumer
    sftp.mkdir(auth_path, 0700)
129 05cd934d René Nussbaumer
  except IOError:
130 05cd934d René Nussbaumer
    # Sadly paramiko doesn't provide errno or similiar
131 05cd934d René Nussbaumer
    # so we can just assume that the path already exists
132 5c654e95 Iustin Pop
    logging.info("Path %s seems already to exist on remote node. Ignoring.",
133 05cd934d René Nussbaumer
                 auth_path)
134 05cd934d René Nussbaumer
135 05cd934d René Nussbaumer
  for name, (data, perm) in filemap.iteritems():
136 05cd934d René Nussbaumer
    _WriteSftpFile(sftp, name, perm, data)
137 05cd934d René Nussbaumer
138 05cd934d René Nussbaumer
  authorized_keys = sftp.open(auth_keys, "a+")
139 05cd934d René Nussbaumer
  try:
140 634a9a35 Iustin Pop
    # Due to the way SFTPFile and BufferedFile are implemented,
141 634a9a35 Iustin Pop
    # opening in a+ mode and then issuing a read(), readline() or
142 634a9a35 Iustin Pop
    # iterating over the file (which uses read() internally) will see
143 634a9a35 Iustin Pop
    # an empty file, since the paramiko internal file position and the
144 634a9a35 Iustin Pop
    # OS-level file-position are desynchronized; therefore, we issue
145 634a9a35 Iustin Pop
    # an explicit seek to resynchronize these; writes should (note
146 634a9a35 Iustin Pop
    # should) still go to the right place
147 634a9a35 Iustin Pop
    authorized_keys.seek(0, 0)
148 05cd934d René Nussbaumer
    # We don't have to close, as the close happened already in AddAuthorizedKey
149 05cd934d René Nussbaumer
    utils.AddAuthorizedKey(authorized_keys, filemap[pub_key][0])
150 05cd934d René Nussbaumer
  finally:
151 05cd934d René Nussbaumer
    authorized_keys.close()
152 05cd934d René Nussbaumer
153 05cd934d René Nussbaumer
  _InvokeDaemonUtil(transport, "reload-ssh-keys")
154 05cd934d René Nussbaumer
155 05cd934d René Nussbaumer
156 05cd934d René Nussbaumer
def ParseOptions():
157 05cd934d René Nussbaumer
  """Parses options passed to program.
158 05cd934d René Nussbaumer
159 05cd934d René Nussbaumer
  """
160 05cd934d René Nussbaumer
  program = os.path.basename(sys.argv[0])
161 05cd934d René Nussbaumer
162 05cd934d René Nussbaumer
  parser = optparse.OptionParser(usage=("%prog [--debug|--verbose] <node>"
163 05cd934d René Nussbaumer
                                        " <node...>"), prog=program)
164 05cd934d René Nussbaumer
  parser.add_option(cli.DEBUG_OPT)
165 05cd934d René Nussbaumer
  parser.add_option(cli.VERBOSE_OPT)
166 310a8944 René Nussbaumer
  parser.add_option(cli.NOSSH_KEYCHECK_OPT)
167 7a6a27af Iustin Pop
  default_key = ssh.GetUserFiles(constants.GANETI_RUNAS)[0]
168 7a6a27af Iustin Pop
  parser.add_option(optparse.Option("-f", dest="private_key",
169 7a6a27af Iustin Pop
                                    default=default_key,
170 7a6a27af Iustin Pop
                                    help="The private key to (try to) use for"
171 7a6a27af Iustin Pop
                                    "authentication "))
172 7a6a27af Iustin Pop
  parser.add_option(optparse.Option("--key-type", dest="key_type",
173 7a6a27af Iustin Pop
                                    choices=("rsa", "dsa"), default="dsa",
174 7a6a27af Iustin Pop
                                    help="The private key type (rsa or dsa)"))
175 05cd934d René Nussbaumer
176 05cd934d René Nussbaumer
  (options, args) = parser.parse_args()
177 05cd934d René Nussbaumer
178 05cd934d René Nussbaumer
  return (options, args)
179 05cd934d René Nussbaumer
180 05cd934d René Nussbaumer
181 05cd934d René Nussbaumer
def SetupLogging(options):
182 05cd934d René Nussbaumer
  """Sets up the logging.
183 05cd934d René Nussbaumer
184 05cd934d René Nussbaumer
  @param options: Parsed options
185 05cd934d René Nussbaumer
186 05cd934d René Nussbaumer
  """
187 05cd934d René Nussbaumer
  fmt = "%(asctime)s: %(threadName)s "
188 05cd934d René Nussbaumer
  if options.debug or options.verbose:
189 05cd934d René Nussbaumer
    fmt += "%(levelname)s "
190 05cd934d René Nussbaumer
  fmt += "%(message)s"
191 05cd934d René Nussbaumer
192 05cd934d René Nussbaumer
  formatter = logging.Formatter(fmt)
193 05cd934d René Nussbaumer
194 05cd934d René Nussbaumer
  file_handler = logging.FileHandler(constants.LOG_SETUP_SSH)
195 05cd934d René Nussbaumer
  stderr_handler = logging.StreamHandler()
196 05cd934d René Nussbaumer
  stderr_handler.setFormatter(formatter)
197 05cd934d René Nussbaumer
  file_handler.setFormatter(formatter)
198 5c654e95 Iustin Pop
  file_handler.setLevel(logging.INFO)
199 05cd934d René Nussbaumer
200 05cd934d René Nussbaumer
  if options.debug:
201 5c654e95 Iustin Pop
    stderr_handler.setLevel(logging.DEBUG)
202 05cd934d René Nussbaumer
  elif options.verbose:
203 05cd934d René Nussbaumer
    stderr_handler.setLevel(logging.INFO)
204 05cd934d René Nussbaumer
  else:
205 5c654e95 Iustin Pop
    stderr_handler.setLevel(logging.WARNING)
206 05cd934d René Nussbaumer
207 05cd934d René Nussbaumer
  root_logger = logging.getLogger("")
208 3dc66ebc Iustin Pop
  root_logger.setLevel(logging.NOTSET)
209 05cd934d René Nussbaumer
  root_logger.addHandler(stderr_handler)
210 05cd934d René Nussbaumer
  root_logger.addHandler(file_handler)
211 8647a52c Iustin Pop
212 8647a52c Iustin Pop
  # This is the paramiko logger instance
213 8647a52c Iustin Pop
  paramiko_logger = logging.getLogger("paramiko")
214 05cd934d René Nussbaumer
  paramiko_logger.addHandler(file_handler)
215 5c654e95 Iustin Pop
  # We don't want to debug Paramiko, so filter anything below warning
216 5c654e95 Iustin Pop
  paramiko_logger.setLevel(logging.WARNING)
217 05cd934d René Nussbaumer
218 05cd934d René Nussbaumer
219 3dc66ebc Iustin Pop
def LoadPrivateKeys(options):
220 3dc66ebc Iustin Pop
  """Load the list of available private keys
221 05cd934d René Nussbaumer
222 3dc66ebc Iustin Pop
  It loads the standard ssh key from disk and then tries to connect to
223 3dc66ebc Iustin Pop
  the ssh agent too.
224 05cd934d René Nussbaumer
225 3dc66ebc Iustin Pop
  @rtype: list
226 3dc66ebc Iustin Pop
  @return: a list of C{paramiko.PKey}
227 05cd934d René Nussbaumer
228 3dc66ebc Iustin Pop
  """
229 7a6a27af Iustin Pop
  if options.key_type == "rsa":
230 7a6a27af Iustin Pop
    pkclass = paramiko.RSAKey
231 7a6a27af Iustin Pop
  elif options.key_type == "dsa":
232 7a6a27af Iustin Pop
    pkclass = paramiko.DSSKey
233 7a6a27af Iustin Pop
  else:
234 7a6a27af Iustin Pop
    logging.critical("Unknown key type %s selected (choose either rsa or dsa)",
235 7a6a27af Iustin Pop
                     options.key_type)
236 7a6a27af Iustin Pop
    sys.exit(1)
237 7a6a27af Iustin Pop
238 7a6a27af Iustin Pop
  try:
239 7a6a27af Iustin Pop
    private_key = pkclass.from_private_key_file(options.private_key)
240 7a6a27af Iustin Pop
  except (paramiko.SSHException, EnvironmentError), err:
241 7a6a27af Iustin Pop
    logging.critical("Can't load private key %s: %s", options.private_key, err)
242 7a6a27af Iustin Pop
    sys.exit(1)
243 7a6a27af Iustin Pop
244 3dc66ebc Iustin Pop
  try:
245 3dc66ebc Iustin Pop
    agent = paramiko.Agent()
246 3dc66ebc Iustin Pop
    agent_keys = agent.get_keys()
247 3dc66ebc Iustin Pop
  except paramiko.SSHException, err:
248 3dc66ebc Iustin Pop
    # this will only be seen when the agent is broken/uses invalid
249 3dc66ebc Iustin Pop
    # protocol; for non-existing agent, get_keys() will just return an
250 3dc66ebc Iustin Pop
    # empty tuple
251 3dc66ebc Iustin Pop
    logging.warning("Can't connect to the ssh agent: %s; skipping its use",
252 3dc66ebc Iustin Pop
                    err)
253 3dc66ebc Iustin Pop
    agent_keys = []
254 3dc66ebc Iustin Pop
255 3dc66ebc Iustin Pop
  return [private_key] + list(agent_keys)
256 3dc66ebc Iustin Pop
257 3dc66ebc Iustin Pop
258 3dc66ebc Iustin Pop
def LoginViaKeys(transport, username, keys):
259 3dc66ebc Iustin Pop
  """Try to login on the given transport via a list of keys.
260 3dc66ebc Iustin Pop
261 3dc66ebc Iustin Pop
  @param transport: the transport to use
262 3dc66ebc Iustin Pop
  @param username: the username to login as
263 3dc66ebc Iustin Pop
  @type keys: list
264 3dc66ebc Iustin Pop
  @param keys: list of C{paramiko.PKey} to use for authentication
265 3dc66ebc Iustin Pop
  @rtype: boolean
266 3dc66ebc Iustin Pop
  @return: True or False depending on whether the login was
267 3dc66ebc Iustin Pop
      successfull or not
268 3dc66ebc Iustin Pop
269 3dc66ebc Iustin Pop
  """
270 3dc66ebc Iustin Pop
  for private_key in keys:
271 3dc66ebc Iustin Pop
    try:
272 3dc66ebc Iustin Pop
      transport.auth_publickey(username, private_key)
273 3dc66ebc Iustin Pop
      fpr = ":".join("%02x" % ord(i) for i in private_key.get_fingerprint())
274 3dc66ebc Iustin Pop
      if isinstance(private_key, paramiko.AgentKey):
275 3dc66ebc Iustin Pop
        logging.debug("Authentication via the ssh-agent key %s", fpr)
276 3dc66ebc Iustin Pop
      else:
277 3dc66ebc Iustin Pop
        logging.debug("Authenticated via public key %s", fpr)
278 3dc66ebc Iustin Pop
      return True
279 3dc66ebc Iustin Pop
    except paramiko.SSHException:
280 3dc66ebc Iustin Pop
      continue
281 3dc66ebc Iustin Pop
  else:
282 3dc66ebc Iustin Pop
    # all keys exhausted
283 3dc66ebc Iustin Pop
    return False
284 3dc66ebc Iustin Pop
285 3dc66ebc Iustin Pop
286 310a8944 René Nussbaumer
def LoadKnownHosts():
287 310a8944 René Nussbaumer
  """Loads the known hosts
288 310a8944 René Nussbaumer
289 310a8944 René Nussbaumer
    @return L{paramiko.util.load_host_keys} dict
290 310a8944 René Nussbaumer
291 310a8944 René Nussbaumer
  """
292 310a8944 René Nussbaumer
  homedir = utils.GetHomeDir(constants.GANETI_RUNAS)
293 310a8944 René Nussbaumer
  known_hosts = os.path.join(homedir, ".ssh", "known_hosts")
294 310a8944 René Nussbaumer
295 310a8944 René Nussbaumer
  try:
296 310a8944 René Nussbaumer
    return paramiko.util.load_host_keys(known_hosts)
297 310a8944 René Nussbaumer
  except EnvironmentError:
298 310a8944 René Nussbaumer
    # We didn't found the path, silently ignore and return an empty dict
299 310a8944 René Nussbaumer
    return {}
300 310a8944 René Nussbaumer
301 310a8944 René Nussbaumer
302 3dc66ebc Iustin Pop
def main():
303 3dc66ebc Iustin Pop
  """Main routine.
304 3dc66ebc Iustin Pop
305 3dc66ebc Iustin Pop
  """
306 3dc66ebc Iustin Pop
  (options, args) = ParseOptions()
307 3dc66ebc Iustin Pop
308 3dc66ebc Iustin Pop
  SetupLogging(options)
309 3dc66ebc Iustin Pop
310 3dc66ebc Iustin Pop
  all_keys = LoadPrivateKeys(options)
311 3dc66ebc Iustin Pop
312 7a6a27af Iustin Pop
  passwd = None
313 7a6a27af Iustin Pop
  username = constants.GANETI_RUNAS
314 7bff16bd Iustin Pop
  ssh_port = netutils.GetDaemonPort("ssh")
315 310a8944 René Nussbaumer
  host_keys = LoadKnownHosts()
316 05cd934d René Nussbaumer
317 8647a52c Iustin Pop
  # Below, we need to join() the transport objects, as otherwise the
318 8647a52c Iustin Pop
  # following happens:
319 8647a52c Iustin Pop
  # - the main thread finishes
320 8647a52c Iustin Pop
  # - the atexit functions run (in the main thread), and cause the
321 8647a52c Iustin Pop
  #   logging file to be closed
322 8647a52c Iustin Pop
  # - a tiny bit later, the transport thread is finally ending, and
323 8647a52c Iustin Pop
  #   wants to log one more message, which fails as the file is closed
324 8647a52c Iustin Pop
  #   now
325 8647a52c Iustin Pop
326 05cd934d René Nussbaumer
  for host in args:
327 7bff16bd Iustin Pop
    transport = paramiko.Transport((host, ssh_port))
328 7a6a27af Iustin Pop
    transport.start_client()
329 310a8944 René Nussbaumer
    server_key = transport.get_remote_server_key()
330 310a8944 René Nussbaumer
    keytype = server_key.get_name()
331 310a8944 René Nussbaumer
332 310a8944 René Nussbaumer
    our_server_key = host_keys.get(host, {}).get(keytype, None)
333 310a8944 René Nussbaumer
    if options.ssh_key_check:
334 310a8944 René Nussbaumer
      if not our_server_key:
335 310a8944 René Nussbaumer
        hexified_key = ssh.FormatParamikoFingerprint(
336 310a8944 René Nussbaumer
            server_key.get_fingerprint())
337 310a8944 René Nussbaumer
        msg = ("Unable to verify hostkey of host %s: %s. Do you want to accept"
338 310a8944 René Nussbaumer
               " it?" % (host, hexified_key))
339 310a8944 René Nussbaumer
340 310a8944 René Nussbaumer
        if cli.AskUser(msg):
341 310a8944 René Nussbaumer
          our_server_key = server_key
342 310a8944 René Nussbaumer
343 310a8944 René Nussbaumer
      if our_server_key != server_key:
344 310a8944 René Nussbaumer
        logging.error("Unable to verify identity of host. Aborting")
345 310a8944 René Nussbaumer
        transport.close()
346 310a8944 René Nussbaumer
        transport.join()
347 310a8944 René Nussbaumer
        # TODO: Run over all hosts, fetch the keys and let them verify from the
348 310a8944 René Nussbaumer
        #       user beforehand then proceed with actual work later on
349 310a8944 René Nussbaumer
        raise paramiko.SSHException("Unable to verify identity of host")
350 310a8944 René Nussbaumer
351 8647a52c Iustin Pop
    try:
352 3dc66ebc Iustin Pop
      if LoginViaKeys(transport, username, all_keys):
353 7a6a27af Iustin Pop
        logging.info("Authenticated to %s via public key", host)
354 3dc66ebc Iustin Pop
      else:
355 7a6a27af Iustin Pop
        logging.warning("Authentication to %s via public key failed, trying"
356 7a6a27af Iustin Pop
                        " password", host)
357 7a6a27af Iustin Pop
        if passwd is None:
358 7a6a27af Iustin Pop
          passwd = getpass.getpass(prompt="%s password:" % username)
359 7a6a27af Iustin Pop
        transport.auth_password(username=username, password=passwd)
360 7a6a27af Iustin Pop
        logging.info("Authenticated to %s via password", host)
361 7a6a27af Iustin Pop
    except paramiko.SSHException, err:
362 8647a52c Iustin Pop
      logging.error("Connection or authentication failed to host %s: %s",
363 8647a52c Iustin Pop
                    host, err)
364 8647a52c Iustin Pop
      transport.close()
365 8647a52c Iustin Pop
      # this is needed for compatibility with older Paramiko or Python
366 8647a52c Iustin Pop
      # versions
367 8647a52c Iustin Pop
      transport.join()
368 8647a52c Iustin Pop
      continue
369 05cd934d René Nussbaumer
    try:
370 05cd934d René Nussbaumer
      try:
371 05cd934d René Nussbaumer
        SetupSSH(transport)
372 05cd934d René Nussbaumer
      except errors.GenericError, err:
373 8647a52c Iustin Pop
        logging.error("While doing setup on host %s an error occured: %s",
374 8647a52c Iustin Pop
                      host, err)
375 05cd934d René Nussbaumer
    finally:
376 05cd934d René Nussbaumer
      transport.close()
377 8647a52c Iustin Pop
      # this is needed for compatibility with older Paramiko or Python
378 8647a52c Iustin Pop
      # versions
379 8647a52c Iustin Pop
      transport.join()
380 05cd934d René Nussbaumer
381 05cd934d René Nussbaumer
382 05cd934d René Nussbaumer
if __name__ == "__main__":
383 05cd934d René Nussbaumer
  main()