Ansible: VMware offline Snapshot erstellen

Wenn man mit Ansible VMware VMs automatisch aktualisiert wäre es nützlich, wenn man zuvor auch automatisch einen offline Snapshot (im ausgeschalteten Zustand) machen könnte. Die Erstellung des Snapshots im ausgeschalteten Zustand stellt die Konsistenz sicher. Auch wenn man einen Snapshot mit Memory Dump online machen könnte, bevorzuge ich immer die offline Methode.

Mittels Ansible lässt sich der ganze Prozess komplett automatisieren.

Der Prozess im Ansible play sieht folgendermassen aus:

  • Ansible fährt den Host herunter und wartet bis er im VMware OFF ist
  • Ein VM Snapshot wird erstellt
  • Der Host wird wieder hochgefahren
  • Warten bis der Service wieder online ist
  • Update Prozess starten

Zunächst wird ein neuer Task ansible/tasks/vmware-offline-snapshot.yml erstellt, welcher dann im Playbook geladen werden kann:

---

- name: Make sure all the required variables are set
  ansible.builtin.assert:
    that:
      - vcenter_hostname is defined
      - vcenter_username is defined
      - vcenter_password is defined


- name: Stop application services
  ansible.builtin.systemd:
    name: "{{ services_to_stop }}"
    state: stopped
  loop: "{{ services_to_stop | default([]) }}"


- name: Trigger guest shutdown via vCenter
  vmware.vmware.vm_powerstate:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ vcenter_datacenter }}"
    name: "{{ ansible_facts['hostname'] }}"
    state: shutdown-guest
    validate_certs: false
  register: __vm_shutdown
  delegate_to: localhost
  become: false

- name: Wait for VM to be powered off
  community.vmware.vmware_guest_info:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    name: "{{  ansible_facts['hostname'] }}"
    validate_certs: false
  delegate_to: localhost
  become: false
  register: vm_info
  retries: 30
  delay: 10
  until: (vm_info.instance.hw_power_status | default('')) == "poweredOff"
  when: ( __vm_shutdown.changed | default(false) ) and not ansible_check_mode

    
- name: Get current snapshots
  community.vmware.vmware_guest_snapshot_info:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ vcenter_datacenter }}"
    folder: "/{{ vcenter_datacenter }}/vm"
    name: "{{ ansible_facts['hostname'] }}"
    validate_certs: false
  delegate_to: localhost
  become: false
  register: snapshot_info

- name: Remove existing snapshot if overwrite is enabled
  vmware.vmware.vm_snapshot:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ vcenter_datacenter }}"
    folder: "/{{ vcenter_datacenter }}/vm"
    name: "{{ ansible_facts['hostname'] }}"
    state: absent
    snapshot_name: "{{ vcenter_snapshot_name }}"
    validate_certs: false
  delegate_to: localhost
  become: false
  when: 
    - vcenter_overwrite_snapshot | default(false) | bool
    - vcenter_snapshot_name in (snapshot_info.guest_snapshots.snapshots | default([]) | map(attribute='name') | list)

- name: Create snapshot of stopped VM
  vmware.vmware.vm_snapshot:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ vcenter_datacenter }}"
    folder: "/{{ vcenter_datacenter }}/vm"
    name: "{{ ansible_facts['hostname'] }}"
    state: present
    snapshot_name: "{{ vcenter_snapshot_name }}"
    description: "{{ vcenter_snapshot_description }}"
    validate_certs: false
  delegate_to: localhost
  become: false
  register: snapshot_result


- name: Power on VM
  vmware.vmware.vm_powerstate:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ vcenter_datacenter }}"
    name: "{{  ansible_facts['hostname'] }}"
    state: powered-on
    validate_certs: false
  delegate_to: localhost
  become: false
  register: __vm_poweron


# Optional
- name: Wait until Website answers after the update
  ansible.builtin.uri:
    url: "{{ website_url }}"
    method: GET
    follow_location: true
    status_code: 200
    validate_certs: false
    use_proxy: false
  delegate_to: localhost
  become: false
  register: __http_postcheck
  retries: 30
  delay: 10
  until: (__http_postcheck.content | default('')) is search('Willkomen', ignorecase=true)
  changed_when: false
  when: ( __vm_poweron.changed | default(false) ) and not ansible_check_mode

In ansible/group_vars/all/vmware.yml kommt nun die Konfiguration:

---

vcenter_hostname: "vcenter.example.com"
vcenter_username: "ansible@vsphere.local"
vcenter_datacenter: "MYDC"

vcenter_snapshot_name: "ansible-before-updates"
vcenter_snapshot_description: "Before automatic update with Ansible"
vcenter_overwrite_snapshot: true

Die VMware vCenter Zugangsdaten werden natürlich im ansible-vault abgelegt:

ansible-vault create ansible/group_vars/all/vmware.secrets.yml
vcenter_password: "secret"

Nun noch das Playbook dazu machen:

- name: Set downtime and make snapshots prior to GitLab update
  hosts: gitlab
  pre_tasks:
    - name: Determine whether packages would get updated
      ansible.builtin.package:
        name: '*'
        state: latest
      register __update_packages
      check_mode: false
      
    - name: Make offline snapshots when update would happen
      when: __update_packages.changed | default(false) | bool
      block:
        - import_tasks: tasks/vmware-offline-snapshot.yml

  tasks:
    - name: Updated all packages
      ansible.builtin.package:
        name: '*'
        state: latest

Published by

Steven Varco

Steven ist ein Redhat RHCE- und Kubernetes CKA Zertifizierter Linux-Crack und ist seit über 20 Jahren sowohl beruflich wie auch privat auf Linux spezialisiert. In seinem Keller steht ein Server Rack mit diversen ESX und Linux Servern.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert