c7testvm: Script to create a CentOS 7 test VM
This script creates a new virtual machine using `virt-install`. It then starts an HTTP server to serve a kickstart configuration file to the new VM, which is used to automatically install CentOS.master
parent
1f2d490e44
commit
0f8df4ce89
|
@ -0,0 +1,226 @@
|
||||||
|
#!/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('--ram', type=int, default=1024)
|
||||||
|
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()
|
||||||
|
|
||||||
|
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-type': 'linux',
|
||||||
|
'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()
|
||||||
|
p.wait()
|
||||||
|
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()
|
Loading…
Reference in New Issue