224 lines
7 KiB
Python
224 lines
7 KiB
Python
"""
|
|
Use virt-install to create a VM suitable for installing a RHOSP/tripleo AIO
|
|
"""
|
|
|
|
import argparse
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
|
|
MD_PATH = "./meta-data"
|
|
UD_PATH = "./user-data"
|
|
ND_PATH = "./network-config"
|
|
|
|
|
|
def parse_args():
|
|
"""Parse the command line arguments"""
|
|
template_path = pathlib.Path(__file__).parent.parent / "virt-install"
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--password", required=True)
|
|
parser.add_argument("--public-key", required=True, type=open)
|
|
parser.add_argument("--local-hostname", required=True)
|
|
parser.add_argument("--user-data", default=template_path / "user-data.tpl")
|
|
parser.add_argument("--meta-data", default=template_path / "meta-data.tpl")
|
|
parser.add_argument(
|
|
"--network-data", default=template_path / "network-config.tpl"
|
|
)
|
|
parser.add_argument("--instance-id", required=True, help="Hostname for the new VM")
|
|
parser.add_argument("--output-image", required=True)
|
|
parser.add_argument("--image-size", default="100G")
|
|
parser.add_argument("--input-image", required=True)
|
|
parser.add_argument("--os-variant", required=True)
|
|
parser.add_argument("--name", required=True)
|
|
parser.add_argument("--memory", default=16384) # , type="int")
|
|
parser.add_argument("--cpus", default=4) # , type="int")
|
|
parser.add_argument("--gateway")
|
|
parser.add_argument("--cidr-1")
|
|
parser.add_argument("--cidr-2")
|
|
parser.add_argument("--mac-1",default="RANDOM")
|
|
parser.add_argument("--mac-2",default="RANDOM")
|
|
parser.add_argument("--dns",)
|
|
parser.add_argument("--search-domain")
|
|
parser.add_argument("-v", "--verbose", action="store_true")
|
|
parser.add_argument("--rhn-user", required=True)
|
|
parser.add_argument("--rhn-password", required=True)
|
|
|
|
args = parser.parse_args()
|
|
|
|
if (args.cidr_1 or args.cidr_2) and not (args.cidr_1 and args.cidr_2):
|
|
print("You must specify two addresses if you specify any addresses")
|
|
sys.exit(1)
|
|
|
|
if args.cidr_1 and not args.gateway:
|
|
print("You must specify a gateway if you specify any addresses")
|
|
sys.exit(1)
|
|
|
|
if args.cidr_1 and not args.search_domain:
|
|
print("You must specify a search domain if you specify any addresses")
|
|
sys.exit(1)
|
|
|
|
if args.cidr_1 and not args.dns:
|
|
print("You must specify a DNS server if you specify any addresses")
|
|
sys.exit(1)
|
|
|
|
if not (args.cidr_1 or args.cidr_2) and (args.dns or args.gateway or args.search_domain):
|
|
print("There's no point specifying DNS, gateway or search_domain if yoou don't specify addresses")
|
|
sys.exit(1)
|
|
|
|
args.public_key = args.public_key.read()
|
|
|
|
with open(args.user_data) as user_data:
|
|
args.user_data = user_data.read()
|
|
generate_boot_cmd(args)
|
|
args.user_data = args.user_data.format(data=args)
|
|
|
|
with open(args.meta_data) as meta_data:
|
|
args.meta_data = meta_data.read()
|
|
args.meta_data = args.meta_data.format(data=args)
|
|
|
|
with open(args.network_data) as network_data:
|
|
args.network_data = network_data.read()
|
|
args.network_data = args.network_data.format(data=args)
|
|
|
|
output_image = pathlib.Path(args.output_image)
|
|
if output_image.exists():
|
|
raise ValueError(f"{args.output_image} already exists")
|
|
args.output_image = output_image
|
|
|
|
input_image = pathlib.Path(args.input_image)
|
|
if not input_image.exists():
|
|
raise ValueError(f"{args.input_image} not found")
|
|
args.input_image = input_image
|
|
|
|
return args
|
|
|
|
def generate_boot_cmd(data):
|
|
data.bootcmd=""
|
|
if data.cidr_1:
|
|
data.bootcmd = (
|
|
"bootcmd:\n"
|
|
f' - "nmcli con modify \'System eth0\' ipv4.address {data.cidr_1} ipv4.method static ipv4.gateway {data.gateway} ipv4.dns {data.dns}"\n'
|
|
f' - "nmcli con modify \'Wired connection 1\' ipv4.address {data.cidr_2} ipv4.method static ipv4.gateway {data.gateway} ipv4.dns {data.dns}"\n'
|
|
' - "nmcli networking off"\n'
|
|
' - "nmcli networking on"\n'
|
|
)
|
|
|
|
def write_user_data(data):
|
|
"""Write out a temporary user data file"""
|
|
|
|
with open(UD_PATH, "w", encoding="utf-8") as user_data_file:
|
|
user_data_file.write(data)
|
|
|
|
|
|
def write_meta_data(meta_data):
|
|
"""Write out a temporary meta data file"""
|
|
with open(MD_PATH, "w", encoding="utf-8") as meta_data_file:
|
|
meta_data_file.write(meta_data)
|
|
|
|
|
|
def write_network_data(network_data):
|
|
"""Write out a temporary network data file"""
|
|
with open(ND_PATH, "w", encoding="utf-8") as network_data_file:
|
|
network_data_file.write(network_data)
|
|
|
|
|
|
def create_image(args):
|
|
"""
|
|
Create a new image file bashed on the input image, resized to the
|
|
specified size.
|
|
"""
|
|
|
|
print("creating image")
|
|
cmd = (
|
|
"qemu-img create -f qcow2 -o preallocation=metadata "
|
|
f"{args.output_image} {args.image_size}"
|
|
)
|
|
|
|
result = subprocess.check_output(cmd, shell=True, universal_newlines=True)
|
|
if args.verbose:
|
|
print(result)
|
|
|
|
print("Resizing image")
|
|
cmd = f"virt-resize --expand /dev/sda3 {args.input_image} {args.output_image}"
|
|
|
|
result = subprocess.check_output(cmd, shell=True, universal_newlines=True)
|
|
|
|
if args.verbose:
|
|
print(result)
|
|
|
|
print("Image resized")
|
|
|
|
def virt_install_cmd(args):
|
|
"""Build and execute the virt-install command"""
|
|
cmd = f"""
|
|
sudo virt-install \\
|
|
--os-variant {args.os_variant} \\
|
|
--name {args.name} \\
|
|
--memory {args.memory} \\
|
|
--disk {args.output_image} \\
|
|
--vcpus {args.cpus} \\
|
|
--import \\
|
|
--graphics spice,listen=127.0.0.1 \\
|
|
--video virtio \\
|
|
--channel spicevmc \\
|
|
--wait 0 \\
|
|
--cloud-init meta-data=./meta-data,user-data=./user-data \\
|
|
--network bridge=bridge0,mac={args.mac_1} \\
|
|
--network bridge=bridge0,mac={args.mac_2}
|
|
"""
|
|
|
|
print("running virt-install")
|
|
|
|
if args.verbose:
|
|
print(cmd)
|
|
|
|
result = subprocess.check_output(cmd, shell=True, universal_newlines=True)
|
|
if args.verbose:
|
|
print(result)
|
|
|
|
|
|
def delete_meta_data():
|
|
"""Delete the temporary metadata file"""
|
|
pathlib.Path.unlink(pathlib.Path(MD_PATH))
|
|
|
|
|
|
def delete_user_data():
|
|
"""Delete the temporary userdata file"""
|
|
pathlib.Path.unlink(pathlib.Path(UD_PATH))
|
|
|
|
|
|
def install_packages(args):
|
|
"""Install the packages needed for virt-install to work"""
|
|
cmd = (
|
|
"sudo dnf install -y virt-install virt-viewer qemu-img "
|
|
"libguestfs.x86_64"
|
|
)
|
|
|
|
print("installing needed packages...")
|
|
|
|
if args.verbose:
|
|
print(
|
|
subprocess.check_output(cmd, shell=True, universal_newlines=True)
|
|
)
|
|
|
|
|
|
def main():
|
|
"""main function"""
|
|
args = parse_args()
|
|
|
|
if args.verbose:
|
|
print(args)
|
|
|
|
install_packages(args)
|
|
create_image(args)
|
|
write_meta_data(args.meta_data)
|
|
write_user_data(args.user_data)
|
|
|
|
virt_install_cmd(args)
|
|
|
|
delete_meta_data()
|
|
delete_user_data()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|