Назад | Перейти на главную страницу

Хосты Puppet для записи с использованием вызова API

Я пытаюсь написать марионеточную функцию, которая вызывает мою среду хостинга (облачный атм) для вывода списка серверов, а затем обновляет файл моих хостов.

Моя функция get_hosts сейчас такова:

require 'rubygems'
require 'cloudservers'

module Puppet::Parser::Functions
  newfunction(:get_hosts, :type => :rvalue) do |args|
    unless args.length == 1
      raise Puppet::ParseError, "Must provide the datacenter"
    end

    DC       = args[0] 
    USERNAME = DC == "us" ? "..." : "..."
    API_KEY  = DC == "us" ? "..." : "..."
    AUTH_URL = DC == "us" ? CloudServers::AUTH_USA : CloudServers::AUTH_UK
    DOMAIN   = "..."

    cs = CloudServers::Connection.new(:username => USERNAME, :api_key => API_KEY, :auth_url => AUTH_URL)

    cs.list_servers_detail.map {|server|
      server.map {|s| { s[:name] + "." + DC + DOMAIN => {
                          :ip      => s[:addresses][:private][0],
                          :aliases => s[:name]
      }}}
    }
  end
end

И у меня есть hosts.pp, который вызывает это и «должен» записывать его в / etc / hosts.

class hosts::us {
    $hosts = get_hosts("us")

    hostentry { $hosts: }
}

define hostentry() {
  host{ $name: ip => $name[ip], host_aliases => $name[aliases] }
}

Как вы понимаете, в настоящее время это не работает, и я получаю сообщение об ошибке «Символ как индекс массива в /etc/puppet/manifests/hosts.pp:2». Думаю, как только я пойму, что делаю неправильно, в будущем появятся новые ошибки.

Это хорошая идея? Может ли кто-нибудь помочь мне понять, как это сделать?

Обновить

Наконец-то удалось заставить это работать (с помощью комментариев)! Вот мой get_hosts.rb:

require 'rubygems'
require 'cloudservers'

module Puppet::Parser::Functions
  newfunction(:get_hosts, :type => :rvalue) do |args|
    unless args.length == 1
      raise Puppet::ParseError, "Must provide the datacenter"
    end

    dc       = args[0] 
    username = dc == "us" ? "..." : "..."
    api_key  = dc == "us" ? "..." : "..."
    auth_url = dc == "us" ? CloudServers::AUTH_USA : CloudServers::AUTH_UK
    domain   = "...."

    cs = CloudServers::Connection.new(:username => username, :api_key => api_key, :auth_url => auth_url)

    cs.list_servers_detail.map {|server|
      server[:name] + "." + dc + domain + "," + 
          server[:addresses][:private][0] + "," + 
          server[:name]
    }
  end
end

и hosts.pp

class hosts::us {
    $hosts = get_hosts("us")

    hostentry { $hosts: }
}

define hostentry() {
  $parts   = split($name, ',')
  $address = $parts[0]
  $ip      = $parts[1]
  $aliases = $parts[2]
  host{ $address: ip => $ip, host_aliases => $aliases }
}

Довольно неприятно так упорядочивать namevar, но это был единственный способ заставить его работать. Любые улучшения приветствуются.

Я бы не назвал это плохой идеей, но вам действительно нужно прекратить использовать константы повсюду в вашем коде (все, что начинается с заглавной буквы в Ruby, является константой).

Чтобы отладить вашу проблему, вы, вероятно, захотите использовать --trace в вызове марионеточного мастера, поэтому он напечатает обратную трассировку вместо того, чтобы съесть настоящее исключение и выдать вам бесполезное сообщение об ошибке. У вас есть целая куча разыменований в вашем map звонки; я предполагаю, что вы неправильно поняли часть структуры данных, исходящую из API, и ваш код спотыкается об этом недоразумении. Запустите отладчик (или обильно посыпьте puts через ваш код), и вы увидите, что у вас не так, в довольно короткие сроки.

Я бы разделил эту функциональность иначе.

Используйте свою функцию, чтобы создать чистые данные в виде хэша, представляющего список ресурсов хоста.

Затем используйте функцию create_resources для создания фактических ресурсов хоста из этих данных:

https://puppet.com/docs/puppet/latest/function.html#createresources

Пример должен дать вам достаточно, чтобы начать работу:

    # A hash of user resources:
    $myusers = {
      'nick' => { uid    => '1330',
                  group  => allstaff,
                  groups => ['developers', 'operations', 'release'], }
      'dan'  => { uid    => '1308',
                  group  => allstaff,
                  groups => ['developers', 'prosvc', 'release'], }
    }

    create_resources(user, $myusers)

Имеет ли это смысл?

Тогда вы можете избежать определенного типа и вы можете выполнять все операции с данными в вызове функции, чтобы получить хеш хэшей, и у вас есть хорошее разделение между функцией, которая собирает данные, и функцией, которая создает экземпляры ресурсов.

Это должно решить любую необходимость в классической итерации, если я правильно понимаю вашу проблему.