Files
RHCE/create-lab.yml
2026-01-25 08:31:56 -07:00

576 lines
18 KiB
YAML

---
###############################################################################
# Combined Ansible Playbook
###############################################################################
###############################################################################
# Play 1: Deploy multiple KubeVirt VMs from Block PVC template with cloud-init
###############################################################################
- name: Deploy multiple VMs from Block template PVC with cloud-init ISO
hosts: localhost
gather_facts: false
collections:
- kubernetes.core
vars:
namespace: default
vm_domain: lab.example.com
rootdisk_size: 64Gi
disk2_size: 2Gi
disk3_size: 1Gi
vm_list:
- name: controller
ip: 10.4.0.100
source_pvc: rhce-template
- name: node1
ip: 10.4.0.101
source_pvc: rhce-template
- name: node2
ip: 10.4.0.102
source_pvc: rhce-template
- name: node3
ip: 10.4.0.103
source_pvc: rhce-template
- name: node4
ip: 10.4.0.104
source_pvc: rhce-template
- name: node5
ip: 10.4.0.105
source_pvc: rhce-template
- name: utility
ip: 10.4.0.106
source_pvc: utility-template
tasks:
###########################################################################
# Create PVCs
###########################################################################
- name: Create rootdisk PVC from template
k8s:
state: present
definition:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: "{{ item.name }}-rootdisk"
namespace: "{{ namespace }}"
spec:
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
accessModes:
- ReadWriteMany
volumeMode: Block
resources:
requests:
storage: "{{ rootdisk_size }}"
dataSource:
name: "{{ item.source_pvc }}"
kind: PersistentVolumeClaim
apiGroup: ""
loop: "{{ vm_list }}"
loop_control:
label: "{{ item.name }}"
- name: Create disk2 PVC (2Gi)
k8s:
state: present
definition:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: "{{ item.name }}-disk2"
namespace: "{{ namespace }}"
spec:
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
accessModes:
- ReadWriteMany
volumeMode: Block
resources:
requests:
storage: "{{ disk2_size }}"
loop: "{{ vm_list }}"
loop_control:
label: "{{ item.name }}-disk2"
- name: Create disk3 PVC (1Gi)
k8s:
state: present
definition:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: "{{ item.name }}-disk3"
namespace: "{{ namespace }}"
spec:
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
accessModes:
- ReadWriteMany
volumeMode: Block
resources:
requests:
storage: "{{ disk3_size }}"
loop: "{{ vm_list }}"
loop_control:
label: "{{ item.name }}-disk3"
###########################################################################
# Wait for PVCs
###########################################################################
- name: Wait for all PVCs to be bound
k8s_info:
api_version: v1
kind: PersistentVolumeClaim
name: "{{ item.0.name }}-{{ item.1 }}"
namespace: "{{ namespace }}"
register: pvc_status
until: pvc_status.resources[0].status.phase == "Bound"
retries: 30
delay: 5
loop: "{{ vm_list | product(['rootdisk', 'disk2', 'disk3']) | list }}"
loop_control:
label: "{{ item.0.name }}-{{ item.1 }}"
###########################################################################
# Create VirtualMachines
###########################################################################
- name: Create VirtualMachine with additional raw disks
k8s:
state: present
definition:
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: "{{ item.name }}"
namespace: "{{ namespace }}"
spec:
running: true
template:
metadata:
labels:
kubevirt.io/domain: "{{ item.name }}"
spec:
domain:
cpu:
cores: 1
resources:
requests:
memory: 2Gi
devices:
disks:
- name: rootdisk
disk:
bus: virtio
- name: disk2
disk:
bus: virtio
- name: disk3
disk:
bus: virtio
- name: cloudinitdisk
disk:
bus: virtio
interfaces:
- name: default
bridge: {}
networks:
- name: default
multus:
networkName: rhce
volumes:
- name: rootdisk
persistentVolumeClaim:
claimName: "{{ item.name }}-rootdisk"
- name: disk2
persistentVolumeClaim:
claimName: "{{ item.name }}-disk2"
- name: disk3
persistentVolumeClaim:
claimName: "{{ item.name }}-disk3"
- name: cloudinitdisk
cloudInitNoCloud:
hostname: "{{ item.name }}"
fqdn: "{{ item.name }}.{{ vm_domain }}"
manage_etc_hosts: true
networkData: |
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses:
- "{{ item.ip }}/24"
gateway4: 10.4.0.1
nameservers:
search:
- "{{ vm_domain }}"
addresses:
- 10.1.0.1
userData: |
#cloud-config
users:
- name: redhat
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: false
chpasswd:
list: |
redhat:redhat
expire: false
ssh_pwauth: true
user: redhat
password: redhat
loop: "{{ vm_list }}"
loop_control:
label: "{{ item.name }}"
###############################################################################
# Play 2: Add static DNS entries to dnsmasq on OPNsense
###############################################################################
- name: Add static DNS entries to dnsmasq on OPNsense
hosts: opnsense.lab.cudanet.org
become: true
remote_user: root
gather_facts: false
vars:
ansible_python_interpreter: /usr/local/bin/python3
dnsmasq_hosts_file: /usr/local/etc/dnsmasq.conf.d/lab.conf
vms:
- ip: "10.4.0.100"
hostname: "controller.lab.example.com"
- ip: "10.4.0.101"
hostname: "node1.lab.example.com"
- ip: "10.4.0.102"
hostname: "node2.lab.example.com"
- ip: "10.4.0.103"
hostname: "node3.lab.example.com"
- ip: "10.4.0.104"
hostname: "node4.lab.example.com"
- ip: "10.4.0.105"
hostname: "node5.lab.example.com"
- ip: "10.4.0.106"
hostname: "utility.lab.example.com"
tasks:
- name: Ensure dnsmasq hosts file exists
file:
path: "{{ dnsmasq_hosts_file }}"
state: touch
owner: root
group: wheel
mode: "0644"
- name: Add static DNS entries to dnsmasq hosts file
lineinfile:
path: "{{ dnsmasq_hosts_file }}"
line: "address=/{{ item.hostname }}/{{ item.ip }}"
state: present
create: yes
backup: yes
loop: "{{ vms }}"
- name: Reload dnsmasq service
ansible.builtin.shell: pluginctl dns
- name: Ping each host from OPNsense to verify connectivity
ansible.builtin.shell: ping -c 3 {{ item.ip }}
register: ping_result
ignore_errors: yes
loop: "{{ vms }}"
- name: Show ping results
debug:
msg: |
Ping to {{ item.item.hostname }} returned (rc={{ item.rc }}):
{{ item.stdout }}
loop: "{{ ping_result.results }}"
###############################################################################
# Play 3: Register system, configure services, and mirror EE to local registry
###############################################################################
- name: Register system, configure services, and mirror EE to local registry
hosts: utility
become: true
vars:
sat_user: "{{ vault_sat_user }}"
sat_passwd: "{{ vault_sat_passwd }}"
sat_orgid: "{{ vault_sat_orgid }}"
redhat_env: "{{ vault_redhat_env }}"
registry_host: utility.lab.example.com
registry_port: 5000
host_port: 5000
registry_image: docker.io/library/registry:2
podman_user: "{{ vault_podman_user }}"
podman_passwd: "{{ vault_podman_passwd }}"
ee_source_image: registry.redhat.io/ansible-automation-platform-25/ee-supported-rhel9:latest
ee_target_image: "{{ registry_host }}:{{ registry_port }}/ee-supported-rhel9:latest"
tasks:
- name: Register system with Red Hat Subscription Management
community.general.redhat_subscription:
username: "{{ sat_user }}"
password: "{{ sat_passwd }}"
org_id: "{{ sat_orgid }}"
environment: "{{ redhat_env }}"
state: present
- name: Install required packages
ansible.builtin.dnf:
name:
- httpd
- firewalld
- podman
- policycoreutils-python-utils
state: present
- name: Enable and start httpd
ansible.builtin.service:
name: httpd
state: started
enabled: true
- name: Enable and start firewalld
ansible.builtin.service:
name: firewalld
state: started
enabled: true
- name: Allow HTTP service through firewall
ansible.posix.firewalld:
service: http
permanent: true
state: enabled
immediate: true
- name: Allow registry port through firewall
ansible.posix.firewalld:
port: "{{ registry_port }}/tcp"
permanent: true
state: enabled
immediate: true
- name: Ensure correct permissions on web root
ansible.builtin.file:
path: /var/www/html
recurse: true
mode: "0755"
- name: Set SELinux context for Ansible Automation Platform content
community.general.sefcontext:
target: "/var/www/html/ansible-automation-platform(/.*)?"
setype: httpd_sys_content_t
state: present
- name: Set SELinux context for RHEL 9 content
community.general.sefcontext:
target: "/var/www/html/rhel9(/.*)?"
setype: httpd_sys_content_t
state: present
- name: Set SELinux context for files
community.general.sefcontext:
target: "/var/www/html/files(/.*)?"
setype: httpd_sys_content_t
state: present
- name: Restore SELinux contexts
ansible.builtin.command: restorecon -Rv /var/www/html
changed_when: false
- name: Create registry quadlet file
ansible.builtin.copy:
dest: /etc/containers/systemd/registry.container
mode: "0644"
content: |
[Unit]
Description=Registry
[Container]
ContainerName=registry
Image={{ registry_image }}
PublishPort={{ registry_port }}:{{ host_port }}
[Install]
WantedBy=multi-user.target
- name: Reload Systemd Daemons
ansible.builtin.systemd:
daemon_reload: yes
become: true
- name: Start registry.service
ansible.builtin.systemd:
name: registry.service
state: started
become: true
- name: Create containers config directory
ansible.builtin.file:
path: /root/.config/containers
state: directory
mode: "0700"
- name: Configure insecure registry
ansible.builtin.copy:
dest: /root/.config/containers/registries.conf
mode: "0600"
content: |
[[registry]]
location = "{{ registry_host }}:{{ registry_port }}"
insecure = true
- name: Login to Red Hat registry
containers.podman.podman_login:
username: "{{ podman_user }}"
password: "{{ podman_passwd }}"
registry: registry.redhat.io
- name: Pull Execution Environment image
containers.podman.podman_image:
name: "{{ ee_source_image }}"
state: present
- name: Tag EE image for local registry
ansible.builtin.command:
cmd: podman tag {{ ee_source_image }} {{ ee_target_image }}
changed_when: true
- name: Push EE image to local registry
ansible.builtin.command:
cmd: podman push --remove-signatures {{ ee_target_image }}
changed_when: true
- name: Install chrony package
ansible.builtin.package:
name: chrony
state: present
- name: Configure chrony as NTP server
ansible.builtin.lineinfile:
path: /etc/chrony.conf
regexp: '^allow'
line: 'allow 0.0.0.0/0'
state: present
- name: Ensure chrony service is enabled and started
ansible.builtin.service:
name: chronyd
state: started
enabled: true
- name: Open NTP service in firewall
ansible.builtin.firewalld:
service: ntp
permanent: true
state: enabled
immediate: true
when: ansible_facts.services['firewalld.service'] is defined
###############################################################################
# Play 4: Configure the Controller node
###############################################################################
- name: Configure the Controller node
hosts: controller
become: true
vars:
registry_host: utility.lab.example.com
registry_port: 5000
tasks:
- name: Create repo file
ansible.builtin.copy:
content: |
[ansible-automation-platform-2.5]
name=Ansible Automation Platform 2.5
metadata_expire=-1
gpgcheck=1
enabled=1
baseurl=http://utility.lab.example.com/ansible-automation-platform/2.5
gpgkey=http://utility.lab.example.com/rhel9/RPM-GPG-KEY-redhat-release
[BaseOS]
name=BaseOS Packages Red Hat Enterprise Linux 9
metadata_expire=-1
gpgcheck=1
enabled=1
baseurl=http://utility.lab.example.com/rhel9/BaseOS
gpgkey=http://utility.lab.example.com/rhel9/RPM-GPG-KEY-redhat-release
[AppStream]
name=AppStream Packages Red Hat Enterprise Linux 9
metadata_expire=-1
gpgcheck=1
enabled=1
baseurl=http://utility.lab.example.com/rhel9/AppStream/
gpgkey=http://utility.lab.example.com/rhel9/rpm-gpg/RPM-GPG-KEY-redhat-release
dest: /etc/yum.repos.d/rhce.repo
- name: Install required packages
ansible.builtin.dnf:
name:
- podman
- ansible-core
- ansible-navigator
state: present
- name: Create directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0700"
owner: ansible
group: ansible
loop:
- /home/ansible/.config
- /home/ansible/.config/containers
- /home/ansible/ansible
- /home/ansible/ansible/roles
- /home/ansible/ansible/mycollections
- name: Configure insecure registry
ansible.builtin.copy:
dest: /home/ansible/.config/containers/registries.conf
mode: "0600"
content: |
[[registry]]
location = "{{ registry_host }}:{{ registry_port }}"
insecure = true
owner: ansible
group: ansible
- name: Configure ansible.cfg
ansible.builtin.copy:
dest: /home/ansible/ansible/ansible.cfg
content: |
[defaults]
inventory = /home/ansible/ansible/inventory
remote_user = ansible
roles_path = /home/ansible/ansible/roles
collections_path = /home/ansible/ansible/mycollections
- name: Configure ansible-navigator.yml
ansible.builtin.copy:
dest: /home/ansible/ansible/ansible-navigator.yml
content: |
---
ansible-navigator:
execution-environment:
image: utility.lab.example.com:5000/ee-supported-rhel9:latest
pull:
policy: missing
playbook-artifact:
enable: false
- name: Create test.yml
ansible.builtin.copy:
dest: /home/ansible/ansible/test.yml
content: |
---
- name: A simple playbook to test that Ansible is configured
hosts: localhost
tasks:
- name: Run test playbook
ansible.builtin.debug:
msg: "If you're reading this, Ansible is configured on your system."