Puppet supports holding multiple values as an environment variable. This feature is supported in Puppet by using facter. In Puppet, facter is a standalone tool that holds the environment level variable. In can be considered similar to env variable of Bash or Linux. Sometimes there can be an overlap between the information stored in facts and environment variable of the machine. In Puppet, the key-value pair is known as “fact”. Each resource has its own facts and in Puppet the user has the leverage to build their own custom facts.
# facter
Facter command can be used to list all the different environment variables and its associated values. These collection of facts comes with facter out-of-the-box and are referred to as core facts. One can add custom facts to the collection.
If one wants to view only one variable. It can be done using the following command.
# facter {Variable Name} Example [root@puppetmaster ~]# facter virtual virtualbox
The reason why facter is important for Puppet is that facter and facts are available throughout Puppet code as “global variable”, which means it can be used in the code at any point of time without any other reference.
[root@puppetmaster modules]# tree brcle_account brcle_account └── manifests └── init.pp [root@puppetmaster modules]# cat brcle_account/manifests/init.pp class brcle_account { user { 'G01063908': ensure => 'present', uid => '121', shell => '/bin/bash', home => '/home/G01063908', } file {'/tmp/userfile.txt': ensure => file, content => "the value for the 'OperatingSystem' fact is: $OperatingSystem \n", } }
[root@puppetmaster modules]# puppet agent --test Notice: /Stage[main]/Activemq::Service/Service[activemq]/ensure: ensure changed 'stopped' to 'running' Info: /Stage[main]/Activemq::Service/Service[activemq]: Unscheduling refresh on Service[activemq] Notice: Finished catalog run in 4.09 seconds [root@puppetmaster modules]# cat /tmp/testfile.txt the value for the 'OperatingSystem' fact is: Linux [root@puppetmaster modules]# facter OperatingSystem Linux
As we can notice in the above code snippet, we haven’t defined the OperatingSystem. We have just replaced the value with soft coded value $OperatingSystem as normal variable.
In Puppet, there are three types of fact that can be used and defined −
Core facts are defined at the top level and accessible to all at any point in the code.
Just before an agent requests for a catalog from the master, the agent first compiles a complete list of information available in itself in the form of a key value pair. The information on the agent is gathered by a tool called facter and each key-value pair is referred as a fact. Following is a common output of facts on an agent.
[root@puppetagent1 ~]# facter architecture => x86_64 augeasversion => 1.0.0 bios_release_date => 13/09/2012 bios_vendor => innotek GmbH bios_version => VirtualBox blockdevice_sda_model => VBOX HARDDISK blockdevice_sda_size => 22020587520 blockdevice_sda_vendor => ATA blockdevice_sr0_model => CD-ROM blockdevice_sr0_size => 1073741312 blockdevice_sr0_vendor => VBOX blockdevices => sda,sr0 boardmanufacturer => Oracle Corporation boardproductname => VirtualBox boardserialnumber => 0 domain => codingbee.dyndns.org facterversion => 2.1.0 filesystems => ext4,iso9660 fqdn => puppetagent1.codingbee.dyndns.org hardwareisa => x86_64 hardwaremodel => x86_64 hostname => puppetagent1 id => root interfaces => eth0,lo ipaddress => 172.228.24.01 ipaddress_eth0 => 172.228.24.01 ipaddress_lo => 127.0.0.1 is_virtual => true kernel => Linux kernelmajversion => 2.6 kernelrelease => 2.6.32-431.23.3.el6.x86_64 kernelversion => 2.6.32 lsbdistcodename => Final lsbdistdescription => CentOS release 6.5 (Final) lsbdistid => CentOS lsbdistrelease => 6.5 lsbmajdistrelease => 6 lsbrelease => :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0noarch:graphics-4.0-amd64: graphics-4.0-noarch:printing-4.0-amd64:printing-4.0noarch macaddress => 05:00:22:47:H9:77 macaddress_eth0 => 05:00:22:47:H9:77 manufacturer => innotek GmbH memoryfree => 125.86 GB memoryfree_mb => 805.86 memorysize => 500 GB memorysize_mb => 996.14 mtu_eth0 => 1500 mtu_lo => 16436 netmask => 255.255.255.0 netmask_eth0 => 255.255.255.0 network_lo => 127.0.0.0 operatingsystem => CentOS operatingsystemmajrelease => 6 operatingsystemrelease => 6.5 osfamily => RedHat partitions => {"sda1"=>{ "uuid"=>"d74a4fa8-0883-4873-8db0-b09d91e2ee8d", "size" =>"1024000", "mount" => "/boot", "filesystem" => "ext4"}, "sda2"=>{"size" => "41981952", "filesystem" => "LVM2_member"} } path => /usr/lib64/qt3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin physicalprocessorcount => 1 processor0 => Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz processor1 => Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz processor2 => Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz processorcount => 3 productname => VirtualBox ps => ps -ef puppetversion => 3.6.2 rubysitedir => /usr/lib/ruby/site_ruby/1.8 rubyversion => 1.8.7 selinux => true selinux_config_mode => enforcing selinux_config_policy => targeted selinux_current_mode => enforcing selinux_enforced => true selinux_policyversion => 24 serialnumber => 0 sshdsakey => AAAAB3NzaC1kc3MAAACBAK5fYwRM3UtOs8zBCtRTjuHLw56p94X/E0UZBZwFR3q7 WH0x5+MNsjfmdCxKvpY/WlIIUcFJzvlfjXm4qDaTYalbzSZJMT266njNbw5WwLJcJ74KdW92ds76pjgm CsjAh+R9YnyKCEE35GsYjGH7whw0gl/rZVrjvWYKQDOmJA2dAAAAFQCoYABgjpv3EkTWgjLIMnxA0Gfud QAAAIBM4U6/nerfn6Qvt43FC2iybvwVo8ufixJl5YSEhs92uzsW6jiw68aaZ32q095/gEqYzeF7a2knr OpASgO9xXqStYKg8ExWQVaVGFTR1NwqhZvz0oRSbrN3h3tHgknoKETRAg/imZQ2P6tppAoQZ8wpuLrXU CyhgJGZ04Phv8hinAAAAIBN4xaycuK0mdH/YdcgcLiSn8cjgtiETVzDYa+jF swapfree => 3.55 GB swapfree_mb => 2015.99 swapsize => 3.55 GB swapsize_mb => 2015.99 timezone => GMT type => Other uniqueid => a8c0af01 uptime => 45:012 hours uptime_days => 0 uptime_hours => 6 uptime_seconds => 21865 uuid => BD8B9D85-1BFD-4015-A633-BF71D9A6A741 virtual => virtualbox
In the above code, we can see some of the data overlap with few of the information available in bash “env” variable. Puppet directly does not use the data, instead it makes use of facter data, Facter data is treated as global variable.
The facts are then available as top level variable and the Puppet master can use them to compile the Puppet catalog for the requesting agent. Facters are called in manifest as normal variable with $ prefix.
if ($OperatingSystem == "Linux") { $message = "This machine OS is of the type $OperatingSystem \n" } else { $message = "This machine is unknown \n" } file { "/tmp/machineOperatingSystem.txt": ensure => file, content => "$message" }
The above manifest file only bothers about a single file called machineOperatingSystem.txt, where the content of this file is deducted by the fact called OperatingSystem.
[root@puppetagent1 /]# facter OperatingSystem Linux [root@puppetagent1 /]# puppet apply /tmp/ostype.pp Notice: Compiled catalog for puppetagent1.codingbee.dyndns.org in environment production in 0.07 seconds Notice: /Stage[main]/Main/File[/tmp/machineOperatingSystem.txt]/ensure: defined content as '{md5}f59dc5797d5402b1122c28c6da54d073' Notice: Finished catalog run in 0.04 seconds [root@puppetagent1 /]# cat /tmp/machinetype.txt This machine OS is of the type Linux
All the above facts that we have seen are the core facts of the machine. One can add this custom facts to the node in the following ways −
One can manually add the facts using the export FACTER_{fact’s name} syntax.
[root@puppetagent1 facter]# export FACTER_tallest_mountain="Everest" [root@puppetagent1 facter]# facter tallest_mountain Everest
In Ruby, $LOAD_PATH is equivalent to Bash special parameter. Although it is similar to bash $PATH variable, in real facts $LOAD_PATH is not an environment variable, instead it is a pre-defined variable.
$LOAD_PATH has a synonym “$:”. This variable is an array to search and load the values.
[root@puppetagent1 ~]# ruby -e 'puts $LOAD_PATH' # note you have to use single quotes. /usr/lib/ruby/site_ruby/1.6 /usr/lib64/ruby/site_ruby/1.6 /usr/lib64/ruby/site_ruby/1.6/x86_64-linux /usr/lib/ruby/site_ruby /usr/lib64/ruby/site_ruby /usr/lib64/site_ruby/1.6 /usr/lib64/site_ruby/1.6/x86_64-linux /usr/lib64/site_ruby /usr/lib/ruby/1.6 /usr/lib64/ruby/1.6 /usr/lib64/ruby/1.6/x86_64-linux
Let’s take an example of creating a directory facter and adding a .pp file and appending a content to it.
[root@puppetagent1 ~]# cd /usr/lib/ruby/site_ruby/ [root@puppetagent1 site_ruby]# mkdir facter [root@puppetagent1 site_ruby]# cd facter/ [root@puppetagent1 facter]# ls [root@puppetagent1 facter]# touch newadded_facts.rb
Add the following content to the custom_facts.rb file.
[root@puppetagent1 facter]# cat newadded_facts.rb Facter.add('tallest_mountain') do setcode "echo Everest" end
Facter works in the method of scanning through all the folder listed in $LOAD_PATH, and looks for a director called facter. Once it finds that particular folder, it will load them anywhere in the folder structure. If it finds this folder then it looks for any Ruby file in that facter folder and loads all the defined facts about any particular configuration in the memory.
In Puppet, FACTERLIB works very much similar to $LOAD_PATH but with only one key difference that, it is a OS level environment parameter rather than a Ruby special variable. By default, the environment variable may not be set.
[root@puppetagent1 facter]# env | grep "FACTERLIB" [root@puppetagent1 facter]#
To test FACTERLIB, we need to perform the following steps.
Create a folder called test_facts in the following structure.
[root@puppetagent1 tmp]# tree /tmp/test_facts/ /tmp/some_facts/ ├── vipin │ └── longest_river.rb └── testing └── longest_wall.rb
Add the following contents to the .rb files.
[root@puppetagent1 vipin]# cat longest_river.rb Facter.add('longest_river') do setcode "echo Nile" end [root@puppetagent1 testing]# cat longest_wall.rb Facter.add('longest_wall') do setcode "echo 'China Wall'" end
Use the export statement.
[root@puppetagent1 /]# export FACTERLIB = "/tmp/some_facts/river:/tmp/some_facts/wall" [root@puppetagent1 /]# env | grep "FACTERLIB" FACTERLIB = /tmp/some_facts/river:/tmp/some_facts/wall
Test the new facter.
[root@puppetagent1 /]# facter longest_river Nile [root@puppetagent1 /]# facter longest_wall China Wall
External facts are very useful when the user wishes to apply some new facts created at the provisioning time. External facts are one of the key ways of applying metadata to a VM at its provisioning stage (e.g. using vSphere, OpenStack, AWS, etc.)
All the metadata and its details created can be used by Puppet to determine what details should be present in the catalog, which is going to be applied.
On the agent machine, we need to create a directory as mentioned below.
$ mkdir -p /etc/facter/facts.d
Create a Shell script in the directory with the following content.
$ ls -l /etc/facter/facts.d total 4 -rwxrwxrwx. 1 root root 65 Sep 18 13:11 external-factstest.sh $ cat /etc/facter/facts.d/external-factstest.sh #!/bin/bash echo "hostgroup = dev" echo "environment = development"
Change the permission of the script file.
$ chmod u+x /etc/facter/facts.d/external-facts.sh
Once done, we can now see the variable present with the key/value pair.
$ facter hostgroup dev $ facter environment development
One can write custom facts in Puppet. As a reference, use the following link from the Puppet site.
https://docs.puppet.com/facter/latest/fact_overview.html#writing-structured-facts