Puppet Data Mining
Report processors, stored configs and more

PuppetCamp 22nd March 2012


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/map408/2412123378
Gareth Rushgrove @garethr


gareth rushgrove | morethanseven.net
Blog at morethanseven.net


gareth rushgrove | morethanseven.net
Curate devopsweekly.com


gareth rushgrove | morethanseven.net
Text




Work at UK Government Digital Service


gareth rushgrove | morethanseven.net
Serious Government Business


gareth rushgrove | morethanseven.net
-      Why do we want it?
-      How do we get our hands on it
-      What can we build with it




Puppet data


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/iancarroll/5027441664
-      Why do we want it?
-      How do we get our hands on it
-      What can we build with it




Puppet data


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/iancarroll/5027441664
-      Why do we want it?
-      How do we get our hands on it
-      What can we build with it




Puppet data


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/iancarroll/5027441664
Why
Single source of truth


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/joeshlabotnik/4132307070/
-      Spreadsheets
-      Wiki
-      Deployment scripts
-      Application code
-      SSH configs
-      More deployment scripts
-      Monitoring configuration




Example: find a list of hostnames


gareth rushgrove | morethanseven.net
Operational reports


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/nettsu/5276327339
Company hardware




Example: hardware spreadsheet


gareth rushgrove | morethanseven.net
Hooks for tooling


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/nickstone333/3135318558
Example: Nagios integration


gareth rushgrove | morethanseven.net
Example: Rundeck integration


gareth rushgrove | morethanseven.net
How
-      Report processors
-      Stored configurations
-      Puppet internal APIs




Where to find the data


gareth rushgrove | morethanseven.net
Report processors


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/nathaninsandiego/3757033518
[agent]
     report = true
     pluginsync = true

     [master]
     reports = store
     pluginsync = true



Configuration


gareth rushgrove | morethanseven.net
[agent]
     report = true
     pluginsync = true

     [master]
     reports = store,http,log,sample
     pluginsync = true



Adding reports


gareth rushgrove | morethanseven.net
Built in


gareth rushgrove | morethanseven.net
Store the YAML report on disk. Each host sends
        its report as a YAML dump and this just stores
        the file on disk, in the reportdir directory.




Store


gareth rushgrove | morethanseven.net
Send all received logs to the local log
        destinations. Usually the log destination is syslog.




Log


gareth rushgrove | morethanseven.net
Send report information via HTTP to the
        reporturl. Each host sends its report as a YAML
        dump and this sends this YAML to a client via
        HTTP POST.




HTTP


gareth rushgrove | morethanseven.net
This report sends specific log messages to
        specific email addresses based on the tags in
        the log messages.




Tagmail


gareth rushgrove | morethanseven.net
Graph all available data about hosts using
        the RRD library.




RRDgraph


gareth rushgrove | morethanseven.net
.
     └── sample-report
         ├── lib
         │   └── puppet
         │       └── reports
         │           └── sample.rb
         └── manifests
             └── init.pp



Simple report structure


gareth rushgrove | morethanseven.net
require 'puppet'
 require 'yaml'
 require 'logger'

 LOG = Logger.new('/tmp/puppet.log')

 Puppet::Reports.register_report(:sample) do
   def process
     message = "Puppet run for #{self.host} #{self.status}"
     Puppet.debug "Hello from sample report processor"
     LOG.info message
   end
 end




Simple report processor


gareth rushgrove | morethanseven.net
require 'puppet'
 require 'yaml'
 require 'logger'

 LOG = Logger.new('/tmp/puppet.log')

 Puppet::Reports.register_report(:sample) do
   def process
     message = "Puppet run for #{self.host} #{self.status}"
     Puppet.debug "Hello from sample report processor"
     LOG.info message
   end
 end




Register report


gareth rushgrove | morethanseven.net
require 'puppet'
 require 'yaml'
 require 'logger'

 LOG = Logger.new('/tmp/puppet.log')

 Puppet::Reports.register_report(:sample) do
   def process
     message = "Puppet run for #{self.host} #{self.status}"
     Puppet.debug "Hello from sample report processor"
     LOG.info message
   end
 end




Do something with the data in self


gareth rushgrove | morethanseven.net
Open source examples


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/nettsu/5771348892
IRC/Campfire


gareth rushgrove | morethanseven.net
IRC/Campfire


gareth rushgrove | morethanseven.net
Ganglia


gareth rushgrove | morethanseven.net
Graphs!


gareth rushgrove | morethanseven.net
Zendesk


gareth rushgrove | morethanseven.net
James Turnbull will have got there first


gareth rushgrove | morethanseven.net
Stored configuration database


gareth rushgrove | morethanseven.net
[master]
     storeconfigs = true
     dbadapter = mysql
     dbuser = puppet
     dbpassword = password
     dbserver = localhost
     dbsocket = /var/run/mysql.sock



Configuration


gareth rushgrove | morethanseven.net
SQL


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/nettsu/4997777753
mysql> SHOW TABLES;
     +------------------+
     | Tables_in_puppet |
     +------------------+
     | fact_names        |
     | fact_values       |
     | hosts             |
     | inventory_facts |
     | inventory_nodes |
     | param_names       |
     | param_values      |
     | puppet_tags       |
     | resource_tags     |
     | resources         |
     | source_files      |
     +------------------+
     11 rows in set (0.00 sec)




Lots of tables to explore


gareth rushgrove | morethanseven.net
mysql> SELECT name,ip,environment,last_compile FROM hosts;
     +--------------------+-----------------+-------------+---------------------+
     | name               | ip              | environment | last_compile        |
     +--------------------+-----------------+-------------+---------------------+
     | ubuntu.localdomain | 192.168.157.129 | production | 2012-03-04 21:19:41 |
     +--------------------+-----------------+-------------+---------------------+
     1 row in set (0.00 sec)




Easily get a list of hosts


gareth rushgrove | morethanseven.net
mysql> SELECT filename,updated_at FROM source_files;
      +-----------------------------------------------------+---------------------+
      | filename                                            | updated_at          |
      +-----------------------------------------------------+---------------------+
      | NULL                                                | 2011-10-11 10:34:36 |
      | /etc/puppet/modules/users/manifests/init.pp         | 2011-10-12 14:13:49 |
      | /etc/puppet/modules/nagios/manifests/init.pp        | 2011-10-12 14:13:49 |
      | /etc/puppet/modules/ntp/manifests/init.pp           | 2011-10-12 14:13:50 |
      | /etc/puppet/modules/base_packages/manifests/init.pp | 2011-10-12 14:13:50 |
      | /etc/puppet/modules/sudo/manifests/init.pp          | 2011-10-12 14:13:50 |
      | /etc/puppet/modules/apt/manifests/init.pp           | 2011-10-12 14:13:50 |
      | /etc/puppet/modules/logrotate/manifests/init.pp     | 2011-10-12 14:13:51 |
      | /etc/puppet/modules/ganglia/manifests/init.pp       | 2011-10-12 14:13:52 |
      | /etc/puppet/modules/motd/manifests/init.pp          | 2011-10-12 14:13:57 |
      | /etc/puppet/manifests/classes.pp                    | 2011-10-12 14:17:25 |
      | /etc/puppet/modules/mysql/manifests/init.pp         | 2011-10-12 14:17:25 |
      | /etc/puppet/modules/apache2/manifests/init.pp       | 2011-10-12 14:17:25 |
      | /etc/puppet/modules/passenger/manifests/init.pp     | 2011-10-12 14:17:26 |
      +-----------------------------------------------------+---------------------+




Other interesting data too


gareth rushgrove | morethanseven.net
Active Record


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/simplebitsdan/480696708
#!/usr/bin/env ruby

    require 'puppet/rails'

    Puppet[:config] = '/etc/puppet/puppet.conf'
    Puppet.parse_config
    config = Puppet.settings.instance_variable_get(:@values)
    master_config = config[:master]




Load Puppet master configuration


gareth rushgrove | morethanseven.net
adapter = master_conf[:dbadapter]
    args = {:adapter => adapter,
      :log_level => master_conf[:rails_loglevel]}

    args[:host]                        =   master_conf[:dbserver]
    args[:username]                    =   master_conf[:dbuser]
    args[:password]                    =   master_conf[:dbpassword]
    args[:database]                    =   master_conf[:dbname]
    args[:database]                    =   "puppet" unless not args[:database].to_s.empty?
    args[:port]                        =   master_conf[:dbport]
    socket                             =   master_conf[:dbsocket]
    args[:socket]                      =   socket unless socket.to_s.empty?




Map database connection settings


gareth rushgrove | morethanseven.net
ActiveRecord::Base.establish_connection(args)

    Puppet::Rails::Host.all.each { |h|
      puts "#{h.name}"
    }




Make an active record query


gareth rushgrove | morethanseven.net
⚡ ./list.rb
      ubuntu.localdomain




Example run


gareth rushgrove | morethanseven.net
Code spelunking


gareth rushgrove | morethanseven.net
#!/usr/bin/env ruby

     require 'puppet'
     Puppet[:config] = "/etc/puppet/puppet.conf"
     Puppet.parse_config
     Puppet[:clientyamldir] = Puppet[:yamldir]
     Puppet::Node.indirection.terminus_class = :yaml




Load Puppet master configuration


gareth rushgrove | morethanseven.net
nodes = Puppet::Node.indirection.search("*")
     nodes.each do |n|
       facts = Puppet::Node::Facts.indirection.find(n.name)
       tags = Puppet::Resource::Catalog.indirection.find(n.name).tags
       puts "#{n.name} - #{tags.join(', ')}"
     end




Make a query using the internal API


gareth rushgrove | morethanseven.net
⚡ sudo ./puppettags.rb
      ubuntu.localdomain - settings, default, node




Needs root permissions


gareth rushgrove | morethanseven.net
What
⚡ ./puppetlast.rb
      +-----------+-----------------+
      | hostname | last puppet run |
      +-----------+-----------------+
      | hostname4 | 100 minutes     |
      | hostname1 | 25 minutes      |
      | hostname5 | 10 minutes      |
      | hostname2 | 15 minutes      |
      | hostname3 | 5 minutes       |
      +-----------+-----------------+



Command line tools


gareth rushgrove | morethanseven.net
Dashboards


gareth rushgrove | morethanseven.net
web-puppet


gareth rushgrove | morethanseven.net
JSON over HTTP


gareth rushgrove | morethanseven.net
capistrano-puppet


gareth rushgrove | morethanseven.net
require 'capistrano-puppet'

     web_puppet = CapistranoPuppet::Server.new(
       'http://username:password@localhost:9295')

     role :web do
       web_puppet.get_servers('webserver')
     end




Get hosts from puppet


gareth rushgrove | morethanseven.net
Visualisation


gareth rushgrove | morethanseven.net
Your imagination
                                       ?
gareth rushgrove | morethanseven.net
-      web-puppet - https://github.com/garethr/web-puppet
-      capistrano-puppet - https://github.com/garethr/capistrano-puppet
-      puppet-ganglia - https://github.com/jamtur01/puppet-ganglia
-      puppet-zendesk - https://github.com/jamtur01/puppet-zendesk
-      puppet-irc - https://github.com/jamtur01/puppet-irc
-      puppet-campfire - https://github.com/jamtur01/puppet-campfire




Links


gareth rushgrove | morethanseven.net
One more thing


gareth rushgrove | morethanseven.net   http://www.flickr.com/photos/benterrett/6852348725/
Questions?


gareth rushgrove | morethanseven.net   http://flickr.com/photos/psd/102332391/

Puppet Data Mining

  • 1.
    Puppet Data Mining Reportprocessors, stored configs and more PuppetCamp 22nd March 2012 gareth rushgrove | morethanseven.net http://www.flickr.com/photos/map408/2412123378
  • 2.
    Gareth Rushgrove @garethr garethrushgrove | morethanseven.net
  • 3.
    Blog at morethanseven.net garethrushgrove | morethanseven.net
  • 4.
  • 5.
    Text Work at UKGovernment Digital Service gareth rushgrove | morethanseven.net
  • 6.
    Serious Government Business garethrushgrove | morethanseven.net
  • 7.
    - Why do we want it? - How do we get our hands on it - What can we build with it Puppet data gareth rushgrove | morethanseven.net http://www.flickr.com/photos/iancarroll/5027441664
  • 8.
    - Why do we want it? - How do we get our hands on it - What can we build with it Puppet data gareth rushgrove | morethanseven.net http://www.flickr.com/photos/iancarroll/5027441664
  • 9.
    - Why do we want it? - How do we get our hands on it - What can we build with it Puppet data gareth rushgrove | morethanseven.net http://www.flickr.com/photos/iancarroll/5027441664
  • 10.
  • 11.
    Single source oftruth gareth rushgrove | morethanseven.net http://www.flickr.com/photos/joeshlabotnik/4132307070/
  • 12.
    - Spreadsheets - Wiki - Deployment scripts - Application code - SSH configs - More deployment scripts - Monitoring configuration Example: find a list of hostnames gareth rushgrove | morethanseven.net
  • 13.
    Operational reports gareth rushgrove| morethanseven.net http://www.flickr.com/photos/nettsu/5276327339
  • 14.
    Company hardware Example: hardwarespreadsheet gareth rushgrove | morethanseven.net
  • 15.
    Hooks for tooling garethrushgrove | morethanseven.net http://www.flickr.com/photos/nickstone333/3135318558
  • 16.
    Example: Nagios integration garethrushgrove | morethanseven.net
  • 17.
    Example: Rundeck integration garethrushgrove | morethanseven.net
  • 18.
  • 19.
    - Report processors - Stored configurations - Puppet internal APIs Where to find the data gareth rushgrove | morethanseven.net
  • 20.
    Report processors gareth rushgrove| morethanseven.net http://www.flickr.com/photos/nathaninsandiego/3757033518
  • 21.
    [agent] report = true pluginsync = true [master] reports = store pluginsync = true Configuration gareth rushgrove | morethanseven.net
  • 22.
    [agent] report = true pluginsync = true [master] reports = store,http,log,sample pluginsync = true Adding reports gareth rushgrove | morethanseven.net
  • 23.
    Built in gareth rushgrove| morethanseven.net
  • 24.
    Store the YAMLreport on disk. Each host sends its report as a YAML dump and this just stores the file on disk, in the reportdir directory. Store gareth rushgrove | morethanseven.net
  • 25.
    Send all receivedlogs to the local log destinations. Usually the log destination is syslog. Log gareth rushgrove | morethanseven.net
  • 26.
    Send report informationvia HTTP to the reporturl. Each host sends its report as a YAML dump and this sends this YAML to a client via HTTP POST. HTTP gareth rushgrove | morethanseven.net
  • 27.
    This report sendsspecific log messages to specific email addresses based on the tags in the log messages. Tagmail gareth rushgrove | morethanseven.net
  • 28.
    Graph all availabledata about hosts using the RRD library. RRDgraph gareth rushgrove | morethanseven.net
  • 29.
    . └── sample-report ├── lib │   └── puppet │   └── reports │   └── sample.rb └── manifests └── init.pp Simple report structure gareth rushgrove | morethanseven.net
  • 30.
    require 'puppet' require'yaml' require 'logger' LOG = Logger.new('/tmp/puppet.log') Puppet::Reports.register_report(:sample) do def process message = "Puppet run for #{self.host} #{self.status}" Puppet.debug "Hello from sample report processor" LOG.info message end end Simple report processor gareth rushgrove | morethanseven.net
  • 31.
    require 'puppet' require'yaml' require 'logger' LOG = Logger.new('/tmp/puppet.log') Puppet::Reports.register_report(:sample) do def process message = "Puppet run for #{self.host} #{self.status}" Puppet.debug "Hello from sample report processor" LOG.info message end end Register report gareth rushgrove | morethanseven.net
  • 32.
    require 'puppet' require'yaml' require 'logger' LOG = Logger.new('/tmp/puppet.log') Puppet::Reports.register_report(:sample) do def process message = "Puppet run for #{self.host} #{self.status}" Puppet.debug "Hello from sample report processor" LOG.info message end end Do something with the data in self gareth rushgrove | morethanseven.net
  • 33.
    Open source examples garethrushgrove | morethanseven.net http://www.flickr.com/photos/nettsu/5771348892
  • 34.
  • 35.
  • 36.
    Ganglia gareth rushgrove |morethanseven.net
  • 37.
    Graphs! gareth rushgrove |morethanseven.net
  • 38.
    Zendesk gareth rushgrove |morethanseven.net
  • 39.
    James Turnbull willhave got there first gareth rushgrove | morethanseven.net
  • 40.
    Stored configuration database garethrushgrove | morethanseven.net
  • 41.
    [master] storeconfigs = true dbadapter = mysql dbuser = puppet dbpassword = password dbserver = localhost dbsocket = /var/run/mysql.sock Configuration gareth rushgrove | morethanseven.net
  • 42.
    SQL gareth rushgrove |morethanseven.net http://www.flickr.com/photos/nettsu/4997777753
  • 43.
    mysql> SHOW TABLES; +------------------+ | Tables_in_puppet | +------------------+ | fact_names | | fact_values | | hosts | | inventory_facts | | inventory_nodes | | param_names | | param_values | | puppet_tags | | resource_tags | | resources | | source_files | +------------------+ 11 rows in set (0.00 sec) Lots of tables to explore gareth rushgrove | morethanseven.net
  • 44.
    mysql> SELECT name,ip,environment,last_compileFROM hosts; +--------------------+-----------------+-------------+---------------------+ | name | ip | environment | last_compile | +--------------------+-----------------+-------------+---------------------+ | ubuntu.localdomain | 192.168.157.129 | production | 2012-03-04 21:19:41 | +--------------------+-----------------+-------------+---------------------+ 1 row in set (0.00 sec) Easily get a list of hosts gareth rushgrove | morethanseven.net
  • 45.
    mysql> SELECT filename,updated_atFROM source_files; +-----------------------------------------------------+---------------------+ | filename | updated_at | +-----------------------------------------------------+---------------------+ | NULL | 2011-10-11 10:34:36 | | /etc/puppet/modules/users/manifests/init.pp | 2011-10-12 14:13:49 | | /etc/puppet/modules/nagios/manifests/init.pp | 2011-10-12 14:13:49 | | /etc/puppet/modules/ntp/manifests/init.pp | 2011-10-12 14:13:50 | | /etc/puppet/modules/base_packages/manifests/init.pp | 2011-10-12 14:13:50 | | /etc/puppet/modules/sudo/manifests/init.pp | 2011-10-12 14:13:50 | | /etc/puppet/modules/apt/manifests/init.pp | 2011-10-12 14:13:50 | | /etc/puppet/modules/logrotate/manifests/init.pp | 2011-10-12 14:13:51 | | /etc/puppet/modules/ganglia/manifests/init.pp | 2011-10-12 14:13:52 | | /etc/puppet/modules/motd/manifests/init.pp | 2011-10-12 14:13:57 | | /etc/puppet/manifests/classes.pp | 2011-10-12 14:17:25 | | /etc/puppet/modules/mysql/manifests/init.pp | 2011-10-12 14:17:25 | | /etc/puppet/modules/apache2/manifests/init.pp | 2011-10-12 14:17:25 | | /etc/puppet/modules/passenger/manifests/init.pp | 2011-10-12 14:17:26 | +-----------------------------------------------------+---------------------+ Other interesting data too gareth rushgrove | morethanseven.net
  • 46.
    Active Record gareth rushgrove| morethanseven.net http://www.flickr.com/photos/simplebitsdan/480696708
  • 47.
    #!/usr/bin/env ruby require 'puppet/rails' Puppet[:config] = '/etc/puppet/puppet.conf' Puppet.parse_config config = Puppet.settings.instance_variable_get(:@values) master_config = config[:master] Load Puppet master configuration gareth rushgrove | morethanseven.net
  • 48.
    adapter = master_conf[:dbadapter] args = {:adapter => adapter, :log_level => master_conf[:rails_loglevel]} args[:host] = master_conf[:dbserver] args[:username] = master_conf[:dbuser] args[:password] = master_conf[:dbpassword] args[:database] = master_conf[:dbname] args[:database] = "puppet" unless not args[:database].to_s.empty? args[:port] = master_conf[:dbport] socket = master_conf[:dbsocket] args[:socket] = socket unless socket.to_s.empty? Map database connection settings gareth rushgrove | morethanseven.net
  • 49.
    ActiveRecord::Base.establish_connection(args) Puppet::Rails::Host.all.each { |h| puts "#{h.name}" } Make an active record query gareth rushgrove | morethanseven.net
  • 50.
    ⚡ ./list.rb ubuntu.localdomain Example run gareth rushgrove | morethanseven.net
  • 51.
  • 52.
    #!/usr/bin/env ruby require 'puppet' Puppet[:config] = "/etc/puppet/puppet.conf" Puppet.parse_config Puppet[:clientyamldir] = Puppet[:yamldir] Puppet::Node.indirection.terminus_class = :yaml Load Puppet master configuration gareth rushgrove | morethanseven.net
  • 53.
    nodes = Puppet::Node.indirection.search("*") nodes.each do |n| facts = Puppet::Node::Facts.indirection.find(n.name) tags = Puppet::Resource::Catalog.indirection.find(n.name).tags puts "#{n.name} - #{tags.join(', ')}" end Make a query using the internal API gareth rushgrove | morethanseven.net
  • 54.
    ⚡ sudo ./puppettags.rb ubuntu.localdomain - settings, default, node Needs root permissions gareth rushgrove | morethanseven.net
  • 55.
  • 56.
    ⚡ ./puppetlast.rb +-----------+-----------------+ | hostname | last puppet run | +-----------+-----------------+ | hostname4 | 100 minutes | | hostname1 | 25 minutes | | hostname5 | 10 minutes | | hostname2 | 15 minutes | | hostname3 | 5 minutes | +-----------+-----------------+ Command line tools gareth rushgrove | morethanseven.net
  • 57.
  • 58.
  • 59.
    JSON over HTTP garethrushgrove | morethanseven.net
  • 60.
  • 61.
    require 'capistrano-puppet' web_puppet = CapistranoPuppet::Server.new( 'http://username:password@localhost:9295') role :web do web_puppet.get_servers('webserver') end Get hosts from puppet gareth rushgrove | morethanseven.net
  • 62.
  • 63.
    Your imagination ? gareth rushgrove | morethanseven.net
  • 64.
    - web-puppet - https://github.com/garethr/web-puppet - capistrano-puppet - https://github.com/garethr/capistrano-puppet - puppet-ganglia - https://github.com/jamtur01/puppet-ganglia - puppet-zendesk - https://github.com/jamtur01/puppet-zendesk - puppet-irc - https://github.com/jamtur01/puppet-irc - puppet-campfire - https://github.com/jamtur01/puppet-campfire Links gareth rushgrove | morethanseven.net
  • 65.
    One more thing garethrushgrove | morethanseven.net http://www.flickr.com/photos/benterrett/6852348725/
  • 66.
    Questions? gareth rushgrove |morethanseven.net http://flickr.com/photos/psd/102332391/