Showing posts with label puppet. Show all posts
Showing posts with label puppet. Show all posts

Monday, August 10, 2015

Just What Is OpenStack Infra?

I work for HP doing two things. By day I work inside the HP firewall setting up and running a CI system for testing HP's OpenStack technology. We call this system Gozer. (By the way we are hiring). By night I work upstream (in the Open Source world) with the OpenStack Infrastructure Team setting up and running a CI system for OpenStack developers.

This blog post concerns my work upstream.

One of my chief initiatives since joining the team two years ago is to make the Puppet codebase used by infra more in-line with standards, more reusable, and generally better. I have never attempted to use infra as a testbed for experimental uses of Puppet, I've always tried to apply the best practices known in the community. Of course there are exceptions to this (see all the Ansible stuff). This initiative is codified in a few different specifications accepted by the team (you don't need to read these):

One mark of the success of this ongoing initiative is that I am now in a place where I am recommending parts of our code to other people in my community. Those are the people for whom I intend this blog post. Someone sees a neat part of the Puppet OpenStack 'stuff' and wants to use it, but it needs a patch or a use case covered. This blog post is supposed to provide a high level overview of what we do, who 'we' are, and the bigger pieces and how they interact with each other. We'll start with a long series of names and definitions.


Naming things is hard

So what is OpenStack? OpenStack is an Open Source software collection based around providing cloud software. The OpenStack Foundation is a nonprofit organization that provides centralized resources to support the effort, this comes in both technical (sysadmins) and other forms (legal, conference organizing, etc). OpenStack is made up of many components, the simplest is that 'nova' provides a compute layer to the cloud i.e. kvm or xen management.

OpenStack can be installed with Puppet. The Puppet code that does this is called "OpenStack Puppet Modules." These modules install OpenStack services such as nova, glance, and cinder. Their source code is available by searching for openstack/puppet-*. The team that develops this code is called the OpenStack Puppet Module Team. This team uploads to the forge under the namespaces 'openstack' or 'stackforge.'

I do not work with these modules on a daily basis.

I work with the OpenStack Infrastructure Team. This team deploys and maintains the CI system used by OpenStack upstream developers. We have our own set of Puppet modules that are completely unrelated to the OpenStack Puppet Modules. Their source code can be found by searching for openstack-infra/puppet-*. These modules are uploaded under the forge namespaces 'openstackci' and 'openstackinfra.' We use these modules to deploy services like Gerrit, Jenkins, and Drupal. We also have a number of utility modules useful for generic Linux administration. We have Precise, Trusty, Centos 6, and various Fedora flavors in our infrastructure, so our modules often have good cross-platform support.

Central Nexus


All the openstack-infra/puppet-* modules are consumed from master by our 'central nexus' repository: system-config. System-config uses a second repository for flat-files: project-config. System-config contains node definitions, public hiera data(soon), a few utility scripts, a modules.env file, a single module to stick 'roles' in called 'openstack_project'. The more 'core' roles in openstack_project call out to another repo called: puppet-openstackci. The secrets are stored in a hiera directory that is not public.


Crude Drawing


The crude drawing above shows a typical flow. A node definition lives in site.pp, which include a role class from openstack_project, which includes a role class from the openstackci module, which then uses resources and classes from the other modules, in this case puppet-iptables.

There are other code paths too. Sometimes, often in fact, an openstack_project role will include openstack_project::server or openstack_project::template, these classes wrap up most of the 'basics' of linux administration. Template or server will go on to include more resources.

There are multiple places to integrate here. At the most basic, a Puppet user could include our puppet-iptables module in their modulepath and start using it. An individual who wants a jenkins server or another server like ours could use openstackci and it's dependencies and write their own openstack_project wrapper classes to include openstackci classes.

We do not encourage site.pp or openstack_project classes to be extended at this time, we instead encourage features or compatibility extensions to be put into openstackci or the service-specific modules themselves. This is a work in progress and some important logic still lives in openstack_project and should be moved out. A stretch-goal is to move to a place where all of openstack infra runs out of openstackci, providing only a hiera yaml file to set parameters.

Continuous Deployment

A note about modules.env: OpenStack-infra has a modules.env file instead of a Puppetfile. This file contains the location, name, and ref of git repositories to put inside the modulepath on the Puppetmaster. OpenStack infra deploys all of its own Puppet modules from master, so any change to any module can break the whole system. We counteract this danger by having lots of testing and code review before any change goes through.

A note about project-config: One of the patterns we use in OpenStack Infra is to push our configuration into flat files as much as possible. We have one repository, project-config, which holds files that control the behaviour of our services, Puppet's job is only to copy files out of the repo and into the correct location. This makes it easier for people to find these often-changed files, and means we can provide more people access to merge code there than we would with our system-config repository.

A note about puppet agent: We run puppet-agent, but it is fired from the Puppetmaster by an ansible run. We hope to move to puppet apply triggered by ansible soon.


The part where I give you things

There are two modules right now that you might be interested in using yourself. The first is our puppet-httpd module. This module was forked from puppetlabs-apache at version 0.0.4. It has seen some minor improvements from us but nothing major, other than a name change from 'apache' to 'httpd'. You can see why we forked in the Readme of the project but the kicker is that this module allows you to use raw 'myhost.vhost.erb' templates with apache. You no longer need to know how to translate the apache syntax you want into puppetlabs-apache parameters. Let's see what this looks like:

openstack_project/templates/status.vhost.erb:
# ************************************
# Managed by Puppet
# ************************************

NameVirtualHost <%= @vhost_name %>:<%= @port %>
<VirtualHost <%= @vhost_name %>:<%= @port %>>
  ServerName <%= @srvname %>
<% if @serveraliases.is_a? Array -%>
<% @serveraliases.each do |name| -%><%= " ServerAlias #{name}\n" %><% end -%>
<% elsif @serveraliases != '' -%>
<%= " ServerAlias #{@serveraliases}" %>
<% end -%>
  DocumentRoot <%= @docroot %>

  Alias /bugday /srv/static/bugdaystats
  <Directory /srv/static/bugdaystats>
      AllowOverride None
      Order allow,deny
      allow from all
  </Directory>

  Alias /reviews /srv/static/reviewday
  <Directory /srv/static/reviewday>
      AllowOverride None
      Order allow,deny
      allow from all
  </Directory>

  Alias /release /srv/static/release

  <Directory <%= @docroot %>>
    Options <%= @options %>
    AllowOverride None
    Order allow,deny
    allow from all
  </Directory>

  # Sample elastic-recheck config file, adjust prefixes
  # per your local configuration. Because these are nested
  # we need the more specific one first.
  Alias /elastic-recheck/data /var/lib/elastic-recheck
  <Directory /var/lib/elastic-recheck>
      AllowOverride None
      Order allow,deny
      allow from all
  </Directory>

  RedirectMatch permanent ^/rechecks(.*) /elastic-recheck
  Alias /elastic-recheck /usr/local/share/elastic-recheck
  <Directory /usr/local/share/elastic-recheck>
      AllowOverride None
      Order allow,deny
      allow from all
  </Directory>


  ErrorLog /var/log/apache2/<%= @name %>_error.log
  LogLevel warn
  CustomLog /var/log/apache2/<%= @name %>_access.log combined
  ServerSignature Off
</VirtualHost>



::httpd::vhost { 'status.openstack.org':
  port     => 80,
  priority => '50',
  docroot  => '/srv/static/status',
  template => 'openstack_project/status.vhost.erb',
  require  => File['/srv/static/status'],

}


If you don't need a vhost and just want to serve a directory, you can:

::httpd::vhost { 'tarballs.openstack.org':
  port     => 80,
  priority => '50',
  docroot  => '/srv/static/tarballs',
  require  => File['/srv/static/tarballs'],

}

The second is puppet-iptables, which provides the ability to spit direct iptables rules into a Puppet class and have those rules set. You can also specify the ports to open up. Again this is an example of weak modeling. Concat resources around specific rules are coming soon in this change. Let's see what using the iptables module looks like:

class { '::iptables':
  public_tcp_ports => ['80', '443', '8080'],
  public_udp_ports => ['2003'],
  rules4           => ['-m state --state NEW -m tcp -p tcp --dport 8888 -s somehost.openstack.org -j ACCEPT'],
  rules6           => ['-m state --state NEW -m tcp -p tcp --dport 8888 -s somehost.openstack.org -j ACCEPT'],


This enables you to manage iptables the way you view iptables. It is easy to debug, easy to reason about, and extensible. We think it provides a significant advantage over the puppetlabs-firewall module. Unfortunately, the puppet-iptables module currently is hardcoded to open up certain openstack hosts, that should be fixed very soon (possibly by you!). Both of these modules try to be as simple as possible.

Getting these modules right now is done through git. If you don't want to ride the 'master' train with us, you can hop in #openstack-infra on freenode and ask for a tag to be created at the revision you need. We're working on getting forge publishing in to the pipeline, it's not a priority for us right now but if you need it you can ask for it and we can see about increasing focus there.

There are two generic modules that advance the puppet ecosystem coming out of OpenStack Infra and we hope there will be more to come. If you'd like to help us develop these modules we'd love the help. You can start learning how to contribute to OpenStack here.

Saturday, August 1, 2015

Inspecting Puppet Module Metadata

Last week at #puppethack, @hunner helped me land a patch to stdlib to add a load_module_metadata function. This function came out of several Puppet module triage sessions and a patch from @raphink inspired by a conversation with @hirojin.

The load_module_metadata function is available in master of puppetlabs-stdlib, hopefully it will be wrapped up into one of the later 4.x releases, but will almost certainly make it into 5.x.

On it's own this function doesn't do much, but it is composable. Let's see some basic usage:

$: cat metadata.pp

$metadata = load_module_metadata('stdlib')

notify { $metadata['name']: }


$: puppet apply --modulepath=modules metadata.pp
Notice: Compiled catalog for hertz in environment production in 0.03 seconds
Notice: puppetlabs-stdlib
Notice: /Stage[main]/Main/Notify[puppetlabs-stdlib]/message: defined 'message' as 'puppetlabs-stdlib'
Notice: Finished catalog run in 0.03 seconds


As you can see this isn't the most amazing thing ever. However access to that information is very useful in the following case:


$apache_metadata = load_module_metadata('apache')

case $apache_metadata['name'] {
  'puppetlabs-apache': {
    # invoke apache as required by puppetlabs-apache
   }
  'example42-apache': {
    # inovke apache as required by example42-apache
  }
  default: {
    fail("Apache module author not recognized, please add it here")
  }
}


This is an example of Puppet code that can inspect the libraries loaded in the modulepath, then make intelligent decisions about how to use them. This means that module authors can support multiple versions of 'library' modules and not force their users into one or the other.

This is a real problem in Puppet right now. For every 'core' module there are multiple implementations, with the same name. Apache, nginx, mysql, archive, wget, the list goes on. Part of this is a failure of the community to band behind a single module, but we can't waste time finger pointing now. The cat is out of the bag and we have to deal with it.

We've had metadata.json and dependencies for a while now. However, due to the imperfectness of the puppet module tool, most advanced users do not depend on dependency resolution from metadata.json. At my work we simply clone every module we need from git, users of r10k do much the same.

load_metadata_json enables modules to enforce that their dependencies are being met. Simply put a stanza like this in params.pp:

$unattended_upgrades_metadata = load_module_metadata('unattended_upgrades') 
$healthcheck_metadata = load_module_metadata('healthcheck')

if versioncmp($healthcheck_metadata['version'], '0.0.1') < 0 {
  fail("Puppet-healthcheck is too old to work")
}
if versioncmp($unattended_upgrades_metadata['version'], '2.0.0') < 0 {
  fail("Puppet-unattended_upgrades is too old to work")
}


As we already saw, modules can express dependencies on specific implementations and versions. They can also inspect the version available and use that. This is extremely useful when building a module that depends on another module, and that module is crossing a symver major version boundary. In the past, in the OpenStack modules, we passed a parameter called 'mysql_module_version' to each class which allowed that class to use the correct invocation of the mysql module. Now classes anywhere in your puppet code base can inspect the mysql module directly and determine which invocation syntax to use.

$mysql_metadata = load_module_metadata('mysql')

if versioncmp($mysql_metadata['version'], '2.0.0') <= 0 {
  # Use mysql 2.0 syntax
} else {
  # Use mysql 3.0 syntax
}


Modules can even open up their own metadata.json, and while it is clunky, it is possible to dynamically assert that dependencies are available and in the correct versions.

I'm excited to see what other tricks people can do with this. I'm anticipating it will make collaboration easier, upgrades easier, and make Puppet runs even more safe. If you come up with a neat trick, please share it with the community and ping me on twitter(@nibalizer) or IRC: nibalizer.



Sunday, May 10, 2015

Overview of Puppet in OpenStack Infra

Last week I gave this presentation at the PDX Puppet Users group. It is an overview of how we use Puppet in the OpenStack Infra project. There is no video or audio recording.

Presentation

Monday, December 15, 2014

Puppet cert inspector

Today while poking around in the puppet source code, I came across a utility in the ext/ directory called cert_inspector. This seems to be a little utility that opens up certificates and interrogates them for useful data. This is better than what I usually do, which is incanting openssl directly. It also is capable of chewing up an entire /var/lib/puppet/ssl directory and dumping information on every cert and key it finds. See the output below:



 (master u=)$: ./ext/cert_inspector ~/.puppet/ssl/certs/ca.pem
/home/nibz/.puppet/ssl/certs/ca.pem:
  Certificate assigning name /CN=Puppet CA: zabava.cat.pdx.edu to key</CN=Puppet CA: zabava.cat.pdx.edu>
    serial number 1
    issued by /CN=Puppet CA: zabava.cat.pdx.edu
    signed by key</CN=Puppet CA: zabava.cat.pdx.edu>

 (master u=)$: ./ext/cert_inspector ~/.puppet/ssl/
WARNING: file "/home/nibz/.puppet/ssl/public_keys/hunner_what_r_u_doin.pem" could not be interpreted
WARNING: file "/home/nibz/.puppet/ssl/public_keys/hunner_stahp.pem" could not be interpreted
WARNING: file "/home/nibz/.puppet/ssl/public_keys/maxwell.hsd1.or.comcast.net.pem" could not be interpreted
WARNING: file "/home/nibz/.puppet/ssl/public_keys/hunner.pem" could not be interpreted
/home/nibz/.puppet/ssl/certs/ca.pem:
  Certificate assigning name /CN=Puppet CA: zabava.cat.pdx.edu to key</CN=Puppet CA: zabava.cat.pdx.edu>
    serial number 1
    issued by /CN=Puppet CA: zabava.cat.pdx.edu
    signed by key</CN=Puppet CA: zabava.cat.pdx.edu>

/home/nibz/.puppet/ssl/certificate_requests/hunner.pem:
  Certificate request for /CN=hunner having key key</CN=hunner>
    signed by key</CN=hunner>

/home/nibz/.puppet/ssl/certificate_requests/hunner_stahp.pem:
  Certificate request for /CN=hunner_stahp having key key</CN=hunner_stahp>
    signed by key</CN=hunner_stahp>

/home/nibz/.puppet/ssl/certificate_requests/hunner_what_r_u_doin.pem:
  Certificate request for /CN=hunner_what_r_u_doin having key key</CN=hunner_what_r_u_doin>
    signed by key</CN=hunner_what_r_u_doin>

/home/nibz/.puppet/ssl/private_keys/hunner.pem:
  Private key for key</CN=hunner>

/home/nibz/.puppet/ssl/private_keys/hunner_stahp.pem:
  Private key for key</CN=hunner_stahp>

/home/nibz/.puppet/ssl/private_keys/hunner_what_r_u_doin.pem:
  Private key for key</CN=hunner_what_r_u_doin>

/home/nibz/.puppet/ssl/private_keys/maxwell.hsd1.or.comcast.net.pem:
  Private key for key</home/nibz/.puppet/ssl/private_keys/maxwell.hsd1.or.comcast.net.pem>

Tuesday, December 9, 2014

Testing Puppet node definitions

Sometimes Puppet node definitions get a little hairy. Here is a quick trick I use to validate them manually. This is inspired by this review.

Given a regex node definition create a test file called node.pp:

node /^git(-frontend\d+)?\.openstack\.org$/ { 
  notify { 'match': }


 Then, using the --certname="testnode" syntax to puppet apply, do some quick spot testing to see what happens.

$: puppet apply nodedef.pp 
Error: Could not find default node or by name with 'maxwell.pdx.edu, maxwell.pdx, maxwell' on node maxwell.pdx.edu
Error: Could not find default node or by name with 'maxwell.pdx.edu, maxwell.pdx, maxwell' on node maxwell.pdx.edu
$: puppet apply --certname='git.openstack.org' nodedef.pp  
Notice: Compiled catalog for git.openstack.org in environment production in 0.02 seconds
Notice: match
Notice: /Stage[main]/Main/Node[git-frontendd.openstack.org]/Notify[match]/message: defined 'message' as 'match'
Notice: Finished catalog run in 0.03 seconds
$: puppet apply --certname='git48.openstack.org' nodedef.pp  
Error: Could not find default node or by name with 'git48.openstack.org, git48.openstack, git48, maxwell.pdx.edu, maxwell.pdx, maxwell' on node git48.openstack.org
Error: Could not find default node or by name with 'git48.openstack.org, git48.openstack, git48, maxwell.pdx.edu, maxwell.pdx, maxwell' on node git48.openstack.org
$: puppet apply --certname='git-frontend01.openstack.org' nodedef.pp  
Notice: Compiled catalog for git-frontend01.openstack.org in environment production in 0.02 seconds
Notice: match
Notice: /Stage[main]/Main/Node[git-frontendd.openstack.org]/Notify[match]/message: defined 'message' as 'match'
Notice: Finished catalog run in 0.03 seconds 
 
  
This gives us the confidence to push this node definition to production without worrying about affecting existing git servers.

Monday, December 8, 2014

#puppethack

#puppethack is the new version of the Puppet triage-a-thon. It is a decentralized hackathon for open source Puppet projects. This year I participated mostly by contributing to the puppetlabs-rabbitmq module. I worked closely with Colleen Murphy of Puppet Labs on this.

When we started there were 31 outstanding pull requests. Now there are only 21.  And five of those have been opened during or after the hackathon.


I am most proud of my beaker testing PR which added beaker (acceptance and integration) testing to the rabbitmq_user, rabbitmq_vhost, and rabbitmq_policy types.

Overall #puppethack was a success and I am glad I participated. I want to thank my employer, HP, for allowing me to participate in the open source ecosystem. I am looking forward to doing it next year!

Sunday, December 7, 2014

Puppet Functions in stdlib

You should read up on the Puppet Functions in puppetlabs/stdlib. Seriously.

If you consider yourself a serious Puppet user, i.e. you use it more than twice a month, you owe it to yourself to read through them. The README has a brief description of the functions that are available. Every time I read through it, I find more useful functions have been added. And with stronger protections for function composability, there is no reason not to use functions all the time, every time.

Even if all you do is learn about the existence of the validation functions, you will be able to in two lines of code make your code more robust and easier on users.

For extra credit check out the puppet-community extlib module which has more functions not deemed cool enough for puppet core.

To eat my own words, I'll now post some functions whose existence I did not know about:

  • chomp
  • chop
  • defined_with_params
  • diference
  • delete_undef_values
  • empty (OMG USEFUL)
  • get_param (this changes eveeeeerything)
  • private
  • reject (duuuuuudeee)
  • squeeze
Happy hacking, fellow Puppeteers!

Saturday, July 20, 2013

Puppet Case insensitivity

I was helping my friend roll out some new services today and a section of his Puppet code caught my eye. This is what I saw:
...
  case $::kernel {
    'linux': {
...
This caught my eye because I had been explicitly capitalizing the 'L' in the $::kernel fact for years. I thought to myself "Is the fact capitalized?"
zeratul:~# facter -p kernel
Linux
What's going on here? Is the case operator insensitive?
case $::kernel {
  'sunos': { notify { $::kernel: }}
}
notice: SunOS
notice: /Stage[main]//Notify[SunOS]/message: defined 'message' as 'SunOS'
Wow. Is the '==' operator in Puppet case-insensitive as well?
if $::kernel == 'sunos' {
  notify { 'lasers': }
}
notice: lasers
notice: /Stage[main]//Notify[lasers]/message: defined 'message' as 'lasers'
Is this a problem with facter or puppet?
if "YES" == "yes" {
  notify { "false is true": }
}
notice: false is true
notice: /Stage[main]//Notify[false is true]/message: defined 'message' as 'false is true'
Seriously? Yep. Turns out the '==' operator is case-insensitive. The '=~' is case-sensitive, but you have to use regular expression syntax in order to use it:
if "YES" =~ /^yes$/ {
  notify { "false is true": }
}
notice: Finished catalog run in 1.30 seconds
Note that we should use '^$' to enclose the string so we don't accidentally get a substring match.

Tested on Puppet 2.7.x and 3.2.x

Thursday, March 7, 2013

Git-sync

Stardate: 90784.4428183

Where I work(read as: play) we use a lot of git. As an operator we often have a service running with its configs in git. A common pattern we use is to have a post-receive hook on the git repository set up to update the git checkout on a remote server. We accomplish this through a post-receive hook that sshes into the remote server and calls a script called git-sync with some options. The git-sync script github project forked from the puppet-sync script project that we use specifically for puppet dynamic git environments. Hunner <3 More dynamic git environments with puppet. Finch <3.

A hook for a project goes in the hooks/post-receive file of the git server's bare repo. Lets look at one now:

Example git post-receive hook

#!/bin/sh
## File: akwardly incorrect

PWD=`pwd`
REPONAME=`basename $PWD | sed 's/.git$//'`
REPO="git@gitserver.cat.pdx.edu:$REPONAME.git"
DEPLOY="/etc/nagios3/conf.d/flat"
SSH_ARGS="-i /shadow/home/git/.ssh/git@gitserver.cat.pdx.edu"
GITSYNC="nagios@nagios.cat.pdx.edu"
SYNC_COMMAND="/usr/local/bin/git-sync"

while read oldrev newrev refname
do
  BRANCH=`echo $refname | sed -n 's/^refs\/heads\///p'`
  if [ $BRANCH != "master" ]; then 
    echo "Branch is not master, therefore not pushing to nagios"
    exit 0
  fi
  [ "$newrev" -eq 0 ] 2> /dev/null && DELETE='--delete' || DELETE=''
  ssh $SSH_ARGS "$GITSYNC" "$SYNC_COMMAND" \
    --branch "$BRANCH" \
    --repository "$REPO" \
    --deploy "$DEPLOY" \
    $DELETE

done

ssh nagios@gitserver.cat.pdx.edu '/etc/init.d/nagios3 reload'

The hook will exit before doing anything if the branch is not

master
and if it is, will run the git-sync script remotely on the nagios host, then go back in to bounce the nagios service.

The git-sync script essentially performs a

git fectch; git checkout HEAD
It doesn't worry itself with merging, and it is submodule aware.

A file, .git-sync-stamp, must be created by the administrator of the system. This is how git-sync knows it is in charge of managing the repository. It is definitely not recommended that you add this file to git. However, that should more or less work if you never want to think about it. I also wrote this puppet defined type to manage the stamp, initial vcsrepo, and public_key_file for you.

A puppet defined type to initalize gitsync managed folders

define gitsync::gitsync(
  $deploy,
  $repo,
  $user,
  $source,
  $public_key,
  $public_key_type
){

  ssh_authorized_key { "${user}-${name}-gitsync":
    user   => $user,
    ensure => $present,
    type   => $public_key_type,
  }

  vcsrepo { $deploy:
    ensure    => present,
    provider  => git,
    user      => $user,
    source    => $source
    require   => Ssh_authorized_key["${user}-${name}-gitsync"],
  }

  file { "${deploy}/.git-sync-stamp":
    ensure  => present,
    owner   => $user,
    mode    => 0644,
    require => Vcsrepo[$deploy],
  }

The last thing to note is that I didn't write git-sync. I've modified it but it was mostly written by Reid Vandewielle and others. Marut <3 Enjoy

Monday, February 11, 2013

Nagios, maintenance windows and puppet

Stardate: 90721.31
Nagios is an excellent network monitoring service. It is used in production where I work. We wanted to be able to create maintenance windows using the web gui. (Actually we wanted a command line utility, but thats a separate post.) Turning off the default 'readonly' mode turned out to be a real pain and poorly documented. I ended up following the recommendations of this blog as well as some of the comments by readers. I also took the time to create a snipped of puppet code you can put in your manifests to make it easy to turn the command mode on. Note that the puppet code uses the default 'nagiosadmin' user and that it uses file_line from the puppet labs stdlib module. The code is available with syntax highlighting here.
  # the following is for enabling write access to the web gui
  if $readonly_web == false {

    file_line {.
      '/etc/nagios3/nagios.cfg-external-commands-yes':
        line    => "check_external_commands=1",
        path    => '/etc/nagios3/nagios.cfg',
        notify  => Service['nagios3'],
        require => Package['nagios3'];
      '/etc/nagios3/cgi.cfg-all_service_commands':
        line    => "authorized_for_all_host_commands=nagiosadmin",
        path    => '/etc/nagios3/cgi.cfg',
        notify  => Service['nagios3'],
        require => Package['nagios3'];
      '/etc/nagios3/cgi.cfg-all_host_commands':
        line    => "authorized_for_all_service_commands=nagiosadmin",
        path    => '/etc/nagios3/cgi.cfg',
        notify  => Service['nagios3'],
        require => Package['nagios3'];
    }   

    user { 'nagios':
      groups      => ['nagios', 'www-data'],
      membership  => minimum,
      require     => Package['nagios3'];
    }   

    file { '/var/lib/nagios3/rw':
      owner   => 'nagios',
      group   => 'www-data',
      mode    => '2710',
      ensure  => directory,
      require => Package['nagios3'];
    }   

    file { '/var/lib/nagios3':
      owner   => 'nagios',
      group   => 'nagios',
      mode    => '0751',
      ensure  => directory,
      require => Package['nagios3'];
    }   

  }

Tuesday, January 29, 2013

Puppet Fact Fix after Ruby 1.9 upgrade

Stardate: 90683.2
We recently upgraded our entire infrastructure to Puppet 3. As if that wasn't ambitious enough (though I suppose Puppet 3.1 has an rc), we are slowly bringing ruby to 1.9 from 1.8.7. Surprisingly, puppet actually works under ruby 1.9. This blows my mind since I remember having to do some seriously ugly hacks to get puppet working under ruby 1.9. Unfortunately, and embarrassingly, one of my custom facts was not forward compatible.
...
Info: Loading facts in /var/lib/puppet/lib/facter/nvidia_graphics_device.rb
Could not retrieve dns_ip_6: undefined method `each' for "yermom.cat.pdx.edu has address 131.252.222.10\n":String
Could not retrieve dns_ip_6: undefined method `each' for "yermom.cat.pdx.edu has address 131.252.222.10\n":String
...
The unfortunate cause of this (other than that I didn't write 1.9 compatible code) is that string.each has been removed from ruby as of ruby 1.9. :(
For those of you unfamiliar with puppet hacking, most testing should be done through git dynamic environments. Unfortunately, there is a bug in puppet that prevents facts from being able to be tested on any branch but production. You can add the facts to git and push them to the environments directory on the puppet master, but unless they are in branch production you won't see them get filebucketted or run.
So what to do? The answer is to develop on the box itself, usually as root, though sudo is an idea. (Hopefully I can get a friend of mine to guest post on why sudo is the correct way to attain root privileges for administration, and I can counterpost on why su - is the correct way.) On ubuntu (as of 12.04, anyways) the puppet configuration dir is /etc/puppet, but the puppet var dir is /var/lib/puppet. Facts live in /var/lib/puppet/lib/facter. The best way to get information on a current puppet's installation and configuration is through:
puppet config print all | grep vardir
vardir = /var/lib/puppet
Change directory to the puppet vardir and modify the facts in place. Then run the facter utility with the '-p' argument. The '-p' argument tells facter to run all its normal facts as well as facts loaded in from puppet.

root@yermom:/var/lib/puppet/lib/facter# facter -p | grep dns
dns_ip_4 => ["131.252.222.10"]
dns_ip_6 => []

Fantastic. All is well again.

Monday, January 28, 2013

PuppetDB/Storeconfigs Cache expiry

Stardate: 90682.98
After a couple of weeks of getting frustrated with puppet's Storeconfigs/puppetdb features, I have emerged victorious. PuppetDB is the newer, better, postgressier backend for puppet Storeconfigs. PuppetDB sports some really nice features including a fancy status/metrics web dashboard:
As you can see this is some interesting and potentially beneficial feedback. It is updated live and is mobile browser compatible. Personally, I'm happy to get graphs of this data any way I can, but I would prefer not to be locked into their dashboard. I would rather be able to get these data out of an often updated file or udp port so that I could send it to graphite for real time graphing and correlation with other metrics. I also don't see the point of having it be mobile friendly, since most everyone will have their puppetmaster/puppetdb server firewalled heavily and mobile devices have no business on the internal network. Some of the metrics can lead to actually tuning and performance boosts: mostly this is in the increasing the number of threads and the max jvm heap size.
The punchline here is that with
storeconfigs = true
in puppet.conf you can do exported/collected resource magics. When doing this with nagios resources I've been able to export and collect resources flawlessly. The problems came up when I tried to modify a resource. Since we make heavy use of dynamic git environments with puppet I was running something like
 puppet agent --test --environment=nagios 
on a host at random and
 puppet agent --test --environment=nagios 
on the nagios server, hoping to collect exported resources. The problem was they were not changing. As it turns out puppetdb can cache old exported resources for up to an hour. My advice for others having problem getting nagios or other exported resources to change or purge is to give it time. Run a big ssh for loop or use mcollective to hit all your boxes and hit the coffee cart for a quick pick me up. Chances are good you just need to give it time.