Merge "launch: refactor to work"
This commit is contained in:
commit
b9c18d104c
@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Launch a new OpenStack project infrastructure node.
|
||||
# Output DNS for a new host
|
||||
|
||||
# Copyright (C) 2013 OpenStack Foundation
|
||||
# Copyright (C) 2023 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -19,50 +20,53 @@
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
from .sshfp import sshfp_print_records
|
||||
from .ssh_knownhosts import generate_known_hosts
|
||||
import subprocess
|
||||
|
||||
def print_sshfp_records(hostname, ip):
|
||||
'''Given a hostname and and IP address, scan the IP address (hostname
|
||||
not in dns yet) and return a bind string with sshfp records'''
|
||||
p = ['ssh-keyscan', '-D', ip]
|
||||
s = subprocess.run(p,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE).stdout.decode('utf-8')
|
||||
fingerprints = []
|
||||
for line in s.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
_, _, _, algo, key_type, fingerprint = line.split(' ')
|
||||
# ssh-keygen on the host seems to return DSS/DSA keys, which
|
||||
# aren't valid to log in and not shown by ssh-keyscan -D
|
||||
# ... prune it.
|
||||
if algo == '2':
|
||||
continue
|
||||
fingerprints.append(
|
||||
(algo, key_type, fingerprint))
|
||||
|
||||
# sort by algo and key_type to keep it consistent
|
||||
fingerprints = sorted(fingerprints,
|
||||
key=lambda x: (x[0], x[1]))
|
||||
|
||||
dns_hostname = hostname.split('.')[0]
|
||||
for f in fingerprints:
|
||||
print(f"{dns_hostname}\t\t\tIN\tSSHFP\t{f[0]} {f[1]} {f[2]}")
|
||||
|
||||
def print_dns(cloud, server):
|
||||
ip4 = server.public_v4
|
||||
ip6 = server.public_v6
|
||||
# note handle things like mirror.iad.rax.opendev.org
|
||||
domain = '.'.join(server.name.split('.')[-2:])
|
||||
host = '.'.join(server.name.split('.')[0:-2])
|
||||
|
||||
if server.name.endswith('opendev.org'):
|
||||
print_dns_opendev(server.name.rsplit('.', 2)[0], ip4, ip6)
|
||||
# openstack.org is the only domain we deal with that is still in
|
||||
# RAX DNS
|
||||
if (domain == "openstack.org"):
|
||||
print("Add the following manually to openstack.org domain in RAX\n")
|
||||
else:
|
||||
print("Login to manage.rackspace.com and setup DNS manually.")
|
||||
|
||||
print_inventory_yaml(server, ip4, ip6)
|
||||
|
||||
|
||||
def print_dns_opendev(name, ip4, ip6):
|
||||
print("\n")
|
||||
print("Put the following into zone-opendev.org:zones/opendev.org/zone.db")
|
||||
print("{name} IN A {ip4}".format(name=name, ip4=ip4))
|
||||
print(f"Put the following into zone-{domain}:zones/{domain}/zone.db\n")
|
||||
print(f"{host} IN A {ip4}")
|
||||
if ip6:
|
||||
print("{name} IN AAAA {ip6}".format(name=name, ip6=ip6))
|
||||
sshfp_print_records(name, ip4)
|
||||
|
||||
|
||||
def print_inventory_yaml(server, ip4, ip6):
|
||||
known_hosts = generate_known_hosts(ip4)
|
||||
|
||||
print("\n")
|
||||
print("Put the following into system-config:inventory/base/hosts.yaml")
|
||||
print("\n")
|
||||
print(" {name}:".format(name=server.name))
|
||||
print(" ansible_host: {ip}".format(ip=ip4 or ip6))
|
||||
print(" location:")
|
||||
print(" cloud: {cloud}".format(cloud=server.location['cloud']))
|
||||
print(" region_name: {region_name}".format(
|
||||
region_name=server.location['region_name']))
|
||||
print(" public_v4: {ip4}".format(ip4=ip4))
|
||||
if ip6:
|
||||
print(" public_v6: {ip6}".format(ip6=ip6))
|
||||
print(" host_keys:")
|
||||
for (key, fingerprint) in known_hosts:
|
||||
print(" - '%s %s'" % (key, fingerprint))
|
||||
|
||||
print(f"{host} IN AAAA {ip6}")
|
||||
print_sshfp_records(server.name, ip4)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
@ -32,6 +32,7 @@ import traceback
|
||||
from . import dns
|
||||
from . import rax_rdns
|
||||
from . import utils
|
||||
from .ssh_knownhosts import generate_known_hosts
|
||||
|
||||
import openstack
|
||||
import paramiko
|
||||
@ -222,6 +223,7 @@ def bootstrap_server(server, key, name, volume_device, keep,
|
||||
|
||||
try:
|
||||
ssh_client.ssh("reboot")
|
||||
ssh_client.close()
|
||||
except Exception as e:
|
||||
# Some init system kill the connection too fast after reboot.
|
||||
# Deal with it by ignoring ssh errors when rebooting.
|
||||
@ -230,19 +232,14 @@ def bootstrap_server(server, key, name, volume_device, keep,
|
||||
else:
|
||||
raise
|
||||
|
||||
timeout = 120
|
||||
start = time.perf_counter()
|
||||
while True:
|
||||
try:
|
||||
print("Waiting for ssh...")
|
||||
with socket.create_connection((host_ip, 22), timeout=5):
|
||||
break
|
||||
except OSError as e:
|
||||
if time.perf_counter() - start >= timeout:
|
||||
raise Exception("Host did not reboot within timeout")
|
||||
time.sleep(10)
|
||||
print("Host rebooted!")
|
||||
|
||||
# Wait a bit and make sure we can ssh back in
|
||||
print("Waiting 30 seconds for reboot")
|
||||
time.sleep(30)
|
||||
ssh_client = utils.ssh_connect(ip, 'root', ssh_kwargs, timeout=90)
|
||||
if not ssh_client:
|
||||
raise Exception("Failed to log into host")
|
||||
ssh_client.close()
|
||||
print("Host alive")
|
||||
|
||||
def build_server(cloud, name, image, flavor,
|
||||
volume, keep, network, boot_from_volume, config_drive,
|
||||
@ -330,6 +327,27 @@ def build_server(cloud, name, image, flavor,
|
||||
|
||||
return server
|
||||
|
||||
def print_inventory_yaml(server):
|
||||
ip4 = server.public_v4
|
||||
ip6 = server.public_v6
|
||||
cloud = server.location['cloud']
|
||||
region = server.location['region_name']
|
||||
|
||||
known_hosts = generate_known_hosts(ip4)
|
||||
|
||||
print(f"Put the following into system-config:inventory/base/hosts.yaml")
|
||||
print()
|
||||
print(f" {server.name}:")
|
||||
print(f" ansible_host: {ip4}")
|
||||
print(f" location:")
|
||||
print(f" cloud: {cloud}")
|
||||
print(f" region_name: {region}")
|
||||
print(f" public_v4: {ip4}")
|
||||
if ip6:
|
||||
print(f" public_v6: {ip6}")
|
||||
print(f" host_keys:")
|
||||
for (key, fingerprint) in known_hosts:
|
||||
print(f" - {key} {fingerprint}")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
@ -432,9 +450,18 @@ def main():
|
||||
options.timeout, options.ignore_ipv6,
|
||||
options.playbooks)
|
||||
if 'rax' in cloud.config.name:
|
||||
print("Setting reverse DNS for RAX")
|
||||
rax_rdns.set_rax_reverse_dns(cloud, server,
|
||||
server.public_v4, server.public_v6)
|
||||
|
||||
print()
|
||||
print("-------- CONFIGURATION --------\n")
|
||||
dns.print_dns(cloud, server)
|
||||
print()
|
||||
print_inventory_yaml(server)
|
||||
print()
|
||||
print("-------------------------------")
|
||||
print()
|
||||
print("If this is a server that is expected to send email (ask, review,")
|
||||
print("lists, etc) double check that the server's IPs are not listed on")
|
||||
print("the spamhaus pbl.\n")
|
||||
|
@ -67,3 +67,6 @@ class SSHClient(object):
|
||||
f = ftp.open(path, mode)
|
||||
yield f
|
||||
ftp.close()
|
||||
|
||||
def close(self):
|
||||
self.client.close()
|
||||
|
@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
def generate_sshfp_records(hostname, ip):
|
||||
'''Given a hostname and and IP address, scan the IP address (hostname
|
||||
not in dns yet) and return a bind string with sshfp records'''
|
||||
|
||||
p = ['ssh-keyscan', '-D', ip]
|
||||
s = subprocess.run(p,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE).stdout.decode('utf-8')
|
||||
fingerprints = []
|
||||
for line in s.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
_, _, _, algo, key_type, fingerprint = line.split(' ')
|
||||
# ssh-keygen on the host seems to return DSS/DSA keys, which
|
||||
# aren't valid to log in and not shown by ssh-keyscan -D
|
||||
# ... prune it.
|
||||
if algo == '2':
|
||||
continue
|
||||
fingerprints.append(
|
||||
(algo, key_type, fingerprint))
|
||||
|
||||
# sort by algo and key_type to keep it consistent
|
||||
fingerprints = sorted(fingerprints,
|
||||
key=lambda x: (x[0], x[1]))
|
||||
|
||||
ret = ''
|
||||
first = True
|
||||
dns_hostname = hostname.split('.')[0]
|
||||
for f in fingerprints:
|
||||
ret += '%s%s\t\tIN\tSSHFP\t%s %s %s' % \
|
||||
("\n" if not first else '', dns_hostname, f[0], f[1], f[2])
|
||||
first = False
|
||||
return ret
|
||||
|
||||
|
||||
def sshfp_print_records(hostname, ip):
|
||||
print(generate_sshfp_records(hostname, ip))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("hostname", help="hostname")
|
||||
parser.add_argument("ip", help="address to scan")
|
||||
args = parser.parse_args()
|
||||
|
||||
sshfp_print_records(args.hostname, args.ip)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user