commit 5f0f242ab92af7a612245fc836d0189361fbf2ed Author: Simon Cornet Date: Fri May 30 15:04:18 2025 +0200 feat: initial commit diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..9b9b52a --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,23 @@ +--- + +exclude_paths: + - ".gitlab/*" + - ".gitlab-ci.yml" + - "defaults/main.yaml" + - "meta/main.yaml" + - "vars/*" + +kinds: + - playbook: "**/*.{yml,yaml}" + +skip_list: + - "command-shell" + - "experimental" + - "git-latest" + - "no-changed-when" + - "no-handler" + - "name[casing]" + - "name[template]" + - "risky-file-permissions" + - "schema[playbook]" + - "var-naming[no-role-prefix]" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..904cae8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.ansible diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..fdb0b13 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,18 @@ +--- + +# gitLab ci stages +stages: + + # deployment + - "gitleaks" + - "linting" + - "deployment" + + +# include jobs +include: + + # deployment + - local: ".gitlab/gitleaks.yaml" + - local: ".gitlab/linting.yaml" + - local: ".gitlab/deployment.yaml" diff --git a/.gitlab/deployment.yaml b/.gitlab/deployment.yaml new file mode 100644 index 0000000..040754b --- /dev/null +++ b/.gitlab/deployment.yaml @@ -0,0 +1,31 @@ +--- +# deploy ansible/roles/common code +deployment: + stage: "deployment" + image: + name: "cr.simoncor.net/siempie/ssh-client:latest" + entrypoint: ["/bin/sh", "-c"] + rules: + + # run only on push to default branch + - if: + '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == + $CI_DEFAULT_BRANCH' + - when: "never" + + # prepare ssh + before_script: + # prepare ssh + - | + # prepare ssh + mkdir -p ~/.ssh + chmod 700 ~/.ssh + echo "$SSH_CONFIG" > ~/.ssh/config + echo "$SSH_DEPLOYMENT_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + + # deployment commands + script: + - | + # install ansible roles dependancies + ssh $SSH_DEPLOYMENT_USER@$ANSIBLE_SERVER "sudo /usr/local/bin/ansible-galaxy install -r /etc/ansible/roles/requirements.yaml --force" diff --git a/.gitlab/gitleaks.yaml b/.gitlab/gitleaks.yaml new file mode 100644 index 0000000..ed2b2c8 --- /dev/null +++ b/.gitlab/gitleaks.yaml @@ -0,0 +1,16 @@ +--- + +# gitleaks +gitleaks: + stage: "gitleaks" + image: + name: "ghcr.io/gitleaks/gitleaks:latest" + rules: + + # run only on push to default branch + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + - when: "never" + + # start linting + script: + - "gitleaks detect --source . --verbose --redact" diff --git a/.gitlab/linting.yaml b/.gitlab/linting.yaml new file mode 100644 index 0000000..01b8aa3 --- /dev/null +++ b/.gitlab/linting.yaml @@ -0,0 +1,17 @@ +--- + +# linting +linting: + stage: "linting" + image: + name: "cr.simoncor.net/siempie/ansible-deployment:latest" + entrypoint: ["/bin/sh", "-c"] + rules: + + # run only on push to default branch + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + - when: "never" + + # start linting + script: + - "ansible-lint -c .ansible-lint ." diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..f11bd84 --- /dev/null +++ b/.yamllint @@ -0,0 +1,30 @@ +--- +extends: 'default' + +rules: + braces: + max-spaces-inside: 1 + forbid: true + comments: + min-spaces-from-content: 1 + comments-indentation: false + empty-lines: + max: 2 + indentation: + spaces: 2 + check-multi-line-strings: true + line-length: + max: 120 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: 'enable' + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true + truthy: + allowed-values: + - 'true' + - 'false' + quoted-strings: + quote-type: 'any' + required: true diff --git a/meta/main.yaml b/meta/main.yaml new file mode 100644 index 0000000..76fd6c1 --- /dev/null +++ b/meta/main.yaml @@ -0,0 +1,8 @@ +--- + +galaxy_info: + author: "siempie" + description: "update proxmox cluster" + license: "MIT" + role_name: "proxmox" +dependencies: [] diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..053177f --- /dev/null +++ b/readme.md @@ -0,0 +1,10 @@ +# Overview +This role updates a Proxmox Cluster. + +# Supported Operating Systems +| Operating System | Version | +| --- | ----- | +| Debian | 12 | + +# Tags +This role has no tags. diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..da8f5e1 --- /dev/null +++ b/renovate.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ "local>cicd/renovate" ], + "ansible": { + "fileMatch": [ "(.*).ya?ml$" ] + } +} diff --git a/tasks/ceph/enter-maint.yaml b/tasks/ceph/enter-maint.yaml new file mode 100644 index 0000000..e4bc6df --- /dev/null +++ b/tasks/ceph/enter-maint.yaml @@ -0,0 +1,29 @@ +--- + +# enable ceph maintenance mode +- name: "enable ceph maintenance mode" + block: + + # set ceph osd noout settings + - name: "set ceph noout" + ansible.builtin.shell: + cmd: "sudo ceph osd set noout" + changed_when: false + failed_when: "ceph_noout_result.rc != 0" + register: "ceph_noout_result" + + # set ceph osd nobackfill settings + - name: "set ceph nobackfill" + ansible.builtin.shell: + cmd: "sudo ceph osd set nobackfill" + changed_when: false + failed_when: "ceph_nobackfill_result.rc != 0" + register: "ceph_nobackfill_result" + + # set ceph osd norebalance settings + - name: "set ceph norebalance" + ansible.builtin.shell: + cmd: "sudo ceph osd set norebalance" + changed_when: false + failed_when: "ceph_norebalance_result.rc != 0" + register: "ceph_norebalance_result" diff --git a/tasks/ceph/exit-maint.yaml b/tasks/ceph/exit-maint.yaml new file mode 100644 index 0000000..d79c6d8 --- /dev/null +++ b/tasks/ceph/exit-maint.yaml @@ -0,0 +1,43 @@ +--- + +# exit ceph maintenance mode +- name: "exit ceph maintenance mode" + block: + + # unset ceph osd noout settings + - name: "unset ceph noout" + ansible.builtin.shell: + cmd: "sudo ceph osd unset noout" + changed_when: false + failed_when: "ceph_noout_result.rc != 0" + register: "ceph_noout_result" + tags: "cluster" + + # unset ceph osd nobackfill settings + - name: "unset ceph nobackfill" + ansible.builtin.shell: + cmd: "sudo ceph osd unset nobackfill" + changed_when: false + failed_when: "ceph_nobackfill_result.rc != 0" + register: "ceph_nobackfill_result" + tags: "cluster" + + # unset ceph osd norebalance settings + - name: "unset ceph norebalance" + ansible.builtin.shell: + cmd: "sudo ceph osd unset norebalance" + changed_when: false + failed_when: "ceph_norebalance_result.rc != 0" + register: "ceph_norebalance_result" + tags: "cluster" + + # wait for ceph to be healthy + - name: "wait for ceph to be healthy" + ansible.builtin.shell: + cmd: "sudo ceph -s" + changed_when: false + delay: 10 + register: "ceph_status" + retries: 30 + tags: "cluster" + until: "'HEALTH_OK' in ceph_status.stdout" diff --git a/tasks/main.yaml b/tasks/main.yaml new file mode 100644 index 0000000..7a499e4 --- /dev/null +++ b/tasks/main.yaml @@ -0,0 +1,25 @@ +--- + +# update cluster +- name: "update cluster" + tags: "update-cluster" + block: + + # collect proxmox cluster nodes + - name: "collect proxmox cluster nodes" + set_fact: + leader_node: "{{ groups['proxmox'] | default([]) | first | default('') }}" + + # enter ceph maintenance mode + - name: "set ceph enter maintenance mode" + ansible.builtin.include_tasks: "ceph/enter-maint.yaml" + when: "inventory_hostname == leader_node" + + # update proxmox cluster + - name: "update proxmox cluster nodes" + ansible.builtin.include_tasks: "proxmox/update-node.yaml" + + # exit ceph maintenance mode + - name: "exit ceph maintenance mode" + ansible.builtin.include_tasks: "ceph/exit-maint.yaml" + when: "inventory_hostname == leader_node" diff --git a/tasks/proxmox/update-node.yaml b/tasks/proxmox/update-node.yaml new file mode 100644 index 0000000..3597ddf --- /dev/null +++ b/tasks/proxmox/update-node.yaml @@ -0,0 +1,64 @@ +--- + +# enter maintenance mode +- name: "enter maintenance mode" + ansible.builtin.shell: + cmd: "sudo /usr/sbin/ha-manager crm-command node-maintenance enable {{ inventory_hostname_short }}" + changed_when: false + failed_when: "maintenance_result.rc != 0" + register: "maintenance_result" + +# wait for host to be empty +- name: "wait for host to be empty" + ansible.builtin.shell: + cmd: "set -o pipefail; \ + sudo pvesh get /cluster/resources | \ + grep {{ inventory_hostname_short }} | \ + egrep -v \"node/pve|storage/pve|sdn/pve|template\"" + changed_when: false + delay: 10 + failed_when: "running_guests.rc >= 2" + register: "running_guests" + retries: 30 + until: "running_guests.stdout_lines | length == 0" + +# install node updates +- name: "install node updates" + ansible.builtin.apt: + dpkg_options: "force-confdef,force-confold" + force_apt_get: true + update_cache: true + upgrade: "dist" + changed_when: false + +# install firmware updates +- name: "install firmware updates" + ansible.builtin.shell: + cmd: "sudo fwupdmgr upgrade --no-reboot-check --assume-yes" + changed_when: false + failed_when: false + +# initiate reboot +- name: "reboot host" + ansible.builtin.reboot: + reboot_timeout: 300 + pre_reboot_delay: 5 + post_reboot_delay: 30 + test_command: "uptime" + changed_when: false + when: "reboot_required" + +# exit maintenance mode +- name: "exit maintenance mode" + ansible.builtin.shell: + cmd: "sudo /usr/sbin/ha-manager crm-command node-maintenance disable {{ inventory_hostname_short }}" + changed_when: false + delay: 10 + failed_when: "maintenance_exit_result.rc != 0" + register: "maintenance_exit_result" + retries: 3 + +# calm down period +- name: "calming down" + ansible.builtin.wait_for: + timeout: 120