gateway: "{{ net.mgmt.gw }}"
nameservers: "{{ net.mgmt.dns }}"
indices:
+ metrics: 74
testvm: 99
---
localconfig_ssh_config_user: root
+
+vm_host: alfred
+
+install:
+ host: "{{ vm_host }}"
+ mem: 1024
+ numcpu: 2
+ disks:
+ primary: /dev/vda
+ virtio:
+ vda:
+ vg: "{{ vm_host }}"
+ lv: "{{ inventory_hostname }}"
+ size: 10g
+ vdb:
+ vg: "{{ vm_host }}"
+ lv: "{{ inventory_hostname }}-data"
+
+ interfaces:
+ - bridge: "{{ hostvars[vm_host].vm_host.network.interface }}"
+ name: mgmt0
+ autostart: True
+
+network:
+ nameservers: "{{ hostvars[vm_host].vm_host.network.nameservers }}"
+ domain: realraum.at
+ systemd_link:
+ interfaces: "{{ install.interfaces }}"
+ primary:
+ interface: mgmt0
+ ip: "{{ (hostvars[vm_host].vm_host.network.ip+'/'+hostvars[vm_host].vm_host.network.mask) | ipaddr(hostvars[vm_host].vm_host.network.indices[inventory_hostname]) | ipaddr('address') }}"
+ mask: "{{ hostvars[vm_host].vm_host.network.mask }}"
+ gateway: "{{ hostvars[vm_host].vm_host.network.gateway | default(hostvars[vm_host].vm_host.network.ip) }}"
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+import traceback
+import time
+
+try:
+ import libvirt
+except ImportError:
+ HAS_VIRT = False
+else:
+ HAS_VIRT = True
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_native
+
+
+VIRT_FAILED = 1
+VIRT_SUCCESS = 0
+VIRT_UNAVAILABLE = 2
+
+VIRT_STATE_NAME_MAP = {
+ 0: "running",
+ 1: "running",
+ 2: "running",
+ 3: "paused",
+ 4: "shutdown",
+ 5: "shutdown",
+ 6: "crashed"
+}
+
+
+class VMNotFound(Exception):
+ pass
+
+
+class LibvirtConnection(object):
+
+ def __init__(self, uri, module):
+
+ self.module = module
+
+ cmd = "uname -r"
+ rc, stdout, stderr = self.module.run_command(cmd)
+
+ if "xen" in stdout:
+ conn = libvirt.open(None)
+ elif "esx" in uri:
+ auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT], [], None]
+ conn = libvirt.openAuth(uri, auth)
+ else:
+ conn = libvirt.open(uri)
+
+ if not conn:
+ raise Exception("hypervisor connection failure")
+
+ self.conn = conn
+
+ def find_vm(self, vmid):
+ """
+ Extra bonus feature: vmid = -1 returns a list of everything
+ """
+ conn = self.conn
+
+ vms = []
+
+ # this block of code borrowed from virt-manager:
+ # get working domain's name
+ ids = conn.listDomainsID()
+ for id in ids:
+ vm = conn.lookupByID(id)
+ vms.append(vm)
+ # get defined domain
+ names = conn.listDefinedDomains()
+ for name in names:
+ vm = conn.lookupByName(name)
+ vms.append(vm)
+
+ if vmid == -1:
+ return vms
+
+ for vm in vms:
+ if vm.name() == vmid:
+ return vm
+
+ raise VMNotFound("virtual machine %s not found" % vmid)
+
+ def get_status(self, vmid):
+ state = self.find_vm(vmid).info()[0]
+ return VIRT_STATE_NAME_MAP.get(state, "unknown")
+
+
+class Virt(object):
+
+ def __init__(self, uri, module):
+ self.module = module
+ self.uri = uri
+
+ def __get_conn(self):
+ self.conn = LibvirtConnection(self.uri, self.module)
+ return self.conn
+
+ def status(self, vmid):
+ """
+ Return a state suitable for server consumption. Aka, codes.py values, not XM output.
+ """
+ self.__get_conn()
+ return self.conn.get_status(vmid)
+
+
+def core(module):
+
+ states = module.params.get('states', None)
+ guest = module.params.get('name', None)
+ uri = module.params.get('uri', None)
+ delay = module.params.get('delay', None)
+ sleep = module.params.get('sleep', None)
+ timeout = module.params.get('timeout', None)
+
+ v = Virt(uri, module)
+ res = {'changed': False, 'failed': True}
+
+ if delay > 0:
+ time.sleep(delay)
+
+ for _ in range(0, timeout, sleep):
+ state = v.status(guest)
+ if state in states:
+ res['state'] = state
+ res['failed'] = False
+ res['msg'] = "guest '%s' has reached state: %s" % (guest, state)
+ return VIRT_SUCCESS, res
+
+ time.sleep(sleep)
+
+ res['msg'] = "timeout waiting for guest '%s' to reach one of states: %s" % (guest, ', '.join(states))
+ return VIRT_FAILED, res
+
+
+def main():
+
+ module = AnsibleModule(argument_spec=dict(
+ name=dict(aliases=['guest'], required=True),
+ states=dict(type='list', required=True),
+ uri=dict(default='qemu:///system'),
+ delay=dict(type='int', default=0),
+ sleep=dict(type='int', default=1),
+ timeout=dict(type='int', default=300),
+ ))
+
+ if not HAS_VIRT:
+ module.fail_json(
+ msg='The `libvirt` module is not importable. Check the requirements.'
+ )
+
+ for state in module.params.get('states', None):
+ if state not in set(VIRT_STATE_NAME_MAP.values()):
+ module.fail_json(
+ msg="states contains invalid state '%s', must be one of %s" % (state, ', '.join(set(VIRT_STATE_NAME_MAP.values())))
+ )
+
+ rc = VIRT_SUCCESS
+ try:
+ rc, result = core(module)
+ except Exception as e:
+ module.fail_json(msg=to_native(e), exception=traceback.format_exc())
+
+ if rc != 0: # something went wrong emit the msg
+ module.fail_json(rc=rc, msg=result)
+ else:
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+vm_define_autostart: "{{ not vm_define_installer and hostvars[hostname].install_cooked.autostart | default(False) }}"
+vm_define_start: yes
+
+vm_define_installer: no
--- /dev/null
+---
+- name: check if vm already exists
+ virt:
+ name: "{{ hostname }}"
+ command: info
+ register: vmhost_info
+
+- when: hostname in vmhost_info
+ block:
+ - name: destroy exisiting vm
+ virt:
+ name: "{{ hostname }}"
+ state: destroyed
+
+ - name: wait for vm to be destroyed
+ wait_for_virt:
+ name: "{{ hostname }}"
+ states: shutdown,crashed
+ timeout: 5
+
+ - name: undefining exisiting vm
+ virt:
+ name: "{{ hostname }}"
+ command: undefine
+
+
+- name: redefine vm
+ virt:
+ name: "{{ hostname }}"
+ command: define
+ xml: "{{ lookup('template', 'libvirt-domain.xml.j2') }}"
+
+- when: vm_define_start
+ block:
+ - name: start vm
+ virt:
+ name: "{{ hostname }}"
+ state: running
+
+ - name: wait for VM to start
+ wait_for_virt:
+ name: "{{ hostname }}"
+ states: running
+ timeout: 10
+
+- name: mark vm as autostarted
+ when: vm_define_autostart
+ virt:
+ name: "{{ hostname }}"
+ autostart: yes
+ command: info ## virt module needs either command or state
--- /dev/null
+<domain type='kvm'>
+ <name>{{ hostname }}</name>
+ <memory>{{ hostvars[hostname].install_cooked.mem * 1024 }}</memory>
+ <currentMemory>{{ hostvars[hostname].install_cooked.mem * 1024 }}</currentMemory>
+ <vcpu>{{ hostvars[hostname].install_cooked.numcpu }}</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-0.12'>hvm</type>
+{% if vm_define_installer %}
+ <kernel>{{ debian_installer_path }}/{{ install_distro }}-{{ install_codename }}/{{ hostvars[hostname].install_cooked.arch | default('amd64') }}/linux</kernel>
+ <initrd>{{ preseed_tmpdir }}/initrd.preseed.gz</initrd>
+ <cmdline>console=ttyS0,115200n8</cmdline>
+{% endif %}
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+{% if vm_define_installer %}
+ <on_reboot>destroy</on_reboot>
+ <on_crash>destroy</on_crash>
+{% else %}
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+{% endif %}
+ <devices>
+ <emulator>/usr/bin/kvm</emulator>
+ <!-- Provide a virtualized RNG to the guest -->
+ <rng model='virtio'>
+ <!-- Allow consuming up to 10kb/s, measured over 2s -->
+ <rate period="2000" bytes="20480"/>
+ <backend model='random'>/dev/urandom</backend>
+ </rng>
+
+{% if 'virtio' in hostvars[hostname].install_cooked.disks %}
+{% for device, lv in hostvars[hostname].install_cooked.disks.virtio.items() %}
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw' cache='none' discard='unmap'/>
+ <source dev='/dev/mapper/{{ lv.vg | replace('-', '--') }}-{{ lv.lv | replace('-', '--') }}'/>
+ <target dev='{{ device }}' bus='virtio'/>
+ </disk>
+{% endfor %}
+{% endif %}
+
+{% if 'scsi' in hostvars[hostname].install_cooked.disks %}
+ <controller type='scsi' index='0' model='virtio-scsi'/>
+{% for device, lv in hostvars[hostname].install_cooked.disks.scsi.items() %}
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw' cache='none' discard='unmap'/>
+ <source dev='/dev/mapper/{{ lv.vg | replace('-', '--') }}-{{ lv.lv | replace('-', '--') }}'/>
+ <target dev='{{ device }}' bus='scsi'/>
+ </disk>
+{% endfor %}
+{% endif %}
+
+{% if hostvars[hostname].install_cooked.interfaces %}
+{% for if in hostvars[hostname].install_cooked.interfaces %}
+ <interface type='bridge'>
+ <source bridge='{{ if.bridge }}'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x0{{ loop.index }}' function='0x0'/>
+ </interface>
+{% endfor %}
+{% endif %}
+
+ <serial type='pty'>
+ <target port='0'/>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ </devices>
+</domain>
+++ /dev/null
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-import traceback
-import time
-
-try:
- import libvirt
-except ImportError:
- HAS_VIRT = False
-else:
- HAS_VIRT = True
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-
-VIRT_FAILED = 1
-VIRT_SUCCESS = 0
-VIRT_UNAVAILABLE = 2
-
-VIRT_STATE_NAME_MAP = {
- 0: "running",
- 1: "running",
- 2: "running",
- 3: "paused",
- 4: "shutdown",
- 5: "shutdown",
- 6: "crashed"
-}
-
-
-class VMNotFound(Exception):
- pass
-
-
-class LibvirtConnection(object):
-
- def __init__(self, uri, module):
-
- self.module = module
-
- cmd = "uname -r"
- rc, stdout, stderr = self.module.run_command(cmd)
-
- if "xen" in stdout:
- conn = libvirt.open(None)
- elif "esx" in uri:
- auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT], [], None]
- conn = libvirt.openAuth(uri, auth)
- else:
- conn = libvirt.open(uri)
-
- if not conn:
- raise Exception("hypervisor connection failure")
-
- self.conn = conn
-
- def find_vm(self, vmid):
- """
- Extra bonus feature: vmid = -1 returns a list of everything
- """
- conn = self.conn
-
- vms = []
-
- # this block of code borrowed from virt-manager:
- # get working domain's name
- ids = conn.listDomainsID()
- for id in ids:
- vm = conn.lookupByID(id)
- vms.append(vm)
- # get defined domain
- names = conn.listDefinedDomains()
- for name in names:
- vm = conn.lookupByName(name)
- vms.append(vm)
-
- if vmid == -1:
- return vms
-
- for vm in vms:
- if vm.name() == vmid:
- return vm
-
- raise VMNotFound("virtual machine %s not found" % vmid)
-
- def get_status(self, vmid):
- state = self.find_vm(vmid).info()[0]
- return VIRT_STATE_NAME_MAP.get(state, "unknown")
-
-
-class Virt(object):
-
- def __init__(self, uri, module):
- self.module = module
- self.uri = uri
-
- def __get_conn(self):
- self.conn = LibvirtConnection(self.uri, self.module)
- return self.conn
-
- def status(self, vmid):
- """
- Return a state suitable for server consumption. Aka, codes.py values, not XM output.
- """
- self.__get_conn()
- return self.conn.get_status(vmid)
-
-
-def core(module):
-
- states = module.params.get('states', None)
- guest = module.params.get('name', None)
- uri = module.params.get('uri', None)
- delay = module.params.get('delay', None)
- sleep = module.params.get('sleep', None)
- timeout = module.params.get('timeout', None)
-
- v = Virt(uri, module)
- res = {'changed': False, 'failed': True}
-
- if delay > 0:
- time.sleep(delay)
-
- for _ in range(0, timeout, sleep):
- state = v.status(guest)
- if state in states:
- res['state'] = state
- res['failed'] = False
- res['msg'] = "guest '%s' has reached state: %s" % (guest, state)
- return VIRT_SUCCESS, res
-
- time.sleep(sleep)
-
- res['msg'] = "timeout waiting for guest '%s' to reach one of states: %s" % (guest, ', '.join(states))
- return VIRT_FAILED, res
-
-
-def main():
-
- module = AnsibleModule(argument_spec=dict(
- name=dict(aliases=['guest'], required=True),
- states=dict(type='list', required=True),
- uri=dict(default='qemu:///system'),
- delay=dict(type='int', default=0),
- sleep=dict(type='int', default=1),
- timeout=dict(type='int', default=300),
- ))
-
- if not HAS_VIRT:
- module.fail_json(
- msg='The `libvirt` module is not importable. Check the requirements.'
- )
-
- for state in module.params.get('states', None):
- if state not in set(VIRT_STATE_NAME_MAP.values()):
- module.fail_json(
- msg="states contains invalid state '%s', must be one of %s" % (state, ', '.join(set(VIRT_STATE_NAME_MAP.values())))
- )
-
- rc = VIRT_SUCCESS
- try:
- rc, result = core(module)
- except Exception as e:
- module.fail_json(msg=to_native(e), exception=traceback.format_exc())
-
- if rc != 0: # something went wrong emit the msg
- module.fail_json(rc=rc, msg=result)
- else:
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
lv: "{{ item.value.lv }}"
size: "{{ item.value.size }}"
-- name: check if vm already exists
- virt:
- name: "{{ hostname }}"
- command: info
- register: vmhost_info
-
-- block:
- - name: destroy exisiting vm
- virt:
- name: "{{ hostname }}"
- state: destroyed
-
- - name: wait for vm to be destroyed
- wait_for_virt:
- name: "{{ hostname }}"
- states: shutdown,crashed
- timeout: 5
-
- - name: undefining exisiting vm
- virt:
- name: "{{ hostname }}"
- command: undefine
-
- when: hostname in vmhost_info
- block:
- name: create a temporary workdir
etype: user
permissions: rx
- - name: define new installer vm
- virt:
- name: "{{ hostname }}"
- command: define
- xml: "{{ lookup('template', 'libvirt-domain.xml.j2') }}"
+ - import_role:
+ name: vm/define
vars:
- run_installer: yes
+ vm_define_installer: yes
preseed_tmpdir: "{{ tmpdir.path }}"
- - name: start vm
- virt:
- name: "{{ hostname }}"
- state: running
-
- - name: wait for installer to start
- wait_for_virt:
- name: "{{ hostname }}"
- states: running
- timeout: 10
-
- debug:
msg: "you can check on the status of the installer running this command 'virsh console {{ hostname }}' on host {{ inventory_hostname }}."
register: installer_result
failed_when: installer_result.failed or installer_result.state == "crashed"
- - name: undefining installer vm
- virt:
- name: "{{ hostname }}"
- command: undefine
-
always:
- name: cleanup temporary workdir
file:
path: "{{ tmpdir.path }}"
state: absent
-- name: define new production vm
- virt:
- name: "{{ hostname }}"
- command: define
- xml: "{{ lookup('template', 'libvirt-domain.xml.j2') }}"
+- import_role:
+ name: vm/define
vars:
- run_installer: no
-
-- name: start vm
- virt:
- name: "{{ hostname }}"
- state: running
-
-- name: mark vm as autostarted
- virt:
- name: "{{ hostname }}"
- autostart: "{{ hostvars[hostname].install_cooked.autostart }}"
- command: info ## virt module needs either command or state
- when: hostvars[hostname].install_cooked.autostart is defined
+ vm_define_installer: no
+++ /dev/null
-<domain type='kvm'>
- <name>{{ hostname }}</name>
- <memory>{{ hostvars[hostname].install_cooked.mem * 1024 }}</memory>
- <currentMemory>{{ hostvars[hostname].install_cooked.mem * 1024 }}</currentMemory>
- <vcpu>{{ hostvars[hostname].install_cooked.numcpu }}</vcpu>
- <os>
- <type arch='x86_64' machine='pc-0.12'>hvm</type>
-{% if run_installer %}
- <kernel>{{ debian_installer_path }}/{{ install_distro }}-{{ install_codename }}/{{ hostvars[hostname].install_cooked.arch | default('amd64') }}/linux</kernel>
- <initrd>{{ preseed_tmpdir }}/initrd.preseed.gz</initrd>
- <cmdline>console=ttyS0,115200n8</cmdline>
-{% endif %}
- <boot dev='hd'/>
- </os>
- <features>
- <acpi/>
- <apic/>
- <pae/>
- </features>
- <clock offset='utc'/>
- <on_poweroff>destroy</on_poweroff>
-{% if run_installer %}
- <on_reboot>destroy</on_reboot>
- <on_crash>destroy</on_crash>
-{% else %}
- <on_reboot>restart</on_reboot>
- <on_crash>restart</on_crash>
-{% endif %}
- <devices>
- <emulator>/usr/bin/kvm</emulator>
- <!-- Provide a virtualized RNG to the guest -->
- <rng model='virtio'>
- <!-- Allow consuming up to 10kb/s, measured over 2s -->
- <rate period="2000" bytes="20480"/>
- <backend model='random'>/dev/urandom</backend>
- </rng>
-
-{% if 'virtio' in hostvars[hostname].install_cooked.disks %}
-{% for device, lv in hostvars[hostname].install_cooked.disks.virtio.items() %}
- <disk type='block' device='disk'>
- <driver name='qemu' type='raw' cache='none' discard='unmap'/>
- <source dev='/dev/mapper/{{ lv.vg | replace('-', '--') }}-{{ lv.lv | replace('-', '--') }}'/>
- <target dev='{{ device }}' bus='virtio'/>
- </disk>
-{% endfor %}
-{% endif %}
-
-{% if 'scsi' in hostvars[hostname].install_cooked.disks %}
- <controller type='scsi' index='0' model='virtio-scsi'/>
-{% for device, lv in hostvars[hostname].install_cooked.disks.scsi.items() %}
- <disk type='block' device='disk'>
- <driver name='qemu' type='raw' cache='none' discard='unmap'/>
- <source dev='/dev/mapper/{{ lv.vg | replace('-', '--') }}-{{ lv.lv | replace('-', '--') }}'/>
- <target dev='{{ device }}' bus='scsi'/>
- </disk>
-{% endfor %}
-{% endif %}
-
-{% if hostvars[hostname].install_cooked.interfaces %}
-{% for if in hostvars[hostname].install_cooked.interfaces %}
- <interface type='bridge'>
- <source bridge='{{ if.bridge }}'/>
- <model type='virtio'/>
- <address type='pci' domain='0x0000' bus='0x01' slot='0x0{{ loop.index }}' function='0x0'/>
- </interface>
-{% endfor %}
-{% endif %}
-
- <serial type='pty'>
- <target port='0'/>
- </serial>
- <console type='pty'>
- <target type='serial' port='0'/>
- </console>
- </devices>
-</domain>
--- /dev/null
+---
+- name: preperations and sanity checks
+ hosts: "{{ hostname }}"
+ gather_facts: no
+ tasks:
+ - name: setup variables
+ set_fact:
+ network_cooked: "{{ network }}"
+ install_cooked: "{{ install }}"
+
+ - name: create temporary host group for vm host
+ changed_when: no
+ add_host:
+ name: "{{ install_cooked.host }}"
+ inventory_dir: "{{inventory_dir}}"
+ group: _vmhost_
+ # TODO: add some sanity checks
+
+- name: Update network configuration & shutdown VM
+ hosts: "{{ hostname }}"
+ roles:
+ - vm/network
+
+ post_tasks:
+ - name: Shutdown VM
+ async: 1
+ poll: 0
+ ignore_errors: true
+ shell: sleep 2 && poweroff
+
+- name: Update VM definition
+ hosts: _vmhost_
+ pre_tasks:
+ - name: Wait for VM to shut down
+ wait_for_virt:
+ name: "{{ hostname }}"
+ states: shutdown
+ timeout: 30
+
+ roles:
+ - vm/define
+
+- name: Apply the base vm/guest role
+ hosts: "{{ hostname }}"
+ pre_tasks:
+ - name: Wait for the VM to be up and running
+ wait_for_connection:
+ timeout: 120
+
+ roles:
+ - vm/guest