From 64563902d48cb83aa6eb172605dc2cd8033e9cec Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 31 Mar 2024 20:21:55 +1100 Subject: [PATCH] feat: deploy cobbler enc - install python3.11 on all nodes - create python3.11 venv for cobbler-enc - install requirements in cobbler-enc venv - symlink to /usr/local/bin/ --- hieradata/common.yaml | 1 + hieradata/roles/infra/puppet/master.yaml | 6 ++ site/profiles/manifests/puppet/cobbler_enc.pp | 76 +++++++++++++++++++ .../profiles/manifests/puppet/puppetmaster.pp | 3 +- .../templates/puppet/server/cobbler-enc.erb | 46 +++++++++++ 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 site/profiles/manifests/puppet/cobbler_enc.pp create mode 100644 site/profiles/templates/puppet/server/cobbler-enc.erb diff --git a/hieradata/common.yaml b/hieradata/common.yaml index b2ad056..5cb9769 100644 --- a/hieradata/common.yaml +++ b/hieradata/common.yaml @@ -58,6 +58,7 @@ profiles::packages::install: - pbzip2 - pigz - pv + - python3.11 - rsync - screen - socat diff --git a/hieradata/roles/infra/puppet/master.yaml b/hieradata/roles/infra/puppet/master.yaml index f475770..b489b30 100644 --- a/hieradata/roles/infra/puppet/master.yaml +++ b/hieradata/roles/infra/puppet/master.yaml @@ -10,6 +10,12 @@ profiles::puppet::autosign::domains: # profiles::puppet::autosign::nodes: # - 'somenode.main.unkin.net' +profiles::puppet::cobbler_enc::cobbler_scheme: https +profiles::puppet::cobbler_enc::cobbler_hostname: cobbler.main.unkin.net +profiles::puppet::cobbler_enc::version: 'system' +profiles::puppet::cobbler_enc::packages: + - 'requests' + - 'PyYAML' profiles::puppet::enc::repo: https://git.unkin.net/unkinben/puppet-enc.git profiles::puppet::r10k::r10k_repo: https://git.unkin.net/unkinben/puppet-r10k.git profiles::puppet::g10k::bin_path: '/opt/puppetlabs/bin/g10k' diff --git a/site/profiles/manifests/puppet/cobbler_enc.pp b/site/profiles/manifests/puppet/cobbler_enc.pp new file mode 100644 index 0000000..a3e9708 --- /dev/null +++ b/site/profiles/manifests/puppet/cobbler_enc.pp @@ -0,0 +1,76 @@ +# Class: profiles::puppet::cobbler_enc +# +# This will deploy the cobbler-enc tool for puppetmasters +# +# wrapper class for python, pip and venv +class profiles::puppet::cobbler_enc ( + Stdlib::Host $cobbler_hostname, + Enum['http','https'] $cobbler_scheme = 'https', + String $script_name = 'cobbler-enc', + Stdlib::AbsolutePath $base_path = "/opt/${script_name}", + Stdlib::AbsolutePath $venv_path = "${base_path}/venv", + String $owner = 'root', + String $group = 'root', + Boolean $systempkgs = false, + String $version = 'system', + Array[String[1]] $packages = ['sys','requests','pyyaml'], + Stdlib::AbsolutePath $trusted_ca_cert = '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem' +){ + + # set the cobbler url, required for the erb template + $cobbler_base_url = "${cobbler_scheme}://${cobbler_hostname}" + + if $::facts['python3_version'] { + + $python_version = $version ? { + 'system' => $::facts['python3_version'], + default => $version, + } + + # ensure the base_path exists + file { $base_path: + ensure => directory, + mode => '0755', + owner => $owner, + group => $group, + } + + # create a venv + python::pyvenv { $venv_path : + ensure => present, + version => $python_version, + systempkgs => $systempkgs, + venv_dir => $venv_path, + owner => $owner, + group => $group, + require => File[$base_path], + } + + # install the required pip packages + $packages.each |String $package| { + python::pip { "${venv_path}_${package}": + ensure => present, + pkgname => $package, + virtualenv => $venv_path, + } + } + + # create the script from a template + file { "${base_path}/${script_name}": + ensure => file, + mode => '0755', + content => template("profiles/puppet/server/${script_name}.erb"), + require => [ + Python::Pyvenv[$venv_path], + Package['python3.11'] + ], + } + + # create symbolic link in $PATH + file { "/usr/local/bin/${script_name}": + ensure => 'link', + target => "${base_path}/${script_name}", + require => File["${base_path}/${script_name}"], + } + } +} diff --git a/site/profiles/manifests/puppet/puppetmaster.pp b/site/profiles/manifests/puppet/puppetmaster.pp index bf7254b..a5b7c74 100644 --- a/site/profiles/manifests/puppet/puppetmaster.pp +++ b/site/profiles/manifests/puppet/puppetmaster.pp @@ -28,6 +28,7 @@ class profiles::puppet::puppetmaster ( include profiles::puppet::r10k include profiles::puppet::g10k include profiles::puppet::enc + include profiles::puppet::cobbler_enc include profiles::puppet::autosign include profiles::puppet::gems include profiles::helpers::certmanager @@ -54,7 +55,7 @@ class profiles::puppet::puppetmaster ( ], server => 'prodinf01n01.main.unkin.net', node_terminus => 'exec', - external_nodes => '/opt/puppetlabs/bin/enc', + external_nodes => '/opt/cobbler-enc/cobbler-enc', autosign => '/etc/puppetlabs/puppet/autosign.conf', default_manifest => '/etc/puppetlabs/code/environments/develop/manifests', default_environment => 'develop', diff --git a/site/profiles/templates/puppet/server/cobbler-enc.erb b/site/profiles/templates/puppet/server/cobbler-enc.erb new file mode 100644 index 0000000..27dd30e --- /dev/null +++ b/site/profiles/templates/puppet/server/cobbler-enc.erb @@ -0,0 +1,46 @@ +#!<%= @venv_path %>/bin/python +""" +External Node Classifier (ENC) for Puppet. + +If the environment specified in the YAML file is 'testing', +the environment is not included in the output. +""" + +import sys +import yaml +import requests + +def fetch_enc_data(cobbler_url: str, hostname: str) -> str: + """ + Fetches and modifies ENC data from a given URL to ensure classes are in list format. + """ + url = f"{cobbler_url}/cblr/svc/op/puppet/hostname/{hostname}" + try: + response = requests.get(url, verify='<%= @trusted_ca_cert %>') + response.raise_for_status() + except requests.RequestException as e: + sys.exit(f"Request failed: {e}") + + data = yaml.safe_load(response.text) + data["parameters"] = data.get("parameters", {}) + + # Ensure 'classes' is in the desired list format + if "classes" in data: + if isinstance(data["classes"], dict): + data["parameters"]["enc_role"] = list(data["classes"].keys()) + data["classes"] = list(data["classes"].keys()) + else: + data["parameters"]["enc_role"] = list(data["classes"]) + data["classes"] = list(data["classes"]) + + if "environment" in data: + data["parameters"]["enc_env"] = data["environment"] + if data["environment"] == "testing": + del data["environment"] + + return yaml.dump(data) + +if __name__ == "__main__": + if len(sys.argv) != 2: + sys.exit(f"Usage: {sys.argv[0]} ") + print(fetch_enc_data("<%= @cobbler_base_url %>", sys.argv[1]))