--- # showcase/common.yml — Common setup tasks for all showcase instances # # Included by showcase-post-deploy.yml after name parsing. # Uses vars from the parent play: incus_api, incus_cert, incus_key, # base_packages, ffsdn_*, blueprint_name, component_name. # # Adds a temporary incusbr0 NIC for internet access during package # installation (OVN SNAT may not provide outbound internet in all labs), # then the deploy-instance playbook removes it after setup completes. # ── Step 1: Add temporary internet NIC ────────────────────────────── - name: Add temporary incusbr0 NIC for internet access ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}" method: PATCH client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false body_format: json body: devices: eth-setup: type: nic network: incusbr0 name: eth1 status_code: [200, 202] return_content: true register: nic_add_result - name: Wait for NIC addition if async ansible.builtin.uri: url: "{{ incus_api }}{{ nic_add_result.json.operation }}/wait?timeout=30" method: GET client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false return_content: true when: nic_add_result.status == 202 - name: Push NIC activation script ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/tmp/activate-eth1.sh" method: POST client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false body: | #!/bin/bash # Configure eth1 for DHCP via systemd-networkd mkdir -p /etc/systemd/network cat > /etc/systemd/network/80-eth1.network << 'EOF' [Match] Name=eth1 [Network] DHCP=yes [DHCPv4] RouteMetric=100 EOF ip link set eth1 up systemctl restart systemd-networkd # Wait up to 15s for IPv4 address for i in $(seq 1 15); do if ip -4 addr show eth1 | grep -q 'inet '; then exit 0 fi sleep 1 done # Last resort: try dhclient dhclient eth1 2>/dev/null || true sleep 2 headers: Content-Type: "application/octet-stream" X-Incus-type: "file" X-Incus-mode: "0755" status_code: [200] - name: Activate temporary NIC inside container ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec" method: POST client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false body_format: json body: command: ["/bin/bash", "/tmp/activate-eth1.sh"] record-output: true interactive: false wait-for-websocket: false status_code: [202] return_content: true register: nic_activate_exec - name: Wait for NIC activation (up to 30s) ansible.builtin.uri: url: "{{ incus_api }}{{ nic_activate_exec.json.operation }}/wait?timeout=30" method: GET client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false return_content: true - name: Wait for DHCP on temporary NIC (up to 30s) ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/state" method: GET client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false return_content: true register: nic_state until: >- nic_state.json.metadata.network is defined and nic_state.json.metadata.network.eth1 is defined and nic_state.json.metadata.network.eth1.addresses | selectattr('family', 'equalto', 'inet') | list | length > 0 retries: 6 delay: 5 # ── Step 2: Run base setup with internet access ──────────────────── - name: Generate base setup script ansible.builtin.set_fact: base_setup_script: | #!/bin/bash set -e echo "=== Showcase base setup starting ===" echo "Blueprint: {{ blueprint_name }}, Component: {{ component_name }}" # Set hostname hostname {{ ffsdn_instance_name }} echo "{{ ffsdn_instance_name }}" > /etc/hostname sed -i '/^127\.0\.1\.1/d' /etc/hosts echo "127.0.1.1 {{ ffsdn_instance_name }}" >> /etc/hosts echo "[OK] Hostname set to {{ ffsdn_instance_name }}" # Update package cache and install base packages export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq {{ base_packages }} 2>&1 | tail -1 echo "[OK] Base packages installed" # Set timezone ln -sf /usr/share/zoneinfo/UTC /etc/localtime echo "UTC" > /etc/timezone echo "[OK] Timezone set to UTC" # Write deployment metadata cat > /etc/deploy-info << 'DEPLOYINFO' blueprint={{ blueprint_name }} component={{ component_name }} hostname={{ ffsdn_instance_name }} ip={{ ffsdn_instance_ip | default('unknown') }} cluster={{ ffsdn_cluster_name | default('unknown') }} deployed_by={{ ffsdn_deployed_by | default('unknown') }} image={{ ffsdn_image_os | default('') }}/{{ ffsdn_image_release | default('') }} DEPLOYINFO echo "deployed=$(date -Iseconds)" >> /etc/deploy-info echo "[OK] Deployment metadata written" echo "=== Showcase base setup complete ===" - name: Push base setup script to instance ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/tmp/showcase-setup.sh" method: POST client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false body: "{{ base_setup_script }}" headers: Content-Type: "application/octet-stream" X-Incus-type: "file" X-Incus-mode: "0755" status_code: [200] - name: Execute base setup script ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec" method: POST client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false body_format: json body: command: ["/bin/bash", "/tmp/showcase-setup.sh"] record-output: true interactive: false wait-for-websocket: false environment: DEBIAN_FRONTEND: "noninteractive" status_code: [202] return_content: true register: base_setup_exec - name: Wait for base setup to complete (up to 3 minutes) ansible.builtin.uri: url: "{{ incus_api }}{{ base_setup_exec.json.operation }}/wait?timeout=180" method: GET client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false return_content: true timeout: 200 register: base_setup_wait failed_when: base_setup_wait.json.metadata.status != "Success" - name: Verify base setup exit code ansible.builtin.assert: that: - base_setup_wait.json.metadata.metadata.return | int == 0 fail_msg: "Base setup exited with code {{ base_setup_wait.json.metadata.metadata.return }}" success_msg: "Base setup completed successfully" - name: Clean up base setup script ansible.builtin.uri: url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec" method: POST client_cert: "{{ incus_cert }}" client_key: "{{ incus_key }}" validate_certs: false body_format: json body: command: ["/bin/rm", "-f", "/tmp/showcase-setup.sh"] record-output: false interactive: false wait-for-websocket: false status_code: [202] ignore_errors: true