ぶていのログでぶログ

思い出したが吉日

明日から使えるかもしれないカスタムfacter5選

この記事は Puppet Advent Calendar 2015 の11日目の記事です。 昨日は @udzura さんのPuppet - Defined Type の使い所を考える - Qiita でした!


みなさんpuppetizeしてますか!!! 今日は私が普段使っているカスタムfacterをご紹介します。

前提

Master/Agent構成でpuppetを使っておらず、 Standalone構成で使っています。 その上で、以下の様なカスタムfacterを定義しておくと便利なのではないかと思います。

1. environment

環境を定義するカスタムfacterです。 production, staging, などを定義しておくと何かと便利です。 基本的には、FQDNで判別しています。 もっとかっこよく、VMのmetadataとかから取りたいなぁ。っていうかそうしよう!(いま閃いた

environmentって打つのがめんどくさいので env も定義しています。

Facter.add(:environment) do
  setcode do
    case Facter.value(:fqdn)
    when /jp$|\.jp$/
      'production'
    when /pb$|\.pb$/
      'staging'
    when /pbdev$|\.pbdev$/
      'development'
    else
      nil
    end
  end
end

Facter.add(:env) do
  setcode do
    Facter.value(:environment)
  end
end

2. hieradir

hieraを使う時の :datadir: に指定する際に使用します。 vagrantとその他の環境でパスが違うので、そこを吸収するために使用しています。

Facter.add(:hieradir) do
  setcode do
    if Facter.value(:location) == 'local'
      # vagrant provisionでのapply実行時に見えるように
      '/vagrant/hieradata'
    else
      # その他は任意のpathに依存したapplyは想定してないので固定
      # どうしても変更したいなら
      #   FACTER_hieradir=/path/to
      # すればよい
      '/var/puppet/current/hieradata'
    end
  end
end

3. localipaddress / localipaddress_interface

ローカル側のIPとそのインターフェイス名です。 facterで ipaddress_<インターフェイス名> で取得できますが、 サーバによってインターフェイス名が違ったりする(例えば、bondを組んでいたり)ので便利です。

localipaddress

Facter.add(:localipaddress) do
  setcode do
    interface = Facter.value("localipaddress_interface")
    ip = Facter.value("ipaddress_#{interface}".to_sym)
    ip
  end
end

localipaddress_interface

require 'ipaddr'

Facter.add(:localipaddress_interface) do
  setcode do
    # private network RFC1928
    # Vagrant環境はクラスCのみにする
    def private_network?(ipaddr)
      iprange = [ IPAddr.new('192.168.0.0/16') ]
      if Facter.value(:fqdn) !~ /\.pbdev/
        iprange.concat([
          IPAddr.new('172.16.0.0/12'),
          IPAddr.new('10.0.0.0/8'),
        ])
      end
      iprange.any? {|i| i.include? IPAddr.new(ipaddr) }
    end

    interfaces = Facter.value(:interfaces).split(',')

    interfaces.find do |interface|
      ip = Facter.value("ipaddress_#{interface}".to_sym)

      # デバイスはあってもIPがついていない場合は次のデバイスに
      next unless ip

      private_network? ip
    end
  end
end

4. location

DCが複数あり、それにより設定を変える必要がある(DNSの向け先とかProxyサーバのIPとか)ため追加しました。 DCが複数なくても、vagrantとDCみたいな定義にすると、vagrantだけ設定を変えるみたいなことが簡単に定義できると思います。

判別はグローバルIPでやっています (以下はマスクしています)

Facter.add(:location) do
  setcode do
    # NyahのDBロールはWANを持たせない
    if Facter.value(:localipaddress_interface) == "eth0"
      'nyah'
    else
      case Facter.value(:network_eth0)
      when (DC1IPアドレス帯)
        'DC1'
      when (DC2IPアドレス帯)
        'DC2'
      when (DC3IPアドレス帯)
        'DC3'
      else
        'vagrant'
      end
    end
  end
end

5. operatingsystemmajrelease

以下のコード内のコメント通りで、facter2.1未満で存在せず未定義の変数となってしまって エラーになってしまうので互換性を保つために定義しています。

# facter2.1未満では存在しないので、
# 下位互換のためにカスタムファクタで定義する
Facter.add(:operatingsystemmajrelease) do
  setcode do
    Facter.value(:operatingsystemrelease).match(/^\d+/)[0]
  end
end

以上です。 役に立つ情報はありましたでしょうか? これ以外にも便利なカスタムFacterがあったらぜひ教えて下さい 🙇🏻

明日は kijibato さんです!