вторник, 30 апреля 2013 г.

MCollective: пишем custom data plugin



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

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

Например, той же роли: сколько у нас вторичных серверов БД, какие версии БД установлены и т.д. Можно, конечно, воспользоваться простым regexp фильтром. Но в данном случае он не очень удобен - имена ролей, в принципе, могут частично пересекаться. К примеру, роль у нас прописана в puppet манифесте для одного хоста
role = "db-master,puppet-master"
а на втором
role= "db2-master"
уже имеем сложность отличить при помощи regexp db от db2

Поэтому логично сделать простой data plugin, который самостоятельно распарсит роль и выдаст результат

В документации к mcollective процедура создания плагина описана неплохо, но пропущены несколько подводных камней, которые основательно мне потрепали нервы.

Приступим к процедуре. Сразу оговорюсь: описанная процедура касается mcollective версии старше 2.2.1 (я проверял на последней на данный момент, mcollective 2.3.1). Также следует учитывать, что пути к файлам будут указаны для CentOS (RedHat, OracleLinux) ES.

Переходим в каталог /usr/libexec/mcollective/mcollective/data . Да, это не опечатка - почему-то авторы mcollective сделали двойное вложение. Пусть эта непонятка остается на их совести :).

Нам необходимо создать два файла: руби-код плагина .rb и описание .ddl.
Назовем новую функцию has_role, в качестве параметра будем передавать regexp или строку, возвращаться будет boolean true или false, в зависимости от того, имеет ли нода указанную роль или нет.

Важный момент (грабли номер раз)! Имя файлов строго определено (это касается data plugin'ов): <classname>_data.rb и <classname>_data.ddl. Строго говоря, <classname> не обязательно должен совпадать с реальным именем класса, но лучше это делать так, чтобы не путаться потом. А вот суффикс _data обязателен, иначе плагин нормально не инициализируется.

Содержимое has_role-data.rb :

 module MCollective  
   module Data  
     class Has_role_data<Base  
       query do |arg|  
         roles = Facts["role"]  
         ret = false  
         if roles.nil? or roles == false  
           ret = false  
         else  
         # check is argument regexp  
           arg = Regexp.new(arg.gsub("\/", "")) if arg.match("^/")  
         # convert roles to array  
           roles = roles.split(",") if (roles.is_a? String)  
         # trim spaces  
           roles = roles.collect{|x| x.strip}  
           if arg.is_a?(Regexp)  
             roles.each do |role|  
               ret = true if role.grep(arg).size > 0  
             end  
           else  
             ret = roles.include?(arg)  
           end  
         end  
       result[:value] = ret  
       end  
     end  
   end  
 end  

Некоторые пояснение по коду. Первые две строки - стандартные для дата-модуля.
Третья строка - описание класса. Имя класса должно быть с заглавной буквы!
Четвертая - вычитываем аргументы. Тут два замечания. Хотя и вычитывается массив, согласно документации принимается только один аргумент. Второе. Я бы очень не рекомендовал выносить перед этим циклом вообще какие-либо действия, могут возникать ошибки на голом месте :(
Строка 24 - возврат результата выполнения функции.

Теперь создадим описание плагина. Оно должно находиться в том же каталоге и иметь то же имя, но другое расширение.


metadata    :name        => "Has role",
            :description => "Check is node has role",
            :author      => "PAL ",
            :license     => "GPL 3.0",
            :version     => "0.1",
            :url         => "http://",
            :timeout     => 1

requires :mcollective => "2.2.1"

dataquery   :description => "Has role" do
    input   :query,
            :prompt      => "Role",
            :description => "Check role name",
            :type        => :string,
            :validation  => /^.+$/,
            :maxlength   => 120

   output   :value,
            :description => "True if node has role, false if has not",
            :display_as  => "Has role"
end

В принципе, тут все понятно. Буквально пару замечаний. В разделах input и output описываются имена (для input еще и тип) переменных, используемых для ввода и вывода. Разумеется, эти имена должны совпадать с теми. которые используются в руби-коде :)

Теперь очень важный момент (грабли №2). Нужно обязательно перезапустить mcollective:
# /sbin/service mcollective restart

Иначе плагин будет совсем как настоящий (и даже отображаться в выводе mco plugin doc) но не будет работать! Причем при каждом исправлении кода - обязательно нужно перезапускать.

Все, наш плагин готов :)
Использовать можно, к примеру, вот так:
# mco find -S 'has_role("database-master").value=true'

Комментариев нет:

Отправить комментарий