diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-08-20 19:32:08 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-08-20 19:32:08 +0300 |
commit | cb437c9e3a1f7b25e1306e504656286c24186e4c (patch) | |
tree | c8314be1f48e51b0f145450c3a91a8b79390499e | |
parent | d6789ab0b7175173ea73723fc2a686097569bf6f (diff) | |
download | ansibleness-cb437c9e3a1f7b25e1306e504656286c24186e4c.tar.gz |
drop old crap
Sponsored-by: author
-rw-r--r-- | README | 48 | ||||
-rwxr-xr-x | create-vm | 173 | ||||
-rw-r--r-- | heat/pieni.heat | 109 | ||||
-rwxr-xr-x | new-create-vm | 167 | ||||
-rwxr-xr-x | new-remove-vm | 26 | ||||
-rw-r--r-- | new.yaml | 7 | ||||
-rwxr-xr-x | remove-vm | 55 |
7 files changed, 0 insertions, 585 deletions
@@ -1,48 +0,0 @@ -Managing my virtual machines -============================ - -I have a laptop with 16 GiB RAM, which is enough to run a few virtual -machines. I make use of this to run VMs as, for example, workers for -my CI setup, such that I have two or three versions of Debian -(oldstable, stable, unstable) and two CPU architectures (i386, amd64). -This lets me test the software I develop on a large fraction of the -environments in which my users run it on. - -I have a few other VMs as well. Managing all of these would be tricky, -but I've gathered some tools for this: - -* The actual virtualisation is KVM, managed by libvirt. This is less - invasive than running OpenStack of similar cloud services on my - laptop. - -* I use Ansible for configuration management. It's easy to use, and - doesn't require Ruby or running an agent on each machine. - -* I create each VM from a base image. The base images are created by - vmdebootstrap, a tool I wrote and that Neil Williams now maintains. - The base images have a minimal install, with a user `ansible` that - is allowed passwordless sudo, and an `authorized_keys` file that - allows my Ansible to log into them and do things. - -The creation of a new VM is a somewhat intricate process: - -* Create a new LV of the same size as the base images. - -* Unpack the correct base image onto the LV. The base images are - stored compressed in an archive location. - -* Create the VM, with `virt-install`. This chooses a random MAC - address for the new VM, and also starts it. - -* Find the new VM's MAC address in the - `/var/lib/libvirt/dnsmasq/default.leases` file, and pick out the IP - address given to it. - -* Add to `/etc/hosts` a line that gives the VM's IP address a name - that's the new VM's hostname. - -All of the above is automated into a script. - -After this, the new VM can be managed by Ansible. Ansible gets run -manually after the VM creation script, and after I've added the new VM -to my Ansible config. diff --git a/create-vm b/create-vm deleted file mode 100755 index ecb760a..0000000 --- a/create-vm +++ /dev/null @@ -1,173 +0,0 @@ -#!/bin/sh -# -# Create a new VM for liw. - - -set -eu - - -verbose() -{ - echo "INFO: $@" -} - - -die() -{ - echo "$@" 1>&2 - exit 1 -} - - -get_ip() -{ - python -c ' -import sys, json -leases = json.load(open(sys.argv[1])) -for lease in leases: - if lease["mac-address"] == sys.argv[2]: - print lease["ip-address"] -' "$1" "$2" || true -} - - -xz_uncompressed_size() -{ - xz --robot --verbose --list "$1" | awk '/^file/ { print $5 }' -} - -raw_uncompressed_size() -{ - stat -c %s "$1" -} - - -# Check parameters. - -if [ "$#" -lt 2 ] -then - die "Usage: $0 NAME BASE [virt-install-options]" -fi -verbose "Command line args OK" - - -# Config variables. -. "$HOME/.config/ansibleness/vm.conf" -verbose "Loaded config file" - -# Command line parameters: name of VM and base image (no .img.xz suffix). -name="$1" -case "$2" in - */*) basepath="$2" ;; - *) basepath="$imagedir/base-$2.img.xz" ;; -esac -verbose "basepath=$basepath" - -shift 2 - -# Does the base image exist? -if [ ! -e "$basepath" ] -then - echo "$basepath does not exist" 1>&2 - exit 1 -fi -verbose "$basepath exists" - -# Is base image compressed? -case "$basepath" in - *.xz) xz=true ;; - *) xz=false ;; -esac - - -# How large is the (uncompressed) image? In bytes. -if $xz -then - size="$(xz_uncompressed_size "$basepath")" -else - size="$(raw_uncompressed_size "$basepath")" -fi -verbose "Image is $size bytes" - -# Create new LV. -verbose "Creating LV /dev/$vg/$name" -sudo lvcreate --name "$name" --size "${size}b" \ - --zero y --wipesignatures n --activate y "$vg" -lvpath="/dev/$vg/$name" -verbose "lvpath=$lvpath" -#sudo lvchange -ay "$lvpath" - -# Copy uncompressed image to LV. -if $xz -then - verbose "Uncompressing and copying image to LV" - unxz < "$basepath" | - pv --size "$size" | - sudo ionice -c3 tee "$lvpath" > /dev/null -else - verbose "Copying uncompressed imagew to LV" - pv "$basepath" | - sudo ionice -c3 tee "$lvpath" > /dev/null -fi - -# Edit /etc/hostname -verbose "Set hostname on new system to $name" -mnt="$(mktemp -d)" -for part in $(sudo kpartx -asv "$lvpath" | awk '/^add map / { print $3 }') -do - sudo mount "/dev/mapper/$part" "$mnt" - if [ -e "$mnt/etc/hostname" ] - then - echo "$name" | sudo tee "$mnt/etc/hostname" - fi - sudo umount "$mnt" -done -sudo kpartx -dsv "$lvpath" - -# Create VM. -verbose "Creating VM" -virt-install --connect qemu:///system \ - --quiet \ - --name="$name" \ - --memory=1024 \ - --cpu=host-passthrough \ - --import \ - --os-variant=debian9 \ - --disk="path=$lvpath,cache=none" \ - --network="$vmnetwork" \ - --graphics=spice \ - --noautoconsole \ - "$@" - -# If we're using the virtual network "default", wait for the VM to get -# a DHCP response and add it to /etc/hosts. We don't do it for other -# types of network (e.g., bridge=br0), since we ... can't. - -if [ "$vmnetwork" = "network=default" ] -then - verbose "Waiting for VM to boot and get IP" - - # Get the MAC address. - mac="$(virsh -c qemu:///system dumpxml "$name" | - sed -n "/<mac address=/s/^.*'\(.*\)'.*/\1/p")" - verbose "MAC: $mac" - - # Get IP address related to the MAC address. Append that to /etc/hosts. - leases=/var/lib/libvirt/dnsmasq/virbr0.status - - ip="" - while [ "$ip" = "" ] - do - sleep 1 - if [ -s "$leases" ] - then - ip="$(get_ip "$leases" "$mac")" - fi - done - echo "$ip $name" | sudo tee -a /etc/hosts > /dev/null - - # Done. - echo "Virtual machine $name ($ip) has been created and started." -else - echo "Virtual machine $name has been created and started, and may be ready soon." -fi diff --git a/heat/pieni.heat b/heat/pieni.heat deleted file mode 100644 index 0ab8aa5..0000000 --- a/heat/pieni.heat +++ /dev/null @@ -1,109 +0,0 @@ ---- -heat_template_version: 2015-04-30 - -description: >- - pieni.net. - -parameters: - - key_name: - type: string - label: Key name - description: Name of key-pair to be used for compute instance - - instance_flavor: - type: string - label: Instance Type - description: Type of instance (flavor) to be used - default: nbl-n1-medium - - image_id: - type: string - label: Image ID - description: "stretch" - default: 64e1068f-09f0-4eb3-aca1-05946de594c3 - - floating_network: - type: string - label: Public network UUID - description: UUID of the public network - default: Public-Helsinki-1 - - availability_zone: - type: string - label: Availability zone - description: Name of the Availability zone - default: helsinki-1 - - public_network: - type: string - description: The network for the VM in helsinki-1 - default: Network-Public-Helsinki-1 - -resources: - - sg: - type: OS::Neutron::SecurityGroup - properties: - description: some stuff - rules: - # Allow ssh in. - - direction: ingress - ethertype: IPv4 - port_range_min: 22 - port_range_max: 22 - protocol: tcp - # Allow smtp in. - - direction: ingress - ethertype: IPv4 - port_range_min: 25 - port_range_max: 25 - protocol: tcp - # Allow https in. - - direction: ingress - ethertype: IPv4 - port_range_min: 80 - port_range_max: 80 - protocol: tcp - # Allow imaps in. - - direction: ingress - ethertype: IPv4 - port_range_min: 993 - port_range_max: 993 - protocol: tcp - # Allow smtp submission in. - - direction: ingress - ethertype: IPv4 - port_range_min: 587 - port_range_max: 587 - protocol: tcp - # Allow git in. - - direction: ingress - ethertype: IPv4 - port_range_min: 9418 - port_range_max: 9418 - protocol: tcp - - public_port: - type: OS::Neutron::Port - properties: - network: { get_param: public_network } - security_groups: [{ get_resource: sg }] - - public_ip: - type: OS::Neutron::FloatingIP - depends_on: public_port - properties: - floating_network: { get_param: floating_network } - port_id: { get_resource: public_port } - - pieni: - type: OS::Nova::Server - depends_on: public_port - properties: - availability_zone : { get_param: availability_zone } - key_name: { get_param: key_name } - image: { get_param: image_id } - flavor: { get_param: instance_flavor } - networks: - - port: { get_resource: public_port } diff --git a/new-create-vm b/new-create-vm deleted file mode 100755 index 6350247..0000000 --- a/new-create-vm +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python3 - -import os -import shutil -import socket -import subprocess -import sys -import tempfile -import time -import yaml - - -def msg(s): - print(f"{s}") - - -def cloud_init_iso(iso, hostname, pubkey): - tmp = tempfile.mkdtemp() - - with open(os.path.join(tmp, "meta-data"), "w") as f: - f.write( - f"""\ -# Amazon EC2 style metadata -local-hostname: {hostname} -""" - ) - - with open(os.path.join(tmp, "user-data"), "w") as f: - f.write( - f"""\ -#cloud-config -ssh_authorized_keys: -- {pubkey} -""" - ) - - subprocess.check_call( - [ - "genisoimage", - "-quiet", - "-volid", - "cidata", - "-joliet", - "-rock", - "-output", - iso, - tmp, - ] - ) - shutil.rmtree(tmp) - - -def create_vm(vm, image, iso, network, memory=1024, cpus=1): - subprocess.check_call( - [ - "virt-install", - "--name", - vm, - "--memory", - str(memory), - "--vcpus", - str(cpus), - f"--disk=path={image},cache=none", - f"--disk=path={iso},readonly=on", - f"--network={network}", - "--connect", - "qemu:///system", - "--cpu=host-passthrough", - "--os-variant=debian9", - "--import", - "--graphics=spice", - "--noautoconsole", - "--quiet", - ] - ) - - -def wait_for_ssh(hostname): - ssh_port = 22 - while True: - time.sleep(5) - try: - conn = socket.create_connection((hostname, ssh_port), timeout=1) - except Exception: - continue - conn.close() - break - - -def provision(vm, pubkey): - ssh_opts = [ - "ControlMaster=auto", - "ControlPersist=60s", - "StrictHostKeyChecking=accept-new", - "UserKnownHostsFile=/dev/null", - ] - - env = dict(os.environ) - env["ANSIBLE_SSH_ARGS"] = " ".join(f"-o{opt}" for opt in ssh_opts) - - vars_file = {"user_pub": pubkey} - - fd, filename = tempfile.mkstemp() - os.write(fd, yaml.safe_dump(vars_file).encode("UTF-8")) - os.close(fd) - - argv = [ - "ansible-playbook", - "-i", - "hosts", - "manager.yml", - f"-eansible_ssh_host={vm}", - f"-e@{filename}", - ] - subprocess.check_output(argv, env=env) - - os.remove(filename) - - -def main(): - config = yaml.safe_load(open(sys.argv[1])) - vm = sys.argv[2] - - msg(f"creating VM {vm}") - - config = config[vm] - base = os.path.expanduser(config["base_image"]) - img = os.path.expanduser(config["image_file"]) - size = config["image_size"] - pubkey = os.path.expanduser(config["public_key"]) - memory = config.get("memory", 1024) - cpus = config.get("cpus", 1) - network = config.get("network", "network=default") - - memory = int(memory) - cpus = int(cpus) - pubkey = open(pubkey).read().rstrip() - iso = f"{vm}.iso" - - msg(f"creating cloud-init config {iso}") - cloud_init_iso(iso, vm, pubkey) - - if os.path.exists(img): - msg(f"removing existing image {img}") - os.remove(img) - msg(f"creating new image {img} as a copy of {base}") - shutil.copy(base, img) - - msg(f"resizing to {size}") - subprocess.check_call(["qemu-img", "resize", "-q", img, size]) - - msg(f"creating VM") - create_vm(vm, img, iso, network, memory=memory, cpus=cpus) - - msg(f"waiting for SSH to be available") - wait_for_ssh(vm) - - msg(f"removing cloud-init config") - subprocess.check_output( - ["virsh", "detach-disk", "--persistent", "--live", vm, "vdb"] - ) - os.remove(iso) - - msg("OK") - - -main() diff --git a/new-remove-vm b/new-remove-vm deleted file mode 100755 index 67a078d..0000000 --- a/new-remove-vm +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -set -eu -o pipefail - -running() -{ - virsh domstate "$1" | grep -q -Fx running -} - -shutoff() -{ - virsh domstate "$1" | grep -q -Fx "shut off" -} - -for vm in "$@" -do - if running "$vm" - then - virsh shutdown "$vm" > /dev/null - while ! shutoff "$vm" - do - sleep 1 - done - fi - virsh undefine "$vm" > /dev/null -done diff --git a/new.yaml b/new.yaml deleted file mode 100644 index b2d6d06..0000000 --- a/new.yaml +++ /dev/null @@ -1,7 +0,0 @@ -testvm: - base_image: ~/tmp/debian-10-openstack-amd64.qcow2 - image_file: ~/tmp/vms/testvm.qcow2 - image_size: 10G - memory: 1024 - cpus: 2 - public_key: ~/.ssh/liw-openpgp.pub diff --git a/remove-vm b/remove-vm deleted file mode 100755 index e60bc93..0000000 --- a/remove-vm +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -# -# Remove a VM for liw. - -set -eu - -run_virsh() -{ - virsh -c qemu:///system "$@" -} - -vm_exists() -{ - run_virsh list --all | grep -F "$name" > /dev/null -} - -remove_vm() -{ - local name="$1" - - # Shut down and remove the VM, if it exists. - if vm_exists "$name" - then - run_virsh destroy "$name" || true - run_virsh undefine --nvram "$name" - fi - - - # Remove the LV, if it exists. - lvpath="/dev/$vg/$name" - if [ -e "$lvpath" ] - then - if ! sudo lvremove --force "$lvpath" - then - # In case the LV was kpartx'd, undo that - sudo kpartx -dsv "$lvpath" - sudo lvremove --force "$lvpath" - fi - fi - - # Remove the host from /etc/hosts, if there. - awk -v "name=$name" '$2 != name' /etc/hosts | sudo sponge /etc/hosts - - # Done. - echo "Virtual machine $name is gone." -} - - -# Read config variables. -. "$HOME/.config/ansibleness/vm.conf" - -for name in "$@" -do - remove_vm "$name" -done |