launch-node : make into a small package

This turns launch-node into an installable package.  This is not meant
for distribution, we just encapsulate the installation in a virtualenv
on the bastion host.  Small updates to documentation and simple
testing are added (also remove some spaces to make

Change-Id: Ibcb4774114d73600753ca155ed277d775964bc79
Ian Wienand 2 months ago
parent 95c9cf6ec6
commit ed7083ed88
No known key found for this signature in database

@ -1,7 +1,8 @@
Create Server
The commands in this section should be run as root.
The commands in this section should be run as root on the bastion
To launch a node in the OpenStack CI account (production servers)::
@ -9,24 +10,24 @@ To launch a node in the OpenStack CI account (production servers)::
export FLAVOR="8 GB Performance"
cd /opt/system-config/launch/
./ $FQDN --flavor "$FLAVOR" \
/usr/launcher-venv/bin/launch-node $FQDN --flavor "$FLAVOR" \
--cloud=$OS_CLOUD --region=$OS_REGION_NAME
Manually add the hostname to DNS (the launch script does not do so
automatically, but it prints the commands to run). Note that for
* hosts you'll only be able to add the reverse dns
records via the printed commands. Forward A and AAAA records should
be added to opendev/
be added to ``opendev/``.
We need to add the host to our static inventory file so that
the ansible runs see the new host. The launch script prints out
the appropriate lines to add to
In order for Ansible to work, you also need to accept the root SSH
key for the new server. Once the new DNS entries have propagated,
as root on
as ``root`` on the bastion server::
ssh root@$FQDN
@ -38,6 +39,6 @@ Add DNS Records
The launch-node script will print the commands needed to be
run to configure DNS for a newly launched server. To see the commands
for an existing server, run:
for an existing server, run::
./ $FQDN
/usr/launcher-venv/bin/show-dns $FQDN

@ -0,0 +1,32 @@
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
name = "opendev_launch"
version = "1.0.0"
description = "launch nodes"
requires-python = ">=3.6"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache2 License",
"Operating System :: OS Independent",
dependencies = [
# This is a very specific list which is known to work
# with RAX storage...
"Homepage" = ""
launch-node = "opendev_launch:launch_node.main"
show-dns = "opendev_launch:dns.main"
opendev_launch = ["*.sh"]

@ -19,7 +19,7 @@
# limitations under the License.
import argparse
from sshfp import sshfp_print_records
from .sshfp import sshfp_print_records
def get_href(server):
if not hasattr(server, 'links'):
@ -131,6 +131,3 @@ def main():
" openstacksdk >= 0.12 is required")
print_dns(cloud, server)
if __name__ == '__main__':

@ -28,15 +28,15 @@ import tempfile
import time
import traceback
import dns
import utils
from . import dns
from . import utils
import openstack
import paramiko
from sshclient import SSHException
from .sshclient import SSHException
SCRIPT_DIR = os.path.dirname(sys.argv[0])
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
# This unactionable warning does not need to be printed over and over.
@ -419,6 +419,3 @@ def main():
print("When requesting an exception you can use the")
print(" email address to verify the responsible")
if __name__ == '__main__':

@ -23,7 +23,7 @@ import socket
import paramiko
from sshclient import SSHClient
from .sshclient import SSHClient
def iterate_timeout(max_seconds, purpose):

@ -0,0 +1 @@
Install the launch node script to a venv

@ -0,0 +1,11 @@
- name: Create launcher venv
name: create-venv
create_venv_path: '/usr/launcher-venv'
- name: Install node launcher
name: 'file:///home/zuul/src/'
virtualenv: '/usr/launcher-venv'

@ -62,3 +62,7 @@
logrotate_file_name: /var/log/ansible/zuul_reboot.log
logrotate_frequency: weekly
- name: Install node launcher
name: install-launch-node

@ -23,14 +23,12 @@ def test_zuul_data(host, zuul_data):
assert 'extra' in zuul_data
assert 'zuul' in zuul_data['extra']
def test_clouds_yaml(host):
clouds_yaml = host.file('/etc/openstack/clouds.yaml')
assert clouds_yaml.exists
assert b'password' in clouds_yaml.content
def test_openstacksdk_config(host):
f = host.file('/etc/openstack')
assert f.exists
@ -47,7 +45,6 @@ def test_openstacksdk_config(host):
assert == 'root'
assert f.mode == 0o640
def test_root_authorized_keys(host):
authorized_keys = host.file('/root/.ssh/authorized_keys')
assert authorized_keys.exists
@ -56,14 +53,12 @@ def test_root_authorized_keys(host):
lines = content.split('\n')
assert len(lines) >= 2
def test_ara(host):
ara ='/usr/ansible-venv/bin/ara-manage migrate')
assert ara.rc == 0
database = host.file('/root/.ara/server/ansible.sqlite')
assert database.exists
def test_kube_config(host):
if platform.machine() != 'x86_64':
@ -72,14 +67,12 @@ def test_kube_config(host):
assert b'Z2l0ZWFfazhzX2tleQ==' in kubeconfig.content
def test_kubectl(host):
if platform.machine() != 'x86_64':
kube ='kubectl help')
assert kube.rc == 0
def test_zuul_authorized_keys(host):
authorized_keys = host.file('/home/zuul/.ssh/authorized_keys')
assert authorized_keys.exists
@ -92,7 +85,6 @@ def test_zuul_authorized_keys(host):
for key in keys:
assert 'ssh-rsa' in key
def test_rax_dns_backup(host):
config_file = host.file('/etc/rax-dns-auth.conf')
assert config_file.exists
@ -103,7 +95,6 @@ def test_rax_dns_backup(host):
output_dir = host.file('/var/lib/rax-dns-backup')
assert output_dir.exists
def test_ssh_known_hosts(host):
f = host.file('/etc/ssh/ssh_known_hosts')
@ -116,3 +107,8 @@ def test_ssh_known_hosts(host):
# Nothing special about this host, just testing it has an entry we
# expect.
assert b',,2001:4800:7818:103:be76:4eff:fe04:48c1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGG6WTR3dkhn766C69IRcLNN1Oxx7WMrcNsN03r+uZbU' in f.content
def test_launch_node_venv(host):
launch ='/usr/launcher-venv/bin/launch-node --help')
assert 'usage: launch-node' in launch.stdout
assert launch.rc == 0