Execution
Date 23 Apr 2026 13:59:36 +0100
Duration 00:00:03.08
Controller ssh-gw-4.layershift.com
User root
Versions
Ansible 2.16.13
ara 1.7.5 / 1.7.5
Python 3.10.10
Summary
2 Hosts
4 Tasks
5 Results
8 Plays
1 Files
0 Records

File: /home/ssh-gateway/ansible/kuly/kvm_node_install/setup_new_node.yaml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
---
- name: New KVM server provisioning scripts
  hosts: kvm_node
  gather_facts: true
  vars:
    agent360_api_key: "6225dd3b2c8afd50166ae988"
    firewall_mode: "HARDWARE_NODE"
    timezone: "UTC"
    swap_file_path: "/var/lib/libvirt/swapfile"
    swap_file_size: "64G"
    main_server_ip: "{{ ansible_default_ipv4.address }}"
    monitoring_script_url: "https://monitoring.platform360.io/agent360.sh"
    kernelcare_installer_url: "https://kernelcare.com/installer"
    firewall360_install_url: "https://tgz.thecode.casa/firewall360/install.sh"
    firewall360_reset_url: "https://deploy.thecode.casa/firewall360/reset_firewall.sh"
    agent360_plugins_tgz_url: "https://tgz.thecode.casa/agent360_plugins/install.sh"
    agent360_plugins_deploy_url: "https://deploy.thecode.casa/agent360_plugins/install.sh"
    virtnbdbackup_install_url: "https://tgz.thecode.casa/virtnbdbackup/install.sh"
    backup_manager_install_url: "https://backup-manager.vm.plesk-server.com/agents/install"
    ls_tests_install_url: "https://deploy.thecode.casa/ls-tests/installer.sh"
    requisites:
      - wget
      - curl
      - vim
      - nano
      - net-tools
      - lsof
      - mc
      - bash-completion
      - fuse-sshfs

  tasks:
    # ========================================================================
    # install prerequisite packages
    # ========================================================================
    - name: Install prerequisite packages
      ansible.builtin.dnf:
        name: "{{ requisites }}"
        state: present
        update_cache: true
      tags: [packages, prep, system, stage1]

    # ========================================================================
    # SWAP FILE SETUP
    # ========================================================================
    - name: Create swap file directory if missing
      ansible.builtin.file:
        path: "{{ swap_file_path | dirname }}"
        state: directory
        mode: '0755'
      tags: [swap, stage1]

    - name: Create swap file
      ansible.builtin.command: "fallocate -l {{ swap_file_size }} {{ swap_file_path }}"
      args:
        creates: "{{ swap_file_path }}"
      tags: [swap, stage1]

    - name: Set secure permissions (600) on swap file
      ansible.builtin.file:
        path: "{{ swap_file_path }}"
        mode: '0600'
      tags: [swap, stage1]

    - name: Initialize swap area
      ansible.builtin.command: "mkswap {{ swap_file_path }}"
      args:
        creates: "{{ swap_file_path }}.initialized"
      notify: Mark swap as initialized
      tags: [swap, stage1]

    - name: Enable swap file immediately
      ansible.builtin.command: "swapon {{ swap_file_path }}"
      register: swapon_result
      changed_when: "'already active' not in swapon_result.stderr"
      failed_when: swapon_result.rc != 0 and 'already active' not in swapon_result.stderr
      tags: [swap, stage1]

    - name: Enable all swaps from /etc/fstab (swapon -av)
      ansible.builtin.command: "swapon -av"
      register: swapon_av
      changed_when: false
      tags: [swap, stage1]

    - name: Ensure swap entry exists in /etc/fstab
      ansible.builtin.lineinfile:
        path: /etc/fstab
        line: "{{ swap_file_path }} none swap sw 0 0"
        regexp: "^{{ swap_file_path }}\\s+none\\s+swap"
        state: present
        validate: 'findmnt -T %s'
      tags: [swap, fstab, stage1]

    # ========================================================================
    # TIMEZONE CONFIGURATION
    # ========================================================================
    - name: Set system timezone to {{ timezone }}
      community.general.timezone:
        name: "{{ timezone }}"
      tags: [config, timezone, stage1]

    # ========================================================================
    # KERNELCARE INSTALLATION
    # ========================================================================
    - name: Install KernelCare
      ansible.builtin.shell: |
        set -o pipefail
        curl -s -L {{ kernelcare_installer_url }} | bash
      args:
        executable: /bin/bash
        creates: /usr/bin/kcare
      register: kernelcare_install
      changed_when: kernelcare_install.rc == 0
      tags: [kernelcare, stage1]

    - name: Enable and start KernelCare service
      ansible.builtin.systemd:
        name: kcare
        enabled: true
        state: started
        daemon_reload: true
      tags: [kernelcare, stage1]

    - name: Display KernelCare licensing reminder
      ansible.builtin.debug:
        msg: |
          ⚠️  KERNELCARE LICENSE REMINDER ⚠️
          Please license this IP in your KernelCare portal:
          → {{ main_server_ip }}
          Visit: https://cln.cloudlinux.com/console
      tags: [kernelcare, stage1]

    # ========================================================================
    # AGENT360 MONITORING INSTALLATION
    # ========================================================================
    - name: Download Agent360 installation script
      ansible.builtin.get_url:
        url: "{{ monitoring_script_url }}"
        dest: /tmp/agent360.sh
        mode: '0755'
        timeout: 30
      tags: [monitoring, agent360, stage1]

    - name: Install Agent360 monitoring agent
      ansible.builtin.shell: |
        set -o pipefail
        bash /tmp/agent360.sh {{ agent360_api_key }}
      args:
        chdir: /tmp
      register: agent360_install
      changed_when: agent360_install.rc == 0
      tags: [monitoring, agent360, stage1]

    # ========================================================================
    # FIREWALL360 INSTALLATION
    # ========================================================================
    - name: Install Firewall360
      ansible.builtin.shell: "bash <(curl -sL {{ firewall360_install_url }}) <<< '{{ firewall_mode }}'"
      args:
        executable: /bin/bash
      register: firewall_install
      changed_when: firewall_install.rc == 0
      tags: [firewall, stage1]

    # ========================================================================
    # FIREWALL360 RESET
    # ========================================================================
    - name: Reset Firewall360 configuration
      ansible.builtin.shell: "bash <(curl -sL {{ firewall360_reset_url }})"
      args:
        executable: /bin/bash
      register: firewall_reset
      changed_when: firewall_reset.rc == 0
      tags: [firewall, stage1]

    # ========================================================================
    # AGENT360 CUSTOM PLUGINS
    # ========================================================================
    - name: Install plugin internal_server_metrics
      ansible.builtin.shell: |
        set -o pipefail
        curl -Ls {{ agent360_plugins_tgz_url }} | bash -s internal_server_metrics
      args:
        executable: /bin/bash
      register: plugin_internal
      changed_when: plugin_internal.rc == 0
      tags: [monitoring, plugins, stage1]

    - name: Install plugin vrrp_mon_cr
      ansible.builtin.shell: |
        set -o pipefail
        curl -Ls {{ agent360_plugins_deploy_url }} | bash -s vrrp_mon_cr
      args:
        executable: /bin/bash
      register: plugin_vrrp
      changed_when: plugin_vrrp.rc == 0
      tags: [monitoring, plugins, stage1]

    - name: Ensure pip3 is available
      ansible.builtin.package:
        name: python3-pip
        state: present
      tags: [monitoring, plugins, stage1]

    - name: Install mdjson Python package
      ansible.builtin.pip:
        name: mdjson
        executable: pip3
        state: present
      tags: [monitoring, plugins, stage1]

    - name: Install plugin mdstat_ls
      ansible.builtin.shell: |
        set -o pipefail
        curl -Ls {{ agent360_plugins_deploy_url }} | bash -s mdstat_ls
      args:
        executable: /bin/bash
      register: plugin_mdstat
      changed_when: plugin_mdstat.rc == 0
      tags: [monitoring, plugins, stage1]

    # ========================================================================
    # NBD MODULE + VIRTNBDBACKUP
    # ========================================================================
    - name: Ensure NBD module loads at boot
      ansible.builtin.copy:
        content: "nbd\n"
        dest: /etc/modules-load.d/nbd.conf
        mode: '0644'
      tags: [backup, nbd, prep, stage1]

    - name: Load NBD kernel module immediately
      community.general.modprobe:
        name: nbd
        state: present
      tags: [backup, nbd, prep, stage1]

    - name: Install virtnbdbackup
      ansible.builtin.shell: |
        set -o pipefail
        curl -s {{ virtnbdbackup_install_url }} | bash
      args:
        executable: /bin/bash
      register: virtnbd_install
      changed_when: virtnbd_install.rc == 0
      tags: [backup, virtnbd, stage1]
  handlers:
    - name: Mark swap as initialized
      ansible.builtin.file:
        path: "{{ swap_file_path }}.initialized"
        state: touch
        mode: '0644'

- name: Install backup agent
  hosts: kvm_node
  gather_facts: true
  vars:
    backup_manager_install_url: "https://backup-manager.stage.town/agents/install"
    backup_manager_add_node_url: "https://backup-manager.stage.town/hardwarenodes/"
  tasks:
    - name: Add hw node to backup manager
      ansible.builtin.uri:
        url: "{{ backup_manager_add_node_url }}"
        method: POST
        body_format: json
        body:
          name: "{{ inventory_hostname }}"
          ip: "{{ ansible_default_ipv4.address }}"
        headers:
          Content-Type: "application/json"
        status_code: 200
      tags: [backup, stage2]

    - name: Download backup-manager installer
      ansible.builtin.get_url:
        url: "{{ backup_manager_install_url }}"
        dest: /tmp/installer.sh
        mode: '0755'
        timeout: 30
      tags: [backup, backup-manager, stage2]

    - name: Run backup-manager installer (pass 1)
      ansible.builtin.shell: |
        set -o pipefail
        /tmp/installer.sh
      args:
        chdir: /tmp
      register: backup_install_1
      changed_when: backup_install_1.rc == 0
      tags: [backup, backup-manager, stage2]

- name: Generate ssh key
  hosts: kvm_node
  gather_facts: true
  vars:
    target_user: root
    local_tmp_path: "/tmp/id_rsa_host1.pub"
  tasks:
    - name: Generate SSH key pair
      community.crypto.openssh_keypair:
        path: "/{{ target_user }}/.ssh/id_rsa"
        type: rsa
        size: 2048
        comment: "ansible-generated-on-{{ ansible_fqdn }}"
      register: ssh_key_result
      tags: [stage2]
    - name: Fetch public key
      ansible.builtin.fetch:
        src: "/{{ target_user }}/.ssh/id_rsa.pub"
        dest: "{{ local_tmp_path }}"
        flat: true
      tags: [stage2]

- name: Upload key to storage server
  hosts: backup_node
  gather_facts: false
  vars:
    target_user: root
    local_tmp_path: "/tmp/id_rsa_host1.pub"
  tasks:
    - name: Install public key from local temp file
      ansible.posix.authorized_key:
        user: root
        state: present
        key: "{{ lookup('file', local_tmp_path) }}"
      tags: [stage2]
    - name: Remove temp file
      delegate_to: localhost
      run_once: true
      ansible.builtin.file:
        path: "{{ local_tmp_path }}"
        state: absent
      tags: [stage2]

- name: Create folder and file on backup node
  hosts: backup_node
  gather_facts: false
  vars:
    kvm_host: "{{ hostvars[groups['kvm_node'][0]]['inventory_hostname'] }}"
  tasks:
    - name: Create folder
      ansible.builtin.file:
        force: true
        path: "/vz/vmprivate/backups/{{ kvm_host }}"
        state: directory
        mode: '0755'
      tags: [stage2]
    - name: Create file
      ansible.builtin.file:
        force: true
        path: "/vz/vmprivate/backups/{{ kvm_host }}/mount-check-production.txt"
        state: touch
        mode: '0644'
      tags: [stage2]
# ----------------------------------------------------------------------------------------------------------------------------------
- name: Get backups private ip
  hosts: backup_node
  gather_facts: true
  tasks:
    - name: Find the IP in the 10.10.10.0/21 range
      ansible.builtin.set_fact:
        private_ip: "{{ ansible_all_ipv4_addresses | select('match', '^10\\.10\\.10\\.') | first }}"
      tags: [stage2_1]

- name: Finish setup on kvm node
  hosts: kvm_node
  gather_facts: false
  vars:
    st_ip: "{{ hostvars[groups['backup_node'][0]]['private_ip'] }}"
  tasks:
    - name: Create folder on kvm node
      ansible.builtin.file:
        force: true
        path: "/vz/vmprivate/backups/{{ inventory_hostname }}"
        state: directory
        mode: '0755'
      tags: [stage2_1]
    - name: Add to fstab
      ansible.builtin.lineinfile:
        path: /etc/fstab
        regexp: '^root@.*:/vz/vmprivate/backups/{{ inventory_hostname }}'
        line: "root@{{ st_ip }}:/vz/vmprivate/backups/{{ inventory_hostname }} /vz/vmprivate/backups/{{ inventory_hostname }} fuse.sshfs rw,nosuid,reconnect,ServerAliveInterval=15,nodev,port=22,uid=0,gid=0 0 0"
        state: present
        validate: 'findmnt -T %s'
      tags: [stage2_1]

        #    - name: Run backup-manager installer (pass 2)
        #      ansible.builtin.shell: |
        #        set -o pipefail
        #        /tmp/installer.sh
        #      args:
        #        chdir: /tmp
        #      register: backup_install_2
        #      changed_when: backup_install_2.rc == 0
        #      tags: [backup, backup-manager, stage2_1]

        #    - name: Run backup-manager mount script
        #      ansible.builtin.shell: |
        #        set -o pipefail
        #        /tmp/mount.sh
        #      args:
        #        chdir: /tmp
        #      register: backup_mount
        #      changed_when: backup_mount.rc == 0
        #      tags: [backup, backup-manager, stage2_1]

- name: Run final tests and report
  hosts: all
  gather_facts: true
  tasks:
    # ========================================================================
    # 10. goss ls tests
    # ========================================================================
    - name: Install ls-tests suite
      ansible.builtin.shell: |
        set -o pipefail
        curl -s {{ ls_tests_install_url }} | bash
      args:
        executable: /bin/bash
        creates: /opt/ls_tools/ls-tests/run_tests.sh
      register: ls_tests_install
      changed_when: ls_tests_install.rc == 0
      tags: [tests, ls-tests, stage3]

    - name: Run goss tests
      ansible.builtin.shell: |
        set -o pipefail
        /opt/ls_tools/ls-tests/run_tests.sh
      args:
        executable: /bin/bash
      register: ls_tests_run
      changed_when: ls_tests_run.rc == 0
      tags: [tests, ls-tests, stage3]

    # ========================================================================
    # 11. POST-INSTALL SUMMARY
    # ========================================================================
    - name: Display provisioning completion summary
      ansible.builtin.debug:
        msg: |
          ✅ SERVER PROVISIONING COMPLETE ✅
          Configuration applied:
          • Swap: {{ swap_file_path }} ({{ swap_file_size }}) → active
          • Timezone: {{ timezone }}
          • KernelCare: installed (license IP: {{ main_server_ip }})
          • Firewall360: {{ firewall_mode }} mode + reset
          • Agent360: monitoring + 3 plugins installed
          • NBD/virtnbdbackup: ready
          • backup-manager: installed + mounted
          Quick verification commands:
            swapon --show
            timedatectl
            systemctl status kcare
            firewall360 status
            agent360 status
            lsmod | grep nbd
      tags: [verify, always, stage3]