Puppet exportou recursos para variáveis ​​de arquivo .erb?

8

Cenário: meu arquivo de configuração é definido por um .erbarquivo que inclui o trecho abaixo.

<% 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 -%>

O arquivo de configuração do servidor precisa de uma entrada repetida para cada host do cliente. Se eu fosse criar uma matriz simples, isso funcionaria sem problemas. O que eu quero fazer, no entanto, é que cada host se registre e colete os dados usando o <<| |>>pragma semelhante ao que se faria com os nagios_*tipos.

O exemplo padrão para isso envolve a exportação de um tipo.

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

No entanto, não consigo descobrir como escrever um tipo ou fazer referência a ele de uma maneira que me permita ler essa matriz de valores do .erbmodelo. Existe uma maneira de usar recursos exportados em combinação com um loop variável em um .erbarquivo?

Jeff Ferland
fonte
Eles realmente precisam terminar no mesmo arquivo? Eu gosto de ter cada host em um arquivo separado. Algo como /etc/bacula/clientdefs/*.conf . Isso deve ser mais fácil de lidar.
Zoredache

Respostas:

5

Portanto, para responder sua pergunta diretamente, não acredito que seja possível obter uma lista dos recursos exportados diretamente do erb. Isso ocorre devido à natureza dos recursos exportados. Para o Puppet, são apenas mais recursos que precisam ser criados no host.

Mas há uma maneira de realizar o que você deseja fazer. Faço isso em alguns lugares do meu ambiente.

Aqui, criamos um diretório de arquivos, um para cada host que queremos sinalizar como "bacula_client". Usamos os purge, forcee recurseopções para remover arquivos que não são gerenciados por Puppet (ou seja, se você quiser remover um sistema a partir desta "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" |>>

}

Em seguida, usamos algum código Ruby no .erb para verificar arquivos neste diretório e agir sobre eles:

<% 
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
fonte
Agora me sinto mal porque terminei de escrever meus scripts há dois dias e está muito próximo do formato ... dito isso, vou deixar você escolher se você sente que sua resposta ou minha resposta é mais apropriada para eu aceitar.
Jeff Ferland
Eu diria que sua resposta é mais apropriada se o aplicativo tiver suporte para um diretório de configuração ou uma inclusão por curinga (como parece o bacula). Eu usei isso no meu ambiente para scripts que movem arquivos para um conjunto de hosts de destino. Portanto, os scripts bash simplesmente o fazem ls /path/to/flag/files|while read hostname; do ssh $hostname ..; done.
Kyle Smith
4

Bem, primeiro desisti e defini meu @@tipo de arquivo. A vantagem é que isso ainda está usando as variáveis ​​no host do 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]
    }

    ...
}

Isso permite que eu use entradas no arquivo erb, como:

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

e declarações nos meus arquivos site.pp, como: class { bacula-client: database => "true" }

Para manipular o próprio diretório:

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

        ...
}

Limpar e recuar limpa qualquer coisa não definida. Quando coloco um host offline, puppetstoredconfigclean $hostnamelimparei os fatos e a próxima rodada de marionetes no diretor redefinirá a configuração adequadamente.

Finalmente, o próprio software de diretor do Bacula permite que eu faça o seguinte no final do meu arquivo bacula-dir.conf:

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

Portanto, ainda não parece haver uma maneira direta de usar um modelo de ERB em um conjunto de recursos coletados, mas é possível coletar tipos. Isso pode incluir tipos Augeas para agrupar tudo em um arquivo ou um hack para coletar arquivos em uma configuração. Ainda não inclui o que eu estava procurando na pergunta.

Jeff Ferland
fonte
1

Eu bati em um método usando o serviço PuppetDB que funciona razoavelmente bem para esta situação, embora seja um pouco tolo. Para usar isso, você precisará ter o PuppetDB operacional (o que você já deve ter ao usar os recursos exportados) e a API do PuppetDB precisará ser quierable a partir do puppetmaster (localhost).

Em seguida, você deseja exportar todos os recursos que deseja reunir para sua matriz em um diretório dedicado no sistema de arquivos. Este caminho do diretório será usado para identificar exclusivamente os recursos de destino.

Em seguida, no seu modelo, faça algo assim:

    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

Onde nodename é o FQDN do servidor, pathRegex é o caminho de pesquisa mencionado acima, formatado como um Ruby Regex e retVal é a matriz concluída. Isso permite que o modelo seja processado no puppetmaster, portanto, não são necessárias credenciais especiais da API. Isso também pressupõe que o recurso namevar é o caminho completo dos arquivos de destino, se você tiver namevars complexos e usar o atributo path, uma lógica mais complexa será necessária. Observe também que isso está retornando todos os recursos, exportados e locais. Os dados retornados possuem muitos atributos que podem ser usados ​​para uma lógica mais complexa, se necessário.

Um pouco hacky, mas funciona bem.

TJNII
fonte