Script to create/start/stop/destroy many CoreOS VMs running on a KVM host (referenced from http://www.beginswithdata.com/2016/12/30/centos7-kvm-coreos/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python | |
################################################################################ | |
# Manage (create/start/stop/undefine) CoreOS VMs on CentOS 7 using # | |
# QEMU/KVM/libvirt. Makes it easy to create/undefine container hosts # | |
# for lab purposes. # | |
# # | |
# For more info see this blog post: # | |
# http://beginswithdata.com/2016/12/30/centos7-kvm-coreos/ # | |
# # | |
# Copyright (c) Chris Madden. All rights reserved. # | |
# Specifications subject to change without notice. # | |
################################################################################ | |
import sys, argparse, os, shutil, subprocess, errno, socket, logging | |
parser = argparse.ArgumentParser(description='Manage some container host VMs.') | |
parser.add_argument('--prefix', dest='prefix', help='Prefix hostname for managed VMs', required=True) | |
parser.add_argument('--start', dest='start', type=int, help='Suffix number for managed VMs', required=True) | |
parser.add_argument('--count', dest='count', type=int, help='Count of VMs to manage', required=True) | |
parser.add_argument('--action',dest='action', help='Action to take [create|start|stop|undefine]', required=True) | |
parser.add_argument('--ip', dest='ip', help='Starting IP address if creating VMs or keyword "dns"') | |
args = parser.parse_args() | |
img_name = 'os.img' | |
dir_path = os.path.dirname(os.path.realpath(__file__)) | |
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) | |
# Loop though for quantity of VMs to manage | |
for i in range(args.count): | |
vmname = str(args.prefix) + str(args.start + i ) | |
vm_path = dir_path + '/../' + vmname | |
# create logic | |
if args.action == 'create': | |
logging.info(vmname + ": Creating...") | |
# Determine IP address for VM | |
if args.ip == 'dns': | |
try: | |
ip = socket.gethostbyname(vmname) | |
except Exception as exc: | |
logging.error(vmname + ': DNS lookup failed.') | |
continue | |
else: | |
try: | |
ip_list = args.ip.split('.') | |
except Exception as exc: | |
logging.error(vmname + ': IP address not provided or not parsable') | |
exit() | |
ip = '.'.join(ip_list[:3]) + '.' + str(int(ip_list[3]) + i) | |
logging.info(vmname + ": Using IP " + ip) | |
# Check if IP is in use before starting just in case... | |
if (os.system("ping -c 1 -W 3 " + vmname + "> /dev/null") == 0): | |
logging.error(vmname + ': IP address is pingable; skipping creation') | |
continue | |
# Create subdir | |
try: | |
os.makedirs(vm_path) | |
except OSError as exc: | |
if exc.errno == errno.EEXIST: | |
logging.warning(vmname + ': Skipping creation because the storage directory already exists') | |
continue | |
else: | |
raise | |
# Create cloud-config file for VM doing a search/replace from template | |
logging.info(vmname + ": Creating cloud-config") | |
with open(dir_path + '/user_data', 'r') as file : | |
filedata = file.read() | |
filedata = filedata.replace('@@IP@@', ip) | |
filedata = filedata.replace('@@VMNAME@@', vmname) | |
with open(vm_path + '/user_data', 'w') as file: | |
file.write(filedata) | |
# Generate ISO | |
logging.info(vmname + ": Creating ISO image of cloud-config") | |
os.makedirs('/tmp/new-drive/openstack/latest') | |
shutil.copy(vm_path + '/user_data', '/tmp/new-drive/openstack/latest/user_data') | |
subprocess.call(['mkisofs', '-quiet', '-input-charset', 'iso8859-1', '-R', '-V', 'config-2', '-o', vm_path + '/configdrive.iso', '/tmp/new-drive']) | |
shutil.rmtree('/tmp/new-drive') | |
# Copy master image in place | |
logging.info(vmname + ": Copying master boot image for VM") | |
shutil.copyfile(dir_path + '/' + img_name, vm_path + '/' + img_name) | |
# Create and start VM | |
vi_cmd = [] | |
vi_cmd.extend(['virt-install','--connect', 'qemu:///system']) | |
vi_cmd.extend(['--import', '--name', vmname]) | |
vi_cmd.extend(['--ram', '1024', '--vcpus', '1']) | |
vi_cmd.extend(['--os-type=linux', '--os-variant=virtio26']) | |
vi_cmd.extend(['--disk', 'path=' + vm_path + '/' + img_name + ',format=raw,bus=virtio']) | |
vi_cmd.extend(['--disk', 'path=' + vm_path + '/configdrive.iso,device=cdrom,format=raw,bus=ide']) | |
vi_cmd.extend(['--network=bridge=bridge0']) | |
vi_cmd.extend(['--accelerate']) | |
vi_cmd.extend(['--graphics', "vnc,listen=0.0.0.0"]) | |
vi_cmd.extend(['--noautoconsole']) | |
subprocess.call(vi_cmd) | |
logging.info(vmname + ": VM created and booting...") | |
# stop logic (incl undefine) | |
if args.action in [ 'stop', 'undefine']: | |
logging.info(vmname + ": Stopping...") | |
subprocess.call(['virsh', 'shutdown', vmname]) | |
# undefine logic | |
if args.action == 'undefine': | |
logging.info(vmname + ": Undefining...") | |
subprocess.call(['virsh', 'undefine', vmname]) | |
shutil.rmtree(vm_path) | |
# start logic | |
if args.action == 'start': | |
logging.info(vmname + ": Starting...") | |
subprocess.call(['virsh', 'start', vmname]) | |
### CLI equivalents of the above | |
#mkdir -p /tmp/new-drive/openstack/latest | |
#cp coreos1/user_data /tmp/new-drive/openstack/latest/user_data | |
#mkisofs -R -V config-2 -o coreos1/configdrive.iso /tmp/new-drive | |
#rm -fr /tmp/new-drive | |
#virt-install --connect qemu:///system \ | |
#--import --name coreos1 \ | |
#--ram 1024 --vcpus 1 \ | |
#--os-type=linux --os-variant=virtio26 \ | |
#--disk path=/vm-storage/coreos1/os.img,format=raw,bus=virtio \ | |
#--disk path=/vm-storage/coreos1/configdrive.iso,device=cdrom,format=raw,bus=ide \ | |
#--network=bridge=bridge0 \ | |
#--accelerate \ | |
#--graphics vnc,listen=0.0.0.0 \ | |
#--noautoconsole | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment