""" 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()