Issue 38 stop on wrong value #40

Merged
max merged 6 commits from issue-38-stop-on-wrong-value into dev 2026-09-17 19:53:47 +04:00
13 changed files with 217 additions and 155 deletions
+17 -63
View File
@@ -10,76 +10,30 @@
ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}" ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}"
user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}" user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}"
vars_prompt:
- name: lxc_hostname
prompt: "Hostname for the container (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)"
default: "new-lxc-test"
private: false
- name: lxc_root_password
prompt: "Root password"
default: "new-lxc-test"
private: true
confirm: true
encrypt: sha512_crypt
- name: lxc_ip_address
prompt: "IP address for the container (in x.x.x.x/x formant)"
default: "192.168.0.90/24"
private: false
- name: lxc_is_privileged
prompt: "Unprivileged LXC (true/false)?"
default: true
private: false
- name: lxc_create_human_admin_user
prompt: "Create human admin user? (true/false)?"
default: true
private: false
pre_tasks: pre_tasks:
- name: Validate hostname - name: Include input and validation for hostname
ansible.builtin.fail: ansible.builtin.include_tasks: ../tasks/hostname_validation.yaml
msg: |
Invalid hostname: "{{ lxc_hostname }}" - name: Include input and validation for LXC root password
Hostname must: ansible.builtin.include_tasks: ../tasks/lxc_root_password_validation.yaml
- contain only letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.)
- not start or end with a hyphen or dot - name: Include input and validation for IP
- have each label between 1 and 63 characters ansible.builtin.include_tasks: ../tasks/ip_validation.yaml
- not exceed 253 characters in total
when: > - name: Include input and validation for LXC privilege
lxc_hostname | length > 253 or ansible.builtin.include_tasks: ../tasks/lxc_privileged_validation.yaml
lxc_hostname is not regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
run_once: true - name: Include input and validation for human admin user creation
ansible.builtin.include_tasks: ../tasks/human_admin_user_validation.yaml
- name: Validate IP address
ansible.builtin.fail:
msg: |
Invalid IP address: "{{ lxc_ip_address }}"
IP address must be in x.x.x.x/x format (e.g., 192.168.0.90/24).
when: not lxc_ip_address is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\/(3[0-2]|[12]?\\d))?$')
run_once: true
roles: roles:
- ../roles/deploy_lxc_on_proxmox - ../roles/deploy_lxc_on_proxmox
tasks: tasks:
- name: Make the prompted hostname available to the whole playbook
ansible.builtin.set_fact:
fact_lxc_hostname: "{{ lxc_hostname }}"
- name: Make the prompted IP available to the whole playbook
ansible.builtin.set_fact:
fact_lxc_ip_address: "{{ lxc_ip_address.split('/') | first }}"
- name: Make the "human admin user" selection available to the whole playbook
ansible.builtin.set_fact:
fact_lxc_create_human_admin_user: "{{ lxc_create_human_admin_user }}"
- name: Add the target host to the inventory - name: Add the target host to the inventory
ansible.builtin.add_host: ansible.builtin.add_host:
name: "{{ fact_lxc_ip_address }}" name: "{{ guest_ip_address }}"
groups: new_host groups: new_host
ansible_user: ansible ansible_user: ansible
ansible_become_password: "{{ ansible_become_passwd }}" ansible_become_password: "{{ ansible_become_passwd }}"
@@ -104,10 +58,10 @@
- name: Include human admin user role - name: Include human admin user role
ansible.builtin.include_role: ansible.builtin.include_role:
name: ../roles/human_admin_user name: ../roles/human_admin_user
when: "hostvars['localhost']['fact_lxc_create_human_admin_user'] | bool" when: "hostvars['localhost']['create_human_admin_user'] | bool"
- name: Include update configs role - name: Include update configs role
ansible.builtin.include_role: ansible.builtin.include_role:
name: ../roles/update_configs name: ../roles/update_configs
when: "hostvars['localhost']['fact_lxc_create_human_admin_user'] | bool" when: "hostvars['localhost']['create_human_admin_user'] | bool"
+15 -80
View File
@@ -10,98 +10,33 @@
ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}" ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}"
user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}" user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}"
vars_prompt:
- name: vm_hostname
prompt: "Hostname for the VM (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)"
default: "new-vm-test"
private: false
- name: vm_os_template
prompt: "Select OS template. Available: debian-13 | alpine"
default: "debian-13"
private: false
- name: vm_become_method
prompt: "Select become method. Available: sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl"
default: "sudo"
private: false
- name: vm_create_human_admin_user
prompt: "Create human admin user? (true/false)?"
default: true
private: false
- name: vm_ip_address
prompt: "IP address for the VM (in x.x.x.x/x formant)"
default: "192.168.0.89/24"
private: false
pre_tasks: pre_tasks:
- name: Validate hostname - name: Include input and validation for hostname
ansible.builtin.fail: ansible.builtin.include_tasks: ../tasks/hostname_validation.yaml
msg: |
Invalid hostname: "{{ lxc_hostname }}"
Hostname must:
- contain only letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.)
- not start or end with a hyphen or dot
- have each label between 1 and 63 characters
- not exceed 253 characters in total
when: >
lxc_hostname | length > 253 or
lxc_hostname is not regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
run_once: true
- name: Validate IP address - name: Include input and validation for IP
ansible.builtin.fail: ansible.builtin.include_tasks: ../tasks/ip_validation.yaml
msg: |
Invalid IP address: "{{ vm_ip_address }}"
IP address must be in x.x.x.x/x format (e.g., 192.168.0.89/24).
when: not vm_ip_address is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\/(3[0-2]|[12]?\\d))?$')
run_once: true
- name: Validate OS template - name: Include input and validation for human admin user creation
ansible.builtin.fail: ansible.builtin.include_tasks: ../tasks/human_admin_user_validation.yaml
msg: |
Invalid OS template: "{{ vm_os_template }}"
Select from available options: debian-13, alpine
when: vm_os_template not in ['debian-13', 'alpine']
run_once: true
- name: Validate become method - name: Include input and validation for OS template
ansible.builtin.fail: ansible.builtin.include_tasks: ../tasks/os_template_validation.yaml
msg: |
Invalid become method: "{{ vm_become_method }}" - name: Include input and validation for become method
Select from available options: sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl ansible.builtin.include_tasks: ../tasks/become_method_validation.yaml
when: vm_become_method not in ['sudo', 'su', 'pbrun', 'pfexec', 'doas', 'dzdo', 'ksu', 'runas', 'machinectl']
run_once: true
roles: roles:
- ../roles/deploy_vm_on_proxmox - ../roles/deploy_vm_on_proxmox
tasks: tasks:
- name: Make the hostname available to the whole playbook
ansible.builtin.set_fact:
fact_vm_hostname: "{{ vm_hostname }}"
- name: Make the OS template available to the whole playbook
ansible.builtin.set_fact:
fact_vm_os_template: "{{ vm_os_template }}"
- name: Make the IP available to the whole playbook
ansible.builtin.set_fact:
fact_vm_ip_address: "{{ vm_ip_address.split('/') | first }}"
- name: Make the "human admin user" selection available to the whole playbook
ansible.builtin.set_fact:
fact_vm_create_human_admin_user: "{{ vm_create_human_admin_user }}"
- name: Add the target host to the inventory - name: Add the target host to the inventory
ansible.builtin.add_host: ansible.builtin.add_host:
name: "{{ fact_vm_ip_address }}" name: "{{ guest_ip_address }}"
groups: new_host groups: new_host
ansible_user: ansible ansible_user: ansible
ansible_become_password: "{{ ansible_become_passwd }}" ansible_become_password: "{{ ansible_become_passwd }}"
ansible_become_method: "{{ vm_become_method }}" ansible_become_method: "{{ guest_become_method }}"
- name: Configure VM - name: Configure VM
@@ -123,10 +58,10 @@
- name: Include human admin user role - name: Include human admin user role
ansible.builtin.include_role: ansible.builtin.include_role:
name: ../roles/human_admin_user name: ../roles/human_admin_user
when: "hostvars['localhost']['fact_vm_create_human_admin_user'] | bool" when: "hostvars['localhost']['create_human_admin_user'] | bool"
- name: Include update configs role - name: Include update configs role
ansible.builtin.include_role: ansible.builtin.include_role:
name: ../roles/update_configs name: ../roles/update_configs
when: "hostvars['localhost']['fact_vm_create_human_admin_user'] | bool" when: "hostvars['localhost']['create_human_admin_user'] | bool"
+4 -4
View File
@@ -12,9 +12,9 @@
cmode: "shell" cmode: "shell"
password: "{{ lxc_root_password }}" password: "{{ lxc_root_password }}"
hostname: "{{ lxc_hostname }}" hostname: "{{ guest_hostname }}"
ostemplate: 'main:vztmpl/debian-13-golden-image.tar.gz' ostemplate: 'main:vztmpl/debian-13-golden-image.tar.gz'
unprivileged: "{{ lxc_is_privileged }}" unprivileged: "{{ lxc_privileged }}"
memory: 2048 memory: 2048
cores: 2 cores: 2
state: present state: present
@@ -26,7 +26,7 @@
storage: local storage: local
pubkey: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/ansible_key.pub') }}" pubkey: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/ansible_key.pub') }}"
netif: netif:
net0: "name=eth0,gw=192.168.0.1,ip={{ lxc_ip_address }},bridge=vmbr0" net0: "name=eth0,gw=192.168.0.1,ip={{ guest_ip_address }}/{{ guest_netmask }},bridge=vmbr0"
- name: Make sure the container has started - name: Make sure the container has started
become: false become: false
@@ -38,7 +38,7 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
hostname: "{{ lxc_hostname }}" hostname: "{{ guest_hostname }}"
state: started state: started
retries: 3 retries: 3
delay: 15 delay: 15
@@ -10,8 +10,8 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
clone: "{{ vm_os_template }}-cloud-init-template" clone: "{{ guest_os_template }}-cloud-init-template"
name: "{{ vm_hostname }}" name: "{{ guest_hostname }}"
storage: main storage: main
format: qcow2 format: qcow2
full: true full: true
@@ -33,7 +33,7 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
name: "{{ vm_hostname }}" name: "{{ guest_hostname }}"
# Cloud-init parameters # Cloud-init parameters
update: true update: true
ide: ide:
@@ -41,7 +41,7 @@
ciuser: "ansible" ciuser: "ansible"
cipassword: "{{ ansible_become_passwd }}" cipassword: "{{ ansible_become_passwd }}"
ipconfig: ipconfig:
ipconfig0: "ip={{ vm_ip_address }},gw=192.168.0.1" ipconfig0: "ip={{ guest_ip_address }}/{{ guest_netmask }},gw=192.168.0.1"
- name: Tweak the hardware settings - name: Tweak the hardware settings
become: false become: false
@@ -53,7 +53,7 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
name: "{{ vm_hostname }}" name: "{{ guest_hostname }}"
cores: 4 cores: 4
memory: 4096 # Minimal value for debian based OS for smooth workflow memory: 4096 # Minimal value for debian based OS for smooth workflow
update: true update: true
@@ -68,5 +68,5 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
name: "{{ vm_hostname }}" name: "{{ guest_hostname }}"
state: started state: started
+1 -1
View File
@@ -9,7 +9,7 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
name: "{{ vm_hostname }}" name: "{{ guest_hostname }}"
state: current state: current
ignore_errors: true ignore_errors: true
register: vm_status register: vm_status
@@ -10,7 +10,7 @@
api_token_id: ansible api_token_id: ansible
api_token_secret: "{{ proxmox_token_secret }}" api_token_secret: "{{ proxmox_token_secret }}"
name: "{{ vm_hostname }}" name: "{{ guest_hostname }}"
state: started state: started
- name: Sleep for 120 seconds to ensure that Proxmox has cloned the VM - name: Sleep for 120 seconds to ensure that Proxmox has cloned the VM
+22
View File
@@ -0,0 +1,22 @@
---
- name: Set default become method
ansible.builtin.set_fact:
guest_become_method: "sudo"
- name: Prompt for become method with immediate validation
ansible.builtin.pause:
prompt: "Choose become method, available: sudo, su, pbrun, pfexec, doas, dzdo, ksu, runas, machinectl [sudo]"
register: become_method_result
until: >
become_method_result.user_input | length == 0 or
(
become_method_result.user_input in ['sudo', 'su', 'pbrun', 'pfexec', 'doas', 'dzdo', 'ksu', 'runas', 'machinectl']
)
retries: 2
delay: 0
- name: Use user input only if provided, otherwise keep the default
ansible.builtin.set_fact:
guest_become_method: "{{ become_method_result.user_input }}"
when: become_method_result.user_input | length > 0
+23
View File
@@ -0,0 +1,23 @@
---
- name: Set default hostname
ansible.builtin.set_fact:
guest_hostname: "test-guest"
- name: Prompt for hostname with immediate validation
ansible.builtin.pause:
prompt: "Enter hostname (letters, digits, hyphens (but not leading and trailing), dots; max 253 chars) [{{ guest_hostname }}]"
register: hostname_result
until: >
hostname_result.user_input | length == 0 or
(
hostname_result.user_input | length <= 253 and
hostname_result.user_input is regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
)
retries: 2
delay: 0
- name: Use user input only if provided, otherwise keep the default
ansible.builtin.set_fact:
guest_hostname: "{{ hostname_result.user_input }}"
when: hostname_result.user_input | length > 0
+22
View File
@@ -0,0 +1,22 @@
---
- name: Set default human admin user
ansible.builtin.set_fact:
create_human_admin_user: true
- name: Prompt for human admin user creation with immediate validation
ansible.builtin.pause:
prompt: "Create human admin user? (true/false) [true]"
register: human_admin_user_result
until: >
human_admin_user_result.user_input | length == 0 or
(
human_admin_user_result.user_input in ['true', 'false']
)
retries: 2
delay: 0
- name: Use user input only if provided, otherwise keep the default
ansible.builtin.set_fact:
create_human_admin_user: "{{ human_admin_user_result.user_input }}"
when: human_admin_user_result.user_input | length > 0
+29
View File
@@ -0,0 +1,29 @@
---
- name: Set default ip
ansible.builtin.set_fact:
guest_ip_address: "192.168.0.90"
- name: Set default network mask
ansible.builtin.set_fact:
guest_netmask: "24"
- name: Prompt for IP with immediate validation
ansible.builtin.pause:
prompt: "Enter ip in x.x.x.x/x format [192.168.0.90/24]"
register: ip_result
until: >
ip_result.user_input | length == 0 or
ip_result.user_input is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)/(3[0-2]|[12]?\\d)$')
retries: 2
delay: 0
- name: Use user input only if provided, otherwise keep the default (IP)
ansible.builtin.set_fact:
guest_ip_address: "{{ ip_result.user_input.split('/') | first }}"
when: ip_result.user_input | length > 0
- name: Use user input only if provided, otherwise keep the default (netmask)
ansible.builtin.set_fact:
guest_netmask: "{{ ip_result.user_input.split('/') | second }}"
when: ip_result.user_input | length > 0
+22
View File
@@ -0,0 +1,22 @@
---
- name: Set default privileged
ansible.builtin.set_fact:
lxc_privileged: false
- name: Prompt for privileged with immediate validation
ansible.builtin.pause:
prompt: "Privileged LXC? (true/false) [false]"
register: privileged_result
until: >
privileged_result.user_input | length == 0 or
(
privileged_result.user_input in ['true', 'false']
)
retries: 2
delay: 0
- name: Use user input only if provided, otherwise keep the default
ansible.builtin.set_fact:
lxc_privileged: "{{ privileged_result.user_input }}"
when: privileged_result.user_input | length > 0
+33
View File
@@ -0,0 +1,33 @@
---
- name: Set default password
ansible.builtin.set_fact:
lxc_root_password: "test-guest"
- name: Prompt for password
ansible.builtin.pause:
prompt: "Enter your password [{{ lxc_root_password }}]"
echo: false
register: password_result_1
until: password_result_1.user_input | length == 0
retries: 2
delay: 0
- name: Prompt for password (confirmation)
ansible.builtin.pause:
prompt: "Confirm your new password"
echo: false
register: password_result_2
- name: Ensure passwords match
ansible.builtin.assert:
that:
- password_result_1.user_input == password_result_2.user_input
fail_msg: "Passwords do not match!"
success_msg: "Password confirmed."
- name: Use user input only if provided, otherwise keep the default
ansible.builtin.set_fact:
lxc_root_password: "{{ password_result_1.user_input }}"
when: password_result_1.user_input | length > 0
+22
View File
@@ -0,0 +1,22 @@
---
- name: Set default OS template
ansible.builtin.set_fact:
guest_os_template: "debian-13"
- name: Prompt for OS template with immediate validation
ansible.builtin.pause:
prompt: "Choose OS template, available: debian-13, alpine [debian-13]"
register: os_template_result
until: >
os_template_result.user_input | length == 0 or
(
os_template_result.user_input in ['debian-13', 'alpine']
)
retries: 2
delay: 0
- name: Use user input only if provided, otherwise keep the default
ansible.builtin.set_fact:
guest_os_template: "{{ os_template_result.user_input }}"
when: os_template_result.user_input | length > 0