Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe3c1a1410 |
@@ -409,45 +409,6 @@ profiles::ceph::client::mons:
|
|||||||
- 10.18.15.2
|
- 10.18.15.2
|
||||||
- 10.18.15.3
|
- 10.18.15.3
|
||||||
|
|
||||||
# de96a98f cluster topology (prodnxsr0001-0019) - single source of truth for
|
|
||||||
# /etc/ceph/ceph.conf rendered by profiles::ceph::client on the k8s (osd) and
|
|
||||||
# incus (mon/mgr/mds) roles. fsid/mons are overridden per-role in the role hiera.
|
|
||||||
# public_network is the /32 of every ceph host's ceph-public loopback.
|
|
||||||
profiles::ceph::client::cluster_public_ips:
|
|
||||||
- 198.18.23.1
|
|
||||||
- 198.18.23.2
|
|
||||||
- 198.18.23.3
|
|
||||||
- 198.18.23.4
|
|
||||||
- 198.18.23.5
|
|
||||||
- 198.18.23.6
|
|
||||||
- 198.18.23.7
|
|
||||||
- 198.18.23.8
|
|
||||||
- 198.18.23.9
|
|
||||||
- 198.18.23.10
|
|
||||||
- 198.18.23.11
|
|
||||||
- 198.18.23.12
|
|
||||||
- 198.18.23.13
|
|
||||||
- 198.18.23.14
|
|
||||||
- 198.18.23.15
|
|
||||||
- 198.18.23.16
|
|
||||||
- 198.18.23.17
|
|
||||||
- 198.18.23.18
|
|
||||||
- 198.18.23.19
|
|
||||||
profiles::ceph::client::mon_initial_members:
|
|
||||||
- prodnxsr0009
|
|
||||||
- prodnxsr0010
|
|
||||||
- prodnxsr0011
|
|
||||||
- prodnxsr0012
|
|
||||||
- prodnxsr0013
|
|
||||||
# two mds daemon instances per mon/mgr/mds host (rendered only where
|
|
||||||
# render_mds_config is true, i.e. the incus node role).
|
|
||||||
profiles::ceph::client::mds_instances:
|
|
||||||
prodnxsr0009: 2
|
|
||||||
prodnxsr0010: 2
|
|
||||||
prodnxsr0011: 2
|
|
||||||
prodnxsr0012: 2
|
|
||||||
prodnxsr0013: 2
|
|
||||||
|
|
||||||
profiles::ceph::conf::config:
|
profiles::ceph::conf::config:
|
||||||
global:
|
global:
|
||||||
auth_client_required: 'cephx'
|
auth_client_required: 'cephx'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
timezone::timezone: 'Australia/Sydney'
|
timezone::timezone: 'Australia/Sydney'
|
||||||
certbot::client::webserver: ausyd1nxvm2057.main.unkin.net
|
certbot::client::webserver: ausyd1nxvm2057.main.unkin.net
|
||||||
profiles_dns_upstream_forwarder_unkin:
|
profiles_dns_upstream_forwarder_unkin:
|
||||||
- 198.18.200.7
|
- 198.18.19.15
|
||||||
profiles_dns_upstream_forwarder_consul:
|
profiles_dns_upstream_forwarder_consul:
|
||||||
- 198.18.19.14
|
- 198.18.19.14
|
||||||
profiles_dns_upstream_forwarder_k8s:
|
profiles_dns_upstream_forwarder_k8s:
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
networking_loopback0_ip: 198.18.19.21 # management loopback
|
|
||||||
networking_loopback1_ip: 198.18.22.14 # ceph-cluster loopback
|
|
||||||
networking_loopback2_ip: 198.18.23.14 # ceph-public loopback
|
|
||||||
networking_1000_ip: 198.18.15.14 # 1gbe network
|
|
||||||
networking_2500_ip: 198.18.21.14 # 2.5gbe network
|
|
||||||
networking_1000_iface: enp1s0
|
|
||||||
networking_2500_iface: enp2s0
|
|
||||||
networking::interfaces:
|
|
||||||
"%{hiera('networking_1000_iface')}":
|
|
||||||
mac: a4:bb:6d:a4:e5:c1
|
|
||||||
"%{hiera('networking_2500_iface')}":
|
|
||||||
mac: c4:62:37:0d:50:03
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
networking_loopback0_ip: 198.18.19.22 # management loopback
|
|
||||||
networking_loopback1_ip: 198.18.22.15 # ceph-cluster loopback
|
|
||||||
networking_loopback2_ip: 198.18.23.15 # ceph-public loopback
|
|
||||||
networking_1000_ip: 198.18.15.15 # 1gbe network
|
|
||||||
networking_2500_ip: 198.18.21.15 # 2.5gbe network
|
|
||||||
networking_1000_iface: enp1s0
|
|
||||||
networking_2500_iface: enp2s0
|
|
||||||
networking::interfaces:
|
|
||||||
"%{hiera('networking_1000_iface')}":
|
|
||||||
mac: a4:bb:6d:a6:30:c4
|
|
||||||
"%{hiera('networking_2500_iface')}":
|
|
||||||
mac: c4:62:37:0d:4f:f4
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
networking_loopback0_ip: 198.18.19.23 # management loopback
|
|
||||||
networking_loopback1_ip: 198.18.22.16 # ceph-cluster loopback
|
|
||||||
networking_loopback2_ip: 198.18.23.16 # ceph-public loopback
|
|
||||||
networking_1000_ip: 198.18.15.16 # 1gbe network
|
|
||||||
networking_2500_ip: 198.18.21.16 # 2.5gbe network
|
|
||||||
networking_1000_iface: enp1s0
|
|
||||||
networking_2500_iface: enp2s0
|
|
||||||
networking::interfaces:
|
|
||||||
"%{hiera('networking_1000_iface')}":
|
|
||||||
mac: a4:bb:6d:9f:22:13
|
|
||||||
"%{hiera('networking_2500_iface')}":
|
|
||||||
mac: c4:62:37:0d:50:0c
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
networking_loopback0_ip: 198.18.19.24 # management loopback
|
|
||||||
networking_loopback1_ip: 198.18.22.17 # ceph-cluster loopback
|
|
||||||
networking_loopback2_ip: 198.18.23.17 # ceph-public loopback
|
|
||||||
networking_1000_ip: 198.18.15.17 # 1gbe network
|
|
||||||
networking_2500_ip: 198.18.21.17 # 2.5gbe network
|
|
||||||
networking_1000_iface: enp1s0
|
|
||||||
networking_2500_iface: enp2s0
|
|
||||||
networking::interfaces:
|
|
||||||
"%{hiera('networking_1000_iface')}":
|
|
||||||
mac: 8c:04:ba:9c:b6:08
|
|
||||||
"%{hiera('networking_2500_iface')}":
|
|
||||||
mac: c4:62:37:0d:50:12
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
networking_loopback0_ip: 198.18.19.25 # management loopback
|
|
||||||
networking_loopback1_ip: 198.18.22.18 # ceph-cluster loopback
|
|
||||||
networking_loopback2_ip: 198.18.23.18 # ceph-public loopback
|
|
||||||
networking_1000_ip: 198.18.15.18 # 1gbe network
|
|
||||||
networking_2500_ip: 198.18.21.18 # 2.5gbe network
|
|
||||||
networking_1000_iface: enp1s0
|
|
||||||
networking_2500_iface: enp2s0
|
|
||||||
networking::interfaces:
|
|
||||||
"%{hiera('networking_1000_iface')}":
|
|
||||||
mac: a4:bb:6d:a4:db:94
|
|
||||||
"%{hiera('networking_2500_iface')}":
|
|
||||||
mac: c4:62:37:0d:4f:fa
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
networking_loopback0_ip: 198.18.19.26 # management loopback
|
|
||||||
networking_loopback1_ip: 198.18.22.19 # ceph-cluster loopback
|
|
||||||
networking_loopback2_ip: 198.18.23.19 # ceph-public loopback
|
|
||||||
networking_1000_ip: 198.18.15.19 # 1gbe network
|
|
||||||
networking_2500_ip: 198.18.21.19 # 2.5gbe network
|
|
||||||
networking_1000_iface: enp1s0
|
|
||||||
networking_2500_iface: enp2s0
|
|
||||||
networking::interfaces:
|
|
||||||
"%{hiera('networking_1000_iface')}":
|
|
||||||
mac: a4:bb:6d:a4:56:11
|
|
||||||
"%{hiera('networking_2500_iface')}":
|
|
||||||
mac: c4:62:37:0d:50:00
|
|
||||||
@@ -203,9 +203,7 @@ profiles::accounts::sysadmin::extra_groups:
|
|||||||
- incus-admin
|
- incus-admin
|
||||||
|
|
||||||
# manage cephfs mounts
|
# manage cephfs mounts
|
||||||
profiles::ceph::client::manage_ceph_conf: true
|
profiles::ceph::client::manage_ceph_conf: false
|
||||||
# mon/mgr/mds host: render the [mds] + [mds.*] sections
|
|
||||||
profiles::ceph::client::render_mds_config: true
|
|
||||||
profiles::ceph::client::manage_ceph_package: false
|
profiles::ceph::client::manage_ceph_package: false
|
||||||
profiles::ceph::client::manage_ceph_paths: false
|
profiles::ceph::client::manage_ceph_paths: false
|
||||||
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
|
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ hiera_include:
|
|||||||
# manage rke2
|
# manage rke2
|
||||||
rke2::bootstrap_node: prodnxsr0001.main.unkin.net
|
rke2::bootstrap_node: prodnxsr0001.main.unkin.net
|
||||||
rke2::join_url: https://join-k8s.service.consul:9345
|
rke2::join_url: https://join-k8s.service.consul:9345
|
||||||
# pull the airgap image bundle via artifactapi (host-reachable pre-CNI), not github directly
|
|
||||||
rke2::container_archive_source: 'https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/rancher/rke2/releases/download'
|
|
||||||
rke2::manage_registries: true
|
rke2::manage_registries: true
|
||||||
rke2::registries:
|
rke2::registries:
|
||||||
docker.io:
|
docker.io:
|
||||||
@@ -92,7 +90,7 @@ profiles::packages::include:
|
|||||||
|
|
||||||
profiles::selinux::setenforce::mode: disabled
|
profiles::selinux::setenforce::mode: disabled
|
||||||
|
|
||||||
profiles::ceph::client::manage_ceph_conf: true
|
profiles::ceph::client::manage_ceph_conf: false
|
||||||
profiles::ceph::client::manage_ceph_package: false
|
profiles::ceph::client::manage_ceph_package: false
|
||||||
profiles::ceph::client::manage_ceph_paths: false
|
profiles::ceph::client::manage_ceph_paths: false
|
||||||
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
|
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ rke2::config_hash:
|
|||||||
- '--node-monitor-period=4s'
|
- '--node-monitor-period=4s'
|
||||||
protect-kernel-defaults: true
|
protect-kernel-defaults: true
|
||||||
disable-kube-proxy: false
|
disable-kube-proxy: false
|
||||||
# taint the control-plane/etcd nodes so only tolerating workloads schedule here
|
|
||||||
node-taint:
|
|
||||||
- "node-role.kubernetes.io/control-plane=true:NoSchedule"
|
|
||||||
|
|
||||||
# configure consul service
|
# configure consul service
|
||||||
consul::services:
|
consul::services:
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ profiles::packages::include:
|
|||||||
|
|
||||||
profiles::selinux::setenforce::mode: disabled
|
profiles::selinux::setenforce::mode: disabled
|
||||||
|
|
||||||
profiles::ceph::client::manage_ceph_conf: true
|
profiles::ceph::client::manage_ceph_conf: false
|
||||||
profiles::ceph::client::manage_ceph_package: false
|
profiles::ceph::client::manage_ceph_package: false
|
||||||
profiles::ceph::client::manage_ceph_paths: false
|
profiles::ceph::client::manage_ceph_paths: false
|
||||||
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
|
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
|
||||||
|
|||||||
@@ -40,11 +40,3 @@ profiles::packages::include:
|
|||||||
ensure: '0.1.1'
|
ensure: '0.1.1'
|
||||||
openbao-plugin-secrets-gitea:
|
openbao-plugin-secrets-gitea:
|
||||||
ensure: '0.1.0'
|
ensure: '0.1.0'
|
||||||
openbao-plugin-secrets-apptoken:
|
|
||||||
ensure: '0.1.0'
|
|
||||||
openbao-plugin-secrets-netbox:
|
|
||||||
ensure: '0.1.0'
|
|
||||||
openbao-plugin-secrets-ghp:
|
|
||||||
ensure: '0.1.0'
|
|
||||||
openbao-plugin-secrets-arrstack:
|
|
||||||
ensure: '0.2.0'
|
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
---
|
---
|
||||||
# physical hosts only (facts.virtual == 'physical'); merged 'unique' with the
|
|
||||||
# common hiera_include in profiles::base.
|
|
||||||
hiera_include:
|
|
||||||
- profiles::lldpd
|
|
||||||
|
|
||||||
profiles::packages::include:
|
profiles::packages::include:
|
||||||
"%{hiera('lm-sensors::package')}": {}
|
"%{hiera('lm-sensors::package')}": {}
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'facter'
|
|
||||||
require 'json'
|
|
||||||
|
|
||||||
# Exposes LLDP neighbour topology (switch/port each interface is cabled to) as
|
|
||||||
# the structured `lldp` fact, keyed by local interface. This is the only source
|
|
||||||
# of physical switch/port topology in the estate and feeds NetBox. Uses
|
|
||||||
# `lldpctl -f json0`: json0 wraps every node in an array regardless of
|
|
||||||
# cardinality, so one neighbour and many neighbours parse identically (plain
|
|
||||||
# `keyvalue` folds the neighbour's sysname into the key path, and plain `json`
|
|
||||||
# collapses single-element arrays into objects). Never raises: any error or a
|
|
||||||
# down daemon yields an empty hash so a puppet run can never break.
|
|
||||||
module LldpFact
|
|
||||||
SOCKETS = ['/run/lldpd.socket', '/var/run/lldpd.socket'].freeze
|
|
||||||
|
|
||||||
module_function
|
|
||||||
|
|
||||||
# First element of a json0 node (everything is array-wrapped), or the value
|
|
||||||
# itself if it is not an array; nil when absent.
|
|
||||||
def first(node)
|
|
||||||
node.is_a?(Array) ? node[0] : node
|
|
||||||
end
|
|
||||||
|
|
||||||
# Array form of a json0 node whatever its cardinality.
|
|
||||||
def list(node)
|
|
||||||
node.is_a?(Array) ? node : [node].compact
|
|
||||||
end
|
|
||||||
|
|
||||||
# Value string of a json0 leaf like [{ 'value' => 'x' }].
|
|
||||||
def leaf(node)
|
|
||||||
entry = first(node)
|
|
||||||
entry.is_a?(Hash) ? entry['value'] : entry
|
|
||||||
end
|
|
||||||
|
|
||||||
# Chassis MAC from its id list, preferring the entry typed 'mac'.
|
|
||||||
def chassis_mac(chassis)
|
|
||||||
ids = list(chassis['id'])
|
|
||||||
mac = ids.find { |id| id.is_a?(Hash) && id['type'] == 'mac' } || ids.first
|
|
||||||
mac.is_a?(Hash) ? mac['value'] : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Topology record for one local interface, or nil when it has no neighbour.
|
|
||||||
def neighbour(iface)
|
|
||||||
chassis = first(iface['chassis'])
|
|
||||||
port = first(iface['port'])
|
|
||||||
return nil unless chassis && port
|
|
||||||
|
|
||||||
chassis_fields(chassis).merge(port_fields(port, first(iface['vlan'])))
|
|
||||||
end
|
|
||||||
|
|
||||||
def chassis_fields(chassis)
|
|
||||||
{
|
|
||||||
'neighbor_chassis_name' => leaf(chassis['name']),
|
|
||||||
'neighbor_chassis_mac' => chassis_mac(chassis),
|
|
||||||
'neighbor_chassis_descr' => leaf(chassis['descr'])
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def port_fields(port, vlan)
|
|
||||||
port_id = first(port['id'])
|
|
||||||
vlan_h = vlan.is_a?(Hash) ? vlan : {}
|
|
||||||
{
|
|
||||||
'neighbor_port_id' => port_id.is_a?(Hash) ? port_id['value'] : port_id,
|
|
||||||
'neighbor_port_descr' => leaf(port['descr']),
|
|
||||||
'vlan_id' => vlan_h['vlan-id'],
|
|
||||||
'vlan_name' => vlan_h['value']
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def interfaces(output)
|
|
||||||
lldp = first(JSON.parse(output)['lldp']) || {}
|
|
||||||
list(lldp['interface'])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Map of local interface => topology record, skipping interfaces with no
|
|
||||||
# neighbour.
|
|
||||||
def collect(ifaces)
|
|
||||||
ifaces.each_with_object({}) do |iface, acc|
|
|
||||||
next unless iface.is_a?(Hash)
|
|
||||||
|
|
||||||
name = iface['name']
|
|
||||||
data = neighbour(iface)
|
|
||||||
acc[name] = data if name && data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def resolve
|
|
||||||
output = Facter::Core::Execution.execute('lldpctl -f json0 2>/dev/null', on_fail: nil)
|
|
||||||
return {} if output.to_s.empty?
|
|
||||||
|
|
||||||
collect(interfaces(output))
|
|
||||||
rescue StandardError
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Facter.add(:lldp) do
|
|
||||||
confine kernel: 'Linux'
|
|
||||||
confine { Facter.value(:is_virtual) == false }
|
|
||||||
confine { Facter::Core::Execution.which('lldpctl') }
|
|
||||||
confine { LldpFact::SOCKETS.any? { |path| File.exist?(path) } }
|
|
||||||
setcode { LldpFact.resolve }
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'facter'
|
||||||
|
require 'net/http'
|
||||||
|
require 'uri'
|
||||||
|
require 'json'
|
||||||
|
require 'fileutils'
|
||||||
|
require 'time'
|
||||||
|
|
||||||
|
# Serves this node's IP/interface data from NetBox as the structured `netbox`
|
||||||
|
# fact. NetBox is authoritative and static IPs never change, so every
|
||||||
|
# successful response is cached forever and reused verbatim whenever NetBox is
|
||||||
|
# unreachable: an outage serves stale-but-correct data and can never fail a
|
||||||
|
# puppet run. Only a host that was never cached returns nothing.
|
||||||
|
module NetboxFacts
|
||||||
|
TOKEN_FILE = '/etc/puppetlabs/netbox.token'
|
||||||
|
URL_FILE = '/etc/puppetlabs/netbox.url'
|
||||||
|
CACHE_FILE = '/var/cache/puppet-netbox/facts.json'
|
||||||
|
DEFAULT_URL = 'https://netbox.k8s.syd1.au.unkin.net'
|
||||||
|
|
||||||
|
# Talks to the NetBox REST API for one node and assembles the fact payload.
|
||||||
|
class Client
|
||||||
|
OPEN_TIMEOUT = 3
|
||||||
|
READ_TIMEOUT = 5
|
||||||
|
PAGE = 500
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@token = File.read(TOKEN_FILE).strip
|
||||||
|
@base = File.exist?(URL_FILE) ? File.read(URL_FILE).strip : DEFAULT_URL
|
||||||
|
@names = [Facter.value(:fqdn), Facter.value(:hostname)].compact.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
# Live payload, or nil when the node is absent from NetBox. Raises on any
|
||||||
|
# transport/parse error so the caller can fall back to cache.
|
||||||
|
def fetch
|
||||||
|
device, kind, filter, iface_base = locate
|
||||||
|
return nil unless device
|
||||||
|
|
||||||
|
primary = device.dig('primary_ip', 'address')
|
||||||
|
{
|
||||||
|
'source' => kind, 'name' => device['name'], 'primary_ip' => primary,
|
||||||
|
'interfaces' => interfaces(iface_base, filter, primary),
|
||||||
|
'fetched_at' => Time.now.utc.iso8601, 'cached' => false
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def locate
|
||||||
|
device = results("/api/dcim/devices/?#{name_query}").first
|
||||||
|
return [device, 'device', "device_id=#{device['id']}", '/api/dcim/interfaces/'] if device
|
||||||
|
|
||||||
|
vm = results("/api/virtualization/virtual-machines/?#{name_query}").first
|
||||||
|
vm && [vm, 'vm', "virtual_machine_id=#{vm['id']}", '/api/virtualization/interfaces/']
|
||||||
|
end
|
||||||
|
|
||||||
|
def name_query
|
||||||
|
@names.map { |name| "name=#{URI.encode_www_form_component(name)}" }.join('&')
|
||||||
|
end
|
||||||
|
|
||||||
|
def interfaces(iface_base, filter, primary)
|
||||||
|
ips = ips_by_interface(filter)
|
||||||
|
results("#{iface_base}?#{filter}&limit=#{PAGE}").map do |iface|
|
||||||
|
addrs = ips[iface['id']] || []
|
||||||
|
mac = iface['mac_address'] || iface.dig('primary_mac_address', 'mac_address')
|
||||||
|
{ 'name' => iface['name'], 'mac' => mac, 'ips' => addrs, 'primary' => addrs.include?(primary) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ips_by_interface(filter)
|
||||||
|
results("/api/ipam/ip-addresses/?#{filter}&limit=#{PAGE}").each_with_object({}) do |ip, map|
|
||||||
|
(map[ip['assigned_object_id']] ||= []) << ip['address']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def results(path)
|
||||||
|
JSON.parse(get(URI("#{@base}#{path}")).body).fetch('results', [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(uri)
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = uri.scheme == 'https'
|
||||||
|
http.open_timeout = OPEN_TIMEOUT
|
||||||
|
http.read_timeout = READ_TIMEOUT
|
||||||
|
response = http.request(request_for(uri))
|
||||||
|
return response if response.is_a?(Net::HTTPSuccess)
|
||||||
|
|
||||||
|
raise "netbox #{uri.path} -> HTTP #{response.code}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_for(uri)
|
||||||
|
request = Net::HTTP::Get.new(uri)
|
||||||
|
request['Authorization'] = "Token #{@token}"
|
||||||
|
request['Accept'] = 'application/json'
|
||||||
|
request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def write_cache(data)
|
||||||
|
FileUtils.mkdir_p(File.dirname(CACHE_FILE))
|
||||||
|
File.write(CACHE_FILE, JSON.pretty_generate(data))
|
||||||
|
File.chmod(0o600, CACHE_FILE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_cache
|
||||||
|
return nil unless File.exist?(CACHE_FILE)
|
||||||
|
|
||||||
|
JSON.parse(File.read(CACHE_FILE)).merge('cached' => true)
|
||||||
|
rescue StandardError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve
|
||||||
|
data = Client.new.fetch
|
||||||
|
return read_cache unless data
|
||||||
|
|
||||||
|
write_cache(data)
|
||||||
|
data
|
||||||
|
rescue StandardError => e
|
||||||
|
Facter.warn("netbox fact: live fetch failed (#{e.message}); serving cache")
|
||||||
|
read_cache
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Facter.add(:netbox) do
|
||||||
|
confine { File.exist?(NetboxFacts::TOKEN_FILE) }
|
||||||
|
setcode { NetboxFacts.resolve }
|
||||||
|
end
|
||||||
@@ -6,17 +6,10 @@ class rke2::install (
|
|||||||
Stdlib::HTTPUrl $container_archive_source = $rke2::container_archive_source,
|
Stdlib::HTTPUrl $container_archive_source = $rke2::container_archive_source,
|
||||||
){
|
){
|
||||||
|
|
||||||
# versionlock rke2 before install so the lock exists before any upgrade is attempted
|
# versionlock rke2
|
||||||
yum::versionlock{"rke2-${node_type}":
|
yum::versionlock{"rke2-${node_type}":
|
||||||
ensure => present,
|
ensure => present,
|
||||||
version => "${rke2_version}~${rke2_release}",
|
version => "${rke2_version}~${rke2_release}",
|
||||||
before => Package["rke2-${node_type}"],
|
|
||||||
}
|
|
||||||
|
|
||||||
# lock rke2-common (a strict = version dep) so the rolling latest channel can't drift it ahead of the pinned server/agent
|
|
||||||
yum::versionlock{'rke2-common':
|
|
||||||
ensure => present,
|
|
||||||
version => "${rke2_version}~${rke2_release}",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# install rke2
|
# install rke2
|
||||||
@@ -34,10 +27,10 @@ class rke2::install (
|
|||||||
before => Service["rke2-${node_type}"],
|
before => Service["rke2-${node_type}"],
|
||||||
}
|
}
|
||||||
|
|
||||||
# preload the airgap bundle (has the default canal CNI images) so canal starts from disk, not the mirror VIP that needs flannel first
|
# download required archive of containers
|
||||||
archive { '/var/lib/rancher/rke2/agent/images/rke2-images.linux-amd64.tar.zst':
|
archive { '/var/lib/rancher/rke2/agent/images/rke2-images.linux-amd64.tar.zst':
|
||||||
ensure => present,
|
ensure => present,
|
||||||
source => "${container_archive_source}/v${rke2_version}%2B${rke2_release}/rke2-images.linux-amd64.tar.zst",
|
source => "https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/rancher/rke2/releases/download/v${rke2_version}%2B${rke2_release}/rke2-images.linux-amd64.tar.zst",
|
||||||
require => [
|
require => [
|
||||||
Package["rke2-${node_type}"],
|
Package["rke2-${node_type}"],
|
||||||
File['/var/lib/rancher/rke2/agent/images'],
|
File['/var/lib/rancher/rke2/agent/images'],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# rke2 params
|
# rke2 params
|
||||||
class rke2::params (
|
class rke2::params (
|
||||||
Enum['server', 'agent'] $node_type = 'agent',
|
Enum['server', 'agent'] $node_type = 'agent',
|
||||||
String $rke2_version = '1.33.13',
|
String $rke2_version = '1.33.4',
|
||||||
String $rke2_release = 'rke2r2',
|
String $rke2_release = 'rke2r1',
|
||||||
Stdlib::Absolutepath $config_file = '/etc/rancher/rke2/config.yaml',
|
Stdlib::Absolutepath $config_file = '/etc/rancher/rke2/config.yaml',
|
||||||
Hash $config_hash = {},
|
Hash $config_hash = {},
|
||||||
Stdlib::HTTPSUrl $join_url = 'https://127.0.0.1:9345',
|
Stdlib::HTTPSUrl $join_url = 'https://127.0.0.1:9345',
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class profiles::base () {
|
|||||||
include profiles::ssh::service
|
include profiles::ssh::service
|
||||||
include profiles::cloudinit::init
|
include profiles::cloudinit::init
|
||||||
include profiles::helpers::node_lookup
|
include profiles::helpers::node_lookup
|
||||||
|
include profiles::netbox::facts
|
||||||
include profiles::consul::client
|
include profiles::consul::client
|
||||||
include victorialogs::client::journald
|
include victorialogs::client::journald
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
class profiles::ceph::client (
|
class profiles::ceph::client (
|
||||||
String $fsid,
|
String $fsid,
|
||||||
Array[Stdlib::Host] $mons,
|
Array[Stdlib::Host] $mons,
|
||||||
# cluster topology (single source of truth: hieradata/common.yaml)
|
|
||||||
Array[Stdlib::Host] $cluster_public_ips,
|
|
||||||
Array[Stdlib::Host] $mon_initial_members,
|
|
||||||
Stdlib::Absolutepath $config_file = '/etc/ceph/ceph.conf',
|
Stdlib::Absolutepath $config_file = '/etc/ceph/ceph.conf',
|
||||||
Boolean $manage_ceph_conf = true,
|
Boolean $manage_ceph_conf = true,
|
||||||
Boolean $manage_ceph_package = true,
|
Boolean $manage_ceph_package = true,
|
||||||
@@ -13,24 +10,6 @@ class profiles::ceph::client (
|
|||||||
String $group = 'ceph',
|
String $group = 'ceph',
|
||||||
Stdlib::Filemode $mode = '0644',
|
Stdlib::Filemode $mode = '0644',
|
||||||
Hash $keyrings = {},
|
Hash $keyrings = {},
|
||||||
# [global] tunables (defaults match the live hand-maintained ceph.conf)
|
|
||||||
String $auth_client_required = 'cephx',
|
|
||||||
String $auth_cluster_required = 'cephx',
|
|
||||||
String $auth_service_required = 'cephx',
|
|
||||||
Boolean $mon_allow_pool_delete = true,
|
|
||||||
Boolean $ms_bind_ipv4 = true,
|
|
||||||
Boolean $ms_bind_ipv6 = false,
|
|
||||||
Integer $osd_crush_chooseleaf_type = 1,
|
|
||||||
Integer $osd_pool_default_min_size = 2,
|
|
||||||
Integer $osd_pool_default_size = 3,
|
|
||||||
Integer $osd_pool_default_pg_num = 128,
|
|
||||||
# mds config sections; only rendered on mon/mgr/mds hosts (render_mds_config)
|
|
||||||
Boolean $render_mds_config = false,
|
|
||||||
Hash[String, Integer] $mds_instances = {},
|
|
||||||
Hash $mds_common = {
|
|
||||||
'keyring' => '/var/lib/ceph/mds/ceph-$id/keyring',
|
|
||||||
'mds_standby_replay' => true,
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
# dont run this on proxmox nodes
|
# dont run this on proxmox nodes
|
||||||
@@ -43,13 +22,6 @@ class profiles::ceph::client (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# only depend on the package when this class manages it; on the ceph
|
|
||||||
# hosts the package is delivered by cephadm / profiles::packages instead.
|
|
||||||
$config_require = $manage_ceph_package ? {
|
|
||||||
true => Package['ceph-common'],
|
|
||||||
default => undef,
|
|
||||||
}
|
|
||||||
|
|
||||||
# manage the ceph directory
|
# manage the ceph directory
|
||||||
if $manage_ceph_paths {
|
if $manage_ceph_paths {
|
||||||
file { '/etc/ceph':
|
file { '/etc/ceph':
|
||||||
@@ -57,11 +29,11 @@ class profiles::ceph::client (
|
|||||||
owner => $owner,
|
owner => $owner,
|
||||||
group => $group,
|
group => $group,
|
||||||
mode => $mode,
|
mode => $mode,
|
||||||
require => $config_require,
|
require => Package['ceph-common'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# render /etc/ceph/ceph.conf from cluster topology in hiera
|
# create a basic client config
|
||||||
if $manage_ceph_conf {
|
if $manage_ceph_conf {
|
||||||
file { $config_file:
|
file { $config_file:
|
||||||
ensure => file,
|
ensure => file,
|
||||||
@@ -69,7 +41,7 @@ class profiles::ceph::client (
|
|||||||
group => $group,
|
group => $group,
|
||||||
mode => $mode,
|
mode => $mode,
|
||||||
content => template('profiles/ceph/client.conf.erb'),
|
content => template('profiles/ceph/client.conf.erb'),
|
||||||
require => $config_require,
|
require => Package['ceph-common'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ class profiles::ceph::osd (
|
|||||||
Boolean $ensure_running = true,
|
Boolean $ensure_running = true,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
# tune the I/O scheduler on the disks backing ceph OSDs
|
|
||||||
include profiles::ceph::osd_scheduler
|
|
||||||
|
|
||||||
if $ensure_running and $facts['is_ceph_osd'] {
|
if $ensure_running and $facts['is_ceph_osd'] {
|
||||||
$facts['ceph_services']['osd'].each |String $svc| {
|
$facts['ceph_services']['osd'].each |String $svc| {
|
||||||
service { $svc:
|
service { $svc:
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
class profiles::ceph::osd_scheduler (
|
|
||||||
String[1] $scheduler = 'none',
|
|
||||||
) {
|
|
||||||
|
|
||||||
$devices = $facts['ceph_osd_devices']
|
|
||||||
|
|
||||||
# no-op where the fact is absent/empty (VMs, non-OSD hosts have no ceph PVs)
|
|
||||||
if $devices =~ Array[String[1], 1] {
|
|
||||||
|
|
||||||
# strip /dev/ so the rule matches the udev KERNEL sysname (e.g. sda)
|
|
||||||
$kernel_names = $devices.map |$dev| { regsubst($dev, '^.*/', '') }
|
|
||||||
$sysname_matches = $kernel_names.map |$name| { "--sysname-match=${name}" }
|
|
||||||
|
|
||||||
file { '/etc/udev/rules.d/60-ceph-osd-scheduler.rules':
|
|
||||||
ensure => file,
|
|
||||||
owner => 'root',
|
|
||||||
group => 'root',
|
|
||||||
mode => '0644',
|
|
||||||
content => template('profiles/ceph/osd-scheduler.rules.erb'),
|
|
||||||
notify => Exec['ceph-osd-scheduler-reload'],
|
|
||||||
}
|
|
||||||
|
|
||||||
# apply immediately; udev re-applies on reboot and device re-add
|
|
||||||
$trigger = "udevadm trigger --subsystem-match=block --action=change ${join($sysname_matches, ' ')}"
|
|
||||||
|
|
||||||
exec { 'ceph-osd-scheduler-reload':
|
|
||||||
command => "udevadm control --reload-rules && ${trigger}",
|
|
||||||
path => ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
|
|
||||||
refreshonly => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# profiles::lldpd
|
|
||||||
#
|
|
||||||
# Runs lldpd on physical hosts so each machine learns its switch/port topology
|
|
||||||
# via LLDP. The `lldp` fact exposes that neighbour data for NetBox. Assigned
|
|
||||||
# via hiera_include from hieradata/virtual/physical.yaml (physicals only); the
|
|
||||||
# lldpd.service ships disabled, so it is explicitly enabled and started here.
|
|
||||||
class profiles::lldpd (
|
|
||||||
Boolean $enabled = true,
|
|
||||||
String $package = 'lldpd',
|
|
||||||
String $service = 'lldpd',
|
|
||||||
){
|
|
||||||
|
|
||||||
if $enabled {
|
|
||||||
package { $package:
|
|
||||||
ensure => installed,
|
|
||||||
}
|
|
||||||
|
|
||||||
service { $service:
|
|
||||||
ensure => running,
|
|
||||||
enable => true,
|
|
||||||
subscribe => Package[$package],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
service { $service:
|
|
||||||
ensure => stopped,
|
|
||||||
enable => false,
|
|
||||||
}
|
|
||||||
|
|
||||||
package { $package:
|
|
||||||
ensure => absent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# profiles::netbox::facts
|
||||||
|
#
|
||||||
|
# Seeds the credentials the `netbox` custom fact needs to read this node's
|
||||||
|
# IP/interface data from NetBox. Inert until $api_token is set: with no token
|
||||||
|
# the fact is confined off (unenrolled hosts no-op).
|
||||||
|
#
|
||||||
|
# NetBox is authoritative and static IPs never change, so the fact caches every
|
||||||
|
# successful response under $cache_dir forever and reuses it during a NetBox
|
||||||
|
# outage - an outage can never fail a puppet run.
|
||||||
|
class profiles::netbox::facts (
|
||||||
|
Optional[Sensitive[String]] $api_token = undef,
|
||||||
|
Stdlib::HTTPSUrl $url = 'https://netbox.k8s.syd1.au.unkin.net',
|
||||||
|
Stdlib::AbsolutePath $token_file = '/etc/puppetlabs/netbox.token',
|
||||||
|
Stdlib::AbsolutePath $url_file = '/etc/puppetlabs/netbox.url',
|
||||||
|
Stdlib::AbsolutePath $cache_dir = '/var/cache/puppet-netbox',
|
||||||
|
) {
|
||||||
|
|
||||||
|
if $api_token =~ Undef {
|
||||||
|
notify { 'netbox-facts-inert':
|
||||||
|
message => 'profiles::netbox::facts: api_token unset; netbox fact disabled on this host.',
|
||||||
|
loglevel => 'info',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
file { $token_file:
|
||||||
|
ensure => file,
|
||||||
|
owner => 'root',
|
||||||
|
group => 'root',
|
||||||
|
mode => '0600',
|
||||||
|
show_diff => false,
|
||||||
|
content => Sensitive("${api_token.unwrap}\n"),
|
||||||
|
}
|
||||||
|
|
||||||
|
file { $url_file:
|
||||||
|
ensure => file,
|
||||||
|
owner => 'root',
|
||||||
|
group => 'root',
|
||||||
|
mode => '0644',
|
||||||
|
content => "${url}\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
file { $cache_dir:
|
||||||
|
ensure => directory,
|
||||||
|
owner => 'root',
|
||||||
|
group => 'root',
|
||||||
|
mode => '0700',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,11 +60,10 @@ class profiles::puppet::agent (
|
|||||||
require => Yumrepo[$use_yumrepo],
|
require => Yumrepo[$use_yumrepo],
|
||||||
}
|
}
|
||||||
|
|
||||||
# versionlock puppet-agent before install so the lock exists before any upgrade is attempted
|
# versionlock puppet-agent
|
||||||
yum::versionlock{$use_package:
|
yum::versionlock{$use_package:
|
||||||
ensure => $agent_versionlock_ensure,
|
ensure => $agent_versionlock_ensure,
|
||||||
version => $agent_versionlock_version,
|
version => $agent_versionlock_version,
|
||||||
before => Package[$use_package],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'Debian': {
|
'Debian': {
|
||||||
|
|||||||
@@ -130,20 +130,6 @@ class profiles::vault::server (
|
|||||||
mode => '0600',
|
mode => '0600',
|
||||||
}
|
}
|
||||||
|
|
||||||
# go-plugin creates each secrets plugin's control socket under TMPDIR
|
|
||||||
# (/tmp/pluginNNN by default); systemd-tmpfiles-clean reaps aged /tmp files
|
|
||||||
# and severs the socket of a long-lived plugin, orphaning the process. Point
|
|
||||||
# TMPDIR at a per-start RuntimeDirectory on /run (tmpfs, no age cleanup).
|
|
||||||
systemd::manage_dropin { 'plugin-tmpdir.conf':
|
|
||||||
unit => 'vault.service',
|
|
||||||
service_entry => {
|
|
||||||
'RuntimeDirectory' => 'vault-plugins',
|
|
||||||
'RuntimeDirectoryMode' => '0700',
|
|
||||||
'Environment' => 'TMPDIR=/run/vault-plugins',
|
|
||||||
},
|
|
||||||
notify => Service['vault'],
|
|
||||||
}
|
|
||||||
|
|
||||||
service { 'vault':
|
service { 'vault':
|
||||||
ensure => true,
|
ensure => true,
|
||||||
enable => true,
|
enable => true,
|
||||||
|
|||||||
@@ -1,28 +1,3 @@
|
|||||||
[global]
|
[global]
|
||||||
auth_client_required = <%= @auth_client_required %>
|
fsid = <%= @fsid %>
|
||||||
auth_cluster_required = <%= @auth_cluster_required %>
|
mon_host = <%= @mons.join(' ') %>
|
||||||
auth_service_required = <%= @auth_service_required %>
|
|
||||||
fsid = <%= @fsid %>
|
|
||||||
mon_allow_pool_delete = <%= @mon_allow_pool_delete %>
|
|
||||||
mon_initial_members = <%= @mon_initial_members.join(',') %>
|
|
||||||
mon_host = <%= @mons.join(',') %>
|
|
||||||
ms_bind_ipv4 = <%= @ms_bind_ipv4 %>
|
|
||||||
ms_bind_ipv6 = <%= @ms_bind_ipv6 %>
|
|
||||||
osd_crush_chooseleaf_type = <%= @osd_crush_chooseleaf_type %>
|
|
||||||
osd_pool_default_min_size = <%= @osd_pool_default_min_size %>
|
|
||||||
osd_pool_default_size = <%= @osd_pool_default_size %>
|
|
||||||
osd_pool_default_pg_num = <%= @osd_pool_default_pg_num %>
|
|
||||||
public_network = <%= @cluster_public_ips.map { |ip| "#{ip}/32" }.join(',') %>
|
|
||||||
<% if @render_mds_config -%>
|
|
||||||
|
|
||||||
[mds]
|
|
||||||
keyring = <%= @mds_common['keyring'] %>
|
|
||||||
mds_standby_replay = <%= @mds_common['mds_standby_replay'] %>
|
|
||||||
<% @mds_instances.sort.each do |host, count| -%>
|
|
||||||
<% (1..count).each do |instance| -%>
|
|
||||||
|
|
||||||
[mds.<%= host %>-<%= instance %>]
|
|
||||||
host = <%= host %>
|
|
||||||
<% end -%>
|
|
||||||
<% end -%>
|
|
||||||
<% end -%>
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# Managed by puppet (profiles::ceph::osd_scheduler).
|
|
||||||
# Set the I/O scheduler to <%= @scheduler %> on ceph OSD block devices.
|
|
||||||
<% @kernel_names.sort.each do |dev| -%>
|
|
||||||
ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="<%= dev %>", ATTR{queue/scheduler}="<%= @scheduler %>"
|
|
||||||
<% end -%>
|
|
||||||
Reference in New Issue
Block a user