Infrastructure roles
These are examples of infrastructure roles for deployment of VM's and/or containers on proxmox.
As OS for these containers/VM's I'm using Rocky Linux.
The container template could be any linux, but I chose to standardize on Rocky.
For virtual machine deployments, I'm using coud-init enabled templates, so configuration is easy during deployment.
role_infrastructure_proxmox_lxc
This role consists of a number of files in the folowing directory structure:
.
├── defaults
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
└── tasks
└── main.yml
As you can see, this is a standard role layout within a collection. In fact is is a role from a collection I use for proxmox.
We will go over the files one-by-one:
README.md
As the name says, read this file to know ho the role works and what is does.
# infrastructure_lxc role
This role will create a lxc container on a proxmox node/cluster from variables given.
It registers the ip address in DNS, and then create the lxc container. The DNS is expected to be a linux based bind9 type dns-server, controlled by ansible.
The container is first created, then started, as the collection will issue an error like "VM with ID <number> not found on cluster" when using state 'started' on creation.
If rhaap has a dynamic inventory for the proxmox instances, the new node will automaticly
pop-up in the inventory.
The default action is to 'create' a new container, possiblew action are:
- create
- remove
- redeploy
- purge
'redeploy' will first remove the container and the 'create' a fresh instance of the same container.
## dependencies
This role needs the following collections/dependecies to run correctly:
### Collections
- community.general
- community.proxmox
- ansible.utils
Community.general and ansible.utils are needed for the upodates to be made on the bind nameserver(s).
Community.proxmox is obvious.
### python
- dnspython
- proxmoxer
- requests
- netaddr
### Extra
Added symlink /usr/bin/python3 to /usr/bin/python3.11 else the inventory will work in controller, but not in the playbook,
this way it will run the plugin correctly in the job.
## inventory vars
Be sure to have the following structure in the inventory of the play that calls this role and that they are part of the proxmox group:
group_vars/all/proxmox.yml
```yml
proxmox_node: proxmox01
proxmox_user: <use a crecential for root user>
proxmox_password: <use a credential for the root password>
proxmox_api_port: '8006'
netmask: '/24'
gateway: <gateway ip>
dns_server: <dns server ip address>
dns_domain: <your dns domain>
dns_rev_zone: <the reverse zone name>
dns_key: <the keyname for ddns>
dns_key_secret: < ddns secret>
dns_key_algorithm: <ddns secret algorithm>
dns_zonefile: <name of the zonefile in dns>
network_bridge: <network bridge name>
template_location: <the full path to the template directory like:'/mnt/pve/NAS-01/import/'>
cloud_init_location: <shared storage name>
default_root_passwd: <the default root password for your containers>
ssh_pub_key: <the public key to be inserted in authorized_keys>
qemu_storage: local-lvm
qemu_template: <the name of the cloudinit enables template for qemu vm like; Rocky-9-GenericCloud.latest.x86_64.qcow2>
lxc_storage: local
lxc_template: <template for lxc containers, like; lxc-rocky-ansible.tar.gz>
drn_net:
netmask: '/24'
network_bridge: <network bridge name>
network_vlan: <vlan>
````
Some of these variables are not used in this role, but will be used by the qemu role.
## survey vars
The extra vars (from survey or other source) for the lxc role to work, are as followes:
These are the vars that can be used at the moment, additional vars will be added later.
````yml
infrastructure_proxmox_action: <one of; 'create', 'remove', 'redeploy', 'purge'>
infrastructure_proxmox_lxc_proxmox_node: <the name of the proxmox node to work on (where the lxc lives)>
infrastructure_proxmox_lxc_host_name: <automaticly set to inventory_hostname>
infrastructure_proxmox_lxc_ip_adress: <ip address for the lxc container>
infrastructure_proxmox_lxc_host_id: <node_id on proxmox cluster, empty will generate one>
infrastructure_proxmox_lxc_cores: <number of cores to assign to container>
infrastructure_proxmox_lxc_disk_size: <integer with storage in GB to assign>
infrastructure_proxmox_lxc_disk_storage: <one of; 'local', 'local-lvm' or other known storage>
infrastructure_proxmox_lxc_template: <template file to use>
infrastructure_proxmox_lxc_memory_size: <memory in MB to allocate to the container>
infrastructure_proxmox_lxc_swap_size: <swap in MB to allocate to the container>
infrastructure_proxmox_lxc_auto_startup: <start the container as proxmox boots?>
````
## example
In rhaap we create a job-template that will fil in the following paramaters for the role:
````yml
infrastructure_proxmox_action: create
infrastructure_proxmox_lxc_host_name: test1.homelab
infrastructure_proxmox_lxc_ip_adress: 10.1.1.5
infrastructure_proxmox_lxc_host_id: 101
infrastructure_proxmox_lxc_cores: 2
infrastructure_proxmox_lxc_disk_size: 3
infrastructure_proxmox_lxc_disk_storage: local
infrastructure_proxmox_lxc_template: lxc-rocky.tar.gz
infrastructure_proxmox_lxc_memory_size: 32
infrastructure_proxmox_lxc_swap_size: 128
infrastructure_proxmox_lxc_auto_startup: true
````
The above example will create a host on proxmox with the name test1.homelab.
This role will do no further configuration on this lxc container, this should be done by other
plays.
## DNS issue
The serial we create for the bind nameserver is created using the years without the century, because named will find the serial out-of-range and thus will not load the new file.
For other actions, just fill the infrastructure_lxc_action with the correct value.
defaults/main.yml
The default values for variables are set here:
---
infrastructure_proxmox_lxc_template: "{{ template }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_host_id: "{{ id }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_cores: "{{ cpu_cores }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_disk_storage: "{{ disk_storage }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_disk_size: "{{ disk_size }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_proxmox_api_node: "{{ proxmox_api_node }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_proxmox_node: "{{ proxmox_node }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_action: create # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_auto_startup: true # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_swap_size: "{{ swap_size | default('0') }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_memory_size: "{{ memory_size | default('128') }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_ip_address: "{{ ip_address }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_dns_zonefile: "{{ dns_zone_file }}" # noqa: var-naming[no-role-prefix]
lxc_features: "{{ lxc_features | default('') }}" # noqa: var-naming[no-role-prefix]
lxc_unprivileged: "{{ lxc_unprivileged | default(true) }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_description: "{{ vm_description | default('# VM name') }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_wait_for_cloudinit: "{{ pause_cloud_init | default('20') }}" # noqa: var-naming[no-role-prefix]
meta/main.yml
---
# This file is required for the requirements.yaml mechanism to work
galaxy_info:
role_name: infrastructure_proxmox_lxc
namespace: wilcofolkers
description: This role creates a lxc container on proxmox
author: Wilco Folkers
license: BSD
min_ansible_version: '2.15'
platforms:
- name: EL
versions: [all]
tasks/main.yml
This is where the magic happens:
---
- name: Set the host_name variable
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_host_name: "{{ inventory_hostname }}"
- name: Excute block when infrastructure_proxmox_lxc_action is 'redeploy'
when: infrastructure_proxmox_lxc_action == "redeploy"
block:
- name: Set the vars for container re-creation
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_lxc_ip_address: "{{ proxmox_net0['ip'] | split('/') | first }}"
infrastructure_proxmox_lxc_host_id: "{{ id }}"
infrastructure_proxmox_lxc_proxmox_api_node: "{{ proxmox_api_node }}"
infrastructure_proxmox_lxc_proxmox_node: "{{ proxmox_node }}"
infrastructure_proxmox_lxc_disk_storage: "{{ proxmox_rootfs['disk_image'] | split(':') | first }}"
infrastructure_proxmox_lxc_disk_size: "{{ proxmox_rootfs['size'][:-1] }}"
infrastructure_proxmox_lxc_auto_startup: true
- name: Excute block when infrastructure_proxmox_lxc_action is 'create'
delegate_to: "{{ dns_server }}"
become: true
when: infrastructure_proxmox_lxc_action == "create"
block:
- name: Create new serial for DNS
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
_dns_serial: "{{ '%y%m%d%H%M' | strftime }}"
infrastructure_proxmox_lxc_ip_address: "{{ ip_address }}"
- name: Create container record in DNS
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.forward"
line: >-
{{ "{:23}".format(inventory_hostname.split('.') | first) }}
{{ "{:7}".format('A') }}
{{ infrastructure_proxmox_lxc_ip_address }}
regexp: "^{{ inventory_hostname.split('.') | first }} "
insertafter: EOF
owner: named
mode: '0644'
state: present
- name: Update Serial in DNS forward zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.forward"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Create reverse DNS entry for container
ansible.builtin.lineinfile: # noqa: jinja[spacing]
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.rev"
line: >-
{{ "{:23}".format(infrastructure_proxmox_lxc_ip_address.split('.') | last) }}
{{ "{:7}".format('PTR') }}
{{ inventory_hostname }}{{'.'}}
regexp: "^{{ infrastructure_proxmox_lxc_ip_address.split('.') | last }} "
insertafter: EOF
owner: named
mode: '0644'
state: present
- name: Update Serial in DNS reverse zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.rev"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Restart named to activate new record
ansible.builtin.service:
name: named
state: restarted
run_once: true
- name: Excute block when infrastructure_proxmox_lxc_action is 'remove' or 'purge'
delegate_to: "{{ dns_server }}"
become: true
when: >
infrastructure_proxmox_lxc_action == "remove" or
infrastructure_proxmox_lxc_action == "purge"
block:
- name: Create new serial for DNS
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
_dns_serial: "{{ '%y%m%d%H%M' | strftime }}"
- name: Remove container record from DNS
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.forward"
line: ''
regexp: "^{{ inventory_hostname.split('.') | first }} "
owner: named
mode: '0644'
state: absent
- name: Update Serial in DNS forward zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.forward"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Remove reverse DNS entry for container
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.rev"
line: ''
regexp: "^{{ infrastructure_proxmox_lxc_ip_address.split('.') | last }} "
owner: named
mode: '0644'
state: absent
- name: Update Serial in DNS reverse zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_lxc_dns_zonefile }}.rev"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Restart named to activate new record
ansible.builtin.service:
name: named
state: restarted
run_once: true
- name: Excute block when infrastructure_proxmox_lxc_action is 'remove, redeploy or purge'
delegate_to: localhost
when: >
infrastructure_proxmox_lxc_action == "remove" or
infrastructure_proxmox_lxc_action == "purge" or
infrastructure_proxmox_lxc_action == "redeploy"
block:
- name: Check if the container exists
community.proxmox.proxmox_vm_info:
api_host: "{{ infrastructure_proxmox_lxc_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
type: lxc
validate_certs: false
name: "{{ infrastructure_proxmox_lxc_host_name }}"
- name: Stop the container
community.proxmox.proxmox:
api_host: "{{ infrastructure_proxmox_lxc_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_lxc_proxmox_node }}"
vmid: "{{ infrastructure_proxmox_lxc_host_id }}"
hostname: "{{ infrastructure_proxmox_lxc_host_name }}"
validate_certs: false
timeout: 120
force: true
state: stopped
- name: Delete the container
community.proxmox.proxmox:
api_host: "{{ infrastructure_proxmox_lxc_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_lxc_proxmox_node }}"
vmid: "{{ infrastructure_proxmox_lxc_host_id }}"
hostname: "{{ infrastructure_proxmox_lxc_host_name }}"
validate_certs: false
state: absent
purge: true
- name: Excute block when infrastructure_proxmox_lxc_action is 'create or redeploy'
delegate_to: localhost
when: >
infrastructure_proxmox_lxc_action == "create" or
infrastructure_proxmox_lxc_action == "redeploy"
block:
- name: Create network interface dict with jinja
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
_net_if: >
{%- set net = dict() -%}
{%- if network_vlan is defined -%}
{%- set _ = net.update({'net0': 'name=eth0,gw=' + gateway + ',ip=' + infrastructure_proxmox_lxc_ip_address + netmask + ',bridge=' + network_bridge + ',tag=' + network_vlan}) -%}
{%- else -%}
{%- set _ = net.update({'net0': 'name=eth0,gw=' + gateway + ',ip=' + infrastructure_proxmox_lxc_ip_address + netmask + ',bridge=' + network_bridge}) -%}
{%- endif -%}
{%- if drn_interface is defined -%}
{%- if drn_net['network_vlan'] is defined -%}
{%- set _ = net.update({'net1': 'name=eth1,ip=' + drn_interface['ip_address'] + drn_net['netmask'] + ',bridge=' + drn_net['network_bridge'] + ',tag=' + drn_net['network_vlan']}) -%}
{%- else -%}
{%- set _ = net.update({'net1': 'name=eth1,ip=' + drn_interface['ip_address'] + drn_net['netmask'] + ',bridge=' + drn_net['network_bridge']}) -%}
{%- endif -%}
{%- endif -%}
{{ net }}
- name: Create the container from template
community.proxmox.proxmox:
api_host: "{{ infrastructure_proxmox_lxc_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_lxc_proxmox_node }}"
vmid: "{{ infrastructure_proxmox_lxc_host_id }}"
password: "{{ default_root_passwd }}"
hostname: "{{ infrastructure_proxmox_lxc_host_name }}"
description: |
"{{ infrastructure_proxmox_lxc_description }}"
cores: "{{ infrastructure_proxmox_lxc_cores }}"
disk: "{{ infrastructure_proxmox_lxc_disk_storage }}:{{ infrastructure_proxmox_lxc_disk_size }}"
nameserver: "{{ dns_servers }}"
searchdomain: "{{ dns_domain }}"
ostemplate: "{{ template_location }}:vztmpl/{{ infrastructure_proxmox_lxc_template }}"
netif:
"{{ _net_if }}"
onboot: "{{ infrastructure_proxmox_lxc_auto_startup | bool }}"
swap: "{{ infrastructure_proxmox_lxc_swap_size }}"
memory: "{{ infrastructure_proxmox_lxc_memory_size }}"
timezone: 'Europe/Amsterdam'
tags: "{{ tags }}"
unprivileged: "{{ lxc_unprivileged }}"
features: "{{ lxc_features }}"
update: true
validate_certs: false
state: present
- name: Let the cluster settle for a moment
ansible.builtin.pause:
seconds: 5
- name: Set swappiness for the container to 0
ansible.builtin.lineinfile:
path: "/etc/pve/lxc/{{ infrastructure_proxmox_lxc_host_id }}.conf"
line: 'lxc.cgroup.memory.swappiness: 0'
insertafter: EOF
owner: root
group: www-data
mode: '0640'
state: present
delegate_to: "{{ infrastructure_proxmox_lxc_proxmox_node }}.homelab"
become: true
- name: Start the container
community.proxmox.proxmox:
api_host: "{{ infrastructure_proxmox_lxc_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_lxc_proxmox_node }}"
vmid: "{{ infrastructure_proxmox_lxc_host_id }}"
hostname: "{{ infrastructure_proxmox_lxc_host_name }}"
validate_certs: false
state: started
- name: Wait for ssh to be ready
ansible.builtin.wait_for:
port: 22
host: "{{ infrastructure_proxmox_lxc_host_name }}"
delay: 10
state: started
timeout: 120
- name: Configure ssh certificate login
ansible.builtin.shell: # noqa: command-instead-of-shell
cmd: "pct exec {{ infrastructure_proxmox_lxc_host_id }} -- bash -c '{{ _command }}'"
changed_when: true
loop: "{{ cloud_commands }}"
loop_control:
loop_var: _command
become: true
delegate_to: "{{ infrastructure_proxmox_lxc_proxmox_node }}.homelab"
role_infrastructure_proxmox_qemu
The role to deploy a VM on proxmox has the following directory layout:
.
├── defaults
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
└── templates
└── cloud-init.yaml.j2
As you can see, this also conformes to the standard role directory layout, whitout the otherwise empty directories.
README.md
The README.md file has the following content, explaining the code and variables:
# role_infrastructure_proxmox_qemu
A role to install a cloudinit vm on a proxmox cluster.
The machine is initially configured with a cloudinit script, after configuration the cloudinit device is removed.
Further configuration is done through ansible playbooks you will have to write.
This role can handle LVM and non-LVM based templates.
## Execution environment
To use this role from this collection, it is best to build a custom execution environment with the following content:
### collection dependencies
The collections used for this role:
```yml
---
collections:
- community.general
- community.proxmox
- ansible.utils
````
### python dependecies
These collections have a number of python dependecies:
```yml
- requests
- dnspython
- netaddr
- proxmoxer
````
### symlink
Added symlink /usr/bin/python3 to /usr/bin/python3.11 else the inventory will work in controller, but not in the playbook,
this way it will run the plugin correctly in the job.
## inventory vars
Be sure to have the following structure in the inventory of the play that calls this role and that they are part of the proxmox group:
group_vars/all/proxmox.yml
```yml
proxmox:
proxmox_node: proxmox01
proxmox_user: <use a crecential for root user>
proxmox_password: <use a credential for the root password>
proxmox_api_port: '8006'
netmask: '/24'
gateway: <gateway ip>
dns_servers: <dns server ip address> # must be only one here! we configure more later
dns_domain: <your dns domain>
dns_rev_zone: <the reverse zone name>
dns_key: <the keyname for ddns>
dns_key_secret: < ddns secret>
dns_key_algorithm: <ddns secret algorithm>
network_bridge: <network bridge name>
template_location: <the full path to the template directory like:'/mnt/pve/NAS-01/import/'>
cloud_init_location: <shared storage name>
default_root_passwd: <the default root password for your containers>
ssh_pub_key: <the public key to be inserted in authorized_keys>
qemu_storage: local-lvm
qemu_template: <the name of the cloudinit enables template for qemu vm like; Rocky-9-GenericCloud.latest.x86_64.qcow2>
lxc_storage: local
lxc_template: <template for lxc containers, like; lxc-rocky-ansible.tar.gz>
drn_net:
netmask: '/24'
network_bridge: <network bridge name>
network_vlan: <vlan>
````
Some of these variables are not used in this role, but will be used by the lxc role.
## host vars
In the host_vars file in the inventory, specify the following:
```yml
host_name: target15.homelab
description: |
"
platform: Rocky Linux
contact: Wilco Folkers
"
ip_address: 192.168.2.135
cpu_cores: 2
memory_size: 2048
id: 435
drn_interface:
ip_address: 10.0.0.135
type: qemu
proxmox_node: proxmox02
root_disk_size: 30
os:
name: LNX_rhel
version: master
disks:
- name: scsi1
size: 10
vg: test
- name: scsi2
size: 20
vg: best
lvm:
- lv_name: log
mountpoint: /var
size: '10'
vg_name: test
- lv_name: app
mountpoint: /opt/app
size: '20'
vg_name: best
````
In the host_vars above, the following variables are optional:
- root_disk_size (default is 10G)
- disks (default no extra disks)
- lvm (mandatory when extra disks are defined)
## example
```yml
---
- name: Example playbook
hosts: target15.homelab
connection: local
gather_facts: false
vars:
infrastructure_proxmox_qemu_action: create
roles:
wf_linux.infra.role_infrastructure_proxmox_qemu
````
This will create the new vm on the proxmox2 node of the cluster...
defaults/main.yml
The defaults.yml holds all variable defaults for this role:
---
infrastructure_proxmox_qemu_template: "{{ template }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_id: "{{ id }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_cores: "{{ cpu_cores | default('2') }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_storage: "{{ disk_storage }}" # noqa: var-naming[no-role-prefix]
# infrastructure_proxmox_qemu_disk_size: 10 # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_action: create # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_auto_startup: true # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_memory_size: "{{ memory_size | default('2048') }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_ip_address: "{{ ip_address }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_proxmox_node: "{{ proxmox_node }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_proxmox_api_node: "{{ proxmox_api_node }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_dns_zonefile: "{{ dns_zone_file }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_description: "{{ vm_description | default('# VM name') }}" # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_wait_for_cloudinit: "{{ pause_cloud_init | default('20') }}" # noqa: var-naming[no-role-prefix]
meta/main.yml
The meta information for this role:
---
# This file is required for the requirements.yaml mechanism to work
galaxy_info:
role_name: infrastructure_proxmox_qemu
namespace: wilcofolkers
description: This role creates a cloudinit vm on proxmox
author: Wilco Folkers
license: BSD
min_ansible_version: '2.15'
platforms:
- name: EL
versions: [all]
tasks/main.yml
This is the code that will deploy a VM on proxmox, using variables and a cloud enabled template.
First the system is registered in DNS, so it will tranlate correctly.
---
- name: Set the host_name variable
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qemu_host_name: "{{ inventory_hostname }}"
- name: Excute block when infrastructure_proxmox_qemu_action is 'redeploy'
when: infrastructure_proxmox_qemu_action == "redeploy"
block:
- name: Get network setings
ansible.builtin.setup:
gather_subset:
- 'default_ipv4'
- name: Set the vars for host re-creation
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
infrastructure_proxmox_qwmu_ip_address: "{{ ansible_facts['default_ipv4']['address'] }}"
infrastructure_proxmox_qemu_id: "{{ proxmox_vmid }}"
infrastructure_proxmox_qemu_proxmox_node: "{{ proxmox_node }}"
infrastructure_proxmox_qemu_proxmox_api_node: "{{ proxmox_api_node }}"
infrastructure_proxmox_qemu_disk_storage: "{{ proxmox_scsi0['disk_image'] | split(':') | first }}"
infrastructure_proxmox_qemu_disk_size: "{{ proxmox_scsi0['size'][:-1] }}"
infrastructure_proxmox_qemu_auto_startup: true
- name: Excute block when infrastructure_proxmox_qemu_action is 'create'
delegate_to: "{{ dns_server }}"
become: true
when: infrastructure_proxmox_qemu_action == "create"
block:
- name: Create new serial for DNS
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
_dns_serial: "{{ '%y%m%d%H%M' | strftime }}"
infrastructure_proxmox_qemu_ip_address: "{{ ip_address }}"
- name: Create host record in DNS
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.forward"
line: >-
{{ "{:23}".format(inventory_hostname.split('.') | first) }}
{{ "{:7}".format('A') }}
{{ infrastructure_proxmox_qemu_ip_address }}
regexp: "^{{ inventory_hostname.split('.') | first }} "
insertafter: EOF
owner: named
mode: '0644'
state: present
- name: Update Serial in DNS forward zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.forward"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Create reverse DNS entry for host
ansible.builtin.lineinfile: # noqa: jinja[spacing]
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.rev"
line: >-
{{ "{:23}".format(infrastructure_proxmox_qemu_ip_address.split('.') | last) }}
{{ "{:7}".format('PTR') }}
{{ inventory_hostname }}{{'.'}}
regexp: "^{{ infrastructure_proxmox_qemu_ip_address.split('.') | last }} "
insertafter: EOF
owner: named
mode: '0644'
state: present
- name: Update Serial in DNS reverse zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.rev"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Restart named to activate new record
ansible.builtin.service:
name: named
state: restarted
run_once: true
- name: Excute block when infrastructure_proxmox_qemu_action is 'remove' or 'purge'
delegate_to: "{{ dns_server }}"
become: true
when: >
infrastructure_proxmox_qemu_action =="remove" or
infrastructure_proxmox_qemu_action == "purge"
block:
- name: Create new serial for DNS
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
_dns_serial: "{{ '%y%m%d%H%M' | strftime }}"
- name: Remove host record from DNS
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.forward"
line: ''
regexp: "^{{ inventory_hostname.split('.') | first }} "
owner: named
mode: '0644'
state: absent
- name: Update Serial in DNS forward zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.forward"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Remove reverse DNS entry for host
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.rev"
line: ''
regexp: "^{{ infrastructure_proxmox_qemu_ip_address.split('.') | last }} "
owner: named
mode: '0644'
state: absent
- name: Update Serial in DNS reverse zone
ansible.builtin.lineinfile:
path: "/var/named/{{ infrastructure_proxmox_qemu_dns_zonefile }}.rev"
line: >-
{{ "{:31}".format(' ') }}
{{ _dns_serial }}
{{ "{:>10}".format('; serial') }}
regexp: "serial$"
owner: named
mode: '0644'
state: present
- name: Restart named to activate new record
ansible.builtin.service:
name: named
state: restarted
run_once: true
- name: Excute block when infrastructure_proxmox_qemu_action is 'remove, redeploy or purge'
delegate_to: localhost
when: >
infrastructure_proxmox_qemu_action =="remove" or
infrastructure_proxmox_qemu_action =="purge" or
infrastructure_proxmox_qemu_action =="redeploy"
block:
- name: Check if the machine exists
community.proxmox.proxmox_vm_info:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
type: qemu
validate_certs: false
name: "{{ infrastructure_proxmox_qemu_host_name }}"
- name: Stop the host
community.proxmox.proxmox_kvm:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_qemu_proxmox_node }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
name: "{{ infrastructure_proxmox_qemu_host_name }}"
validate_certs: false
force: true
state: stopped
- name: Delete the host
community.proxmox.proxmox_kvm:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: '{{ proxmox_password }}'
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_qemu_proxmox_node }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
name: "{{ infrastructure_proxmox_qemu_host_name }}"
validate_certs: false
state: absent
- name: Excute block when infrastructure_proxmox_qemu_action is 'create or redeploy'
delegate_to: localhost
when: >
infrastructure_proxmox_qemu_action =="create" or
infrastructure_proxmox_qemu_action =="redeploy"
block:
- name: Check if the machine exists
community.proxmox.proxmox_vm_info: # noqa: var-naming[no-role-prefix]
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
name: "{{ infrastructure_proxmox_qemu_host_name }}"
register: _vm_info_list
- name: Fail the play if node exists
ansible.builtin.fail:
msg: "Node {{ infrastructure_proxmox_qemu_host_name }} allready exists"
when: host_name in _vm_info_list
- name: Upload Cloudinit user-data file
ansible.builtin.template:
src: cloud-init.yaml.j2
dest: "/mnt/pve/NAS-01/snippets/{{ infrastructure_proxmox_qemu_id }}-cloudinit.yaml"
mode: "0644"
delegate_to: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
become: true
- name: Create network interface dict with jinja
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
net_if: >
{%- set net = dict() -%}
{%- if network_vlan is defined -%}
{%- set _ = net.update({'net0': 'virtio,bridge=' + network_bridge + ',tag=' + network_vlan + ',firewall=1'}) -%}
{%- else -%}
{%- set _ = net.update({'net0': 'virtio,bridge=' + network_bridge + ',firewall=1'}) -%}
{%- endif -%}
{%- if drn_interface is defined -%}
{%- if drn_net['network_vlan'] is defined -%}
{%- set _ = net.update({'net1': 'virtio,bridge=' + drn_net['network_bridge'] + ',tag=' + drn_net['network_vlan'] + ',firewall=1'}) -%}
{%- else -%}
{%- set _ = net.update({'net1': 'virtio,bridge=' + drn_net['network_bridge'] + ',firewall=1'}) -%}
{%- endif -%}
{%- endif -%}
{{ net }}
- name: Create network ipconfig dict with jinja
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
net_ip: >
{%- set netip = dict() -%}
{%- set _ = netip.update({'ipconfig0': 'ip=' + infrastructure_proxmox_qemu_ip_address~netmask + ',gw=' + gateway }) -%}
{%- if drn_interface is defined -%}
{%- set _ = netip.update({'ipconfig1': 'ip=' + drn_interface['ip_address'] + drn_net['netmask']}) -%}
{%- endif -%}
{{ netip }}
- name: Create VM
community.proxmox.proxmox_kvm:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
node: "{{ infrastructure_proxmox_qemu_proxmox_node }}"
# Basic VM info
vmid: "{{ infrastructure_proxmox_qemu_id }}"
name: "{{ infrastructure_proxmox_qemu_host_name }}"
description: |
"{{ infrastructure_proxmox_qemu_description }}"
ostype: l26 # See https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_kvm_module.html
# Hardware info
memory: "{{ infrastructure_proxmox_qemu_memory_size | default('2048') }}"
cores: "{{ infrastructure_proxmox_qemu_cores | default('2') }}"
cpu: host
scsihw: virtio-scsi-pci
ide:
ide2: "{{ infrastructure_proxmox_qemu_storage }}:cloudinit,format=raw"
vga: qxl
boot: order=scsi0
net:
"{{ net_if }}"
ipconfig:
"{{ net_ip }}"
citype: nocloud
cicustom: "user={{ cloud_init_location }}:snippets/{{ infrastructure_proxmox_qemu_id }}-cloudinit.yaml"
agent: "enabled=1"
tags: "{{ tags }}"
validate_certs: false
# Desired state
state: present
# Import the cloud-init disk from the template VM
- name: Import cloud-init disk
community.proxmox.proxmox_disk:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
disk: scsi0
import_from: "{{ template_location }}{{ template }}"
storage: "{{ infrastructure_proxmox_qemu_storage }}"
format: raw
validate_certs: false
state: present
- name: Start VM and run auto config (cloudinit)
community.proxmox.proxmox_kvm:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
validate_certs: false
state: started
- name: Wait for ssh to be ready
ansible.builtin.wait_for:
port: 22
host: "{{ infrastructure_proxmox_qemu_host_name }}"
delay: 10
state: started
timeout: 120
- name: Sleep for a number of seconds
ansible.builtin.pause:
seconds: "{{ infrastructure_proxmox_qemu_wait_for_cloudinit }}"
- name: Check the cloud-init status # noqa: var-naming[no-role-prefix]
ansible.builtin.raw: /usr/bin/cloud-init status
register: _cloudinit
until: |
(_cloudinit.stdout is defined) and
("'done' in _cloudinit.stdout")
retries: 10
delay: 10
delegate_to: "{{ inventory_hostname }}"
become: true
changed_when: false
- name: Add the lvm packages # noqa: command-instead-of-module
ansible.builtin.command:
argv:
- /usr/bin/dnf
- install
- lvm2
- --assumeyes
changed_when: true
become: true
delegate_to: "{{ inventory_hostname }}"
- name: Grow root disk
community.proxmox.proxmox_disk: # noqa: var-naming[no-role-prefix]
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
validate_certs: false
disk: scsi0
size: "{{ root_disk_size }}G"
state: resized
register: _resize_root
when:
- root_disk_size is defined
- name: Re-read partition table on /dev/sda
ansible.builtin.raw: /usr/sbin/sgdisk -e /dev/sda;/usr/sbin/partprobe /dev/sda # noqa: var-naming[no-role-prefix]
become: true
changed_when: false
delegate_to: "{{ inventory_hostname }}"
- name: Select last partition on /dev/sda
ansible.builtin.raw: /usr/sbin/parted /dev/sda print|awk NF |tail -1|cut -d' ' -f2 # noqa: var-naming[no-role-prefix]
become: true
changed_when: false
register: _partnum
delegate_to: "{{ inventory_hostname }}"
- name: Resize the root partition # noqa: no-handler
ansible.builtin.raw: "growpart /dev/sda {{ _partnum.stdout | quote }}"
become: true
changed_when: true
when: _resize_root.changed
delegate_to: "{{ inventory_hostname }}"
- name: Resize xfs filesystem when not a LVM based system # noqa: no-handler
ansible.builtin.raw: "xfsgrow_fs /dev/sda{{ _partnum.stdout | quote }}"
become: true
changed_when: true
when:
- _resize_root.changed
- hostvars[inventory_hostname]['ansible_facts']['devices']['dm-0'] is not defined
delegate_to: "{{ inventory_hostname }}"
- name: Get LV name for root
ansible.builtin.raw: /usr/sbin/lvs | grep root # noqa: var-naming[no-role-prefix]
become: true
changed_when: false
register: _lv_list
delegate_to: "{{ inventory_hostname }}"
- name: Resize root LV in case of LVM
ansible.builtin.raw: "lvextend -r -l +100%FREE /dev/mapper/{{ _lv_list.stdout.split()[1] | quote }}-{{ _lv_list.stdout.split()[0] | quote }}"
become: true
changed_when: true
when:
- _resize_root.changed
- hostvars[inventory_hostname]['ansible_facts']['devices']['dm-0'] is defined
delegate_to: "{{ inventory_hostname }}"
- name: Create new disk in VM (do not rewrite in case it exists already)
community.proxmox.proxmox_disk:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
validate_certs: false
disk: "{{ disk['name'] }}"
backup: true
cache: none
storage: "{{ infrastructure_proxmox_qemu_storage }}"
size: "{{ disk['size'] }}"
state: present
loop: "{{ disks }}"
loop_control:
loop_var: disk
when: disks is defined
- name: Stop VM after config
community.proxmox.proxmox_kvm:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
validate_certs: false
state: stopped
# Remove cloudinit disk and reboot
- name: Remove cdrom device
community.proxmox.proxmox_disk:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
disk: ide2
validate_certs: false
state: absent
- name: Start the vm again
community.proxmox.proxmox_kvm:
api_host: "{{ infrastructure_proxmox_qemu_proxmox_api_node }}"
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password | default(omit) }}"
api_port: "{{ proxmox_api_port }}"
vmid: "{{ infrastructure_proxmox_qemu_id }}"
validate_certs: false
state: started
- name: Wait for ssh to be ready
ansible.builtin.wait_for:
port: 22
host: "{{ infrastructure_proxmox_qemu_host_name }}"
delay: 10
state: started
timeout: 120
- name: Sleep for 10 secs to let host start, before next step
ansible.builtin.pause:
seconds: 10
templates/cloud-init.yaml.j2
This is where you insert the configuration into the VM template.
#cloud-config
{% if rh_subscription is defined %}
rh_subscription:
username: {{ rh_subscription.username }}
password: {{ rh_subscription.password }}
{% endif %}
hostname: {{ infrastructure_proxmox_qemu_host_name }}
timezone: 'Europe/Amsterdam'
users:
- name: ansible
sudo: "ALL=(ALL) NOPASSWD :ALL"
ssh_authorized_keys:
- {{ ssh_pub_key }}
manage_etc_hosts: localhost
fqdn: {{ infrastructure_proxmox_qemu_host_name }}
chpasswd:
expire: False
config:
- type: physical
name: eth0
- type: nameserver
address:
- {{ dns_servers }}
search:
- {{ dns_domain }}
{% if cloud_packages is defined %}
packages: {{ cloud_packages }}
{% endif %}
package_update: false
package_upgrade: false
ssh_pwauth: false
{% if cloud_commands is defined %}
cloud_config_modules:
- runcmd
cloud_final_modules:
- scripts-user
runcmd: {{ cloud_commands }}
{% endif %}
role_infrastructure_lvm
Always add additional filesystems as LVM devices, this simplifies management. This role will create the LVM devices and mountpoints, it ensures the order of creation and mount ing is correct. This play will work on almost any linux distribution.
README.md
#Infrastructure LVM role
This playbook reads the LVM (<http://www.tldp.org/HOWTO/LVM-HOWTO/>) definition from the host variables of an instance and sets up the physical volumes, volume groups and logical volumes accordingly.
General rule for this role: you can only use this playbook to *create* and *extend* disk space in logical volumes. Shrinking and removing disks is **not** supported.
__Requirements__
This playbook cannot be called without a defined `instances` variable.
If the variable `disks` is not defined in the hostvars of vm, the play will end.
If `disks` is present, `lvm` is also expected and the play will continue. If `lvm` is not present and `lvm` is, the play will fail.
The minimal version to run this playbook is ANSIBLE 2.8, as some tasks use options that are not availliable in lower versions of ansible.
__Usage__
In order for this playbook to start making changes, the following items are required:
an `instances` variable (list), filled with hostnames in FQDN, in example:
* node1.domain
* node2.domain
An LVM definition in the host variables (additional disks and pv/vg/lv definitions), in example:
```yml
disks:
- size: 110
vg: oracle
hostname: ora-hb-1.localdomain
lvm:
- lv_name: u01
mountpoint: /u01
size: '40'
vg_name: oracle
- lv_name: oradata
mountpoint: /u01/app/oracle/oradata
size: '20'
vg_name: oracle
- lv_name: orafra
mountpoint: /u01/app/oracle/fast_recovery_area
size: '30'
vg_name: oracle
- lv_name: oraredo
mountpoint: /u01/app/oracle/oraredo
size: '20'
vg_name: oracle
````
The definition snippet above will result in:
* one disks of 110GB attached to the virtual machine, all partitioned to contain a single physical volume
* one volume group (oracle on the physical volume)
* four logical volumes that use 100% of the volume group size (oracle_u01 with size 40GB, oracle_oradata with size 20GB, oracle_oraredo with size 20GB and oracle_orafra with size of 30GB)
* A formatted XFS filesystem on all logical volumes
* Creation of mount points on root filesystem
* Modification of /etc/fstab so that the volumes are mounted on boot
* All logical volumes are mounted on the specified mountpoint
The disk size can be:
* 100%: The logical volume will use all available space in the volume group. When this setting is used the volume group **must** contain only **one** logical volume
* an integer: the created logical volume will have a disk size of the provided integer in GB's. So if size is defined as followed: `"size": 2` then the resulting logical volume will have a size of 2GB.
The mountpoint is the location on the root filesystem where the disk will be mounted (in example, /data).
###### Mandatory keys
Both the disks and the lvm keys and their definitions are mandatory for this playbook to work. The minimal working definition consists of a single disk and a single LV on that disk.
__Role Variables__
This playbook does not use default variables.
__Dependencies__
This playbook is not dependent on Ansible Galaxy roles.
__License__
tbd
__Author Information__
Wilco Folkers
meta/main.yml
---
# This file is required for the requirements.yaml mechanism to work
galaxy_info:
role_name: infrastructure_lvm
namespace: wilcofolkers
description: This creates the LVM disks for the vm
author: Wilco Folkers
license: BSD
min_ansible_version: '2.9'
platforms:
- name: EL
versions: [all]
tasks/main.yml
The playbook of this role that will take the definitions from the host_vars and create LVM devices and mountpoints.
It will then mount all devices as requested in the correct order.
---
- name: Run as block
when:
- disks is defined
- lvm is defined
block:
#
# Define the list of disk names. Within the VMWare definition it is not possible to define disks by name, so we need to have
# the correlation somewhere. The N'th additional disk in instance defintion has name diskname[N], so the first disk has name
# /dev/sdb, and so on
#
- name: Define list of disk names
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
disknames:
- '/dev/sdb'
- '/dev/sdc'
- '/dev/sdd'
- '/dev/sde'
- '/dev/sdf'
- '/dev/sdg'
- '/dev/sdh'
- '/dev/sdi'
- '/dev/sdj'
- '/dev/sdk'
- '/dev/sdl'
- '/dev/sdm'
- '/dev/sdn'
- '/dev/sdo'
- '/dev/sdp'
#
# Create LVM partitions on the additional disks. with_indexed items returns the index of the item and the item itself, so
# single_disk wil contain:
#
# single_disk.0 = index value (0 for the first additional disk, 1 for the second, and so on)
# single_disk.1 = disk list item (not used here, but used later in the play)
#
- name: Use parted to create LVM partitions of all referenced disks on this host
become: true
loop_control:
loop_var: single_disk
community.general.parted:
device: "{{ disknames[single_disk.0] }}"
number: 1
flags: [lvm]
state: present
with_indexed_items:
- "{{ disks }}"
- name: Create dictionary string with all referenced volume groups with their disks
ansible.builtin.set_fact: # noqa: var-naming[no-role-prefix]
_vgs: |-
{%- for d in disks -%}
{{ d['vg'] }},{{ disknames[loop.index0] }}1
{%- if not loop.last -%};
{%- endif -%}
{%- endfor -%}
- name: Get the list of already created vgs
ansible.builtin.command: 'lsblk -lno NAME'
register: _active_vgs # noqa: var-naming[no-role-prefix]
changed_when: false
- name: Create the VG's
become: true
community.general.lvg:
vg: "{{ lv.split(',')[0] }}"
pvs: "{{ lv.split(',')[-1] }}"
loop: "{{ _vgs | split(';') }}"
loop_control:
loop_var: lv
when: lv.split(',')[0] not in _active_vgs.stdout
#
# Create the lv's in the inventory file
#
- name: Get active_mountpoints
ansible.builtin.command: 'lsblk -lno MOUNTPOINT'
register: _active_mounts # noqa: var-naming[no-role-prefix]
changed_when: false
- name: Run as block using admin rights
become: true
block:
- name: Create logical volume and update them when they are defined as 100%
community.general.lvol:
lv: "{{ lv_lvol.lv_name }}"
shrink: false
size: |-
{%- if lv_lvol.size == '100%' -%}100%VG{%- else -%}{{ lv_lvol.size }}G{%- endif -%}
vg: "{{ lv_lvol.vg_name }}"
loop_control:
loop_var: lv_lvol
loop: "{{ lvm }}"
when: lv_lvol.mountpoint not in _active_mounts.stdout
#
# Format volume. vg is the volume group name, single_lv.key is the lv name. Resize the filesystem when required (only
# relevant on updates)
#
- name: Format the logical volume
community.general.filesystem:
dev: "/dev/{{ lv_fmt.vg_name }}/{{ lv_fmt.lv_name }}"
force: false
fstype: xfs
resizefs: true
loop_control:
loop_var: lv_fmt
loop: "{{ lvm }}"
when: lv_fmt.mountpoint not in _active_mounts.stdout
#
# Mount volume
#
- name: Mount the logical volume
ansible.posix.mount:
fstype: xfs
path: "{{ lv_mnt.mountpoint }}"
src: "/dev/{{ lv_mnt.vg_name }}/{{ lv_mnt.lv_name }}"
state: mounted
loop_control:
loop_var: lv_mnt
loop: "{{ lvm }}"
when: lv_mnt.mountpoint not in _active_mounts.stdout