
This was never consistently showing the host key and sshfp records upon launch. Upon digging, a number of things are going wrong. The socket.create_connection() check isn't waiting for the host to be up properly. This means the keyscans were not working, and we'd get blank return values [1]. We have a ssh_connect() routine, rework it to use that to probe. We add a close method to the sshclient so we can shut it down too. I don't know why the inventory output was in dns.py, as it's not really DNS. Move it to the main launch_node.py, and simplify it by using f-strings. While we're here, deliminate the output a bit more and make white-space more consistent. This allows us to simplify dns.py and make it so it handles multiple domains. Since we're actually waiting for ssh to be up now, the keyscan works better and this outputs the information we want. A sample of this is https://paste.opendev.org/show/b1MjiTvYr4E03GTeP56w/ [1] ssh-keyscan has a very short timeout, and just returns blank if it doesn't get a response to it's probes. We weren't checking its return code. Change-Id: I06995027a4b80133bdac91c263d7a92fd495493b
73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Update the base image that is used for devstack VMs.
|
|
|
|
# Copyright (C) 2011-2012 OpenStack LLC.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
#
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import contextlib
|
|
import sys
|
|
|
|
import paramiko
|
|
|
|
|
|
class SSHException(Exception):
|
|
def __init__(self, message, rc):
|
|
super(SSHException, self).__init__(message)
|
|
self.rc = rc
|
|
|
|
|
|
class SSHClient(object):
|
|
def __init__(self, ip, username, password=None, pkey=None):
|
|
client = paramiko.SSHClient()
|
|
client.load_system_host_keys()
|
|
client.set_missing_host_key_policy(paramiko.WarningPolicy())
|
|
client.connect(ip, username=username, password=password, pkey=pkey)
|
|
self.client = client
|
|
|
|
def ssh(self, command, error_ok=False):
|
|
stdin, stdout, stderr = self.client.exec_command(command)
|
|
print('--- ssh: "%s" ---' % command)
|
|
print(' -- stdout --')
|
|
output = ''
|
|
for x in stdout:
|
|
output += x
|
|
sys.stdout.write(" | " + x)
|
|
ret = stdout.channel.recv_exit_status()
|
|
print(" -- stderr --")
|
|
for x in stderr:
|
|
sys.stdout.write(" | " + x)
|
|
if (not error_ok) and ret:
|
|
raise SSHException("Unable to %s" % command, ret)
|
|
print("--- done ---\n")
|
|
return ret, output
|
|
|
|
def scp(self, source, dest):
|
|
print('--- scp copy: %s -> %s' % (source, dest))
|
|
ftp = self.client.open_sftp()
|
|
ftp.put(source, dest)
|
|
ftp.close()
|
|
|
|
@contextlib.contextmanager
|
|
def open(self, path, mode):
|
|
ftp = self.client.open_sftp()
|
|
f = ftp.open(path, mode)
|
|
yield f
|
|
ftp.close()
|
|
|
|
def close(self):
|
|
self.client.close()
|