¿Puppet exportó recursos para variables de archivo .erb?

8

Escenario: mi archivo de configuración está definido por un .erbarchivo que incluye el fragmento a continuación.

<% backupclients.each do |backup_files| -%>
Job {
  Name = "Server"
  JobDefs = "DefaultJob"
  Client = <%= backup_files %>-fd
  Pool = TeraMonth
  Write Bootstrap = "/var/lib/bacula/<%= backup_files %>.bsr"
}
<% end -%>

El archivo de configuración del servidor necesita una entrada repetida para cada host cliente. Si tuviera que crear una matriz simple, esto funcionaría sin problemas. Sin embargo, lo que quiero hacer es que cada host se registre y luego recopile los datos utilizando el <<| |>>pragma similar a lo que se haría con los nagios_*tipos.

El ejemplo estándar para esto implica exportar un tipo.

class ssh {
  @@sshkey { $hostname: type => dsa, key => $sshdsakey }
  Sshkey <<| |>>
}

Sin embargo, no puedo entender cómo escribir un tipo o hacer referencia a él de una manera que me permita leer esa matriz de valores de la .erbplantilla. ¿Hay alguna manera de usar los recursos exportados en combinación con un bucle variable en un .erbarchivo?

Jeff Ferland
fuente
¿Realmente necesitan terminar en el mismo archivo? Me gusta tener cada host en un archivo separado. Algo así como /etc/bacula/clientdefs/*.conf . Esto debería ser más fácil de tratar.
Zoredache

Respuestas:

5

Entonces, para responder su pregunta directamente, no creo que sea posible obtener una lista de recursos exportados directamente de erb. Esto se debe a la naturaleza de los recursos exportados. Para Puppet, son solo más recursos que deben crearse en el host.

Pero, hay una manera de lograr lo que estás buscando hacer. Lo hago en algunos lugares de mi entorno.

Aquí creamos un directorio de archivos, uno para cada host que queremos marcar como "cliente_bacula". Utilizamos las purge, forcey recurselas opciones para eliminar los archivos que no son administrados por la marioneta (es decir, si desea eliminar un sistema de esta "lista").

class bacula::client {

  @@file { "/etc/bacula_clients/$fqdn":
    ensure => present,
    content => "",
    require => File['/etc/bacula_clients'],
    tag => "bacula_client",
  }

}

class bacula::server {

  #
  # .. include whatever else the server requires, like package {} file {} service {}
  #

  file { "/etc/bacula_clients":
    ensure => directory,
    purge => true,
    recurse => true,
    force => true,
  }

  # Populate directory of client files.
  File <<| tag == "bacula_client" |>>

}

A continuación, usamos un código Ruby en el .erb para escanear este directorio en busca de archivos y actuar sobre ellos:

<% 
bacula_clients_dir = '/etc/bacula_clients'
d = Dir.open(bacula_clients_dir)

# Remove directories from the list of entries in the directory (specifically '.' and '..'):
backupclients = d.entries.delete_if { |e| File.directory? "#{bacula_clients_dir}/#{e}" }

backupclients.each do |backup_files| 
-%>
Job {
  Name = "Server"
  JobDefs = "DefaultJob"
  Client = <%= backup_files %>-fd
  Pool = TeraMonth
  Write Bootstrap = "/var/lib/bacula/<%= backup_files %>.bsr"
}
<% end -%>
Kyle Smith
fuente
Ahora me siento mal porque terminé de escribir mis guiones hace dos días y tiene un formato muy cercano ... dicho eso, te dejaré elegir si sientes que tu respuesta o la mía es más apropiada para mí.
Jeff Ferland
Yo diría que su respuesta es más apropiada si la aplicación tiene soporte para un directorio de configuración o una inclusión por comodín (como parece Bacula). Lo he usado en mi entorno para scripts que mueven archivos a un conjunto de hosts de destino. Entonces las secuencias de comandos bash simplemente lo hacen ls /path/to/flag/files|while read hostname; do ssh $hostname ..; done.
Kyle Smith
4

Bueno, primero me di por vencido y configuré mi @@en el tipo de archivo real. Lo bueno es que esto todavía está usando las variables en el host del cliente.

class bacula-client ($database = false) {
    @@file { "${hostname}-bacula-client.conf":
            mode => 600,
            owner => bacula,
            group => root,
            path => "/etc/bacula/conf.d/${hostname}-client.conf",
            content => template("bacula-dir-cliententry.erb"),
            tag => 'bacula-client',
            notify => Service[bacula-director]
    }

    ...
}

Esto me permite usar entradas en el archivo erb como:

<% if has_variable?("database") and database== "true" %>
    ...
<% end -%>

y declaraciones en mis archivos site.pp como: class { bacula-client: database => "true" }

Para manejar el directorio en sí:

class bacula-director {
        file { '/etc/bacula/conf.d':
            ensure => directory,
            owner => bacula,
            group => root,
            mode => 600,
            purge => true,
            recurse => true
        }

        ...
}

La purga y la recidiva limpian todo lo que no está definido. Cuando desconecto un host, puppetstoredconfigclean $hostnamelimpiará los hechos y la próxima ejecución de títere en el director restablecerá la configuración de forma correcta.

Finalmente, el software del director Bacula me permite hacer lo siguiente al final de mi archivo bacula-dir.conf:

@|"sh -c 'for f in /etc/bacula/conf.d/*.conf ; do echo @${f} ; done'"

Por lo tanto, todavía no parece haber una forma directa de usar una plantilla ERB en un conjunto de recursos recopilados, pero se pueden recopilar algunos tipos. Eso puede incluir tipos de Augeas para guardar todo en un archivo, o un truco de recopilar archivos en una configuración. Sin embargo, aún no incluye lo que estaba buscando en la pregunta.

Jeff Ferland
fuente
1

He encontrado un método que utiliza el servicio PuppetDB que funciona bastante bien para esta situación, aunque es un poco hack. Para usar esto, necesitará que PuppetDB esté operativo (que ya debería tener ya que está utilizando recursos exportados) y la API de PuppetDB deberá estar siempre disponible desde puppetmaster (localhost).

Luego, querrá exportar todos los recursos que desea reunir a su matriz en un directorio dedicado en el sistema de archivos. Esta ruta de directorio se utilizará para identificar de forma exclusiva los recursos de destino.

Luego, en su plantilla, haga algo como esto:

    require 'rest_client'
    require 'json'
    resources=JSON.parse(RestClient.get("http://localhost:8080/v2/nodes/#{nodename}/resources", {:accept => :json}))

    retVal = Array.new
    resources.each do |resource|
       if resource["title"] =~ /^#{pathRegex}$/
           retVal.push(resource["title"])
       end
    end

Donde nodename es el FQDN del servidor, pathRegex es la ruta de búsqueda mencionada anteriormente, formateada como Ruby Regex, y retVal es la matriz completa. Esto aprovecha que la plantilla se procesa en el puppetmaster, por lo que no se requieren credenciales especiales de API. Esto también supone que el recurso namevar es la ruta de acceso completa de los archivos de destino, si tiene nombres de nombre complejos y utiliza el atributo de ruta, se requerirá una lógica más compleja. También tenga en cuenta que esto está devolviendo todos los recursos, tanto exportados como locales. Los datos devueltos tienen muchos atributos que pueden usarse para una lógica más compleja si es necesario.

Un poco hacky, pero funciona bien.

TJNII
fuente