Puppet: Verificando conjuntos de variáveis

6

Edit: Eu descobri o que eu acho que é a melhor maneira de fazer isso. Graças às pessoas responderam a minha pergunta - ajudou ao longo do caminho. Vou postar minha solução para que outras pessoas possam encontrá-la, se quiserem.

Pergunta original:

I would like to use puppet to auto configure a number of our NFS filesystems. However, I'd like the manifest to make sure it has an IP on the right network before doing so.

Puppet helpfully provides facts about the IPs the machine has assigned through the interfaces and ipaddress_*** facts. The issue is that I need to check all of the interfaces (I'd rather not make assumptions about how they are connected).

Question 1: Can Puppet loop through a fact list? For example:

for $if in $interfaces {
     //do something
}

Question 2: If Puppet could do that, is there a way to evaluate a string as a variable. interfaces provides a really nice way of knowing how many ipaddress_*** and network_***variables there are, but even if I could loop through it's values, I'd need to do something like:

for $if in $interfaces {
    $net_var = eval("network_${if}")
    if $net_var = /^192\.168\.2\.0$/ {
        //do something
    }
}

Is this possible? Am I going about this completely wrong (kinda new to Puppet)?

O que acabei fazendo:

O Puppet permite que você defina funções de analisador personalizadas para um módulo. Essas funções personalizadas são apenas ruby, então você pode efetivamente fazer o que quiser dentro delas, incluindo looping.

Você os coloca em <module_dir>/lib/puppet/parser/functions/ . Para fazer o que eu queria, criei <module_dir>/lib/puppet/parser/functions/has_network.rb com o seguinte conteúdo:

#has_network.rb
require 'ipaddr'

module Puppet::Parser::Functions
    newfunction(:has_network, :type => :rvalue) do |args|

        retcon = ""

        if not /^[0-9]{1,3}(\.[0-9]{1,3}){3}\/[0-2]?[0-9]$/.match(args[0]) then
            raise Puppet::ParseError, "Network provided was not valid. Provide 192.168.0.0/16 format" 
        else
            requested_network = IPAddr.new(args[0])

            interfaces_fact =  lookupvar('interfaces')
            interfaces = interfaces_fact.split(",")
            interfaces.each do |interface|
                ip = IPAddr.new(lookupvar("ipaddress_#{interface}"))
                if requested_network.include?(ip)
                    retcon = "true"
                end
            end
        end

        retcon
    end
end

Isso adiciona uma nova função que eu posso usar em meus manifestos chamados has_network. Ele retorna true se um dos endereços IP da máquina estiver na rede fornecida para a função. Agora posso fazer coisas como as seguintes:

if has_network("192.168.1.0/24"){
    //do something
}

Algumas notas sobre funções personalizadas

  • Eles são executados no servidor mestre. Observe o uso de lookupvar para realizar operações nos fatos do Facter. Você não pode fazer coisas como criar arquivos no cliente usando um desses.
  • Quando você muda a função, você deve reiniciar o maestro. Se você não o fizer, o mestre de fantoches nunca carregará a função atualizada e você ficará realmente confuso.
por Chris 28.10.2011 / 07:19

2 respostas

6

Como @Zoredache mencionou, não há loop no Puppet. Mas você pode contornar isso usando uma definição, algo assim:

define show_ip {
    $inf = "ipaddress_${name}"
    $ip = inline_template('<%= scope.lookupvar(inf) %>')

    if $ip =~ /192\.168\.3.*/ {
        notify {"Found IP $ip": }
    }
}

$ifs = split($interfaces, ',')

show_ip { $ifs: }

Belows é a saída ao chamar diretamente:

# puppet manifests/defines/net.pp 
warning: Implicit invocation of 'puppet apply' by passing files (or flags) directly
to 'puppet' is deprecated, and will be removed in the 2.8 series.  Please
invoke 'puppet apply' directly in the future.

notice: Found IP 192.168.3.118
notice: /Stage[main]//Show_ip[eth1]/Notify[Found IP 192.168.3.118]/message: defined 'message' as 'Found IP 192.168.3.118'
notice: Finished catalog run in 0.07 seconds

Fonte: link

    
por 28.10.2011 / 11:04
1

Não há looping ou iteração no fantoche.

Jogado um pouco mais, e aqui uma versão alternativa, que deve funcionar para qualquer número de ips em uma única interface e não usa um eval. to_hash.keys retorna uma matriz de todos os nomes de variáveis, o find_all filtra a lista com base na expressão regular, um map ping é usado para procurar o valor real da variável e, finalmente, os resultados são mesclados de volta em um string.

$iplist=inline_template('<%= scope.to_hash.keys.find_all{|i| i =~ /ipaddress_.*/}.map{|j| scope.lookupvar(j)}.join(",")%>')
if $iplist =~ /10\.2\.93.*/ {
      notify {"found $iplist": }
}
# on the client with a matching address we see "notice: found 10.2.93.5,127.0.0.1"

Eu tenho algo que funciona, mas não tenho certeza se é realmente a melhor solução, ou segura. Basicamente eu acho que você pode usar um inline_template. Dentro do modelo in-line, dividimos o fato de interfaces, que é uma lista de todas as interfaces. Usamos um mapeamento para construir uma nova matriz dos endereços IP de todas as interfaces e, finalmente, juntamos novamente a matriz a uma string.

Minha maior preocupação com essa ideia é que estou fazendo uma avaliação sobre algo que é basicamente fornecido pelo sistema do cliente. Eu não tenho certeza de como isso é mal.

Isso também será interrompido se uma interface tiver vários endereços ou se uma interface não tiver endereços. Talvez alguém possa sugerir algumas melhorias. Eu estou apenas aprendendo Ruby.

$iplist=inline_template('<%= interfaces.split(",").map{|x| eval ("ipaddress_"+x)}.join(",")%>')

if $iplist =~ /10\.2\.93.*/ {
      notify {"found $iplist": }
}
# on the client with a matching address we see "notice: found 10.2.93.5,127.0.0.1"

    
por 28.10.2011 / 09:29

Tags