scripts/c7testvm.py

233 lines
5.6 KiB
Python
Executable File

#!/usr/bin/env python
import argparse
import binascii
import http.server
import os
import random
import socket
import string
import subprocess
import time
HTTP_PROXY = 'caithe.pyrocufflink.jazz:3128'
PORT_RANGE = (10000, 65535)
REPO_URL = 'http://mirror.centos.org/centos/7/os/x86_64'
KICKSTART = string.Template('''\
text
install
url --url=http://mirror.centos.org/centos/7/os/x86_64
repo --name=updates --baseurl=http://mirror.centos.org/centos/7/updates/x86_64
repo --name=extras --baseurl=http://mirror.centos.org/centos/7/extras/x86_64
lang en_US.UTF-8
keyboard us
timezone --utc UTC
rootpw --iscrypted x
shutdown
bootloader --location=mbr
clearpart --all --initlabel
autopart --type=lvm
network --hostname=${hostname}
%packages --nocore
@core --nodefaults
-biosdevname
-btrfs-progs
-firewalld
-iprutils
-irqbalance
-kexec-tools
-man-db
-parted
-plymouth
-teamd
-tuned
avahi
qemu-guest-agent
%end
%addon com_redhat_kdump --disable
%end
%post
install -d /root/.ssh
cat > /root/.ssh/authorized_keys <<EOF
${ssh_pubkey}
EOF
sed 's/use-ipv6=.*/use-ipv6=yes/' /etc/avahi/avahi-daemon.conf
%end
''')
class KickstartHTTPServer(http.server.HTTPServer):
address_family = socket.AF_INET6
def __init__(self, kickstart):
port = random.randint(*PORT_RANGE)
super(KickstartHTTPServer, self).__init__(
server_address=('::', port),
RequestHandlerClass=KickstartHTTPRequestHandler,
)
self.kickstart = kickstart
class KickstartHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
self.do_HEAD()
self.wfile.write(self.server.kickstart)
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-Type', 'text/plain; charset=utf-8')
self.send_header('Content-Length', str(len(self.server.kickstart)))
self.end_headers()
def get_server_name():
tries = [
(socket.AF_INET6, '2001:db8::1'),
(socket.AF_INET, '203.0.113.1'),
]
addr = None
for af, remote in tries:
s = socket.socket(af, socket.SOCK_DGRAM)
try:
s.connect((remote, 0))
addr = s.getsockname()[0]
except:
pass
finally:
s.close()
if addr is None:
try:
addr = socket.gethostbyname(socket.gethostname())
except:
return socket.gethostname()
try:
return socket.gethostbyaddr(addr)[0]
except:
return addr
def get_ssh_pubkey():
dirname = os.path.expanduser('~/.ssh')
for t in ('ed25519', 'rsa', 'ecdsa'):
try:
with open(os.path.join(dirname, 'id_{}.pub'.format(t))) as f:
return f.read()
except OSError:
continue
return ''
def virt_install(args):
yield 'virt-install'
for key, value in args.items():
yield '--{}'.format(key)
if value is not None:
yield str(value)
def wait_for_host(host, port, timeout=300):
deadline = time.time() + timeout
while time.time() < deadline:
try:
sock = socket.create_connection((host, port))
except OSError:
pass
else:
sock.close()
return True
return False
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--name')
parser.add_argument('--ram', type=int, default=1536)
parser.add_argument('--vcpus', type=int, default=2)
parser.add_argument('--cpu', default='host')
parser.add_argument('--location', default=REPO_URL)
parser.add_argument('--disk-pool', default='default')
parser.add_argument('--disk-size', type=int, default=6)
parser.add_argument('--network', default='bridge=br0')
parser.add_argument('--sound', default='none')
parser.add_argument('--server-name')
parser.add_argument('--ip', default='dhcp')
parser.add_argument('--ssh-key')
return parser.parse_args()
def main():
args = parse_args()
if args.name:
name = args.name
else:
name = 'C7-{}'.format(binascii.hexlify(os.urandom(3)).decode())
hostname = '{}.local'.format(name.lower())
if args.ssh_key is None:
ssh_pubkey = get_ssh_pubkey()
else:
ssh_pubkey = args.ssh_key
kickstart = KICKSTART.substitute(
hostname=hostname,
ssh_pubkey=ssh_pubkey,
)
httpd = KickstartHTTPServer(kickstart.encode('utf-8'))
kcmdline = 'ip={ip} proxy={proxy} ks=http://{server}:{port}/'.format(
ip=args.ip,
proxy=HTTP_PROXY,
server=args.server_name or get_server_name(),
port=httpd.server_port,
)
cmd = list(virt_install({
'name': name,
'ram': args.ram,
'vcpus': args.vcpus,
'cpu': args.cpu,
'location': args.location,
'extra-args': kcmdline,
'os-variant': 'rhel7',
'disk': 'pool={pool},size={size}'.format(
pool=args.disk_pool,
size=args.disk_size,
),
'network': args.network,
'sound': args.sound,
'redirdev': 'none',
'noautoconsole': None,
'wait': -1,
}))
env = os.environ.copy()
env['http_proxy'] = HTTP_PROXY
p = subprocess.Popen(cmd, env=env)
httpd.handle_request()
httpd.server_close()
try:
p.wait()
except KeyboardInterrupt:
raise SystemExit(1)
print('Waiting for host to come up...')
if wait_for_host(hostname, 22):
os.execlp('ssh', 'ssh', '-oStrictHostKeyChecking=no',
'root@{}'.format(hostname))
else:
sys.stderr.write('Timed out waiting for {}\n'.format(hostname))
raise SystemExit(1)
if __name__ == '__main__':
main()