deploy hermes
This commit is contained in:
400
ansible/playbooks/day1_configure_netbox_catalog.yml
Normal file
400
ansible/playbooks/day1_configure_netbox_catalog.yml
Normal file
@@ -0,0 +1,400 @@
|
||||
---
|
||||
# ansible/playbooks/day1_configure_netbox_catalog.yml
|
||||
#
|
||||
# Creates the tag taxonomy and custom fields in NetBox for service catalog
|
||||
# documentation. Run once (idempotent — uses name-based checks).
|
||||
#
|
||||
# Usage (from ansible/ directory):
|
||||
# ansible-playbook playbooks/day1_configure_netbox_catalog.yml
|
||||
#
|
||||
# Requires:
|
||||
# - vault_netbox_token in Ansible Vault
|
||||
# - netbox reachable at http://fire-station.local.mk-labs.cloud
|
||||
|
||||
- name: Configure NetBox service catalog taxonomy
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
vars_files:
|
||||
- "{{ playbook_dir }}/../group_vars/all/vault"
|
||||
|
||||
vars:
|
||||
netbox_url: "http://fire-station.local.mk-labs.cloud"
|
||||
netbox_token: "{{ vault_netbox_token }}"
|
||||
netbox_api: "{{ netbox_url }}/api"
|
||||
headers:
|
||||
Authorization: "Token {{ netbox_token }}"
|
||||
Content-Type: "application/json"
|
||||
Accept: "application/json"
|
||||
|
||||
# ── Tag definitions ───────────────────────────────────────────────────
|
||||
tags:
|
||||
# Infrastructure type
|
||||
- name: k8s
|
||||
slug: k8s
|
||||
color: "2196f3" # blue
|
||||
description: "Workload running in the fastpass Kubernetes cluster"
|
||||
- name: vm
|
||||
slug: vm
|
||||
color: "4caf50" # green
|
||||
description: "Traditional VM or LXC on Proxmox"
|
||||
# Service tier
|
||||
- name: platform
|
||||
slug: platform
|
||||
color: "9c27b0" # purple
|
||||
description: "Platform/infrastructure service (not user-facing)"
|
||||
- name: application
|
||||
slug: application
|
||||
color: "ff9800" # orange
|
||||
description: "User-facing application workload"
|
||||
# Service categories
|
||||
- name: monitoring
|
||||
slug: monitoring
|
||||
color: "607d8b" # grey
|
||||
description: "Metrics, logging, alerting"
|
||||
- name: auth
|
||||
slug: auth
|
||||
color: "607d8b"
|
||||
description: "Authentication and SSO"
|
||||
- name: gitops
|
||||
slug: gitops
|
||||
color: "607d8b"
|
||||
description: "GitOps and CI/CD"
|
||||
- name: dashboard
|
||||
slug: dashboard
|
||||
color: "607d8b"
|
||||
description: "Dashboard and portal services"
|
||||
- name: storage
|
||||
slug: storage
|
||||
color: "607d8b"
|
||||
description: "Storage and file services"
|
||||
- name: dns
|
||||
slug: dns
|
||||
color: "607d8b"
|
||||
description: "DNS and name resolution"
|
||||
- name: automation
|
||||
slug: automation
|
||||
color: "607d8b"
|
||||
description: "Automation and orchestration"
|
||||
- name: networking
|
||||
slug: networking
|
||||
color: "607d8b"
|
||||
description: "Network infrastructure services"
|
||||
- name: inference
|
||||
slug: inference
|
||||
color: "607d8b"
|
||||
description: "AI/ML inference workloads"
|
||||
|
||||
# ── Custom field definitions ──────────────────────────────────────────
|
||||
# object_types use app_label.model format
|
||||
custom_fields:
|
||||
- name: hostnames
|
||||
label: Hostnames
|
||||
type: longtext
|
||||
object_types:
|
||||
- ipam.ipaddress
|
||||
- virtualization.virtualmachine
|
||||
description: "All DNS names that resolve to this service (comma-separated)"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
|
||||
- name: namespace
|
||||
label: Namespace
|
||||
type: text
|
||||
object_types:
|
||||
- ipam.ipaddress
|
||||
- virtualization.virtualmachine
|
||||
description: "Kubernetes namespace (blank for VM-based services)"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
|
||||
- name: managed_by
|
||||
label: Managed By
|
||||
type: select
|
||||
object_types:
|
||||
- ipam.ipaddress
|
||||
- virtualization.virtualmachine
|
||||
description: "How this service is managed"
|
||||
choices:
|
||||
- ArgoCD
|
||||
- Ansible
|
||||
- Manual
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
|
||||
- name: thematic_name
|
||||
label: Thematic Name
|
||||
type: text
|
||||
object_types:
|
||||
- ipam.ipaddress
|
||||
- virtualization.virtualmachine
|
||||
description: "Disney/Magic Kingdom thematic hostname for this service"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
|
||||
tasks:
|
||||
|
||||
# ── Tags ─────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Fetch existing tags
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/tags/?limit=200"
|
||||
method: GET
|
||||
headers: "{{ headers }}"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
register: existing_tags_response
|
||||
|
||||
- name: Set existing tag slugs fact
|
||||
ansible.builtin.set_fact:
|
||||
existing_tag_slugs: "{{ existing_tags_response.json.results | map(attribute='slug') | list }}"
|
||||
|
||||
- name: Create tags
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/tags/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: "{{ item.name }}"
|
||||
slug: "{{ item.slug }}"
|
||||
color: "{{ item.color }}"
|
||||
description: "{{ item.description }}"
|
||||
status_code: 201
|
||||
loop: "{{ tags }}"
|
||||
when: item.slug not in existing_tag_slugs
|
||||
register: tag_creation
|
||||
changed_when: tag_creation.status == 201
|
||||
|
||||
# ── Custom Fields ─────────────────────────────────────────────────────
|
||||
|
||||
- name: Fetch existing custom fields
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/?limit=200"
|
||||
method: GET
|
||||
headers: "{{ headers }}"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
register: existing_cf_response
|
||||
|
||||
- name: Set existing custom field names fact
|
||||
ansible.builtin.set_fact:
|
||||
existing_cf_names: "{{ existing_cf_response.json.results | map(attribute='name') | list }}"
|
||||
|
||||
- name: Create custom field — hostnames
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: hostnames
|
||||
label: Hostnames
|
||||
type: longtext
|
||||
object_types: "{{ custom_fields[0].object_types }}"
|
||||
description: "{{ custom_fields[0].description }}"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
status_code: 201
|
||||
when: "'hostnames' not in existing_cf_names"
|
||||
register: cf_hostnames
|
||||
changed_when: cf_hostnames.status == 201
|
||||
|
||||
- name: Create custom field — namespace
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: namespace
|
||||
label: Namespace
|
||||
type: text
|
||||
object_types: "{{ custom_fields[1].object_types }}"
|
||||
description: "{{ custom_fields[1].description }}"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
status_code: 201
|
||||
when: "'namespace' not in existing_cf_names"
|
||||
register: cf_namespace
|
||||
changed_when: cf_namespace.status == 201
|
||||
|
||||
- name: Create choice set for managed_by field
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: managed-by-choices
|
||||
extra_choices:
|
||||
- - ArgoCD
|
||||
- ArgoCD
|
||||
- - Ansible
|
||||
- Ansible
|
||||
- - Manual
|
||||
- Manual
|
||||
status_code: [201, 400]
|
||||
register: choice_set
|
||||
changed_when: choice_set.status == 201
|
||||
|
||||
- name: Fetch choice set ID
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=managed-by-choices"
|
||||
method: GET
|
||||
headers: "{{ headers }}"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
register: choice_set_response
|
||||
|
||||
- name: Create custom field — managed_by
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: managed_by
|
||||
label: Managed By
|
||||
type: select
|
||||
object_types: "{{ custom_fields[2].object_types }}"
|
||||
description: "{{ custom_fields[2].description }}"
|
||||
choice_set: "{{ choice_set_response.json.results[0].id }}"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
status_code: 201
|
||||
when: "'managed_by' not in existing_cf_names"
|
||||
register: cf_managed_by
|
||||
changed_when: cf_managed_by.status == 201
|
||||
|
||||
- name: Create custom field — thematic_name
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: thematic_name
|
||||
label: Thematic Name
|
||||
type: text
|
||||
object_types: "{{ custom_fields[3].object_types }}"
|
||||
description: "{{ custom_fields[3].description }}"
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
status_code: 201
|
||||
when: "'thematic_name' not in existing_cf_names"
|
||||
register: cf_thematic_name
|
||||
changed_when: cf_thematic_name.status == 201
|
||||
|
||||
# ── Provisioning Pipeline Fields ──────────────────────────────────────
|
||||
# These fields drive the NetBox → n8n → Terraform pipeline.
|
||||
# proxmox_datastore already exists — PATCH it to add utilidor choice.
|
||||
# data_disk_enabled and data_disk_size_gb are new POSTs.
|
||||
|
||||
- name: Fetch proxmox_datastore field ID
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/?name=proxmox_datastore"
|
||||
method: GET
|
||||
headers: "{{ headers }}"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
register: proxmox_datastore_cf_response
|
||||
|
||||
- name: Fetch proxmox-datastore-choices choice set ID
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
|
||||
method: GET
|
||||
headers: "{{ headers }}"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
register: datastore_choice_set_response
|
||||
|
||||
- name: Create proxmox-datastore-choices choice set if missing
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: proxmox-datastore-choices
|
||||
extra_choices:
|
||||
- - liberty-tree
|
||||
- liberty-tree
|
||||
- - utilidor
|
||||
- utilidor
|
||||
status_code: [201, 400]
|
||||
register: datastore_choice_set_create
|
||||
changed_when: datastore_choice_set_create.status == 201
|
||||
when: datastore_choice_set_response.json.count == 0
|
||||
|
||||
- name: Re-fetch proxmox-datastore-choices choice set ID after possible creation
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
|
||||
method: GET
|
||||
headers: "{{ headers }}"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
register: datastore_choice_set_response
|
||||
|
||||
- name: Patch proxmox_datastore field to use choice set with utilidor
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/{{ proxmox_datastore_cf_response.json.results[0].id }}/"
|
||||
method: PATCH
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
choice_set: "{{ datastore_choice_set_response.json.results[0].id }}"
|
||||
status_code: 200
|
||||
when: proxmox_datastore_cf_response.json.count > 0
|
||||
register: cf_datastore_patch
|
||||
changed_when: cf_datastore_patch.status == 200
|
||||
|
||||
- name: Create custom field — data_disk_enabled
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: data_disk_enabled
|
||||
label: Data Disk Enabled
|
||||
type: boolean
|
||||
object_types:
|
||||
- virtualization.virtualmachine
|
||||
description: "When true, Terraform provisions a second disk for application data storage."
|
||||
default: false
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
status_code: 201
|
||||
when: "'data_disk_enabled' not in existing_cf_names"
|
||||
register: cf_data_disk_enabled
|
||||
changed_when: cf_data_disk_enabled.status == 201
|
||||
|
||||
- name: Create custom field — data_disk_size_gb
|
||||
ansible.builtin.uri:
|
||||
url: "{{ netbox_api }}/extras/custom-fields/"
|
||||
method: POST
|
||||
headers: "{{ headers }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: data_disk_size_gb
|
||||
label: Data Disk Size (GB)
|
||||
type: integer
|
||||
object_types:
|
||||
- virtualization.virtualmachine
|
||||
description: "Size in GB for the optional second data disk. Only used when data_disk_enabled is true."
|
||||
ui_visible: always
|
||||
ui_editable: yes
|
||||
status_code: 201
|
||||
when: "'data_disk_size_gb' not in existing_cf_names"
|
||||
register: cf_data_disk_size
|
||||
changed_when: cf_data_disk_size.status == 201
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Tags created: {{ tag_creation.results | selectattr('status', 'equalto', 201) | list | length }}"
|
||||
- "Tags skipped (already exist): {{ tag_creation.results | selectattr('skipped', 'defined') | list | length }}"
|
||||
- "Custom fields configured: hostnames, namespace, managed_by, thematic_name, proxmox_datastore (patched), data_disk_enabled, data_disk_size_gb"
|
||||
Reference in New Issue
Block a user