<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

  <title><![CDATA[Masterzen's Blog]]></title>
  
  <link href="http://www.masterzen.fr/" />
  <updated>2013-03-12T20:16:17+01:00</updated>
  <id>http://www.masterzen.fr/</id>
  <author>
    <name><![CDATA[Masterzen]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/masterzen" /><feedburner:info uri="masterzen" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title type="html"><![CDATA[Devopsdays Paris For Real!]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/7BHHaHrBiWE/" />
    <updated>2013-03-12T19:43:00+01:00</updated>
    <id>http://www.masterzen.fr/2013/03/12/devopsdays-paris</id>
    
    <category term="devops" />
        
    <content type="html">&lt;p&gt;Paris is more and more becoming the &lt;em&gt;DevOps&lt;/em&gt; place to be. We (apparently) successfully rebooted the &lt;a href="http://parisdevops.fr/"&gt;Paris DevOps Meetups&lt;/a&gt;, with already &lt;a href="http://parisdevops.fr/blog/2013/02/12/prochain-meetup-le-mardi-19-fevrier.html"&gt;two&lt;/a&gt; &lt;a href="http://parisdevops.fr/blog/2013/02/22/prochain-meetup-le-mardi-5-mars.html"&gt;events&lt;/a&gt; so far, and two more already in the pipeline (stay tuned for the announcements).&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re now announcing and pushing hard the &lt;a href="http://www.devopsdays.org/events/2013-paris/"&gt;Paris edition of devopsdays&lt;/a&gt; (well we&amp;#8217;re quite under the shadow of &lt;a href="http://www.devopsdays.org/events/2013-london/"&gt;devopsdays London&lt;/a&gt; at this time; I take this as an extension to our &lt;a href="http://en.wikipedia.org/wiki/France%E2%80%93United_Kingdom_relations"&gt;long time friendly fight with Britons&lt;/a&gt; :)&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Paris edition of DevOpsDays&lt;/em&gt; is being held &lt;strong&gt;18 - 19 April 2013&lt;/strong&gt;, and we want you to be a part of it!  This conference brings together speakers and attendees from around the world, with a focus on &lt;em&gt;DevOps&lt;/em&gt; culture, techniques, and best practices.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2013/03/paris-love-devops.jpg" title="devopsdays Paris" alt="devopsdays Paris" /&gt;&lt;/p&gt;

&lt;p&gt;The format is simple: talks in the morning, and open/hack spaces in the afternoon. We&amp;#8217;ve done 17 very successful events on 5 continents, and we&amp;#8217;re looking forward to &lt;a href="http://www.devopsdays.org/events/2013-paris/"&gt;another great edition here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Perhaps you’re curious, &amp;#8220;what exactly is DevOps?&amp;#8221;&lt;/p&gt;

&lt;p&gt;Well if you already follow this blog, or &lt;a href="https://twitter.com/_masterzen_"&gt;my twitter account&lt;/a&gt; you might already know what is under this term. The term is, of course, a portmanteau of Development and Operations, and is perhaps best thought of as a cultural movement within the IT world.  It stresses communication, collaboration and integration between software developers and IT professionals. &lt;a href="http://en.wikipedia.org/wiki/DevOps"&gt;DevOps&lt;/a&gt; is a response to, and evolution of, the interdependence of software development and IT operations.&lt;/p&gt;

&lt;p&gt;The conference itself will be held at the &lt;a href="http://www.devopsdays.org/events/2013-paris/location/"&gt;MAS&lt;/a&gt;. Tickets can be purchased for one or both days, and include full access to the talks and spaces, as well as a catered lunch.&lt;/p&gt;

&lt;p&gt;What’s more, we’re currently offering 25% off of the ticket price - just use the code &lt;em&gt;WELOVEDEVOPS&lt;/em&gt; when &lt;a href="http://www.devopsdays.org/events/2013-paris/registration/"&gt;you register&lt;/a&gt;. This is a limited-time offer (until the end of this week), so don’t delay!&lt;/p&gt;

&lt;p&gt;And, of course, the &lt;a href="http://devopsdays.org/events/2013-paris/propose/"&gt;Call for Proposals&lt;/a&gt; is still open until &lt;strong&gt;20 March 2013&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we invite you to peruse the &lt;a href="http://devopsdays.org/events/2013-paris/proposals/"&gt;list of proposals&lt;/a&gt;, and to comment and vote for your favorite ones!&lt;/p&gt;

&lt;p&gt;So if you had to choose one &lt;em&gt;devopsdays&lt;/em&gt; this year, choose ours and come&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exchange with lots of talented French people&lt;/li&gt;
&lt;li&gt;taste French food and wine&lt;/li&gt;
&lt;li&gt;learn how we do &lt;em&gt;devops&lt;/em&gt; in 3 hours of work per day (just kidding of course)&lt;/li&gt;
&lt;li&gt;smell the fragrance of Paris in spring (there won&amp;#8217;t be any more snow, I promise)&lt;/li&gt;
&lt;li&gt;visit the &lt;a href="http://en.wikipedia.org/wiki/Paris"&gt;City of Lights&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Looking forward to seeing you in April!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7BHHaHrBiWE:YTZuGurA4F0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7BHHaHrBiWE:YTZuGurA4F0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=7BHHaHrBiWE:YTZuGurA4F0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7BHHaHrBiWE:YTZuGurA4F0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7BHHaHrBiWE:YTZuGurA4F0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7BHHaHrBiWE:YTZuGurA4F0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=7BHHaHrBiWE:YTZuGurA4F0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7BHHaHrBiWE:YTZuGurA4F0:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=7BHHaHrBiWE:YTZuGurA4F0:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2013/03/12/devopsdays-paris/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[The 10 commandments of logging]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/LLJz0hWVfZE/" />
    <updated>2013-01-13T15:55:26+01:00</updated>
    <id>http://www.masterzen.fr/2013/01/13/the-10-commandments-of-logging</id>
    
    <category term="devops" />
        
    <content type="html">&lt;p&gt;Welcome on my blog for this new Year. After writing an answer to a thread regarding monitoring and log monitoring on the Paris devops mailing list, I thought back about a blog post project I had in mind for a long time.&lt;/p&gt;

&lt;p&gt;I wrote this blog post while wearing my Ops hat and this is mostly addressed to developers.&lt;/p&gt;

&lt;p&gt;Knowing how and what to log is, to me, one of the hardest task a software engineer will have to solve. Mostly because this task is akin to divination. It&amp;#8217;s very hard to know what information you&amp;#8217;ll need during troubleshooting&amp;#8230; That&amp;#8217;s the reason I hope those 10 commandments will help you enhance your application logging for the great benefits of the ops engineers :)&lt;/p&gt;

&lt;h2&gt;1. Thou shalt not write log by yourself&lt;/h2&gt;

&lt;p&gt;Never, ever use printf or write your log entries by yourself to files, or handle log rotation by yourself. Please do your ops guys a favor and use a standard library or system API call for this.&lt;/p&gt;

&lt;p&gt;This way, you&amp;#8217;re sure the running application will play nicely with the other system components, will log to the right place or network services without special system configuration.&lt;/p&gt;

&lt;p&gt;So, if you just use the system API, then this means logging with &lt;code&gt;syslog(3)&lt;/code&gt;. Learn how to use it.&lt;/p&gt;

&lt;p&gt;If you instead prefer to use a logging library, there are plenty of those especially in the Java world, like &lt;a href="http://logging.apache.org/log4j/2.x/"&gt;Log4j&lt;/a&gt;, &lt;a href="https://commons.apache.org/logging/guide.html"&gt;JCL&lt;/a&gt;, &lt;a href="http://www.slf4j.org/"&gt;slf4j&lt;/a&gt; and &lt;a href="http://logback.qos.ch/"&gt;logback&lt;/a&gt;. My favorite is the combination of slf4j and logback because it is very powerful and relatively easy to configure (and allows JMX configuration or reloading of the configuration file).&lt;/p&gt;

&lt;p&gt;The best thing about slf4j is that you can change the logging backend when you see fit. It is especially important if you&amp;#8217;re coding a library, because it allows anyone to use your library with their own logging backend without any modification to your library.&lt;/p&gt;

&lt;p&gt;There are also several other logging library for different languages, like for ruby: &lt;a href="http://log4r.rubyforge.org/"&gt;Log4r&lt;/a&gt;, &lt;a href="http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger/Application.html"&gt;stdlib logger&lt;/a&gt;, or the almost perfect &lt;a href="https://github.com/jordansissel/ruby-cabin"&gt;Jordan Sissel&amp;#8217;s Ruby-cabin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if your argument for not using a logging library is CPU consumption, then you have my permission to skip this blog post. Sure you should not put log statements in tight inner loops, but otherwise you&amp;#8217;ll never see the difference.&lt;/p&gt;

&lt;h2&gt;2. Thou shalt log at the proper level&lt;/h2&gt;

&lt;p&gt;If you followed the 1st commandment, then you can use a different log level per log statement in your application. One of the most difficult task is to find at what level this log entry should be logged.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m giving here some advice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;TRACE level&lt;/em&gt;: this is &lt;a href="http://www.lenholgate.com/blog/2005/07/no-thats-not-the-point-and-yes-trace-logging-is-bad.html"&gt;a code smell&lt;/a&gt; if used in production. This should be used during development to track bugs, but never committed to your VCS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;DEBUG level&lt;/em&gt;: log at this level about anything that happens in the program. This is mostly used during debugging, and I&amp;#8217;d advocate trimming down the number of debug statement before entering the production stage, so that only the most meaningful entries are left, and can be activated during troubleshooting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;INFO level&lt;/em&gt;: log at this level all actions that are user-driven, or system specific (ie regularly scheduled operations&amp;#8230;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;NOTICE level&lt;/em&gt;: this will certainly be the level at which the program will run when in production. Log at this level all the notable event that are not considered an error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;WARN level&lt;/em&gt;: log at this level all event that could potentially become an error. For instance if one database call took more than a predefined time, or if a in memory cache is near capacity. This will allow proper automated alerting, and during troubleshooting will allow to better understand how the system was behaving before the failure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;ERROR level&lt;/em&gt;: log every error conditions at this level. That can be API calls that return errors or internal error conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;FATAL level&lt;/em&gt;: too bad it&amp;#8217;s doomsday. Use this very scarcely, this shouldn&amp;#8217;t happen a lot in a real program. Usually logging at this level signifies the end of the program. For instance, if a network daemon can&amp;#8217;t bind a network socket, log at this level and exit is the only sensible thing to do.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Note that the default running level in your program or service might widely vary. For instance I run my server code at level &lt;em&gt;INFO&lt;/em&gt; usually, but my desktop programs runs at level &lt;em&gt;DEBUG&lt;/em&gt;. Because it&amp;#8217;s very hard to troubleshoot an issue on a computer you don&amp;#8217;t have access too, and it&amp;#8217;s far easier when doing support or customer service to ask the user to send you the log than teaching her to change the log level and then send you the log. Of course YMMV :)&lt;/p&gt;

&lt;h2&gt;3. Honor thy log category&lt;/h2&gt;

&lt;p&gt;Most logging library I cited in the 1st commandment allow to specify a logging category. This category allows to classify the log message, and will ultimately, based on the logging framework configuration, be logged in a distinct way or not logged at all.&lt;/p&gt;

&lt;p&gt;Most of the time java developers use the fully qualified class name where the log statement appears as the category. This is a scheme that works relatively fine if your program respects the &lt;a href="https://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;simple responsibility principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Log categories in java logging libraries are hierarchical, so for instance logging with category &lt;code&gt;com.daysofwonder.ranking.ELORankingComputation&lt;/code&gt; would match the top level category &lt;code&gt;com.daysofwonder.ranking&lt;/code&gt;. This would allow the ops engineer to setup a logging configuration that works for all the ranking subsystem by just specifying configuration for this category. But it could at the same time, produce logging configuration for child categories if needed.&lt;/p&gt;

&lt;p&gt;We can extend the paradigm a little bit further to help troubleshooting specific situation. Imagine that you are dealing with a server software that respond to user based request (like a REST API for instance). If your server is logging with this category &lt;code&gt;my.service.api.&amp;lt;apitoken&amp;gt;&lt;/code&gt; (where apitoken is specific to a given user), then you could either log all the api logs by allowing &lt;code&gt;my.service.api&lt;/code&gt; or a single misbehaving api user by logging with a more detailed level and the category &lt;code&gt;my.service.api.&amp;lt;bad-user-api-token&amp;gt;&lt;/code&gt;.
Of course this requires a system where you can change logging configuration on the fly.&lt;/p&gt;

&lt;h2&gt;4. Thou shalt write meaningful logs&lt;/h2&gt;

&lt;p&gt;This might probably be the most important commandment. There&amp;#8217;s nothing worst than cryptic log entries assuming you have a deep understanding of the program internals.&lt;/p&gt;

&lt;p&gt;When writing your log entries messages, always anticipate that there are emergency situations where the only thing you have is the log file, from which you have to understand what happened. Doing it right might be the subtle difference between getting fired and promoted :)&lt;/p&gt;

&lt;p&gt;When a developer writes a log message, it is in the context of the code in which the log directive is to be inserted. In those conditions we tend to write messages the infer on the current context. Unfortunately when reading the log itself this context is absent, and those messages might not be understandable.&lt;/p&gt;

&lt;p&gt;One way to overcome this situation (and that&amp;#8217;s particularly important when writing at the warn or error level), is to add remediation information to the log message, or if not possible, what was the purpose of the operation, and it&amp;#8217;s outcome.&lt;/p&gt;

&lt;p&gt;Also, do not log message that depends on previous messages content. The reason is that those previous messages might not appear if they are logged in a different category or level, or worst can appear in a different place (or way before) in a multi-threaded or asynchronous context.&lt;/p&gt;

&lt;h2&gt;5. Thy log shalt be written in English&lt;/h2&gt;

&lt;p&gt;This might seem a strange commandment, especially coming from a French guy. First, I still think English is much more concise than French and better suits technical language. Why would you want to log in French if the message contains more than 50% of English words in it?&lt;/p&gt;

&lt;p&gt;This being put aside, here are the essential reason behind this commandment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;English means your messages will be in logged with ASCII characters. This is particularly important because you can&amp;#8217;t really know what will happen to the log message, nor what software layer or media it will cross before being archived somewhere. If your message uses a special charset or even UTF-8, it might not render correctly at the end, but worst it could be corrupted in transit and become unreadable. Still remains the question of logging user-input which might be in diverse charset and/or encoding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your program is to be used by the most and you don&amp;#8217;t have the resources for a full localization, then English is probably your best alternative. Now, if you have to localize one thing, localize the interface that is closer to the end-user (it&amp;#8217;s usually not the log entries).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;if you localize your log entries (like for instance all the warning and error level), make sure you prefix those by a specific meaningful error-code. This way people can do language independent Internet search and find information. Such great scheme has been used a while ago in the VMS operating system, and I must admit it is very effective. If you were to design such scheme, you can adopt this scheme: APP-S-CODE or APP-S-SUB-CODE, with respectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APP: your application name on 3 letters&lt;/li&gt;
&lt;li&gt;S: severity on 1 letter (ie D: debug, I: info&amp;#8230;)&lt;/li&gt;
&lt;li&gt;SUB: the sub part of the application this code pertains to&lt;/li&gt;
&lt;li&gt;CODE: a numeric code specific to the error in question&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;6. Thou shalt log with context&lt;/h2&gt;

&lt;p&gt;So, there&amp;#8217;s nothing worst than this kind of log message:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt; Transaction failed&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;or&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;User operation succeeds&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;or in case of API exceptions:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;java.lang.IndexOutOfBoundsException&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Without proper context, those messages are only noise, they don&amp;#8217;t add value and consume space that could have been useful during troubleshooting.&lt;/p&gt;

&lt;p&gt;Messages are much more valuable with added context, like:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;Transaction 2346432 failed: cc number checksum incorrect&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;or&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;User 54543 successfully registered e-mail user@domain.com&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;or&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;IndexOutOfBoundsException: index 12 is greater than collection size 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Since we&amp;#8217;re talking about exceptions in this last context example, if you happen to propagate up exceptions, make sure to enhance them with context appropriate to the current level, to ease troubleshooting, as in this java example:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Context propagation&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='line'&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;storeUserRank&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;deal&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DatabaseException&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RankingException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Can&amp;#39;t store ranking for user &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; in game &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot; because &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;So the upper-layer client of the rank API will be able to log the error with enough context information. It&amp;#8217;s even better if the context becomes parameters of the exception itself instead of the message, this way the upper layer can use remediation if needed.&lt;/p&gt;

&lt;p&gt;An easy way to keep a context is to use the &lt;a href="http://www.slf4j.org/manual.html#mdc"&gt;&lt;em&gt;MDC&lt;/em&gt;&lt;/a&gt; some of the java logging library implements. The &lt;em&gt;MDC&lt;/em&gt; is a per thread associative array. The logger configuration can be modified to always print the &lt;em&gt;MDC&lt;/em&gt; content for every log line. If your program uses a per-thread paradigm, this can help solve the issue of keeping the context. For instance this java example is using the &lt;em&gt;MDC&lt;/em&gt; to log per user information for a given request:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;MDC example&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='line'&gt;  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRequest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="c1"&gt;// ... all logged message now will display the user=&amp;lt;userid&amp;gt; for this thread context ...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Successful execution of request&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="c1"&gt;// user request processing is now finished, no need to log our current user anymore&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Note that the &lt;em&gt;MDC&lt;/em&gt; system doesn&amp;#8217;t play nice with asynchronous logging scheme, like &lt;a href="http://akka.io/"&gt;Akka&lt;/a&gt;&amp;#8217;s logging system. Because the MDC is kept in a per-thread storage area and in asynchronous systems you don&amp;#8217;t have the guarantee that the thread doing the log write is the one that has the MDC. In such situation, you need to log the context manually with every log statement.&lt;/p&gt;

&lt;h2&gt;7. Thou shalt log in machine parseable format&lt;/h2&gt;

&lt;p&gt;Log entries are really good for human, but very poor for machines. Sometimes it is not enough to manually read log files, you need to perform some automated processing (for instance for alerting or auditing). Or you want to store centrally your logs and be able to perform search requests.&lt;/p&gt;

&lt;p&gt;So what happens when you embed the log context in the string like in this hypothetical logging statement:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Difficult to parse&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;User {} plays {} in game {}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gameId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;This will produce this kind of text:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Difficult to parse&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='line'&gt;&lt;span class="mi"&gt;2013&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;656&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO&lt;/span&gt;  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UserRequest&lt;/span&gt;  &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="mi"&gt;1334563&lt;/span&gt; &lt;span class="n"&gt;plays&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;spades&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="mi"&gt;23425656&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Now, if you want to parse this, you&amp;#8217;d need the following (untested) regex:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Difficult to parse&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+)&lt;/span&gt; &lt;span class="n"&gt;plays&lt;/span&gt; &lt;span class="o"&gt;(.+?)&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+)&lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Well, this is not easy and very error-prone, just to get access to string parameters your code already knows natively.&lt;/p&gt;

&lt;p&gt;So what about this idea, I believe &lt;a href="http://www.semicomplete.com/"&gt;Jordan Sissel&lt;/a&gt; first introduced in his ruby-cabin library:
Let add the context in a &lt;em&gt;machine parseable format&lt;/em&gt; in your log entry. Our aforementioned example could be using JSON like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Difficult to parse&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='line'&gt;&lt;span class="mi"&gt;2013&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;656&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO&lt;/span&gt;  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UserRequest&lt;/span&gt;  &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;plays&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1334563&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;spade&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;23425656&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Now your log parsers can be much easier to write, indexing now becomes straightforward and you can enable all &lt;a href="http://logstash.net/"&gt;logstash&lt;/a&gt; power.&lt;/p&gt;

&lt;h2&gt;8. Thou shalt not log too much or too little&lt;/h2&gt;

&lt;p&gt;That might sound stupid, but there is a right balance for the amount of log.&lt;/p&gt;

&lt;p&gt;Too much log and it will really become hard to get any value from it. When manually browsing such logs, there is too much clutter which when trying to troubleshoot a production issue at 3AM is not a good thing.&lt;/p&gt;

&lt;p&gt;Too little log and you risk to not be able to troubleshoot problems: troubleshooting is like solving a difficult puzzle, you need to get enough material for this.&lt;/p&gt;

&lt;p&gt;Unfortunately there is no magic rule when coding to know what to log. It is thus very important to strictly respect the 1st and 2nd commandments so that when the application will be live it will be easier to increase or decrease the log verbosity.&lt;/p&gt;

&lt;p&gt;One way to overcome this issue is during development to log as much as possible (do not confuse this with logging added to debug the program). Then when the application enters production, perform an analysis of the produced logs and reduce or increase the logging statement accordingly to the problems found. Especially during troubleshooting, note the part of the application you wished you could have more context or logging, and make sure to add those log statements to the next version (if possible at the same time you fix the issue to keep the problem fresh in memory). Of course that requires an amount of communication between ops and devs.&lt;/p&gt;

&lt;p&gt;This can be a complex task, but I would recommend to refactor logging statements as much as you refactor the code. The idea would be to have a tight feedback loop between the production logs and the modification of such logging statement. It&amp;#8217;s even more efficient if your organization has a continuous delivery process in place, as the refactoring can be constant.&lt;/p&gt;

&lt;p&gt;Logging statements are some kind of code metadata, at the same level of code comments. It&amp;#8217;s really important to keep the logging statements in sync with the code. There&amp;#8217;s nothing worst when troubleshooting issues to get irrelevant messages that have no relation to the code processed.&lt;/p&gt;

&lt;h2&gt;9. Thou shalt think to the reader&lt;/h2&gt;

&lt;p&gt;Why adding logging to an application?&lt;/p&gt;

&lt;p&gt;The only answer is that someone will have to read it one day or later (or what is the point?). More important it is interesting to think about who will read those lines.
Depending on the person you think will read the log messages you&amp;#8217;re about to write, the log message content, context, category and level will be quite different.&lt;/p&gt;

&lt;p&gt;Those persons can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an end-user trying to troubleshoot herself a problem (imagine a client or desktop program)&lt;/li&gt;
&lt;li&gt;a system-administrator or operation engineer troubleshooting a production issue&lt;/li&gt;
&lt;li&gt;a developer either for debugging during development or solving a production issue&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Of course the developer knows the internals of the program, thus her log messages can be much more complex than if the log message is to be addressed to an end-user. So adapt your language to the intended target audience, you can even dedicate separate categories for this.&lt;/p&gt;

&lt;h2&gt;10. Thou shalt not log only for troubleshooting&lt;/h2&gt;

&lt;p&gt;As the log messages are for a different audience, log messages will be used for different reasons.
Even though troubleshooting is certainly the most evident target of log messages, you can also use log messages very efficiently for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Auditing&lt;/em&gt;: this is sometimes a business requirement. The idea is to capture significant events that matter to the management or legal people. These are statements that describe usually what users of the system are doing (like who signed-in, who edited that, etc&amp;#8230;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Profiling&lt;/em&gt;: as logs are timestamped (sometimes to the millisecond level), it can become a good tool to profile sections of a program, for instance by logging the start and end of an operation, you can either automatically (by parsing the log) or during troubleshooting infer some performance metrics without adding those metrics to the program itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Statistics&lt;/em&gt;: if you log each time a certain event happens (like a certain kind of error or event) you can compute interesting statistics about the running program (or the user behaviors). It&amp;#8217;s also possible to hook this to an alert system that can detect too many errors in a row.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I hope this will help you produce more useful logs, and bear with me if I forgot an essential (to you) commandments. Oh and I can&amp;#8217;t be held responsible if your log doesn&amp;#8217;t get better after reading this blog :)&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s possible that those 10 commandments are not enough, so feel free to use the comment section (or twitter or your own blog) to add more useful tips.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=LLJz0hWVfZE:W06ajFpQPlI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=LLJz0hWVfZE:W06ajFpQPlI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=LLJz0hWVfZE:W06ajFpQPlI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=LLJz0hWVfZE:W06ajFpQPlI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=LLJz0hWVfZE:W06ajFpQPlI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=LLJz0hWVfZE:W06ajFpQPlI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=LLJz0hWVfZE:W06ajFpQPlI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=LLJz0hWVfZE:W06ajFpQPlI:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=LLJz0hWVfZE:W06ajFpQPlI:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2013/01/13/the-10-commandments-of-logging/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Ticket to Devopsdays Roma?]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/-HynlTisGuc/" />
    <updated>2012-07-19T20:18:12+02:00</updated>
    <id>http://www.masterzen.fr/2012/07/19/a-ticket-to-devopsdays-roma</id>
    
    <category term="devops" />
        
    <content type="html">&lt;p&gt;After seeing &lt;a href="http://twitter.com/krisbuytaert"&gt;@KrisBuytaert&lt;/a&gt; tweet a couple of days ago about offering priority registration for the upcoming &lt;a href="http://www.devopsdays.org/events/2012-italy/"&gt;Devopsdays Roma&lt;/a&gt; next October to people blogging about &lt;em&gt;Devops&lt;/em&gt;, I thought why not me?&lt;/p&gt;

&lt;p&gt;I already wanted to attend the last European &lt;a href="http://www.devopsdays.org/events/2011-goteborg/"&gt;Devopsdays session in Goteborg&lt;/a&gt; last year, but time proximity with the &lt;a href="http://puppetconf.com/"&gt;PuppetConf&lt;/a&gt; and some work schedule prevented me to finally join the party.&lt;/p&gt;

&lt;p&gt;This year, I won&amp;#8217;t show up in San Francisco the yearly Puppet big event (which at least from there sounds quite terrific) for various reasons, so if attending a Devopsdays conference had a certain appeal I really couldn&amp;#8217;t resist (and choosing one in Europe, well is much more easier for me).&lt;/p&gt;

&lt;h2&gt;Qualifying&lt;/h2&gt;

&lt;p&gt;I believe (feel free to speak up if you think not) I can qualify for being part of the devops movement (and per se can attend Devopsdays Roma) :)&lt;/p&gt;

&lt;p&gt;One reason is that my work job is mostly programming. But I also have the fine responsibility of bringing what I write to production systems, along with creating and managing the infrastructure.&lt;/p&gt;

&lt;p&gt;This puts me in a situation that not a lot of my fellow programmers experienced. From a long time, producing software for Software Engineers was compiling a binary artifact and handle it into other hands, then work as soon as possible on new software.&lt;/p&gt;

&lt;p&gt;Unfortunately in the real world, this doesn&amp;#8217;t really work. The ops people in charge of the application (and that would be the same for client side products with respect to support engineers) will struggle to deploy, analyze and simply maintain it.&lt;/p&gt;

&lt;p&gt;When you already experienced both side of the world, you know that what matters to a Software Engineers (ie clean code, re-usable components, TDD and unit testing&amp;#8230;) doesn&amp;#8217;t really matters to Operations Engineers.
The latter currently wants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;ease of administration (like changing configuration gracefully)&lt;/li&gt;
&lt;li&gt;ease of introspection (like usable logs, integrated consoles, useful metrics, trace mechanism ala Dapper)&lt;/li&gt;
&lt;li&gt;ease of deployment (like packaging system, artifacts assembly that contains comprehensible dependencies)&lt;/li&gt;
&lt;li&gt;external (and internal) monitoring of all aspects&lt;/li&gt;
&lt;li&gt;wisely chosen hard dependencies (like database, message queues or cache systems)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Well, if you&amp;#8217;re a developer you&amp;#8217;ll recognize like me that the above list is really not trivial to bring, and usually tends to be overcome.&lt;/p&gt;

&lt;p&gt;Unfortunately, an application that doesn&amp;#8217;t implement this will be hard or impossible to properly maintain.&lt;/p&gt;

&lt;p&gt;The first time I tried to deploy software I wrote, I discovered the hard way that those requirements are, well, requirements.
Now, I make sure that the projects I work on have user stories encompassing those essential facets.&lt;/p&gt;

&lt;h2&gt;Ops already know it&lt;/h2&gt;

&lt;p&gt;If you&amp;#8217;re on the operation side, then you already know the value of those software requirements. Our role as operation engineers is to evangelize and teach software engineers those good practices. I strongly believe that &lt;em&gt;devops&lt;/em&gt; is all about that.&lt;/p&gt;

&lt;p&gt;But it&amp;#8217;s not only this. It&amp;#8217;s also being agile on the infrastructure side&amp;#8230;&lt;/p&gt;

&lt;h2&gt;Puppet opened my eyes&lt;/h2&gt;

&lt;p&gt;I always had been interested in managing server infrastructures. I started managing linux servers about 12 years ago (about 5 years after professionally starting as a software developer). Managing servers has always been something on the side for me, my main job being producing software (either client or server side).&lt;/p&gt;

&lt;p&gt;I was lurking the configuration management space and community for a long time before I adopted Puppet. On the small infrastructure I was maintaining, I thought it would be overkill.&lt;/p&gt;

&lt;p&gt;Oh, how I was wrong at that time :)&lt;/p&gt;

&lt;p&gt;Back in 2007, I started using Puppet, and began to write modules for the software we were using in production. I was still too shy to run this in production. Then in 2008, I really started using puppet (and BTW, contributing some features I thought interesting, which you already know if you read this infrequently updated blog).&lt;/p&gt;

&lt;p&gt;Puppet helped me to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;have all my configuration centralized in git&lt;/li&gt;
&lt;li&gt;deploy servers with repeatable process (ie recreate servers from scratch)&lt;/li&gt;
&lt;li&gt;parallelize (clusters can be spawned much more easily)&lt;/li&gt;
&lt;li&gt;prevent configuration drift&lt;/li&gt;
&lt;li&gt;orchestrate multiple nodes configurations (ie publish monitoring or backup information to other nodes)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Puppet helped me understand that tools are a real life savers.&lt;/p&gt;

&lt;p&gt;I also learnt (and actually enforced) good administration practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deploy native packages instead of source installs&lt;/li&gt;
&lt;li&gt;setup monitoring for every installed critical software&lt;/li&gt;
&lt;li&gt;no manual configuration on servers anymore&lt;/li&gt;
&lt;li&gt;configuration testing on VM (thanks Vagrant BTW)&lt;/li&gt;
&lt;li&gt;automate as much as you can&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Puppet also helped me join sysadmin communities (like the find folks at ##infra-talk), which in turn helped me discover other life saver tools.&lt;/p&gt;

&lt;p&gt;This, I think, is part of the &lt;em&gt;devops&lt;/em&gt; culture and community.&lt;/p&gt;

&lt;h2&gt;It&amp;#8217;s not reserved to server-side&lt;/h2&gt;

&lt;p&gt;Yes, it isn&amp;#8217;t. Support staff or customer service staff share the same responsibilities as the operation teams but for client side applications. There is now much more client software than ever with the number of smartphones out there.&lt;/p&gt;

&lt;p&gt;The same benefit of &lt;em&gt;devops&lt;/em&gt; I talked about earlier, can and should also be applied to client side software. Logs are invaluable when trying to understand why some software your dev team wrote doesn&amp;#8217;t work when in the hand of your clients.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s even much more complex than analyzing server-side issues, because when you have the chance of managing client applications that produce logs, it&amp;#8217;s most of the time impossible to get access to them&amp;#8230;&lt;/p&gt;

&lt;h2&gt;Is devops the future?&lt;/h2&gt;

&lt;p&gt;My own small goal (my stone to the devops edifice) is to start the cultural mindset shift of the more developers I can (starting with my fellow co-workers). And I think that&amp;#8217;s our own responsibility as part of the &lt;em&gt;devops&lt;/em&gt; movement (if we can use this word) to initiate such shift.&lt;/p&gt;

&lt;p&gt;I always smile when I see &amp;#8220;devops engineer&amp;#8221; job positions. Devops is not a role, it&amp;#8217;s a mindset that everybody in a given dev and ops team should share. Maybe recruiters use this word as a synonym for &amp;#8220;help us use automation tools&amp;#8221;, as if it was the only solution to a human problem (well obviously if you don&amp;#8217;t use any configuration management you have more problems to solve)&lt;/p&gt;

&lt;p&gt;The same way olympic athletes practice hard every day to reach their level, I strongly believe that those &lt;em&gt;devops&lt;/em&gt; practice I described should be adopted by all software developers.&lt;/p&gt;

&lt;p&gt;Now it&amp;#8217;s our job to spread the word and help engineers to.&lt;/p&gt;

&lt;p&gt;Finally, in my humble opinion, &lt;em&gt;devops&lt;/em&gt; is all about common sense. I think it&amp;#8217;s easier to implement such practices in small companies/teams than in larger already installed teams (people are usually reluctant to changes, being good or bad). Nevertheless, if developers and operations unite and walk in the same direction, big things can be achieved.&lt;/p&gt;

&lt;h2&gt;What happens at Devopsdays&amp;#8230;&lt;/h2&gt;

&lt;p&gt;&amp;#8230; should not stay at Devopsdays (well except maybe for the drinks outcome)&lt;/p&gt;

&lt;p&gt;What do I want to get from attending Devopsdays Roma?&lt;/p&gt;

&lt;p&gt;I really want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;learn new things&lt;/li&gt;
&lt;li&gt;learn about other people experiences in the field&lt;/li&gt;
&lt;li&gt;share about fixing problems that plagues us all like:&lt;/li&gt;
&lt;li&gt;monitoring sucks&lt;/li&gt;
&lt;li&gt;log processing/centralization (and developer access)&lt;/li&gt;
&lt;li&gt;network and learn from wise and knowledgeable peers&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;And now I wish I&amp;#8217;ll be there and that I&amp;#8217;ll meet you :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=-HynlTisGuc:oIBZlgK_we0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=-HynlTisGuc:oIBZlgK_we0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=-HynlTisGuc:oIBZlgK_we0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=-HynlTisGuc:oIBZlgK_we0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=-HynlTisGuc:oIBZlgK_we0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=-HynlTisGuc:oIBZlgK_we0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=-HynlTisGuc:oIBZlgK_we0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=-HynlTisGuc:oIBZlgK_we0:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=-HynlTisGuc:oIBZlgK_we0:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2012/07/19/a-ticket-to-devopsdays-roma/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet Internals: the compiler]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/Jhpw0-KDqRY/" />
    <updated>2012-03-17T19:34:54+01:00</updated>
    <id>http://www.masterzen.fr/2012/03/17/puppet-internals-the-compiler</id>
    
    <category term="puppet" />
        
    <content type="html">&lt;p&gt;And I&amp;#8217;m now proud to present the second installation of my series of post about &lt;strong&gt;Puppet Internals&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Today we&amp;#8217;ll focus on the &lt;strong&gt;compiler&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;The Compiler&lt;/h1&gt;

&lt;p&gt;The compiler is at the heart of Puppet, master/agent or masterless. Its responsibility is to transform the AST into a set of resources called the &lt;em&gt;catalog&lt;/em&gt; that the agent can consume to perform the necessary changes on the node.&lt;/p&gt;

&lt;p&gt;You can see the compiler as a function of the AST and Facts and returning the &lt;em&gt;catalog&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The compiler lives in the &lt;code&gt;lib/puppet/parser/compiler.rb&lt;/code&gt; file and more specifically in the &lt;code&gt;Puppet::Parser::Compiler&lt;/code&gt; class. When a node connects to a master to ask for a catalog, &lt;a href="http://www.masterzen.fr/2011/12/11/the-indirector-puppet-extensions-points-3/"&gt;the Indirector&lt;/a&gt; directs the request to the compiler.&lt;/p&gt;

&lt;p&gt;In a classic master/agent system, the agent does a REST &lt;em&gt;find catalog&lt;/em&gt; request to the master. The master catalog indirection is configured to delegate to the compiler. This happens in the &lt;code&gt;lib/puppet/indirector/catalog/compiler.rb&lt;/code&gt; file. Check this &lt;a href="http://www.masterzen.fr/2011/12/11/the-indirector-puppet-extensions-points-3/"&gt;previous article about the Indirector&lt;/a&gt; if you want to know more.&lt;/p&gt;

&lt;p&gt;The indirector request contains two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what node we should compile&lt;/li&gt;
&lt;li&gt;the node&amp;#8217;s facts&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Produced Catalog&lt;/h2&gt;

&lt;p&gt;When we&amp;#8217;re talking about catalog, in the Puppet system it can mean two distinct things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a containment catalog&lt;/li&gt;
&lt;li&gt;a relationship resource catalog&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The first one is the product of the compiler (which we&amp;#8217;ll delve into in this article). The second one is formed by the transformation of the first one in the agent. This is the later one that we usually call the &lt;strong&gt;puppet catalog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is a simple manifest and the containment catalog that I obtained after compiling:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;/tmp/a&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;test!&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;And here is the produced catalog:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2012/03/containment-catalog.jpg" title="Puppet: containment catalog" alt="Out of compiler containment catalog" /&gt;&lt;/p&gt;

&lt;p&gt;You&amp;#8217;ll notice that as its name implies, the containment catalog is a graph of classes and resources that follows the structure of the manifest.&lt;/p&gt;

&lt;h2&gt;When Facts matter&lt;/h2&gt;

&lt;p&gt;In a master/agent system the facts are coming from the request in a serialized form. Those facts were created by calling &lt;em&gt;Facter&lt;/em&gt; on the remote node.&lt;/p&gt;

&lt;p&gt;Once unserialized, the facts are cached locally as YAML (as per the default terminus for facts on a master). You can find them in the &lt;code&gt;$vardir/yaml/facts/$certname.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;At the same time the compiler catalog terminus compute some server facts that are injected into the current node instance.&lt;/p&gt;

&lt;h2&gt;Looking for the node&lt;/h2&gt;

&lt;p&gt;In Puppet there are several possibilities to store node definitions. They can be defined by &lt;code&gt;node {}&lt;/code&gt; blocks in the &lt;code&gt;site.pp&lt;/code&gt;, by an &lt;em&gt;ENC&lt;/em&gt;, into an LDAP directory, etc&amp;#8230;&lt;/p&gt;

&lt;p&gt;Before the compiler can start, it needs to create an instance of the &lt;code&gt;Puppet::Node&lt;/code&gt; class, and fill this with the node informations.&lt;/p&gt;

&lt;p&gt;The node indirection terminus is controlled by the &lt;code&gt;node_terminus&lt;/code&gt; puppet settings which by default is &lt;code&gt;plain&lt;/code&gt;. This terminus just creates a new empty instance of a &lt;code&gt;Puppet::Node&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In an &lt;em&gt;ENC&lt;/em&gt; setup, the terminus for the node indirection will be &lt;code&gt;exec&lt;/code&gt;. This terminus will create a &lt;code&gt;Puppet::Node&lt;/code&gt; instance initialized with a set of classes and global parameters the compiler will be able to use.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;plain&lt;/code&gt; terminus for nodes calls &lt;code&gt;Puppet::Node#fact_merge&lt;/code&gt;. This methods &lt;em&gt;finds&lt;/em&gt; the current set of Facts of this node. In the &lt;code&gt;plain&lt;/code&gt; case it involves reading the YAML facts we wrote to disk in the last chapter, and merging those to the current node instance parameters.&lt;/p&gt;

&lt;p&gt;Back to the compiler catalog terminus, this one tries to find the node with the given request information and if not present by using the node &lt;code&gt;certname&lt;/code&gt;. Remember that the request to get a catalog from REST matches &lt;code&gt;/catalog/node.domain.com&lt;/code&gt;, in which case the request key is &lt;code&gt;node.domain.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Let&amp;#8217;s compile&lt;/h2&gt;

&lt;p&gt;After that, we really enter the compiler code, when the compiler catalog terminus calls &lt;code&gt;Puppet::Parser::Compiler.compile&lt;/code&gt;, which creates a new &lt;code&gt;Puppet::Parser::Compiler&lt;/code&gt; instance giving it our node instance.&lt;/p&gt;

&lt;p&gt;When creating this compiler instance, the following is created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an empty catalog (an instance of &lt;code&gt;Puppet::Resource::Catalog&lt;/code&gt;). This one will hold the result of the compilation.&lt;/li&gt;
&lt;li&gt;a companion top scope (an instance of &lt;code&gt;Puppet::Parser::Scope&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;some other internal data structures helping the compilation&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If the given node was coming from an &lt;em&gt;ENC&lt;/em&gt;, the catalog is bootstrapped with the known node classes.&lt;/p&gt;

&lt;p&gt;Once done, the &lt;code&gt;compile&lt;/code&gt; method is called on the compiler instance. The first thing done is to bootstrap top scope with the node parameters (which contains the global data coming from the &lt;em&gt;ENC&lt;/em&gt; if one is used and the &lt;em&gt;facts&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;Remember the AST&lt;/h2&gt;

&lt;p&gt;When we left the &lt;a href="http://www.masterzen.fr/2011/12/27/puppet-internals-the-parser/"&gt;Parser post&lt;/a&gt;, we obtained an AST. This AST is a tree of &lt;code&gt;AST&lt;/code&gt; instances that implement the guts of the Puppet language.&lt;/p&gt;

&lt;p&gt;In this previous article we left aside 3 types of AST:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node AST&lt;/li&gt;
&lt;li&gt;Hostclass AST&lt;/li&gt;
&lt;li&gt;Definition AST&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Those are different in the sense that we don&amp;#8217;t strictly evaluate them during compilation (more later on this step). No, those are &lt;em&gt;instantiated&lt;/em&gt; as part of the &lt;em&gt;initial import&lt;/em&gt; of the &lt;em&gt;known types&lt;/em&gt;. If you&amp;#8217;re wondering why I spelled the Class AST as Hostclass, then it&amp;#8217;s because that&amp;#8217;s how it is spelled in the Puppet code; the reason being that &lt;code&gt;class&lt;/code&gt; is a reserved word in Ruby :)&lt;/p&gt;

&lt;p&gt;Using a lazy evaluation scheme, Puppet keeps (actually per environments), a list of all the parsed known types (classes, definitions and nodes that the parser encountered during parsing); this is called the &lt;em&gt;known types&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When this list is first accessed, if it doesn&amp;#8217;t exist, Puppet triggers the parser to populate it. This happens in &lt;code&gt;Puppet::Node::Environment.known_resource_types&lt;/code&gt; which calls the &lt;code&gt;import_ast&lt;/code&gt; method with the result of the parsing phase.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import_ast&lt;/code&gt; adds to the &lt;em&gt;known types&lt;/em&gt; an instance of every definitions, hostclass, node returned by their respective &lt;code&gt;instantiate&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s have a closer look of the hostclass &lt;code&gt;instantiate&lt;/code&gt;:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Simplified Hostclass AST instantiate method&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;new_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:hostclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;all_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;new_class&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;nested_ast_node&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nested_ast_node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;respond_to?&lt;/span&gt; &lt;span class="ss"&gt;:instantiate&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;all_types&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;nested_ast_node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;all_types&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;So &lt;code&gt;instantiate&lt;/code&gt; returns an array of &lt;code&gt;Puppet::Resource::Type&lt;/code&gt; of the given type. You&amp;#8217;ll notice that the hostclass code above analyzes its current class AST children for other &amp;#8216;instantiable&amp;#8217; AST elements that will also end in the &lt;em&gt;known types&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;Known Types&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;known types&lt;/em&gt; I&amp;#8217;m talking about since a while all live in the &lt;code&gt;Puppet::Resource::TypeCollection&lt;/code&gt; object. There&amp;#8217;s one per Puppet environment in fact.&lt;/p&gt;

&lt;p&gt;This object main responsibility is storing all known classes, nodes and definitions to be easily accessed by the compiler. It also watches all loaded files by the parser, so that it can trigger a re-parse when one of those is updated. It also serves as the Puppet class/module autoloader (when asking it for an unknown class, it will first try to load it from disk and parse it).&lt;/p&gt;

&lt;h2&gt;Scopes&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s open a parenthesis to explain a little bit what the scope is. The scope is an instance of &lt;code&gt;Puppet::Parser::Scope&lt;/code&gt; and is simply a symbol table (as explained in the &lt;a href="http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools"&gt;Dragon Book&lt;/a&gt;). It just keeps the values of Puppet variables.&lt;/p&gt;

&lt;p&gt;It forms a tree, with the top scope (the one we saw the creation earlier) being the root of all scopes. This tree contains one child per new namespace.&lt;/p&gt;

&lt;p&gt;The scope supports two operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Looking up a variable value&lt;/li&gt;
&lt;li&gt;Setting a variable value&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Look up is done with the &lt;code&gt;lookupvar&lt;/code&gt; method. If the variable is qualified it will directly ask the correct scope for its value. For instance &lt;code&gt;::$hostname&lt;/code&gt; will fetch directly the top scope fact &lt;code&gt;hostname&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Otherwise it will either return its value in the local scope if it exists or delegate to the parent scope. This can happen up until the top scope. If the value can&amp;#8217;t be found anywhere, the &lt;code&gt;:undef&lt;/code&gt; ruby symbol will be returned.&lt;/p&gt;

&lt;p&gt;Note that this dynamic scope behavior will be removed in the next Puppet version, where only the local scope and the top scope will be supported. More information is available in this &lt;a href="http://docs.puppetlabs.com/guides/scope_and_puppet.html"&gt;Scope and Puppet article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Setting a variable is done with the &lt;code&gt;setvar&lt;/code&gt; method. This method is called for instance by the AST class responsible of variable assignment (the &lt;code&gt;AST::VarDef&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Along with regular variables, each scope has the notion of &lt;em&gt;ephemeral scope&lt;/em&gt;. An &lt;em&gt;ephemeral scope&lt;/em&gt; is a special transient scope that stores only regex capture &lt;code&gt;$0&lt;/code&gt; to &lt;code&gt;$xy&lt;/code&gt; variables.&lt;/p&gt;

&lt;p&gt;Each scope level maintains a stack of &lt;em&gt;ephemeral scopes&lt;/em&gt;, which is by default empty.&lt;/p&gt;

&lt;p&gt;In Puppet there is no scopes for other language structures than classes (and nodes and definitions), so inside the following &lt;code&gt;if&lt;/code&gt;, an ephemeral scope is created, and pushed on the stack, to store the result of the regex match:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Epehmeral scope in action&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vg"&gt;$var&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/test(.*)/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# here $0, $1... are available&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;When Puppet execution reaches the closing &amp;#8216;}&amp;#8217;, the &lt;em&gt;ephemeral scope&lt;/em&gt; is popped from the &lt;em&gt;ephemeral scope&lt;/em&gt; stack, removing the &lt;code&gt;$0&lt;/code&gt; definition.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lookupvar&lt;/code&gt; will also ask the &lt;em&gt;ephemeral scope&lt;/em&gt; stack if needed.&lt;/p&gt;

&lt;p&gt;Orthogonally, the scope instance will also store resource defaults.&lt;/p&gt;

&lt;h2&gt;Talking about AST Evaluation&lt;/h2&gt;

&lt;p&gt;And here we need to take a break from compilation to talk about AST evaluation, which I elegantly eluded from the previous post on the Parser.&lt;/p&gt;

&lt;p&gt;Every AST node (both branch and leaf ones) implements the &lt;code&gt;evaluate&lt;/code&gt; method. This method takes a &lt;code&gt;Puppet::Parser::Scope&lt;/code&gt; instance as parameter. This is the scope instance that is valid at the moment we evaluate this AST node (usually the scope associated with the class where the code we evaluate is).&lt;/p&gt;

&lt;p&gt;There are several outcomes possible after evaluation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manipulation of the scope (like variable assignment, variable lookup, parser function call)&lt;/li&gt;
&lt;li&gt;Evaluation of AST children of this node (for instance &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;case&lt;/code&gt;, selectors need to evaluate code in one their children branch)&lt;/li&gt;
&lt;li&gt;Creation of &lt;code&gt;Puppet::Parser::Resource&lt;/code&gt; when encountering a puppet resource&lt;/li&gt;
&lt;li&gt;Creation of &lt;code&gt;Puppet::Resource::Type&lt;/code&gt; (more puppet classes)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When an AST node evaluates its children it does so by calling &lt;code&gt;safeevaluate&lt;/code&gt; on them which in turn will call &lt;code&gt;evaluate&lt;/code&gt;. Safeevaluate will shield the caller from exceptions, and transform them to parse errors that can specify the line and file of the puppet instruction that triggered the problem.&lt;/p&gt;

&lt;h2&gt;Shouldn&amp;#8217;t we talk about compilation?&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s go back to the compiler now. We left the compiler after we populated the &lt;em&gt;top scope&lt;/em&gt; with the node&amp;#8217;s facts, and we still didn&amp;#8217;t properly started the compilation phase itself.&lt;/p&gt;

&lt;p&gt;Here is what happens after:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Main class evaluation&lt;/li&gt;
&lt;li&gt;Node AST evaluation&lt;/li&gt;
&lt;li&gt;Evaluation of the node classes if any&lt;/li&gt;
&lt;li&gt;Recursive evaluation of definitions and collections (called generators)&lt;/li&gt;
&lt;li&gt;Evaluation of relationships&lt;/li&gt;
&lt;li&gt;Resource overrides evaluation&lt;/li&gt;
&lt;li&gt;Resource finish&lt;/li&gt;
&lt;li&gt;Ship the catalog&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;After that, what remains is the containment catalog. This one will be transformed to a &lt;em&gt;resource&lt;/em&gt; containment catalog. We call &lt;em&gt;resource catalog&lt;/em&gt; an instance of &lt;code&gt;Puppet::Resource::Catalog&lt;/code&gt; where all &lt;code&gt;Puppet::Parser::Resource&lt;/code&gt; have been transformed to &lt;code&gt;Puppet::Resource&lt;/code&gt; instances.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s now see in order the list of operations we outlined above and that form the compilation.&lt;/p&gt;

&lt;h3&gt;Main class evaluation&lt;/h3&gt;

&lt;p&gt;The main class is an hidden class where every code outside any definition, node or class ends. It&amp;#8217;s a kind of top class from which any other class is inner. This class is special because it has an &lt;em&gt;empty name&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Evaluating the main class means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating a companion resource (an instance of &lt;code&gt;Puppet::Parser::Resource&lt;/code&gt;) whose scope is the &lt;em&gt;top scope&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Add this resource to the catalog&lt;/li&gt;
&lt;li&gt;Evaluating the class code of this resource&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Let&amp;#8217;s focus on this last step which happens in &lt;code&gt;Puppet::Parser::Resource.evaluate&lt;/code&gt;.
It involves mainly getting access to the &lt;code&gt;Puppet::Resource::Type&lt;/code&gt; instance matching our class (its type in fact) from the &lt;em&gt;known types&lt;/em&gt;, and then calling the &lt;code&gt;Puppet::Resource::Type.evaluate_code&lt;/code&gt; method.&lt;/p&gt;

&lt;h4&gt;Evaluating code of a class&lt;/h4&gt;

&lt;p&gt;I&amp;#8217;m putting aside the main class evaluation to talk a little bit about code evaluation of a given class because that&amp;#8217;s something we&amp;#8217;ll see for every class or node during compilation.&lt;/p&gt;

&lt;p&gt;This happens during &lt;code&gt;Puppet::Resource::Type.evaluate_code&lt;/code&gt; which essentially does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a scope for this class (unless we&amp;#8217;re compiling the main class which already uses the &lt;em&gt;top scope&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Ask the class AST children to evaluate with this scope&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;We saw in the &lt;a href="http://www.masterzen.fr/2011/12/27/puppet-internals-the-parser/"&gt;Puppet Parser post&lt;/a&gt; how the AST was produced. Eventually some of those AST nodes will end up in the &lt;code&gt;code&lt;/code&gt; element of a given puppet class (you can refer to the Puppet grammar and &lt;code&gt;Puppet::Parser::AST::Hostclass&lt;/code&gt; for the code), under the form of an &lt;code&gt;ASTArray&lt;/code&gt; (which is an array of AST nodes).&lt;/p&gt;

&lt;h3&gt;Node Evaluation&lt;/h3&gt;

&lt;p&gt;As for the main class, the current node compilation phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ask the &lt;em&gt;known types&lt;/em&gt; about the current node, and if none are found ask for a &lt;em&gt;default&lt;/em&gt; node.&lt;/li&gt;
&lt;li&gt;creates a resource for this node, add it to the catalog&lt;/li&gt;
&lt;li&gt;evaluates this node resource&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This last evaluation will execute the given node AST code.&lt;/p&gt;

&lt;h3&gt;Node class evaluation&lt;/h3&gt;

&lt;p&gt;If the node was provided by an ENC, the compiler will then evaluate those classes. This is the same process as for the main class, where for every classes we create a resource, add it to the catalog and then evaluate it.&lt;/p&gt;

&lt;h3&gt;Evaluation of Generators&lt;/h3&gt;

&lt;p&gt;In Puppet the generators are the entities that are able to spawn new resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collections, including storeconfig exported resources&lt;/li&gt;
&lt;li&gt;definitions&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This part of the compilation loops calling &lt;code&gt;evaluate_definitions&lt;/code&gt; and &lt;code&gt;evaluate_collections&lt;/code&gt;, until none of those produces new resources.&lt;/p&gt;

&lt;h4&gt;Definitions&lt;/h4&gt;

&lt;p&gt;During the AST code evaluation, if the compiler encounters a definition call, the &lt;code&gt;Puppet::Parser::AST::Resource.evaluate&lt;/code&gt; will be called (like for every resource).&lt;/p&gt;

&lt;p&gt;Since this resource comes from a definition, a type resource will be instantiated and added to the catalog. This resource will not be evaluated at this stage.&lt;/p&gt;

&lt;p&gt;Later, when &lt;code&gt;evaluate_definitions&lt;/code&gt; is called, it will pick up any resource that hasn&amp;#8217;t been evaluated (which is the case of our definition resources) and evaluates them.&lt;/p&gt;

&lt;p&gt;This operation might in turn create more unevaluated resources (ie new definition spawning more definition resources), which will be evaluated in a subsequent pass over &lt;code&gt;evaluate_definitions&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;Collections&lt;/h4&gt;

&lt;p&gt;When the parser parses a collection which are defined like this in the Puppet language:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="no"&gt;File&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;it creates an AST node of type &lt;code&gt;Puppet::Parser::AST::Collection&lt;/code&gt;. The same happen if you use the &lt;code&gt;realize&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Later when the compiler evaluate code and encounters this collection instance, it will create a &lt;code&gt;Puppet::Parser::Collector&lt;/code&gt; and register it to the compiler.&lt;/p&gt;

&lt;p&gt;Even later, during &lt;code&gt;evaluate_collections&lt;/code&gt;, the &lt;code&gt;evaluate&lt;/code&gt; method of all the registered collectors will be called. This method will either fetch exported resources from storeconfigs or virtual resources, and create &lt;code&gt;Puppet::Parser::Resource&lt;/code&gt; that are registered to the compiler.&lt;/p&gt;

&lt;p&gt;If the collector has created all its resources, it is removed from the compiler.&lt;/p&gt;

&lt;h3&gt;Relationship evaluation&lt;/h3&gt;

&lt;p&gt;The current compiler holds the list of relationships defined with the &lt;code&gt;-&amp;gt;&lt;/code&gt; class of relationship operators (but not the ones defined with the &lt;code&gt;require&lt;/code&gt; or &lt;code&gt;before&lt;/code&gt; meta-parameters).&lt;/p&gt;

&lt;p&gt;During code evaluation, when the compiler encounters the relationship AST node (an instance of &lt;code&gt;Puppet::Parser::AST::Relationship&lt;/code&gt;), it will register a &lt;code&gt;Puppet::Parser::Relationship&lt;/code&gt; instance to the compiler.&lt;/p&gt;

&lt;p&gt;During the &lt;code&gt;evaluate_relationships&lt;/code&gt; method of the compiler, every registered relationship will be evaluated. This evaluation simply adds the destination resource reference to the source resource meta-parameter matching the operator.&lt;/p&gt;

&lt;h3&gt;Resource overrides&lt;/h3&gt;

&lt;p&gt;And the next compilation phase consists in adding all the overrides we discovered during the AST code evaluation. Normally overrides are applied as soon as they are discovered, but it can happen than an override (especially for collection overrides), can not be applied because the resources it should apply on are not yet created.&lt;/p&gt;

&lt;p&gt;Applying an override consist in setting a given resource parameter to the overridden value.&lt;/p&gt;

&lt;h3&gt;Resource finishing&lt;/h3&gt;

&lt;p&gt;During this phase, the compiler will call the &lt;code&gt;finish&lt;/code&gt; method on every created resources.
This methods is responsible of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding resource defaults to the resource parameters&lt;/li&gt;
&lt;li&gt;tagging the resource with the current scope tags&lt;/li&gt;
&lt;li&gt;checking that resource parameter are valid&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Resource meta-parameters&lt;/h3&gt;

&lt;p&gt;The next step in the compilation process is to set all meta-parameter of our created resources, starting from the main class and walking the catalog from there.&lt;/p&gt;

&lt;h3&gt;Finish&lt;/h3&gt;

&lt;p&gt;Once everything has been done, the compiler runs some checks to make sure all overrides and collections have been evaluated.
Then the catalog is transformed to a &lt;code&gt;Puppet::Resource&lt;/code&gt; catalog (which doesn&amp;#8217;t change its layout, just the instance of its vertices).&lt;/p&gt;

&lt;h1&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I hope you now have a better view of the compilation process. As you&amp;#8217;ve seen the compilation is a complex process, which is one of the reason it can take some time. But that&amp;#8217;s the price to pay to produce a data only graph tailored to one host that can be applied on the host.&lt;/p&gt;

&lt;p&gt;Stay tuned here for the next episode of my Puppet Internals series of post. The next installment will certainly cover the Puppet transaction system, whose role is to apply the catalog on the agent.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Jhpw0-KDqRY:XuKI8fYqBaw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Jhpw0-KDqRY:XuKI8fYqBaw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=Jhpw0-KDqRY:XuKI8fYqBaw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Jhpw0-KDqRY:XuKI8fYqBaw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Jhpw0-KDqRY:XuKI8fYqBaw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Jhpw0-KDqRY:XuKI8fYqBaw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=Jhpw0-KDqRY:XuKI8fYqBaw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Jhpw0-KDqRY:XuKI8fYqBaw:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=Jhpw0-KDqRY:XuKI8fYqBaw:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2012/03/17/puppet-internals-the-compiler/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Benchmarking Puppet Stacks]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/AEDb9uaOi-0/" />
    <updated>2012-01-08T19:23:45+01:00</updated>
    <id>http://www.masterzen.fr/2012/01/08/benchmarking-puppet-stacks</id>
    
    <category term="puppet" />
        
    <content type="html">&lt;p&gt;I decided this week-end to try the more popular &lt;em&gt;puppet master stacks&lt;/em&gt; and benchmark them with puppet-load (which is a tool I wrote to simulate concurrent clients).&lt;/p&gt;

&lt;p&gt;My idea was to check the common stacks and see which one would deliver the best concurrency. This article is a follow-up of my previous &lt;a href="http://www.masterzen.fr/2010/10/18/benchmarking-puppetmaster-stacks/"&gt;post about puppet-load and puppet master benchmarking&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Methodology&lt;/h2&gt;

&lt;p&gt;I decided to try the following stacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Apache&lt;/em&gt; and &lt;em&gt;Passenger&lt;/em&gt;, which is the blessed stack, with MRI 1.8.7 and 1.9.2&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Nginx&lt;/em&gt; and &lt;em&gt;Mongrel&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;JRuby&lt;/em&gt; with minzuno&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The setup is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one &lt;em&gt;m1.large&lt;/em&gt; ec2 instance as the master&lt;/li&gt;
&lt;li&gt;one &lt;em&gt;m1.small&lt;/em&gt; ec2 instance as the client (in the same availability zone if that matters)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;To recap, m1.large instances are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 cpu with 2 virtual core each&lt;/li&gt;
&lt;li&gt;8 GiB of RAM&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;All the benchmarks were run on the same instance couples to prevent skew in the numbers.&lt;/p&gt;

&lt;p&gt;The master uses my own production manifests, consisting of about 100 modules. The node for which we&amp;#8217;ll compile a catalog contains 1902 resources exactly (which makes it a big catalog).&lt;/p&gt;

&lt;p&gt;There is no storeconfigs involved at all (this was to reduce setup complexity).&lt;/p&gt;

&lt;p&gt;The methodology is to setup the various stacks on the master instance and run puppet-load on the client instance. To ensure everything is hot on the master, a first run of the benchmark is run at full concurrency first. Then multiple run of puppet-load are performed simulating an increasing number of clients. This pre-heat phase also make sure the manifests are already parsed and no I/O is involved.&lt;/p&gt;

&lt;p&gt;Tuning has been done as best as I could on all stacks. And care was taken for the master instance to never swap (all the benchmarks involved consumed about 4GiB of RAM or less).&lt;/p&gt;

&lt;h2&gt;Puppet Master workload&lt;/h2&gt;

&lt;p&gt;Essentially a puppet master compiling catalog is a CPU bound process (that&amp;#8217;s not because a master speaks HTTP than its workload is a webserver workload). That means during the compilation phase of a client connection, you can be guaranteed that puppet will consume 100% of a CPU core.&lt;/p&gt;

&lt;p&gt;Which essentially means that there is usually little benefit of using more puppet master processes than CPU cores on a server.&lt;/p&gt;

&lt;h2&gt;A little bit of scaling math&lt;/h2&gt;

&lt;p&gt;When we want to scale a puppet master server, there is a rough computation that allows us to see how it will work.&lt;/p&gt;

&lt;p&gt;Here are the elements of our problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2000 clients&lt;/li&gt;
&lt;li&gt;30 minutes sleep interval, clients evenly distributed in time&lt;/li&gt;
&lt;li&gt;master with 8 CPU core and 8GiB of RAM&lt;/li&gt;
&lt;li&gt;our average catalog compilation is 10s&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;30 minutes interval means that every 30 minutes we must compile 2000 catalogs for our 2000 nodes. That leaves us with &lt;code&gt;2000/30 = 66&lt;/code&gt; catalogs per minute.&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s about a new client checking-in about every seconds.&lt;/p&gt;

&lt;p&gt;Since we have 8 CPU, that means we can accommodate 8 catalogs compilation in parallel, not more (because CPU time is a finite quantity).&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;66/8 = 8.25&lt;/code&gt;, we can accommodate &lt;em&gt;8 clients per minute&lt;/em&gt;, which means each client must be serviced in less than &lt;code&gt;60/8.25 = 7.27s&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since our catalogs take about 10s to compile (in my example), we&amp;#8217;re clearly in trouble and we would need to either add more master servers or increase our client sleep time (or not compile catalogs, but that&amp;#8217;s another story).&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;

&lt;h3&gt;Comparing our stacks&lt;/h3&gt;

&lt;p&gt;Let&amp;#8217;s first compare our favorite stacks for an increasing concurrent clients number (increasing concurrency).&lt;/p&gt;

&lt;p&gt;For setups that requires a fixed number of workers (&lt;em&gt;Passenger&lt;/em&gt;, &lt;em&gt;Mongrel&lt;/em&gt;) those were setup with 25 puppet master workers. This was fitting in the available RAM.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;JRuby&lt;/em&gt;, I had to use the at the time of writing &lt;em&gt;jruby-head&lt;/em&gt; because of a bug in 1.6.5.1. I also had to comment out the Puppet execution system (in &lt;code&gt;lib/puppet/util.rb&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Normally this sub-system is in use only on clients, but when the master loads the types it knows for validation, it also autoloads the providers. Those are checking if some support commands are available by trying to execute them (yes I&amp;#8217;m talking to you rpm and yum providers).&lt;/p&gt;

&lt;p&gt;I also had to comment out when puppet tries to become the puppet user, because that&amp;#8217;s not supported under &lt;em&gt;JRuby&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;JRuby&lt;/em&gt; was run with Sun java 1.6.0_26, so it couldn&amp;#8217;t benefit from the invokedynamic work that went into Java 1.7. I fully expect this feature to improve the performances dramatically.&lt;/p&gt;

&lt;p&gt;The main metric I&amp;#8217;m using to compare stacks is the &lt;strong&gt;TPS&lt;/strong&gt; (&lt;em&gt;transaction per seconds&lt;/em&gt;). This is in fact the number of catalogs a master stack can compile in one second. &lt;strong&gt;The higher the better&lt;/strong&gt;. Since compiling a catalog on our server takes about 12s, we have TPS numbers less than 1.&lt;/p&gt;

&lt;p&gt;Here are the main results:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2012/01/tps.png" title="Puppet stack TPS" alt="Puppet Master Stack / Catalog compiled per Seconds" /&gt;&lt;/p&gt;

&lt;p&gt;And, here is the failure rate:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2012/01/failures.png" title="Failure rate" alt="Puppet Master Stack / Failure rate" /&gt;&lt;/p&gt;

&lt;p&gt;First notice that some of the stack exhibited failures at high concurrency. The errors I could observe were clients timeouts., even tough I configured a large client side timeout (around 10 minutes). This is what happens when too many clients connect at the same time. Everything slows down until the client times out.&lt;/p&gt;

&lt;h3&gt;Fairness&lt;/h3&gt;

&lt;p&gt;In this graph, I plotted the min, average, median and max time of compilation for a concurrency of 16 clients.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2012/01/fairness.png" title="Fairness" alt="Puppet Master Stack / fairness" /&gt;&lt;/p&gt;

&lt;p&gt;Of course, the better is when min and max are almost the same.&lt;/p&gt;

&lt;h3&gt;Digging into the number of workers&lt;/h3&gt;

&lt;p&gt;For the stacks that supports a configurable number of workers (mongrel and passenger), I wanted to check what impact it could have. I strongly believe that there&amp;#8217;s no reason to use a large number (compared to I/O bound workloads).&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2012/01/workers.png" title="Workers # influence" alt="Puppet Master Stack / Worker # influence" /&gt;&lt;/p&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;Beside being fun this project shows why Passenger is still the best stack to run Puppet. JRuby shows some great hopes, but I had to massage the Puppet codebase to make it run (I might publish the patches later).&lt;/p&gt;

&lt;p&gt;That&amp;#8217;d would be really awesome if we could settle on a corpus of manifests to allow comparing benchmark results between Puppet users. Anyone want to try to fix this?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=AEDb9uaOi-0:yYJp64OWgpw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=AEDb9uaOi-0:yYJp64OWgpw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=AEDb9uaOi-0:yYJp64OWgpw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=AEDb9uaOi-0:yYJp64OWgpw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=AEDb9uaOi-0:yYJp64OWgpw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=AEDb9uaOi-0:yYJp64OWgpw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=AEDb9uaOi-0:yYJp64OWgpw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=AEDb9uaOi-0:yYJp64OWgpw:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=AEDb9uaOi-0:yYJp64OWgpw:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2012/01/08/benchmarking-puppet-stacks/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet Internals: the parser]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/ks4sb6xrbO4/" />
    <updated>2011-12-27T16:46:51+01:00</updated>
    <id>http://www.masterzen.fr/2011/12/27/puppet-internals-the-parser</id>
    
    <category term="puppet" />
        
    <content type="html">&lt;p&gt;As more or less promised in my series of post about &lt;a href="http://www.masterzen.fr/2011/11/02/puppet-extension-point-part-2/"&gt;Puppet Extension Points&lt;/a&gt;, here is the first post about &lt;strong&gt;Puppet Internals&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea is to produce a series of blog post about each one about a Puppet sub-system.&lt;/p&gt;

&lt;p&gt;Before starting, I first want to present what are the various sub-blocks that forms Puppet, or Puppet: the Big Picture:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2011/12/big-picture.jpg" title="Puppet: the Big Picture" alt="Puppet the Big Picture" /&gt;&lt;/p&gt;

&lt;p&gt;I hope to be able to cover each of those sub-blocks in various posts, but we&amp;#8217;ll today focus on the &lt;strong&gt;Puppet Parser&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;The Puppet Parser&lt;/h1&gt;

&lt;p&gt;The Puppet Parser responsibility is to transform the &lt;em&gt;textual manifests&lt;/em&gt; into a computer usable data structure that could be fed to the compiler to produce the &lt;em&gt;catalog&lt;/em&gt;. This data structure is called an &lt;a href="http://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;AST (Abstract Syntax Tree)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Puppet Parser is the combination of various different sub-systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the lexer&lt;/li&gt;
&lt;li&gt;the racc-based parser&lt;/li&gt;
&lt;li&gt;the AST model&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;The Lexer&lt;/h2&gt;

&lt;p&gt;The purpose of the &lt;em&gt;lexer&lt;/em&gt; is to read manifests characters by characters and to produce a &lt;em&gt;stream of tokens&lt;/em&gt;. A token is just a symbol (combined with data) that represents a valid part of the Puppet language.&lt;/p&gt;

&lt;p&gt;For instance, the &lt;em&gt;lexer&lt;/em&gt; is able to find things such (but not limited to):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reserved keywords (like case, class, define&amp;#8230;)&lt;/li&gt;
&lt;li&gt;quoted strings&lt;/li&gt;
&lt;li&gt;identifiers&lt;/li&gt;
&lt;li&gt;variables&lt;/li&gt;
&lt;li&gt;various operators (like left parenthesis or right curly braces&amp;#8230;)&lt;/li&gt;
&lt;li&gt;regexes&lt;/li&gt;
&lt;li&gt;&amp;#8230;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let&amp;#8217;s take an example and follow what comes out of the &lt;em&gt;lexer&lt;/em&gt; when scanning this manifest:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="vg"&gt;$variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;this is a string&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;And here is the stream of tokens that is the outcome of the &lt;em&gt;lexer&lt;/em&gt;:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:VARIABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;VARIABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;variable&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:EQUALS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;EQUALS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;this is a string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;As you can see, a puppet token is the combination of a symbol and a hash.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s see how we achieved this result. First you must know that the Puppet &lt;em&gt;lexer&lt;/em&gt; is a regex-based system.
Each token is defined as a regex (or a stock string). When reading a character, the &lt;em&gt;lexer&lt;/em&gt; &amp;#8216;just&amp;#8217; checks if one of the string or regex can match. If there is one match, the lexer emits the corresponding token.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s take our example manifest (the variable assignment above), and see what happens in the lexer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;read $ character&lt;/li&gt;
&lt;li&gt;no regex match, let&amp;#8217;s read some more characters&lt;/li&gt;
&lt;li&gt;read &amp;#8216;variable&amp;#8217;, still no match, our current buffer contains &lt;code&gt;$variable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;read &amp;#8217; &amp;#8216;, oh we have a match against the DOLLAR_VARIABLE token regex&lt;/li&gt;
&lt;li&gt;this token is special, it is defined with a ruby block. When one of those token is read and matched, the block is executed.&lt;/li&gt;
&lt;li&gt;the block just emits the &lt;code&gt;VARIABLE("variable")&lt;/code&gt; token&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;The &lt;em&gt;lexer&lt;/em&gt;&amp;#8217;s scanner doesn&amp;#8217;t try every regexes or strings, it does this in a particular order. In short it tries to maximize the length of the matched string, in a word the lexer is greedy. This helps removing ambiguity.&lt;/p&gt;

&lt;p&gt;As seen in the token stream above, the &lt;em&gt;lexer&lt;/em&gt; associates to each token an hash containing the line number where we found it. This allows error messages in case of parsing error to point to the correct line. It also helps &lt;em&gt;puppetdoc&lt;/em&gt; to associate the right comment with the right language structure.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;lexer&lt;/em&gt; also supports lexing contexts. Some tokens are valid in some specific contexts only, this is true especially when parsing quoted strings for variables interpolation.&lt;/p&gt;

&lt;p&gt;Not all lexed tokens emit tokens for the parser. For instance comments are scanned (and stored in a stack for &lt;em&gt;puppetdoc&lt;/em&gt; use), but they don&amp;#8217;t produce a token for the parser: they&amp;#8217;re skipped.&lt;/p&gt;

&lt;p&gt;Finally, the lexer also maintains a stack of the class names it crossed. This is to be able to find the correct fully qualified name of inner classes as seen in the following example:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Fully qualified class names by keeping a stack of names during lexing&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;outer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;middle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="c1"&gt;# we&amp;#39;re in outer::middle::inner&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;If you want more information about the lexer, check the &lt;a href="https://github.com/puppetlabs/puppet/blob/master/lib/puppet/parser/lexer.rb"&gt;Puppet::Parser::Lexer&lt;/a&gt; class.&lt;/p&gt;

&lt;h2&gt;The parser&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;parser&lt;/em&gt; is based on &lt;a href="http://raa.ruby-lang.org/project/racc/"&gt;racc&lt;/a&gt;. Racc is a ruby port of the good old &lt;a href="http://en.wikipedia.org/wiki/Yacc"&gt;Yacc&lt;/a&gt;. Racc, like Yacc, is what we call a &lt;a href="http://en.wikipedia.org/wiki/LALR_parser"&gt;LALR parser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &amp;#8216;cc&amp;#8217; in Racc means &amp;#8216;compiler of compiler&amp;#8217;. It means in fact that the parser is generated from what we call a &lt;em&gt;grammar&lt;/em&gt; (and for LALR parsers, even a &lt;em&gt;context free grammar&lt;/em&gt;). The generated parser is table driven and consumes tokens one by one. Those kind of parsers are sometimes called Shift/Reduce parsers.&lt;/p&gt;

&lt;p&gt;This grammar is written in a language that is a machine readable version of a &lt;a href="http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form"&gt;Backus-Naur Form or &amp;#8220;BNF&amp;#8221;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are different subclasses of &lt;em&gt;context free grammars&lt;/em&gt;. Racc works best with LR(1) grammars, which means it must be possible to parse any portion of an input string with just a single token lookahead. Parsers for LR(1) grammars are deterministic. This means that we only need a fixed number of lookahead tokens (in our case 1) and what we already parsed to find what next rule to apply.&lt;/p&gt;

&lt;p&gt;Roughly it does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;read a token&lt;/li&gt;
&lt;li&gt;shift (this mean put the token on the stack), goto 1. until we can reduce&lt;/li&gt;
&lt;li&gt;reduce the read tokens with a grammar rules (this involves looking ahead)&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;We&amp;#8217;ll have a deeper look in the subsequent chapters. Meanwhile if you want to learn everything about LALR Parsers or parsers in general, I highly recommend the &lt;a href="http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools"&gt;Dragon Book&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;The Puppet Grammar&lt;/h3&gt;

&lt;p&gt;The Puppet Grammar can be found in &lt;code&gt;lib/puppet/parser/grammar.ra&lt;/code&gt; in the sources.
It is a typical racc/yacc grammar that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defines the known tokens (those matches the lexed token names)&lt;/li&gt;
&lt;li&gt;defines the precedence of operators&lt;/li&gt;
&lt;li&gt;various recursive rules that form the definition of the Puppet languages&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let&amp;#8217;s have a look to a bit of the Puppet Grammar to better understand how it works:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Excerpt of the Puppet Grammar&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;statement_or_declaration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;resource&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;assignment&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;casestatement&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ifstatement_begin&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;assignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="no"&gt;VARIABLE&lt;/span&gt; &lt;span class="no"&gt;EQUALS&lt;/span&gt; &lt;span class="n"&gt;expression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="no"&gt;AST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:line&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="no"&gt;AST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VarDef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:line&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;rvalue&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;rvalue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;quotedtext&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;quotedtext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;STRING&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="no"&gt;AST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:line&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;So the closer look above shows 4 rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a non-terminal rule called &lt;code&gt;statement_or_declaration&lt;/code&gt; which is an alternation of sub-rules&lt;/li&gt;
&lt;li&gt;a terminal rule called &lt;code&gt;assignment&lt;/code&gt;, with a ruby code block that will be executed when this rule will be reduced.&lt;/li&gt;
&lt;li&gt;a non terminal rule called &lt;code&gt;expression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;a terminal rule &lt;code&gt;quotedtext&lt;/code&gt; with a ruby block&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;To understand what that means, we could translate those rules by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A statement or declaration can be either a &lt;code&gt;resource&lt;/code&gt; or a &lt;code&gt;collection&lt;/code&gt;, or an &lt;code&gt;assignement&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;assignment&lt;/code&gt; is when the parser finds a &lt;code&gt;VARIABLE&lt;/code&gt; token followed by an &lt;code&gt;EQUALS&lt;/code&gt; token and an &lt;code&gt;expression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;expression&lt;/code&gt; can be a &lt;code&gt;rvalue&lt;/code&gt; or an &lt;code&gt;hash&lt;/code&gt; (all defined later on in the grammar file)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;rvalue&lt;/code&gt; can be among other things a &lt;code&gt;quotedtext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;And finally a &lt;code&gt;quotedtext&lt;/code&gt; can be &lt;code&gt;STRING&lt;/code&gt; (among other things)&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;You can generate yourself the puppet parser by using racc, it&amp;#8217;s as simple as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing racc (available as a gem)&lt;/li&gt;
&lt;li&gt;running: &lt;code&gt;make -C lib/puppet/parser&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;This rebuilds the &lt;code&gt;lib/puppet/parser/parser.rb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;You can generate a debug parser that prints everything it does if you use &lt;code&gt;-g&lt;/code&gt; command-line switch to racc (check the &lt;code&gt;lib/puppet/parser/makefile&lt;/code&gt; and define &lt;code&gt;@@yydebug = true&lt;/code&gt; in the parser class.&lt;/p&gt;

&lt;p&gt;The parser itself is controlled by the &lt;code&gt;Puppet::Parser::Parser&lt;/code&gt; class which is in &lt;code&gt;lib/puppet/parser/parser_support.rb&lt;/code&gt;. This class is requiring the generated parser (both share the same ruby class). That means that the ruby blocks in the grammar will be executed in the context of an instance of the &lt;code&gt;Puppet::Parser::Parser&lt;/code&gt; class. In other words, you can call from the grammar, methods defined in the &lt;code&gt;parser_support.rb&lt;/code&gt; file. That&amp;#8217;s the reason we refer to the &lt;code&gt;ast&lt;/code&gt; method in the above example. This method just creates an instance of the given class and associates some context to it.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s go back a little bit to the reduce operation. When the parser is reducing, it pops from the stack the reduced tokens and pushes the result to the stack. The result can either be what ends in the &lt;code&gt;result&lt;/code&gt; field of the grammar ruby block or the result of the reduction of the mentioned rule (when it&amp;#8217;s a non-terminal one).&lt;/p&gt;

&lt;p&gt;In the ruby block of a terminal rule, it is possible to access the tokens and rule results currently parsed in the &lt;code&gt;val&lt;/code&gt; array. To get back to the assignment statement above, &lt;code&gt;val[0]&lt;/code&gt; is the &lt;code&gt;VARIABLE&lt;/code&gt; token, and &lt;code&gt;val[2]&lt;/code&gt; the result of the reduction of the &lt;code&gt;expression&lt;/code&gt; rule.&lt;/p&gt;

&lt;h3&gt;The AST&lt;/h3&gt;

&lt;p&gt;The AST is the computer model of the parsed manifests. It forms a tree of instances of the AST base class. There are AST classes (all inheriting the AST base class) for every elements of the language. For instance there&amp;#8217;s one for puppet &lt;em&gt;classes&lt;/em&gt;, for &lt;em&gt;if&lt;/em&gt;, &lt;em&gt;case&lt;/em&gt; and so on. You&amp;#8217;ll find all those in &lt;code&gt;lib/puppet/parser/ast/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;There are two kinds of AST classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;leaves: which represent some kind of values (like an identifier or a string)&lt;/li&gt;
&lt;li&gt;branches: which encompass more than one other AST classes (like if, case or class). This is what forms the tree.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;All AST classes implement the &lt;code&gt;evaluate&lt;/code&gt; method which we&amp;#8217;ll cover in the compiler article.&lt;/p&gt;

&lt;p&gt;For instance when parsing an if/else statement like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;An If/Else statement&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vg"&gt;$var&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;var is true&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;var is false&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;The whole if/else once parsed will be an instance of &lt;code&gt;Puppet::Parser::AST::IfStatement&lt;/code&gt; (which can be found in &lt;code&gt;lib/puppet/parser/ast/ifstatement.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This class defines three instance variables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;@test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@statements&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@else&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;The grammar rule for ifstatement is (I simplified it for the purpose of the article):&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;Simplified Grammar rule for If/else&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;ifstatement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="no"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;expression&lt;/span&gt; &lt;span class="no"&gt;LBRACE&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt; &lt;span class="no"&gt;RBRACE&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="ss"&gt;:statements&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="ss"&gt;:else&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="no"&gt;AST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IfStatement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Notice how the &lt;code&gt;AST::IfStatement&lt;/code&gt; is initialized with the args hash containing the &lt;code&gt;test&lt;/code&gt;,&lt;code&gt;statements&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; result of the those rules. Those rules &lt;code&gt;result&lt;/code&gt; will also be AST classes, and will end up in the IFStatement fields we talked about earlier.&lt;/p&gt;

&lt;p&gt;Thus this forms a tree. If you look to the &lt;code&gt;AST::IfStatement#evaluate&lt;/code&gt; implementation you&amp;#8217;ll see that depending on the result of the evaluation of the &lt;code&gt;@test&lt;/code&gt; it will either evaluate &lt;code&gt;@statements&lt;/code&gt; or &lt;code&gt;@else&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Calling the &lt;code&gt;evaluate&lt;/code&gt; method of the root element of this tree will in chain trigger calling &lt;code&gt;evaluate&lt;/code&gt; on children like for the IfStatement example. This process will be explained in details in the compiler article, but that&amp;#8217;s essentially how Puppet compiler works.&lt;/p&gt;

&lt;h3&gt;An Example Step by Step&lt;/h3&gt;

&lt;p&gt;Let&amp;#8217;s see an end-to-end example of parsing a simple manifest:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;A Simple Example&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="s2"&gt;&amp;quot;/tmp/a&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;test!&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;This will produce the following stream of tokens:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;A Simple Example&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:CLASS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CLASS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;class&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:NAME&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:LBRACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LBRACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:NAME&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:LBRACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LBRACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/tmp/a&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:COLON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;COLON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:NAME&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:FARROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FARROW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;=&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;test!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:RBRACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RBRACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="ss"&gt;:RBRACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RBRACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;And now let&amp;#8217;s dive in the parser events (I simplified the outcome because the Puppet grammar is a little bit more complex
than necessary for this article). The following example shows all actions of the Parser and how looks the parser stack after the operation took place. I elided some of the stacks when not strictly needed to understand what happened.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;CLASS&lt;/code&gt; &lt;em&gt;(our parser got the first token from the lexer)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;CLASS&lt;/code&gt; &lt;em&gt;(there&amp;#8217;s nothing else to do for the moment)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;the result of the shift is that we now have one token in the parser stack&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;NAME("test")&lt;/code&gt; &lt;em&gt;(we get one more token)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;NAME&lt;/code&gt; &lt;em&gt;(still no rules can match so we shift it)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS NAME("test") ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;NAME&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;classname&lt;/code&gt; &lt;em&gt;(oh and now we can reduce a rule)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;notice how the stacks now contains a classname and not a NAME&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;LBRACE&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;LBRACE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;NAME("file")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;NAME&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE NAME("file") ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;LBRACE&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;NAME&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;classname&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt;: &lt;code&gt;LBRACE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt; &lt;code&gt;STRING("/tmp/a")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;STRING&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE STRING("/tmp/a") ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;STRING&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;quotedtext&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (quotedtext AST::String("/tmp/a")) ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt; &lt;code&gt;COLON&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;quotedtext&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;resourcename&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;COLON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) COLON ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;NAME("content")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;NAME&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) COLON NAME("content") ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;FARROW&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;FARROW&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) COLON NAME("content") FARROW ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;STRING("test!")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;shift&lt;/em&gt;: &lt;code&gt;STRING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;STRING&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;quotedtext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;RBRACE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;quotedtext&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;rvalue&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) COLON NAME("content") FARROW (rvalue AST::String("test!"))]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;rvalue&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;expression&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) COLON NAME("content") FARROW (expression AST::String("test!"))]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;NAME FARROW expression&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;param&lt;/code&gt;  &lt;em&gt;(we&amp;#8217;ve now a resource parameter)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourcename  AST::String("/tmp/a")) COLON (param AST::ResourceParam("content"=&amp;gt;"test!")))]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;param&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;params&lt;/code&gt; &lt;em&gt;(multiple parameters can form a params)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;resourcename COLON params&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;resourceinst&lt;/code&gt; &lt;em&gt;(name: parameters form a resouce)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourceinst (AST::ResourceInstance(...)))]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;resourceinst&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;resourceinstances&lt;/code&gt; &lt;em&gt;(more than one resourceinst can form resourceinstances)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resourceinstances [(resourceinst (AST::ResourceInstance(...)))] )]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;RBRACE&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;classname LBRACE resourceinstances RBRACE&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;resource&lt;/code&gt; &lt;em&gt;(we&amp;#8217;ve discovered a resource)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resource AST::Resource(...))]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;code&gt;RBRACE&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;resource&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;statement_or_declaration&lt;/code&gt; &lt;em&gt;(a resource is one statement)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;statement_or_declaration&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;statement_and_declarations&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;code&gt;RBRACE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ CLASS (classname "test") LBRACE (classname "file") LBRACE (resource AST::Resource(...)) RBRACE ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt; &lt;code&gt;CLASS classname LBRACE statements_and_declarations RBRACE&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;hostclass&lt;/code&gt; &lt;em&gt;(we&amp;#8217;ve discovered a puppet class)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ (hostclass AST::Hostclass(...)) ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;hostclass&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;statement_or_declaration&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;statement_or_declaration&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;statements_and_declarations&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;receive&lt;/em&gt;: &lt;em&gt;end of file&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;reduce&lt;/em&gt;  &lt;code&gt;statements_and_declarations&lt;/code&gt; &amp;#8211;&gt; &lt;code&gt;program&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;shift&lt;/em&gt; &lt;em&gt;end of file&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;stack: &lt;code&gt;[ (program (AST::ASTArray [AST::Hostclass(...))])) ]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;And the parsing is now over. What is returned is this &lt;code&gt;program&lt;/code&gt;, which is in fact an instance of an &lt;code&gt;AST::ASTArray&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we now analyze the produced AST, we find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AST::ASTarray&lt;/code&gt; - &lt;em&gt;array of AST instances, this is our program&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AST::Hostclass&lt;/code&gt; - &lt;em&gt;an instance of a class&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AST::Resource&lt;/code&gt; - &lt;em&gt;contains an array of resource instances&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AST::ResourceInstance&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AST::ResourceParam&lt;/code&gt; - &lt;em&gt;contains the &amp;#8220;content&amp;#8221; parameter&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AST::String("content")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AST::String("test!")&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;What&amp;#8217;s important to understand is that the AST depends only from the manifests. Thus the Puppet master needs only to reparse manifests only if they change.&lt;/p&gt;

&lt;h2&gt;What&amp;#8217;s next?&lt;/h2&gt;

&lt;p&gt;The next episode will follow-up after the Parser: the compilation. The Puppet compiler takes the AST, injects into it the facts and gets what we call a catalog; that&amp;#8217;s exactly what we&amp;#8217;ll learn in the next article (sorry, no ETA yet).&lt;/p&gt;

&lt;p&gt;Do not hesitate to comment or ask questions on this article with the comment system below :)&lt;/p&gt;

&lt;p&gt;And happy new year all!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ks4sb6xrbO4:qfIDHmFwU54:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ks4sb6xrbO4:qfIDHmFwU54:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=ks4sb6xrbO4:qfIDHmFwU54:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ks4sb6xrbO4:qfIDHmFwU54:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ks4sb6xrbO4:qfIDHmFwU54:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ks4sb6xrbO4:qfIDHmFwU54:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=ks4sb6xrbO4:qfIDHmFwU54:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ks4sb6xrbO4:qfIDHmFwU54:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=ks4sb6xrbO4:qfIDHmFwU54:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2011/12/27/puppet-internals-the-parser/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Protobuf, Maven, M2E and Eclipse are on a boat]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/7Shko1dkEDI/" />
    <updated>2011-12-25T19:30:45+01:00</updated>
    <id>http://www.masterzen.fr/2011/12/25/protobuf-maven-m2e-and-eclipse-are-on-a-boat</id>
    
    <category term="maven" />
    
    <category term="protobuf" />
    
    <category term="m2e" />
        
    <content type="html">&lt;p&gt;At &lt;a href="http://www.daysofwonder.com/"&gt;Days of Wonder&lt;/a&gt; we develop several Java projects (for instance our online game servers).
Those are built with &lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt;, and most if not all are using &lt;a href="http://code.google.com/p/protobuf/"&gt;Google Protocol Buffers&lt;/a&gt; for data interchange.&lt;/p&gt;

&lt;p&gt;Development happens mostly in Eclipse, and until a couple of months ago with &lt;em&gt;m2eclipse&lt;/em&gt;. With the release of &lt;a href="http://eclipse.org/m2e/"&gt;m2e&lt;/a&gt; (m2eclipse successor), our builds don&amp;#8217;t work as is in Eclipse.&lt;/p&gt;

&lt;p&gt;The reason is that we run the &lt;a href="https://github.com/dtrott/maven-protoc-plugin"&gt;maven-protoc-plugin&lt;/a&gt; (the David Trott fork which is more or less now the only one available still seeing development). This maven plugins allows the &lt;code&gt;protoc&lt;/code&gt; &lt;em&gt;Protocol Buffers&lt;/em&gt; compiler to be run at the &lt;code&gt;generate-sources&lt;/code&gt; phase of the &lt;em&gt;Maven Lifecycle&lt;/em&gt;. Under &lt;em&gt;m2eclipse&lt;/em&gt;, this phase was happening outside &lt;em&gt;Eclipse&lt;/em&gt; and the builds was running fine.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;em&gt;m2e&lt;/em&gt; is not able to solve this correctly. It requires using a &lt;em&gt;connector&lt;/em&gt;. Those &lt;em&gt;connectors&lt;/em&gt; are Eclipse plugins that ties a maven plugin to a m2e build lifecycle phase. This way when &lt;em&gt;m2e&lt;/em&gt; needs to execute this phase of the build, it can do so with the &lt;em&gt;connector&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Until now, there wasn&amp;#8217;t any lifecycle connector for the maven-protoc-plugin. This wasn&amp;#8217;t possible to continue without this in the long term for our development team, so I took a stab to build it.&lt;/p&gt;

&lt;p&gt;In fact it was way simpler than what I first thought. I used the &lt;a href="http://wiki.eclipse.org/M2E_Extension_Development"&gt;m2e Extension Development Guide&lt;/a&gt; as a bootstrap (and especially the EGit extension).&lt;/p&gt;

&lt;p&gt;The result of this few hours of development is now open-source and available in the &lt;a href="https://github.com/masterzen/m2e-protoc-connector"&gt;m2e-protoc-connector Github repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;I didn&amp;#8217;t release an Eclipse p2 update repository (mainly because I don&amp;#8217;t really know how to do that), so you&amp;#8217;ll have to build the project by yourself (but it&amp;#8217;s easy).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;/ol&gt;


&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;git clone git://github.com/masterzen/m2e-protoc-connector.git
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;ol&gt;
&lt;li&gt;Build with maven 3&lt;/li&gt;
&lt;/ol&gt;


&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;mvn package
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Once built, you&amp;#8217;ll find the feature packaged in &lt;code&gt;com.daysofwonder.tools.m2e-protoc-connector.feature/target/com.daysofwonder.tools.m2e-protoc-connector.feature-1.0.0.20111130-1035-site.zip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To install in Eclipse Indigo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;open the &lt;code&gt;Install New Software&lt;/code&gt; window from the &lt;code&gt;Help&lt;/code&gt; menu.&lt;/li&gt;
&lt;li&gt;Then click on the &lt;code&gt;Add&lt;/code&gt; button&lt;/li&gt;
&lt;li&gt;select the &lt;code&gt;Archive&lt;/code&gt; button and point it to the:
&lt;code&gt;com.daysofwonder.tools.m2e-protoc-connector.feature/target/com.daysofwonder.tools.m2e-protoc-connector.feature-1.0.0.20111130-1035-site.zip&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Accept the license terms and restart eclipse.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;To use it there is no specific need, as long as your &lt;code&gt;pom.xml&lt;/code&gt; conforms roughly to what we use:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;span class='line-number'&gt;33&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='xml'&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.protobuf.tools&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-protoc-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;generate proto sources&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;generate-sources&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;                &lt;span class="nt"&gt;&amp;lt;protoSourceRoot&amp;gt;&lt;/span&gt;${basedir}/src/main/proto/&lt;span class="nt"&gt;&amp;lt;/protoSourceRoot&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;                &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;                    &lt;span class="nt"&gt;&amp;lt;param&amp;gt;&lt;/span&gt;**/*.proto&lt;span class="nt"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;                &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;...
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.protobuf&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;protobuf-java&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.4.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;...
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;pluginRepositories&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;pluginRepository&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;dtrott-public&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;David Trott&amp;#39;s Public Repository&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;http://maven.davidtrott.com/repository&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;/pluginRepository&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;/pluginRepositories&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;If you find any problem, do not hesitate to open an issue on the &lt;a href="https://github.com/masterzen/m2e-protoc-connector"&gt;github repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7Shko1dkEDI:Qgf85XNd7t8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7Shko1dkEDI:Qgf85XNd7t8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=7Shko1dkEDI:Qgf85XNd7t8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7Shko1dkEDI:Qgf85XNd7t8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7Shko1dkEDI:Qgf85XNd7t8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7Shko1dkEDI:Qgf85XNd7t8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=7Shko1dkEDI:Qgf85XNd7t8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=7Shko1dkEDI:Qgf85XNd7t8:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=7Shko1dkEDI:Qgf85XNd7t8:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2011/12/25/protobuf-maven-m2e-and-eclipse-are-on-a-boat/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[redis-snmp: redis performance monitoring through SNMP]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/snEp12eIJLQ/" />
    <updated>2011-12-25T17:49:18+01:00</updated>
    <id>http://www.masterzen.fr/2011/12/25/redis-snmp-redis-performance-monitoring-through-snmp</id>
    
    <category term="monitoring" />
    
    <category term="sysadmin" />
        
    <content type="html">&lt;p&gt;The same way I created &lt;a href="http://www.masterzen.fr/software-contributions/mysql-snmp-monitor-mysql-with-snmp/"&gt;mysql-snmp&lt;/a&gt; a small &lt;a href="http://www.net-snmp.org/"&gt;Net-SNMP&lt;/a&gt; subagent that allows exporting performance data from MySQL through SNMP, I&amp;#8217;m proud to announce the first release of &lt;strong&gt;redis-snmp&lt;/strong&gt; to monitor &lt;a href="http://redis.io/"&gt;Redis servers&lt;/a&gt;. It is also inspired by the &lt;a href="http://code.google.com/p/mysql-cacti-templates/"&gt;Cacti MySQL Templates&lt;/a&gt; (which also covers Redis).&lt;/p&gt;

&lt;p&gt;I originally created this Net-SNMP perl subagent to monitor some Redis performance metrics with &lt;a href="http://www.opennms.org/"&gt;OpenNMS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;The where&lt;/h2&gt;

&lt;p&gt;You&amp;#8217;ll find the sources (which allows to produce a debian package) in the &lt;a href="http://github/masterzen/redis-snmp"&gt;redis-snmp github repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;The what&lt;/h2&gt;

&lt;p&gt;Here are the kind of graphs and metrics you can export from a redis server:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2011/12/redis-connections.jpg" title="Redis Connections" alt="Redis Connections" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2011/12/redis-commands.jpg" title="Redis Commands" alt="Redis Commands" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2011/12/redis-memory.jpg" title="Redis Memory" alt="Redis Memory" /&gt;&lt;/p&gt;

&lt;h2&gt;The how&lt;/h2&gt;

&lt;p&gt;Like mysql-snmp you need to run redis-snmp on a host that has a connectivity with the monitored redis server (the same host makes sense). You also need the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Net-SNMP &gt;= 5.4.2.1 (older versions contains a 64 bits varbind issue)&lt;/li&gt;
&lt;li&gt;perl (tested under perl 5.10 from debian squeeze)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Once running, you should be able to ask your snmpd about redis values:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;snmpbulkwalk -m&lt;span class="s1"&gt;&amp;#39;REDIS-SERVER-MIB&amp;#39;&lt;/span&gt; -v 2c  -c public redis-server.domain.com .1.3.6.1.4.1.20267.400
&lt;/span&gt;&lt;span class='line'&gt;REDIS-SERVER-MIB::redisConnectedClients.0 &lt;span class="o"&gt;=&lt;/span&gt; Gauge32: 1
&lt;/span&gt;&lt;span class='line'&gt;REDIS-SERVER-MIB::redisConnectedSlaves.0 &lt;span class="o"&gt;=&lt;/span&gt; Gauge32: 0
&lt;/span&gt;&lt;span class='line'&gt;REDIS-SERVER-MIB::redisUsedMemory.0 &lt;span class="o"&gt;=&lt;/span&gt; Counter64: 154007648
&lt;/span&gt;&lt;span class='line'&gt;REDIS-SERVER-MIB::redisChangesSinceLastSave.0 &lt;span class="o"&gt;=&lt;/span&gt; Gauge32: 542
&lt;/span&gt;&lt;span class='line'&gt;REDIS-SERVER-MIB::redisTotalConnections.0 &lt;span class="o"&gt;=&lt;/span&gt; Counter64: 6794739
&lt;/span&gt;&lt;span class='line'&gt;REDIS-SERVER-MIB::redisCommandsProcessed.0 &lt;span class="o"&gt;=&lt;/span&gt; Counter64: 37574019
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Of course you must adjust the hostname and community. SNMP v2c (or better) is mandatory since we&amp;#8217;re reporting 64 bits values.&lt;/p&gt;

&lt;p&gt;Note that you can get the OID translation to name only if the REDIS-SNMP-SERVER MIB is installed on the host where you run the above command.&lt;/p&gt;

&lt;h2&gt;OpeNMS integration&lt;/h2&gt;

&lt;p&gt;To integrate to OpenNMS, it&amp;#8217;s as simple as adding the following group to your &lt;code&gt;datacollection-config.xml&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='xml'&gt;&lt;span class='line'&gt;&lt;span class="c"&gt;&amp;lt;!-- REDIS-SERVER MIB --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;group&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redis&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ifType=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ignore&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;mibObj&lt;/span&gt; &lt;span class="na"&gt;oid=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.1.3.6.1.4.1.20267.400.1.1&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alias=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redisConnectedClnts&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Gauge32&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;mibObj&lt;/span&gt; &lt;span class="na"&gt;oid=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.1.3.6.1.4.1.20267.400.1.2&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alias=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redisConnectedSlavs&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Gauge32&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;mibObj&lt;/span&gt; &lt;span class="na"&gt;oid=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.1.3.6.1.4.1.20267.400.1.3&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alias=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redisUsedMemory&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Gauge64&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;mibObj&lt;/span&gt; &lt;span class="na"&gt;oid=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.1.3.6.1.4.1.20267.400.1.4&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alias=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redisChangsSncLstSv&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Gauge32&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;mibObj&lt;/span&gt; &lt;span class="na"&gt;oid=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.1.3.6.1.4.1.20267.400.1.5&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alias=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redisTotalConnectns&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Counter64&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nt"&gt;&amp;lt;mibObj&lt;/span&gt; &lt;span class="na"&gt;oid=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.1.3.6.1.4.1.20267.400.1.6&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;instance=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;alias=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;redisCommandsPrcssd&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Counter64&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;/group&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;And the following graph definitions to your &lt;code&gt;snmp-graph.properties&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;span class='line-number'&gt;33&lt;/span&gt;
&lt;span class='line-number'&gt;34&lt;/span&gt;
&lt;span class='line-number'&gt;35&lt;/span&gt;
&lt;span class='line-number'&gt;36&lt;/span&gt;
&lt;span class='line-number'&gt;37&lt;/span&gt;
&lt;span class='line-number'&gt;38&lt;/span&gt;
&lt;span class='line-number'&gt;39&lt;/span&gt;
&lt;span class='line-number'&gt;40&lt;/span&gt;
&lt;span class='line-number'&gt;41&lt;/span&gt;
&lt;span class='line-number'&gt;42&lt;/span&gt;
&lt;span class='line-number'&gt;43&lt;/span&gt;
&lt;span class='line-number'&gt;44&lt;/span&gt;
&lt;span class='line-number'&gt;45&lt;/span&gt;
&lt;span class='line-number'&gt;46&lt;/span&gt;
&lt;span class='line-number'&gt;47&lt;/span&gt;
&lt;span class='line-number'&gt;48&lt;/span&gt;
&lt;span class='line-number'&gt;49&lt;/span&gt;
&lt;span class='line-number'&gt;50&lt;/span&gt;
&lt;span class='line-number'&gt;51&lt;/span&gt;
&lt;span class='line-number'&gt;52&lt;/span&gt;
&lt;span class='line-number'&gt;53&lt;/span&gt;
&lt;span class='line-number'&gt;54&lt;/span&gt;
&lt;span class='line-number'&gt;55&lt;/span&gt;
&lt;span class='line-number'&gt;56&lt;/span&gt;
&lt;span class='line-number'&gt;57&lt;/span&gt;
&lt;span class='line-number'&gt;58&lt;/span&gt;
&lt;span class='line-number'&gt;59&lt;/span&gt;
&lt;span class='line-number'&gt;60&lt;/span&gt;
&lt;span class='line-number'&gt;61&lt;/span&gt;
&lt;span class='line-number'&gt;62&lt;/span&gt;
&lt;span class='line-number'&gt;63&lt;/span&gt;
&lt;span class='line-number'&gt;64&lt;/span&gt;
&lt;span class='line-number'&gt;65&lt;/span&gt;
&lt;span class='line-number'&gt;66&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='xml'&gt;&lt;span class='line'&gt;report.redis.redisconnections.name=Redis Connections
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisconnections.columns=redisConnectedClnts,redisConnectedSlavs,redisTotalConnectns
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisconnections.type=nodeSnmp
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisconnections.width=565
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisconnections.height=200
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisconnections.command=--title &amp;quot;Redis Connections&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; --width 565 \
&lt;/span&gt;&lt;span class='line'&gt; --height 200 \
&lt;/span&gt;&lt;span class='line'&gt; DEF:redisConnectedClnts={rrd1}:redisConnectedClnts:AVERAGE \
&lt;/span&gt;&lt;span class='line'&gt; DEF:redisConnectedSlavs={rrd2}:redisConnectedSlavs:AVERAGE \
&lt;/span&gt;&lt;span class='line'&gt; DEF:redisTotalConnectns={rrd3}:redisTotalConnectns:AVERAGE \
&lt;/span&gt;&lt;span class='line'&gt; LINE1:redisConnectedClnts#9B2B1B:&amp;quot;REDIS Connected Clients         &amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisConnectedClnts:AVERAGE:&amp;quot;Avg \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisConnectedClnts:MIN:&amp;quot;Min \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisConnectedClnts:MAX:&amp;quot;Max \\: %8.2lf %s\\n&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; LINE1:redisConnectedSlavs#4A170F:&amp;quot;REDIS Connected Slaves          &amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisConnectedSlavs:AVERAGE:&amp;quot;Avg \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisConnectedSlavs:MIN:&amp;quot;Min \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisConnectedSlavs:MAX:&amp;quot;Max \\: %8.2lf %s\\n&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; LINE1:redisTotalConnectns#38524B:&amp;quot;REDIS Total Connections Received&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisTotalConnectns:AVERAGE:&amp;quot;Avg \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisTotalConnectns:MIN:&amp;quot;Min \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisTotalConnectns:MAX:&amp;quot;Max \\: %8.2lf %s\\n&amp;quot;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redismemory.name=Redis Memory
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redismemory.columns=redisUsedMemory
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redismemory.type=nodeSnmp
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redismemory.width=565
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redismemory.height=200
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redismemory.command=--title &amp;quot;Redis Memory&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  --width 565 \
&lt;/span&gt;&lt;span class='line'&gt;  --height 200 \
&lt;/span&gt;&lt;span class='line'&gt;  DEF:redisUsedMemory={rrd1}:redisUsedMemory:AVERAGE \
&lt;/span&gt;&lt;span class='line'&gt;  AREA:redisUsedMemory#3B7AD9:&amp;quot;REDIS Used Memory&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  GPRINT:redisUsedMemory:AVERAGE:&amp;quot;Avg \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  GPRINT:redisUsedMemory:MIN:&amp;quot;Min \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  GPRINT:redisUsedMemory:MAX:&amp;quot;Max \\: %8.2lf %s\\n&amp;quot;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;report.redis.rediscommands.name=Redis Commands
&lt;/span&gt;&lt;span class='line'&gt;report.redis.rediscommands.columns=redisCommandsPrcssd
&lt;/span&gt;&lt;span class='line'&gt;report.redis.rediscommands.type=nodeSnmp
&lt;/span&gt;&lt;span class='line'&gt;report.redis.rediscommands.width=565
&lt;/span&gt;&lt;span class='line'&gt;report.redis.rediscommands.height=200
&lt;/span&gt;&lt;span class='line'&gt;report.redis.rediscommands.command=--title &amp;quot;Redis Commands&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; --width 565 \
&lt;/span&gt;&lt;span class='line'&gt; --height 200 \
&lt;/span&gt;&lt;span class='line'&gt; DEF:redisCommandsPrcssd={rrd1}:redisCommandsPrcssd:AVERAGE \
&lt;/span&gt;&lt;span class='line'&gt; AREA:redisCommandsPrcssd#FF7200:&amp;quot;REDIS Total Commands Processed&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisCommandsPrcssd:AVERAGE:&amp;quot;Avg \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisCommandsPrcssd:MIN:&amp;quot;Min \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt; GPRINT:redisCommandsPrcssd:MAX:&amp;quot;Max \\: %8.2lf %s\\n&amp;quot;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisunsavedchanges.name=Redis Unsaved Changes
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisunsavedchanges.columns=redisChangsSncLstSv
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisunsavedchanges.type=nodeSnmp
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisunsavedchanges.width=565
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisunsavedchanges.height=200
&lt;/span&gt;&lt;span class='line'&gt;report.redis.redisunsavedchanges.command=--title &amp;quot;Redis Unsaved Changes&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  --width 565 \
&lt;/span&gt;&lt;span class='line'&gt;  --height 200 \
&lt;/span&gt;&lt;span class='line'&gt;  DEF:redisChangsSncLstSv={rrd1}:redisChangsSncLstSv:AVERAGE \
&lt;/span&gt;&lt;span class='line'&gt;  AREA:redisChangsSncLstSv#A88558:&amp;quot;REDIS Changes Since Last Save&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  GPRINT:redisChangsSncLstSv:AVERAGE:&amp;quot;Avg \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  GPRINT:redisChangsSncLstSv:MIN:&amp;quot;Min \\: %8.2lf %s&amp;quot; \
&lt;/span&gt;&lt;span class='line'&gt;  GPRINT:redisChangsSncLstSv:MAX:&amp;quot;Max \\: %8.2lf %s\\n&amp;quot;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Do not forget to register the new graphs in the report list at the top of &lt;code&gt;snmp-graph.properties&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Restart OpenNMS, and it should start graphing your redis performance metrics.
You&amp;#8217;ll find those files in the opennms directory of the source distribution.&lt;/p&gt;

&lt;p&gt;Enjoy :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=snEp12eIJLQ:B9r8JTJ4Rss:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=snEp12eIJLQ:B9r8JTJ4Rss:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=snEp12eIJLQ:B9r8JTJ4Rss:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=snEp12eIJLQ:B9r8JTJ4Rss:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=snEp12eIJLQ:B9r8JTJ4Rss:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=snEp12eIJLQ:B9r8JTJ4Rss:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=snEp12eIJLQ:B9r8JTJ4Rss:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=snEp12eIJLQ:B9r8JTJ4Rss:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=snEp12eIJLQ:B9r8JTJ4Rss:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2011/12/25/redis-snmp-redis-performance-monitoring-through-snmp/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[The Indirector - Puppet Extension Points 3]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/nvOkz6hTCX8/" />
    <updated>2011-12-11T11:34:00+01:00</updated>
    <id>http://www.masterzen.fr/2011/12/11/the-indirector-puppet-extensions-points-3</id>
    
    <category term="puppet" />
    
    <category term="sysadmin" />
        
    <content type="html">&lt;p&gt;This article is a follow-up of those previous two articles of this series on Puppet Internals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.masterzen.fr/2011/10/29/puppet-extension-points-part-1/"&gt;Puppet parser functions and custom facts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.masterzen.fr/2011/11/02/puppet-extension-point-part-2/"&gt;Puppet types and providers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Today we&amp;#8217;ll cover the &lt;strong&gt;The Indirector&lt;/strong&gt;. I believe that at the end of this post, you&amp;#8217;ll know exactly what is the indirector and how it works.&lt;/p&gt;

&lt;h2&gt;The scene&lt;/h2&gt;

&lt;p&gt;The puppet source code needs to deal with lots of different abstractions to do its job. Among those abstraction you&amp;#8217;ll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificates&lt;/li&gt;
&lt;li&gt;Nodes&lt;/li&gt;
&lt;li&gt;Facts&lt;/li&gt;
&lt;li&gt;Catalogs&lt;/li&gt;
&lt;li&gt;&amp;#8230;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Each one those abstractions can be found in the Puppet source code under the form of a &lt;em&gt;model class&lt;/em&gt;. For instance when Puppet needs to deal with the current node, it in fact deals with an &lt;em&gt;instance&lt;/em&gt; of the node model class. This class is called &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/node.rb"&gt;&lt;code&gt;Puppet::Node&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each model can exist physically under different forms. For instance &lt;em&gt;Facts&lt;/em&gt; can come from &lt;em&gt;Facter&lt;/em&gt; or a &lt;em&gt;YAML file&lt;/em&gt;, or &lt;em&gt;Nodes&lt;/em&gt; can come from an &lt;em&gt;ENC&lt;/em&gt;, &lt;em&gt;LDAP&lt;/em&gt;, &lt;em&gt;site.pp&lt;/em&gt; and so on. This is what we call a &lt;strong&gt;Terminus&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Indirector&lt;/em&gt; allows the Puppet programmer to deal with model instances without having to manage herself the gory details of where this model instance is coming/going.&lt;/p&gt;

&lt;p&gt;For instance, the code is the same for the client call site to find a node when it comes from an ENC or LDAP, because it&amp;#8217;s irrelevant to the client code.&lt;/p&gt;

&lt;h2&gt;Actions&lt;/h2&gt;

&lt;p&gt;So you might be wondering what the &lt;em&gt;Indirector&lt;/em&gt; allows to do with our models. Basically the &lt;em&gt;Indirector&lt;/em&gt; implements a basic CRUD (Create, Retrieve, Update, Delete) system. In fact it implements 4 verbs (that maps to the CRUD and REST verb sets):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Find&lt;/em&gt;: allows to retrieve a specific instance, given through the &lt;code&gt;key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Search&lt;/em&gt;: allows to retrieve some instances with a search term&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Destroy&lt;/em&gt;: remove a given instance&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Save&lt;/em&gt;: stores a given instance&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You&amp;#8217;ll see a little bit later how it is wired, but those verbs exist as class and/or instance methods in the models class.&lt;/p&gt;

&lt;p&gt;So back to our &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/node.rb"&gt;&lt;code&gt;Puppet::Node&lt;/code&gt;&lt;/a&gt; example, we can say this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# Finding a specific node&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test.daysofwonder.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# here I can use node, being an instance of Puppet::Node&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;node: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# I can also save the given node (if the terminus allows it of course)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# Note: save is implemented as an instance method&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# we can also destroy a given node (if the terminus implements it):&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unwanted.daysowonder.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;And this works for all the managed models, I could have done the exact same code with certificate instead of nodes.&lt;/p&gt;

&lt;h2&gt;Terminii&lt;/h2&gt;

&lt;p&gt;For the Latin illiterate out-there, terminii is the latin plural for terminus.&lt;/p&gt;

&lt;p&gt;So a terminus is a concrete class that knows how to deal with a specific model type. A terminus exists only for a given model. For instance the catalog indirection can use the Compiler or the YAML terminus among half-dozen of available terminus.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;terminus&lt;/em&gt; is a class that should inherit somewhere in the class hierarchy from &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/terminus.rb"&gt;&lt;code&gt;Puppet::Indirector::Terminus&lt;/code&gt;&lt;/a&gt;. This last sentence might be obscure but if your terminus for a given model directly inherits from &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/terminus.rb"&gt;&lt;code&gt;Puppet::Indirector::Terminus&lt;/code&gt;&lt;/a&gt;, it is considered as an abstract terminus and won&amp;#8217;t work.&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# request.key contains the instance to find&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# request.instance contains the model instance to save&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;The &lt;code&gt;request&lt;/code&gt; parameter used above is an instance of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/request.rb"&gt;&lt;code&gt;Puppet::Indirector::Request&lt;/code&gt;&lt;/a&gt;. This request object contains a handful property that might be of interest when implementing a terminus. The first one is the &lt;code&gt;key&lt;/code&gt; method which returns the name of the instance we want to manipulate. The other is &lt;code&gt;instance&lt;/code&gt; which is available only when saving is a concrete instance of the model.&lt;/p&gt;

&lt;h3&gt;Implementing a terminus&lt;/h3&gt;

&lt;p&gt;To implement a new terminus of a given model, you need to add a ruby file of the terminus name in the &lt;code&gt;puppet/indirector/&amp;lt;indirection&amp;gt;/&amp;lt;terminus&amp;gt;.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For instance if we want to implement a new source of puppet nodes like storing node classes in DNS TXT resource records, we&amp;#8217;d create a &lt;code&gt;puppet/node/dns.rb&lt;/code&gt; file whose find method would ask for TXT RR using &lt;code&gt;request.key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Puppet already defines some common behavior like yaml based files, rest based, code based or executable based. A new terminus can inherit from one of those abstract terminus to inherit from its behavior.&lt;/p&gt;

&lt;p&gt;I contributed (but hasn&amp;#8217;t been merged yet) and &lt;a href="http://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol"&gt;OCSP&lt;/a&gt; system for Puppet. This one defines a new indirection: &lt;code&gt;ocsp&lt;/code&gt;. This indirection contains two terminus:&lt;/p&gt;

&lt;p&gt;The real concrete one that inherits from &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/code.rb"&gt;&lt;code&gt;Puppet::Indirector::Code&lt;/code&gt;&lt;/a&gt;, it in fact delegates the OCSP request verification to the OCSP layer:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet/indirector/ocsp&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet/indirector/code&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet/ssl/ocsp/responder&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Indirector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Ocsp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Ca&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Indirector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Code&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;OCSP request revocation verification through the local CA.&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Ocsp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Responder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;It also has a &lt;em&gt;REST terminus&lt;/em&gt;. This allows for a given implementation to talk to a remote puppet process (usually a puppetmaster) using the indirector without modifying client or server code:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet/indirector/ocsp&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;puppet/indirector/rest&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Indirector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Ocsp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Indirector&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REST&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Remote OCSP certificate REST remote revocation status.&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;use_server_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ca_server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;use_port_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ca_port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;As you can see we can do a REST client without implementing any network stuff!&lt;/p&gt;

&lt;h3&gt;Indirection creation&lt;/h3&gt;

&lt;p&gt;To tell Puppet that a given model class can be indirected it&amp;#8217;s just a matter or adding a little bit of Ruby metaprogramming.&lt;/p&gt;

&lt;p&gt;To keep my OCSP system example, the OCSP request model class is declared like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Ocsp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Request&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Indirector&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# this will tell puppet that we have a new indirection&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# and our default terminus will be found in puppet/indirector/ocsp/ca.rb&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;indirects&lt;/span&gt; &lt;span class="ss"&gt;:ocsp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:terminus_class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ca&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Basically we&amp;#8217;re saying the our model &lt;code&gt;Puppet::SSL::Ocsp::Request&lt;/code&gt; declares an indirection &lt;code&gt;ocsp&lt;/code&gt;, whose default terminus class is &lt;code&gt;ca&lt;/code&gt;. That means, if we straightly try to call &lt;code&gt;Puppet::SSL::Ocsp::Request.find&lt;/code&gt;, the &lt;code&gt;puppet/indirection/ocsp/ca.rb&lt;/code&gt; file will be used.&lt;/p&gt;

&lt;h3&gt;Terminus selection&lt;/h3&gt;

&lt;p&gt;There&amp;#8217;s something I didn&amp;#8217;t talk about. You might ask yourself how Puppet knows which terminus it should use when we call one of the &lt;em&gt;indirector&lt;/em&gt; verb. As seen above, if nothing is done to configure it, it will default to the terminus given on the &lt;code&gt;indirects&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;But it is configurable. The &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector.rb"&gt;&lt;code&gt;Puppet::Indirector&lt;/code&gt;&lt;/a&gt; module defines the &lt;code&gt;terminus_class=&lt;/code&gt; method. This methods when called can be used to change the active terminus.&lt;/p&gt;

&lt;p&gt;For instance in the puppet agent, the catalog indirection has a REST terminus, but in the master the same indirection uses the compiler:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# puppet agent equivalent code&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminus_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:rest&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# puppet master equivalent code&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminus_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:compiler&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;In fact the code is a little bit more complicated than this for the catalog but in the end it&amp;#8217;s equivalent.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s also the possibility for a puppet application to specify a routing table between indirection and terminus to simplify the wiring.&lt;/p&gt;

&lt;h3&gt;More than one type of terminii&lt;/h3&gt;

&lt;p&gt;There&amp;#8217;s something I left aside earlier. There are in fact two types of terminii per indirection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;regular terminus as we saw earlier&lt;/li&gt;
&lt;li&gt;cache terminus&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For every model class we can define the regular indirection terminus and an optional cache terminus.&lt;/p&gt;

&lt;p&gt;Then when finding for an instance the cache terminus will first be asked for. If not found in the cache (or asked to not get from the cache) the regular terminus will be used. Afterward the instance will be &lt;code&gt;save&lt;/code&gt;d in the cache terminus.&lt;/p&gt;

&lt;p&gt;This cache is exploited in lots of place in the Puppet code base.&lt;/p&gt;

&lt;p&gt;Among those, the &lt;code&gt;catalog&lt;/code&gt; cache terminus is set to &lt;code&gt;:yaml&lt;/code&gt; on the agent. The effect is that when the &lt;em&gt;agent&lt;/em&gt; retrieves the catalog from the master through the &lt;code&gt;:rest&lt;/code&gt; regular terminus, it is locally saved by the yaml terminus. This way if the next agent run fails when retrieving the catalog through REST, it will used the previous one locally cached during the previous run.&lt;/p&gt;

&lt;p&gt;Most of the certificate stuff is handled along the line of the catalog, with local caching with a file terminus.&lt;/p&gt;

&lt;h3&gt;REST Terminus in details&lt;/h3&gt;

&lt;p&gt;There is a direct translation between the REST verbs and the indirection verbs. Thus the &lt;code&gt;:rest&lt;/code&gt; terminus:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;transforms the indirection and key to an URI: &lt;code&gt;/&amp;lt;environment&amp;gt;/&amp;lt;indirection&amp;gt;/&amp;lt;key&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;does an HTTP GET|PUT|DELETE|POST depending on the indirection verb&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;On the server side, the Puppet network layer does the reverse, calling the right indirection methods based on the URI and the &lt;em&gt;REST&lt;/em&gt; verb.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s also the possibility to sends parameters to the indirection and with REST, those are transformed into URL request parameters.&lt;/p&gt;

&lt;p&gt;The indirection name used in the URI is pluralized by adding a trailing &amp;#8216;s&amp;#8217; to the indirection name when doing a search, to be more REST. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /production/certificate/test.daysofwonder.com&lt;/code&gt; is find&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /production/certificates/unused&lt;/code&gt; is a search&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When indirecting a model class, Puppet mixes-in the &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/network/format_handler.rb"&gt;&lt;code&gt;Puppet::Network::FormatHandler&lt;/code&gt;&lt;/a&gt; module. This module allows to &lt;code&gt;render&lt;/code&gt; and &lt;code&gt;convert&lt;/code&gt; an instance from and to a serialized format. The most used one in Puppet is called &lt;code&gt;pson&lt;/code&gt;, which in fact is json in disguised name.&lt;/p&gt;

&lt;p&gt;During a REST transaction, the instance can be serialized and deserialized using this format. Each model can define its preferred serialization format (for instance catalog use pson, but certificates prefer raw encoding).&lt;/p&gt;

&lt;p&gt;On the HTTP level, we correctly add the various encoding headers reflecting the serialization used.&lt;/p&gt;

&lt;p&gt;You will find a &lt;a href="http://docs.puppetlabs.com/guides/rest_api.html"&gt;comprehensive list of all REST endpoint in puppet here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Puppet 2.7 indirection&lt;/h3&gt;

&lt;p&gt;The syntax I used in my samples are derived from the 2.6 puppet source. In Puppet 2.7, the dev team introduced (and are now contemplating removing) an &lt;code&gt;indirection&lt;/code&gt; property in the model class which implements the indirector verbs (instead of being implemented directly in the model class).&lt;/p&gt;

&lt;p&gt;This translates to:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# 2.6 way, and possibly 2.8 onward&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# 2.7 way&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indirection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h2&gt;Gory details anyone?&lt;/h2&gt;

&lt;p&gt;OK, so how it works?&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s focus on &lt;code&gt;Puppet::Node.find&lt;/code&gt; call:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ruby loads the &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/node.rb"&gt;&lt;code&gt;Puppet::Node&lt;/code&gt;&lt;/a&gt; class&lt;/li&gt;
&lt;li&gt;When mixing in &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector.rb"&gt;&lt;code&gt;Puppet::Indirector&lt;/code&gt;&lt;/a&gt; we created a bunch of find/destroy&amp;#8230; methods in the current model class&lt;/li&gt;
&lt;li&gt;Ruby execute the &lt;code&gt;indirects&lt;/code&gt; call from the &lt;code&gt;Puppet::Indirector&lt;/code&gt; module

&lt;ol&gt;
&lt;li&gt;This one creates a &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/indirection.rb"&gt;&lt;code&gt;Puppet::Indirector::Indirection&lt;/code&gt;&lt;/a&gt; stored locally in the &lt;code&gt;indirection&lt;/code&gt; class instance variable&lt;/li&gt;
&lt;li&gt;This also registers the given indirection in a global indirection list&lt;/li&gt;
&lt;li&gt;This also register the given default terminus class. The terminus are loaded with a &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/util/autoloader.rb"&gt;&lt;code&gt;Puppet::Util::Autoloader&lt;/code&gt;&lt;/a&gt; through a set of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/util/instance_loader.rb"&gt;&lt;code&gt;Puppet::Util::InstanceLoader&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;When this terminus class is loaded, since it somewhat inherits from &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/terminus.rb"&gt;&lt;code&gt;Puppet::Indirector::Terminus&lt;/code&gt;&lt;/a&gt;, the &lt;code&gt;Puppet::Indirector:Terminus#inherited&lt;/code&gt; ruby callback is executed. This one after doing a bunch of safety checks register the terminus class as a valid terminus for the loaded indirection.&lt;/li&gt;
&lt;li&gt;We&amp;#8217;re now ready to really call &lt;code&gt;Puppet::Node.find&lt;/code&gt;. &lt;code&gt;find&lt;/code&gt; is one of the method that we got when we mixed-in &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector.rb"&gt;&lt;code&gt;Puppet::Indirector&lt;/code&gt;&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;find&lt;/code&gt; first create a &lt;a href="https://github.com/puppetlabs/puppet/blob/2.6.x/lib/puppet/indirector/request.rb"&gt;&lt;code&gt;Puppet::Indirector::Request&lt;/code&gt;&lt;/a&gt;, with the given key.&lt;/li&gt;
&lt;li&gt;It then checks the terminus cache if one has been defined. If the cache terminus finds an instance, this one is returned&lt;/li&gt;
&lt;li&gt;Otherwise &lt;code&gt;find&lt;/code&gt; delegates to the registered terminus, by calling &lt;code&gt;terminus.find(request)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If there&amp;#8217;s a result, this one is cached in the cache terminus&lt;/li&gt;
&lt;li&gt;and the result is returned&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Pretty simple, isn&amp;#8217;t it?
And that&amp;#8217;s about the same mechanism for the three other verbs.&lt;/p&gt;

&lt;p&gt;It is to be noted that the terminus are loaded with the puppet autoloader. That means it should be possible to add more indirection and/or terminus as long as paths are respected and they are in the &lt;code&gt;RUBYLIB&lt;/code&gt;.
I don&amp;#8217;t think though that those paths are pluginsync&amp;#8217;ed.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I know that the indirector can be intimidating at first, but even without completely understanding the internals, it is quite easy to add a new terminus for a given indirection.&lt;/p&gt;

&lt;p&gt;On the same subject, I highly recommends this presentation about &lt;a href="http://rcrowley.org/talks/sv-puppet-2011-01-11/"&gt;Extending Puppet&lt;/a&gt; by Richard Crowley. This presentation also covers the &lt;em&gt;indirector&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This article will certainly close the Puppet Extension Points series. The last remaining extension type (Faces) have already been covered thoroughly on the &lt;a href="http://puppetlabs.com/faces/"&gt;Puppetlabs Docs site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next article will I think cover the full picture of a full puppet agent/master run.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=nvOkz6hTCX8:5x22GTD-jy4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=nvOkz6hTCX8:5x22GTD-jy4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=nvOkz6hTCX8:5x22GTD-jy4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=nvOkz6hTCX8:5x22GTD-jy4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=nvOkz6hTCX8:5x22GTD-jy4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=nvOkz6hTCX8:5x22GTD-jy4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=nvOkz6hTCX8:5x22GTD-jy4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=nvOkz6hTCX8:5x22GTD-jy4:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=nvOkz6hTCX8:5x22GTD-jy4:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2011/12/11/the-indirector-puppet-extensions-points-3/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet Extension Points - part 2]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/ZMmWHX6vBkU/" />
    <updated>2011-11-02T21:00:56+01:00</updated>
    <id>http://www.masterzen.fr/2011/11/02/puppet-extension-point-part-2</id>
    
    <category term="puppet" />
    
    <category term="ruby" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;After the &lt;a href="http://www.masterzen.fr/2011/10/29/puppet-extension-points-part-1/"&gt;first part in this series&lt;/a&gt; of article on Puppet extensions points, I&amp;#8217;m proud to deliver a new episode focusing on &lt;a href="http://projects.puppetlabs.com/projects/1/wiki/Development_Practical_Types"&gt;Types&lt;/a&gt; and &lt;a href="http://projects.puppetlabs.com/projects/puppet/wiki/Development_Provider_Development"&gt;Providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that there&amp;#8217;s a really good chapter on the same topic in &lt;a href="http://www.amazon.com/Pro-Puppet-James-Turnbull/dp/1430230576"&gt;James Turnbull and Jeff McCune Pro Puppet&lt;/a&gt; (which I highly recommend if you&amp;#8217;re a serious puppeteer). Also note that you can attend &lt;a href="http://puppetlabs.com/services/training-workshops/"&gt;Puppetlabs Developper Training&lt;/a&gt;, which covers this topic.&lt;/p&gt;

&lt;h2&gt;Of Types and Providers&lt;/h2&gt;

&lt;p&gt;One of the great force of Puppet is how various heterogenous aspects of a given POSIX system (or not, like the &lt;a href="http://puppetlabs.com/blog/puppet-network-device-management/"&gt;Network Device system&lt;/a&gt; I contributed) are abstracted into simple elements: &lt;strong&gt;types&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Types&lt;/em&gt; are the foundation bricks of Puppet, you use them everyday to model how your systems are formed. Among the core types, you&amp;#8217;ll find user, group, file, &amp;#8230;&lt;/p&gt;

&lt;p&gt;In Puppet, manifests define resources which are &lt;em&gt;instances of their type&lt;/em&gt;. There can be only one resource of a given name (what we call the &lt;em&gt;namevar&lt;/em&gt;, &lt;em&gt;name&lt;/em&gt; or &lt;em&gt;title&lt;/em&gt;) for a given catalog (which usually maps to a given host).&lt;/p&gt;

&lt;p&gt;A type models what facets of a physical entity (like a host user) are managed by Puppet. These model facets are called &amp;#8220;properties&amp;#8221; in Puppet lingo.&lt;/p&gt;

&lt;p&gt;Essentially a type is a name, some properties to be managed and some parameters. Paramaters are values that will help or direct Puppet to manage the resource (for instance the managehome parameter of the &lt;a href="http://docs.puppetlabs.com/references/2.7.5/type.html#user"&gt;user type&lt;/a&gt; is not part of a given user on the host, but explains to Puppet that this user&amp;#8217;s home directory is to be managed).&lt;/p&gt;

&lt;h3&gt;Let&amp;#8217;s follow the life of a resource during a puppet run.&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;During compilation, the puppet parser will instantiate &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parser/resource.rb"&gt;Puppet::Parser::Resource&lt;/a&gt; instances which are &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/resource.rb"&gt;Puppet::Resource&lt;/a&gt; objects. Those contains the various properties and parameters values defined in the manifest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Those resources are then inserted into the catalog (an instance of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/resource/catalog.rb"&gt;Puppet::Resource::Catalog&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The catalog is then sent to the agent (usually in json format)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The agent converts the catalog individual resources into RAL resources by virtue of Puppet::Resource#to_ral. We&amp;#8217;re now dealing with instances of the real puppet type class. RAL means Resource Abstraction Layer.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent then applies the catalog. This process creates the relationships graph so that we can manage resources in an order obeying require/before metaparameters. During catalog application, every RAL resource is evaluated. This process tells a given type to do what is necessary so that every managed property of the real underlying resource match what was specified in the manifest. The software system that does this is the &lt;em&gt;provider&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;So to summarize, a type defines to Puppet what properties it can manage and an accompanying provider is the process to manage them. Those two elements forms the Puppet RAL.&lt;/p&gt;

&lt;p&gt;There can be more than one provider per type, depending on the host or platform. For instance every users have a login name on all kind of systems, but the way to create a new user can be completely different on Windows or Unix. In this case we can have a provider for Windows, one for OSX, one for Linux&amp;#8230; Puppet knows how to select the best provider based on the facts (the same way you can confine facts to some operating systems, you can confine providers to some operating systems).&lt;/p&gt;

&lt;h2&gt;Looking Types into the eyes&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve written a combination of types/providers for this article. It allows to manage DNS zones and DNS Resource Records for DNS hosting providers (like &lt;a href="http://aws.amazon.com/route53/"&gt;AWS Route 53&lt;/a&gt; or &lt;a href="http://www.zerigo.com/managed-dns"&gt;Zerigo&lt;/a&gt;). To simplify development I based the system on &lt;a href="http://fog.io/1.0.0/dns/"&gt;Fog&lt;/a&gt; DNS providers (you need to have the Fog gem installed to use those types on the agent). The full code of this system is available in my &lt;a href="https://github.com/masterzen/puppet-dns"&gt;puppet-dns github repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This work defines two new Puppet types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;dnszone:&lt;/em&gt; manage a given DNS zone (ie a domain)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;dnsrr: &lt;/em&gt;manage an individual DNS RR (like an A, AAAA, &amp;#8230; record). It takes a name, a value and a type.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Here is how to use it in a manifest:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dns.pp'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;dnszone {
    &amp;quot;planetpuppet.org&amp;quot;:
        ensure =&amp;gt; present,
        email =&amp;gt; &amp;quot;brice@planetpuppet.org&amp;quot;
}

dnsrr {
    &amp;quot;www.planetpuppet.org&amp;quot;:
        type =&amp;gt; &amp;quot;A&amp;quot;,
        value =&amp;gt; &amp;quot;2.4.6.9&amp;quot;,
        ensure =&amp;gt; present,
}
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s focus on the &lt;strong&gt;dnszone&lt;/strong&gt; type, which is the simpler one of this module:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;Puppet::Type.newtype(:dnszone) do
  @doc = &amp;quot;Manage a DNS zone&amp;quot;

  ensurable

  newparam(:name) do
    desc &amp;quot;Zone name, this must be a domain name&amp;quot;

    isnamevar
  end

  newparam(:email) do
    desc &amp;quot;E-mail admin of the zone&amp;quot;
  end

  newparam(:yaml_fog_file) do
    desc &amp;quot;Path to a yaml file containing the fog credentials and provider - ignore if the dnszone puppet provider is not fog&amp;quot;
    defaultto '/etc/puppet/fog.yaml'
  end

  autorequire(:file) do
    [self[:yaml_fog_file]]
  end
end&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;Note, that the dnszone type assumes there is a &lt;code&gt;/etc/puppet/fog.yaml&lt;/code&gt; file that contains Fog DNS options and credentials as a hash encoded in yaml. Refer to the aforementioned github repository for more information and use case.&lt;/p&gt;

&lt;p&gt;Exactly like parser functions, types are defined in ruby, and Puppet can autoload them. Thus types should obey to the Puppet type ruby namespace. That&amp;#8217;s the reason we have to put types in &lt;code&gt;puppet/type/&lt;/code&gt;.  Once again this is ruby metaprogramming (in its all glory), to create a specific internal DSL that helps describe types to Puppet with simple directives (the alternative would have been to define a datastructure which would have been much less practical).&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s dive into the &lt;em&gt;dnszone&lt;/em&gt; type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 1,&lt;/em&gt; we&amp;#8217;re calling the &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt;#newtype method passing, first the type name as a ruby symbol (which should be unique among types), second a block (from &lt;em&gt;line 1 to the end&lt;/em&gt;). The newtype method is imported in &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt; but is in fact defined in &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/metatype/manager.rb"&gt;Puppet::Metatype::Manager&lt;/a&gt;. &lt;em&gt;Newtype&lt;/em&gt; job is to create a new singleton class whose parent is &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt; (or a descendant if needed). Then the given block will be evaluated in class context (this means that the block is executed with &lt;code&gt;self&lt;/code&gt; being the just created class). This singleton class is called &lt;code&gt;Puppet::TypeDnszone&lt;/code&gt; in our case (but you see the pattern).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 2&lt;/em&gt;: we&amp;#8217;re assigning a string to the &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt; class variable &lt;code&gt;@doc&lt;/code&gt;. This will be used to to extract type documentation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 4&lt;/em&gt;: This straight word &lt;code&gt;ensurable&lt;/code&gt;, is a class method in &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt;. So when our type block is evaluated, this method will be called. This methods installs a new special property Ensure. This is a shortcut to automatically manage creation/deletion/existence of the managed resource. This automatically adds support for &lt;code&gt;ensure =&amp;amp;gt; (present|absent)&lt;/code&gt; to your type. The provider still has to manage ensurability, though.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 6:&lt;/em&gt; Here we&amp;#8217;re calling &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt;#newparam. This tells our type that we&amp;#8217;re going to have a parameter called &amp;#8220;name&amp;#8221;. Every resource in Puppet must have a unique key, this key is usually called the name or the title. We&amp;#8217;re giving a block to this newparam method. The job of newparam is to create a new class descending of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt;, and to evaluate the given block in the context of this class (which means in this block self is a singleton class of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt;). &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt; defines a bunch of utility class methods (that becomes apparent directives of our parameter DSL), among those we can find &lt;code&gt;isnamevar&lt;/code&gt; which we&amp;#8217;ve used for the name parameter. This tells Puppet type system that the name parameter is what will be the holder of the unique key of this type. The desc method allows to give some documentation about the parameter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 12&lt;/em&gt;: we&amp;#8217;re defining now the email parameter. And we&amp;#8217;re using the &lt;code&gt;newvalues&lt;/code&gt; class method of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt;. This method defines what possible values can be set to this parameter. We&amp;#8217;re passing a regex that allows any string containing an &amp;#8216;@&amp;#8217;, which is certainly the worst regex to validate an e-mail address :) Puppet will raise an error if we don&amp;#8217;t give a valid value to this parameter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 17&lt;/em&gt;: and again a new parameter. This parameter is used to control Fog behavior (ie give to it your credential and fog provider used). Here we&amp;#8217;re using &lt;code&gt;defaultto&lt;/code&gt;, which means if we don&amp;#8217;t pass a value then the defaultto value will be used.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 22&lt;/em&gt;: there is a possibility for a given resource to auto-require another resource. The same way a file resource can automatically add &amp;#8216;require&amp;#8217; to its path ancestor. In our case, we&amp;#8217;re autorequiring the yaml_fog_file, so that if it is managed by puppet, it will be evaluated before our dnszone resource (otherwise our fog provider might not have its credentials available).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let&amp;#8217;s now see another type which uses some other type DSL directives:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnsrr.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;Puppet::Type.newtype(:dnsrr) do
  @doc = &amp;quot;Manage a DNS Resource Record&amp;quot;

  ensurable

  newparam(:name) do
    desc &amp;quot;RR name, this is usually a domain name, but for PTR resource record it will be an IP address&amp;quot;

    isnamevar
  end

  newproperty(:type) do
    defaultto(:A)
    newvalues(:A, :AAAA, :CNAME, :MX, :SRV, :TXT, :PTR)
  end

  newproperty(:value) do
    isrequired
    validate do |value|
      raise ArgumentError, &amp;quot;Empty values are not allowed&amp;quot; if value == &amp;quot;&amp;quot;
    end
  end

  validate do
    begin
      case self[:type]
      when :A, :AAAA
        IPAddr.new(self[:value])
      end
    rescue
      raise ArgumentError, &amp;quot;Invalid IP address #{self[:value]} for given type #{self[:type]}&amp;quot;
    end
  end

  newparam(:zone) do
    defaultto do
      raise ArgumentError, &amp;quot;No zone defined and name is not a FQDN&amp;quot; unless @resource[:name].include?(&amp;quot;.&amp;quot;)
      @resource[:name].gsub(/^[^.]+\./,'')
    end
  end

  autorequire(:dnszone) do
    [self[:zone]]
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;We&amp;#8217;ll pass over the bits we already covered with the first type, and concentrate on new things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 12&lt;/em&gt;: our &lt;em&gt;dnszone&lt;/em&gt; type contained only parameters. Now it&amp;#8217;s the first time we define a &lt;strong&gt;property&lt;/strong&gt;. A property is exactly like a parameter but is fully managed by Puppet (see the chapter below). A property is an instance of a &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Property&lt;/a&gt; class, which itself inherits from &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt;, which means all the methods we&amp;#8217;ve covered in our first example for parameters are available for properties. This type property is interesting because it defines discrete values. If you try to set something outside of this list of possible values, Puppet will raise an error. Values can be either ruby symbols or strings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 17&lt;/em&gt;: a new property is defined here. With the &lt;code&gt;isrequired&lt;/code&gt; method we tell Puppet that is is indeed necessary to have a value. And the validate methods will store the given validate block so that when Puppet will set the desired value to this property it will execute it. In our case we&amp;#8217;ll report an error if the given value is empty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 24&lt;/em&gt;: here we defined a global validation system. This will be called when all properties will have been assigned a value. This block executes in the instance context of the type, which means that we can access all instance variables and methods of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt; (in particualy the [] method that allows to access parameters/properties values). This allows to perform validation across the boundaries of a given parameter/property.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 25&lt;/em&gt;: finally, we declare a new parameter that references a &lt;em&gt;dnszone&lt;/em&gt;. Note that we use a dynamic &lt;em&gt;defaultto&lt;/em&gt; (with a block), so that we can look up the given resource name and derive our zone from the FQDN. This raises an important feature of the type system: &lt;em&gt;the order of the declarations of the various blocks is important&lt;/em&gt;. Puppet will always respect the declaration order of the various properties when evaluating their values. That means a given property can access a value of another properties defined earlier.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I left managing RR TTL as an exercise to the astute reader :)  Also note we didn&amp;#8217;t cover all the directives the type DSL offers us. Notably, we didn&amp;#8217;t see value munging (which allows to transform a string representation coming from the manifest to an internal (to the type) format). For instance that can be used to transform string IP address to the ruby IPAddr type for later use.  I highly recommend you to browse the default types in the Puppet source distribution and check the various directives used there. You can also read &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt;, &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/property.rb"&gt;Puppet::Property&lt;/a&gt; and &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/type.rb"&gt;Puppet::Type&lt;/a&gt; source code to see the ones we didn&amp;#8217;t cover.&lt;/p&gt;

&lt;h2&gt;Life and death of Properties&lt;/h2&gt;

&lt;p&gt;So, we saw that a &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/parameter.rb"&gt;Puppet::Parameter&lt;/a&gt; is just a holder for the value coming from the manifest. A &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/property.rb"&gt;Puppet::Property&lt;/a&gt; is a parameter that along with the desired value (the one coming from the manifest) contains the current value (the one coming from the managed resource on the host).  The first one is called the &amp;#8220;should&amp;#8221;, and the later one is called the &amp;#8220;value&amp;#8221;. Those innocently are methods of the Puppet::Property object and returns respectively those values.  A property implements the following aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;it can &lt;code&gt;retrieve&lt;/code&gt; a value from the managed resource. This is the operation of asking the real host resource to fetch its value. This is usually performed by delegation to the provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it can report its &lt;code&gt;should&lt;/code&gt; which is the value given in the manifest&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it can be &lt;code&gt;insync?&lt;/code&gt;. This returns true if the retrieved value is equal to the &amp;#8220;should&amp;#8221; value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;and finally it might &lt;code&gt;sync&lt;/code&gt;. Which means to the necessary so that &amp;#8220;insync?&amp;#8221; becomes true. If there is a provider for the given type, this one will be called to take care of the change.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When Puppet manages a resource, it does it with the help of a &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/transaction.rb"&gt;Puppet::Transaction&lt;/a&gt;. The given transaction orders the various properties that are not &lt;em&gt;insync?&lt;/em&gt; to &lt;em&gt;sync&lt;/em&gt;. Of course this is a bit more complex than that, because this is done while respecting resource ordering (the one given by the require/before metaparameter), but also propagating change events (so that service can be restarted and so on), and allowing resources to spawn child resources, etc&amp;#8230;  It&amp;#8217;s perfectly possible to write a type without a provider, as long as all properties used implement their respective retrieve and sync methods. Some of the core types are doing this.&lt;/p&gt;

&lt;h2&gt;Providers&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve seen that properties usually delegate to the providers for managing the underlying real resource. In our example, we&amp;#8217;ll have two providers, one for each defined type.  There are two types of providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prefetch/flush&lt;/li&gt;
&lt;li&gt;per properties&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The &lt;em&gt;per properties&lt;/em&gt; providers needs to implement a getter and a setter for every property of the accompanying type.  When the transaction manipulates a given property its provider getter is called, and later on the setter will be called if the property is not &lt;em&gt;insync?&lt;/em&gt;. It is the responsibility of those setters to flush those values to the physical managed resource.  For some providers it is highly impractical or inefficient to flush on every property value change. To solve this issue, a given provider can be a &lt;em&gt;prefetch/flush&lt;/em&gt; one.  A &lt;em&gt;prefetch/flush&lt;/em&gt; provider implements only two methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prefetch&lt;/code&gt;, which given a list of resources will in one call return a set of provider instances filled with the value fetched from the real resource.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flush&lt;/code&gt; will be called after all values will have been set, and that they can be persisted to the real resource.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The two providers I&amp;#8217;ve written for this article are &lt;em&gt;prefetch/flush&lt;/em&gt; ones, because it was impractical to call Fog for every property.&lt;/p&gt;

&lt;h2&gt;Anatomy of the &lt;em&gt;dnszone&lt;/em&gt; provider&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ll focus only on this provider, and I&amp;#8217;ll leave as an exercise to the reader the analysis of the second one.  Providers, being also ruby extensions, must live in the correct path respecting their ruby namespaces. For our dnszone fog provider, it should be in the &lt;code&gt;puppet/provider/dnszone/fog.rb&lt;/code&gt; file.  Unlike what I did for the types, I&amp;#8217;ll split the provider code in parts so that I can explain them with the context. You can still &lt;a href="https://github.com/masterzen/puppet-dns/blob/master/lib/puppet/provider/dnszone/fog.rb"&gt;browse the whole code&lt;/a&gt;.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone-fog-provider-1.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;Puppet::Type.type(:dnszone).provide :fog, :parent =&amp;gt; Puppet::Provider::Fog do
  desc &amp;quot;Fog provider for DNS zones.&amp;quot;

  confine :feature =&amp;gt; :fog
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;This is how we tell Puppet that we have a new provider for a given type. If we decipher this, we&amp;#8217;re fetching the dnszone type (which returns the singleton class of our dnszone type), and call the class method &amp;#8220;provide&amp;#8221;, passing it a name, some options and a big block. In our case, the provider is called &amp;#8220;fog&amp;#8221;, and our parent should be &lt;a href="https://github.com/masterzen/puppet-dns/blob/master/lib/puppet/provider/fog.rb"&gt;Puppet::Provider::Fog&lt;/a&gt; (which defines common methods for both of our fog providers, and is also a descendant of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/provider.rb"&gt;Puppet::Provider&lt;/a&gt;).  Like for types, we have a &lt;em&gt;desc&lt;/em&gt; class method in &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/provider.rb"&gt;Puppet::Provider&lt;/a&gt; to store some documentation strings.  We also have the confine method. This method will help Puppet choose the correct provider for a given type, ie its suitability. The confining system is managed by &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.6/lib/puppet/provider/confiner.rb"&gt;Puppet::Provider::Confiner&lt;/a&gt;. You can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a fact or puppet settings value, as in: &lt;code&gt;confine :operatingsystem =&amp;amp;gt; :windows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;a file existence: &lt;code&gt;confine :exists =&amp;amp;gt; "/etc/passwd"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;a Puppet &amp;#8220;feature&amp;#8221;, like we did for testing the fog library presence&lt;/li&gt;
&lt;li&gt;an arbitrary boolean expression &lt;code&gt;confine :true =&amp;amp;gt; 2 == 2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;A provider can also be the default for a given fact value. This allows to make sure the correct provider is used for a given type, for instance the apt provider on debian/ubuntu platforms.&lt;/p&gt;

&lt;p&gt;And to finish, a provider might need to call executables on the platform (and in fact most of them do). The Puppet::Provider class defines a shortcut to declare and use those executables easily:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=commands.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;  commands ping =&amp;gt; &amp;quot;/usr/bin/ping&amp;quot;
  ...
  # we can use it later with:
  # ping &amp;quot;www.puppetlabs.com&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s continue our exploration of our &lt;em&gt;dnszone&lt;/em&gt; provider&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone-fog-provider-2.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;  # This creates a bunch of getters/setters for our properties/parameters
  # this is only for prefetch/flush providers
  mk_resource_methods
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;mk_resource_methods&lt;/code&gt; is an handy system that creates a bunch of setters/getters for every parameter/properties for us. Those fills values in the &lt;code&gt;@property_hash hash&lt;/code&gt;.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone-fog-provider-3.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;  def self.prefetch(resources)
    resources.each do |name, resource|
      dns = dns(resource[:yaml_fog_file])
      if found = dns.zones.all.find { |z| z.domain =~ /^#{Regexp.quote(name)}\.?$/ }
        result = { :ensure =&amp;gt; :present }
        result[:email] = found.email if found.respond_to?(:email)
        result[:domain] = found.domain
        resource.provider = new(found, result)
      else
        resource.provider = new(nil, :ensure =&amp;gt; :absent)
      end
      resource.provider.dns = dns
    end
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;The prefetch methods calls &lt;em&gt;fog&lt;/em&gt; to fetch all the DNS zones, and then we match those with the ones managed by Puppet (from the resources hash).&lt;/p&gt;

&lt;p&gt;For each match we instantiate a provider filled with the values coming from the underlying physical resource (in our case fog). For those that don&amp;#8217;t match, we create a provider whose only existing properties is that ensure is absent.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone-fog-provider-4.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;  def flush
    case @property_hash[:ensure] 
    when :absent
      @zone.destroy if @zone
    when :present
      if @properties[:ensure] == :absent
        dns.zones.create(:domain =&amp;gt; @property_hash[:domain], :email =&amp;gt; @property_hash[:email])
      else
        @zone.domain = @property_hash[:domain]
        @zone.email = @property_hash[:email] if @zone.respond_to?(:email)
        @zone.save
      end
    end
    @property_hash.clear
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;Flush does the reverse of prefetch. Its role is to make sure the real underlying resource conforms to what Puppet wants it to be.&lt;/p&gt;

&lt;p&gt;There are 3 possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the desired state is &lt;em&gt;absent&lt;/em&gt;. We thus tell fog to destroy the given zone.&lt;/li&gt;
&lt;li&gt;the desired state is &lt;em&gt;present&lt;/em&gt;, but during prefetch we didn&amp;#8217;t find the zone, we&amp;#8217;re going to tell fog to create it.&lt;/li&gt;
&lt;li&gt;the desired state is &lt;em&gt;present&lt;/em&gt;, and we could find it during prefetch, in which case we&amp;#8217;re just refreshing the fog zone.&lt;/li&gt;
&lt;/ul&gt;


&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone-fog-provider-5.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;  def self.instances
    # I fear we can't find any instances without having access to the fog credentials
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;To my knowledge this is used only for ralsh (puppet resource). The problem is that our provider can&amp;#8217;t know how to access fog until it has a dnszone (which creates a chicken and egg problem :)&lt;/p&gt;

&lt;p&gt;And finally we need to manage the Ensure property which requires our provider to implement: create, destroy and exists?.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1328479.js?file=dnszone-fog-provider-6.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;  def create
    @property_hash[:ensure] = :present
    self.class.resource_type.validproperties.each do |property|
      if val = resource.should(property)
        @property_hash[property] = val
      end
    end
  end

  def destroy
    @property_hash[:ensure] = :absent
  end

  def exists?
    @property_hash[:ensure] != :absent
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt; &lt;/p&gt;

&lt;p&gt;In a &lt;em&gt;prefetch/flush provider&lt;/em&gt; there&amp;#8217;s no need to do more than controlling the ensure value.&lt;/p&gt;

&lt;p&gt;Things to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a provider instance can access its resource with the &lt;code&gt;resource&lt;/code&gt; accessor&lt;/li&gt;
&lt;li&gt;a provider can access the current catalog through its &lt;code&gt;resource.catalog&lt;/code&gt; accessor. This allows as I did in the &lt;code&gt;dnsrr/fog.rb&lt;/code&gt; provider to retrieve a given resource (in this case the &lt;em&gt;dnszone&lt;/em&gt; a given &lt;em&gt;dnsrr&lt;/em&gt; depends to find how to access a given zone through fog).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We just surfaced the provider/type system (if you read everything you might disagree, though).&lt;/p&gt;

&lt;p&gt;For instance we didn&amp;#8217;t review the parsed file provider which is a beast in itself (the Pro Puppet book has a section about it if you want to learn how it works, the Puppet core host type is also a parsed file provider if you need a reference).&lt;/p&gt;

&lt;p&gt;Anyway make sure to read the Puppet core code if you want to know more :) feel free to ask questions about Puppet on the &lt;a href="http://groups.google.com/group/puppet-dev"&gt;puppet-dev mailing list&lt;/a&gt; or on the #puppet-dev irc channel on freenode, where you&amp;#8217;ll find me under the &lt;em&gt;masterzen&lt;/em&gt; nick.&lt;/p&gt;

&lt;p&gt;And finally expect a little bit of time before the next episode, which will certainly cover the Indirector and how to add new terminus (but I first need to find an example, so suggestions are welcome).&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ZMmWHX6vBkU:f1JJsaJzpsY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ZMmWHX6vBkU:f1JJsaJzpsY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=ZMmWHX6vBkU:f1JJsaJzpsY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ZMmWHX6vBkU:f1JJsaJzpsY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ZMmWHX6vBkU:f1JJsaJzpsY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ZMmWHX6vBkU:f1JJsaJzpsY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=ZMmWHX6vBkU:f1JJsaJzpsY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=ZMmWHX6vBkU:f1JJsaJzpsY:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=ZMmWHX6vBkU:f1JJsaJzpsY:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2011/11/02/puppet-extension-point-part-2/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet Extension Points - part 1]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/kHP_P6basRY/" />
    <updated>2011-10-29T12:17:23+02:00</updated>
    <id>http://www.masterzen.fr/2011/10/29/puppet-extension-points-part-1</id>
    
    <category term="puppet" />
    
    <category term="ruby" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;It&amp;#8217;s been a long time since my &lt;a href="http://www.masterzen.fr/2010/11/14/puppet-ssl-explained/"&gt;last blog post&lt;/a&gt;, almost a year. Not that I stopped hacking on Puppet or other things (even though I&amp;#8217;m not as productive as I had been in the past), it&amp;#8217;s just that so many things happened last year (&lt;a href="http://www.daysofwonder.com/memoir44-online"&gt;Memoir&amp;#8217;44&lt;/a&gt; release, architecture work at &lt;a href="http://www.daysofwonder.com/"&gt;Days of Wonder&lt;/a&gt;) that I lost the motivation of maintaining this blog.&lt;/p&gt;

&lt;p&gt;But &lt;em&gt;that&amp;#8217;s over,&lt;/em&gt; I plan to start a series of &lt;strong&gt;Puppet internals articles&lt;/strong&gt;. The first one (yes this one) is devoted to Puppet Extension Points.&lt;/p&gt;

&lt;p&gt;Since a long time, Puppet contains a system to &lt;em&gt;dynamically load ruby fragments&lt;/em&gt; to provide new functionalities both for the client and the master. Among the available extension points you&amp;#8217;ll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manifests functions&lt;/li&gt;
&lt;li&gt;custom facts&lt;/li&gt;
&lt;li&gt;types and providers&lt;/li&gt;
&lt;li&gt;faces&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Moreover, Puppet contains a &lt;a href="http://docs.puppetlabs.com/guides/plugins_in_modules.html"&gt;synchronization mechanism &lt;/a&gt;that allows you to ship your extensions into your manifests modules and those will be replicated automatically to the clients. This system is called &lt;strong&gt;pluginsync&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This first article will first dive into the ruby meta-programming used to create (some of) the extension DSL (not to be confused with the Puppet DSL which is the language used in the manifests). We&amp;#8217;ll talk a lot about DSL and ruby meta programming. If you want to know more on those two topics, I&amp;#8217;ll urge you to read those books:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0321712943"&gt;Domain Specific Languages&lt;/a&gt; - Martin Fowler&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/Metaprogramming-Ruby-Program-Like-Pros/dp/1934356476"&gt;Metaprogramming Ruby: Program Like the Ruby Pros&lt;/a&gt; - Paolo Perrotta&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Anatomy of a simple extension&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s start with the simplest form of extension: &lt;em&gt;Parser Functions&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Functions are extensions of the &lt;em&gt;Puppet Parser&lt;/em&gt;, the entity that reads and analyzes the puppet DSL (ie the manifests). This language contains a structure which is called &amp;#8220;function&amp;#8221;. You already use them a lot, for instance &amp;#8220;include&amp;#8221; or &amp;#8220;template&amp;#8221; are functions.&lt;/p&gt;

&lt;p&gt;When the parser analyzes a given manifest, it detects the use of functions, and later on during the compilation phase the function code is executed and the result may be injected back into the compilation.&lt;/p&gt;

&lt;p&gt;Here is a simple function:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1324327.js?file=basename.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;module Puppet::Parser::Functions
  newfunction(:basename, :type =&amp;gt; :rvalue, :doc =&amp;gt; &amp;lt;&amp;lt;-EOS
Returns the basename of a full pathname. For instance if called 
with '/path/to/myfile.txt' will return 'myfile.txt'.
    EOS
  ) do |args|
    
    raise(Puppet::ParseError, &amp;quot;basename(): Wrong number of arguments given (#{args.size} instead of 1)&amp;quot;) if arguments.size != 1
    
    File.basename(args[0])
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;The given function uses the puppet functions DSL to load the extension code into Puppet core code.  This function is simple and does what its basename shell equivalent does: stripping leading paths in a given filename.  For this function to work you need to drop it in the &lt;code&gt;lib/puppet/parser/functions&lt;/code&gt; directory of your module.  Why is that? It&amp;#8217;s because after all, extensions are written in ruby and integrate into the Puppet ruby namespace. Functions in puppet live in the &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb"&gt;Puppet::Parser::Functions&lt;/a&gt; class, which itself belongs to the Puppet scope.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb"&gt;Puppet::Parser::Functions&lt;/a&gt; class in Puppet core has the task of &lt;em&gt;loading all functions&lt;/em&gt; defined in any &lt;code&gt;puppet/parser/functions&lt;/code&gt; directories it will be able to find in the whole &lt;em&gt;ruby load path&lt;/em&gt;. When Puppet uses a module, the modules&amp;#8217; lib directory is automatically added to the ruby load path.  Later on, when parsing manifests and a function call is detected, the &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb"&gt;Puppet::Parser::Functions&lt;/a&gt; will try to load all the ruby files in all the &lt;code&gt;puppet/parser/functions&lt;/code&gt; directory available in the ruby load path. This last task is done by the Puppet autoloader (available into &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/util/autoload.rb"&gt;Puppet::Util::Autoload&lt;/a&gt;).  Let&amp;#8217;s see how the above code is formed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 1&lt;/em&gt;: this is ruby way to say that this file belongs to the puppet function namespace, so that &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb"&gt;Puppet::Parser::Functions&lt;/a&gt; will be able to load it. In real, we&amp;#8217;re opening the ruby class &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb"&gt;Puppet::Parser::Functions&lt;/a&gt;, and all that will follow will apply to this specific puppet class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 2&lt;/em&gt;: this is where ruby meta-programming is used. Translated to standard ruby, we&amp;#8217;re just calling the &amp;#8220;newfunction&amp;#8221; method. Since we&amp;#8217;re in the Puppet::Parser::Functions class, we in fact are just calling the class method &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb#L38"&gt;Puppet::Parser::Functions#newfunction&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt; We pass to it 4 arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;the function name&lt;/em&gt;, encoded as a symbol. Functions name should be unique in a given environment&lt;/li&gt;
&lt;li&gt;&lt;em&gt;the function type&lt;/em&gt;: either your function is a rvalue (meaning a right-value, an entity that lies on the right side of an assignment operation, so in real English: a function that returns a value), or is not (in which case the function is just a side-effect function not returning any values).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;a documentation string&lt;/em&gt; (here we used a ruby heredoc) which might be extracted later.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;and finally we&amp;#8217;re passing a &lt;em&gt;ruby code block&lt;/em&gt; (from the do on line 5, to the inner end on line 10). This code block won&amp;#8217;t be executed when puppet loads the functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Line 5 to 10&lt;/em&gt;. The body of the methods. When ruby loads the function file on behalf of Puppet, it will happily pass the code block to newfunction. This last one will store the code block for later use, and make it available in the Puppet scope class under the name &lt;strong&gt;function_basename&lt;/strong&gt; (that&amp;#8217;s one of the cool thing about ruby, you can arbitrarily create new methods on classes, objects or even instances).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So let&amp;#8217;s see what happens when puppet parses and executes the following manifest:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1324327.js?file=basename.pp'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;$file = basename('/var/lib/puppet/ssl/certs/mycert.pem')
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;The first thing that happens when compiling manifests is that the Puppet &lt;strong&gt;lexer&lt;/strong&gt; triggers. It will read the manifest content and &lt;em&gt;split it in tokens&lt;/em&gt; that the parser knows. So essentially the above content will be transformed in the following stream of tokens:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1324327.js?file=basename-token-stream.txt'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;VARIABLE(file) EQUALS NAME(basename) LPAREN STRING(...) RPAREN
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;The parser, given this input, will reduce this to what we call an &lt;a href="http://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;Abstract Syntax Tree&lt;/a&gt;. That&amp;#8217;s a memory data structure (usually a tree) that represents the orders to be executed that was derived from the language grammar and the stream of tokens. In our case this will schematically be parsed as:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1324327.js?file=basename-ast.txt'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;AST::VarDef
  AST::Variable(file)
  AST::Function(basename)
    AST::String(...)
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;In turns, when puppet will compile the manifest (ie execute the above AST), this will be equivalent to this ruby operation:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1324327.js?file=basename-call.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;scope.setvar('file', scope.function_basename('...'))&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Remember how &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/functions.rb#L38"&gt;Puppet::Parser::Functions#newfunction&lt;/a&gt; created the &lt;em&gt;function_basename&lt;/em&gt;. At that time I didn&amp;#8217;t really told you the exact truth. In fact &lt;em&gt;newfunction&lt;/em&gt; creates a function in an environment specific object instance (so that functions can&amp;#8217;t leak from one Puppet environment to another, which was one of the problem of 0.25.x). And any given Puppet scope which are instances of &lt;a href="https://github.com/puppetlabs/puppet/blob/2.7.x/lib/puppet/parser/scope.rb"&gt;Puppet::Parser::Scope&lt;/a&gt; when constructed will mix in this environment object, and thus bring to life our shiny function as if it was defined in the scope ruby code itself.&lt;/p&gt;

&lt;h2&gt;Pluginsync&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s talk briefly about the way your modules extensions are propagated to the clients. So far we&amp;#8217;ve seen that functions live in the master, but some other extensions types (like facts or types) essentially live in the client.  Since it would be cumbersome for an admin to replicate all the given extensions to all the clients manually, Puppet offers &lt;em&gt;&lt;a href="http://docs.puppetlabs.com/guides/plugins_in_modules.html"&gt;pluginsync&lt;/a&gt;&lt;/em&gt;, a way to distribute this ruby code to the clients.  It&amp;#8217;s part of every puppet agent run, before asking for a catalog to the master.  The interesting thing (and that happens in a lot of place into Puppet, which always amazes me), is that this pluginsync process is using Puppet itself to perform this synchronization.  Puppet is good at synchronizing remotely and recursively a set of files living on the master. So pluginsync just create a small catalog containing a recursive File resource whose source is the plugins fileserver mount on the master, and the destination the current agent puppet lib directory (which is part of the ruby load path).  Then this catalog is evaluated and the Puppet File resource mechanism does its magic and creates all the files locally, or synchronizes them if they differ.  Finally, the agent loads all the ruby files it synchronized, registering the various extensions it contains, before asking for its host catalog.&lt;/p&gt;

&lt;h2&gt;Wants some facts?&lt;/h2&gt;

&lt;p&gt;The other extension point that you certainly already encountered is adding &lt;a href="http://docs.puppetlabs.com/guides/custom_facts.html"&gt;custom facts&lt;/a&gt;.  A fact is simply a &lt;em&gt;key, value tuple&lt;/em&gt; (both are strings). But we also usually call a fact the method that dynamically produces this tuple.  Let&amp;#8217;s see what it does internally. We&amp;#8217;ll use the following example custom fact:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/1324327.js?file=hardware_platform.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# drop this in a module lib/facter/
Facter.add(&amp;quot;hardware_platform&amp;quot;) do
  setcode do
    %x{/bin/uname -i}.chomp
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;  &lt;/p&gt;

&lt;p&gt;It&amp;#8217;s no secret that Puppet uses &lt;a href="http://puppetlabs.com/puppet/related-projects/facter/"&gt;Facter&lt;/a&gt; a lot. When a puppet agent wants a catalog, the first thing it does is asking &lt;em&gt;Facter&lt;/em&gt; for a set of facts pertaining to the current machine. Then those facts are sent to the master when the agent asks for a catalog. The master &lt;strong&gt;injects those facts as variables&lt;/strong&gt; in the root scope when compiling the manifests.&lt;/p&gt;

&lt;p&gt;So, facts are executed in the agent. Those are &lt;em&gt;pluginsync&amp;#8217;ed&lt;/em&gt; as explained above, then loaded into the running process.&lt;/p&gt;

&lt;p&gt;When that happens the add method of the &lt;a href="https://github.com/puppetlabs/facter/blob/1.6.1/lib/facter.rb"&gt;Facter class&lt;/a&gt; is called. The block defined between &lt;em&gt;line 2 and 6&lt;/em&gt; is then executed in the &lt;a href="https://github.com/puppetlabs/facter/blob/1.6.1/lib/facter/util/resolution.rb"&gt;Facter::Util::Resolution&lt;/a&gt; context. So the &lt;a href="https://github.com/puppetlabs/facter/blob/1.6.1/lib/facter/util/resolution.rb#L116"&gt;Facter::Util::Resolution#setcode&lt;/a&gt; method will be called and the block between &lt;em&gt;line 3 and 5&lt;/em&gt; will be stored for later use.&lt;/p&gt;

&lt;p&gt;This Facter::Util::Resolution instance holding our fact code will be in turn stored in the facts collection under the name of the fact (see &lt;em&gt;line 2&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Why is it done in this way? Because not all facts can run on every hosts. For instance our above facts does not work on Windows platform. So we should use facter way of &lt;em&gt;confining&lt;/em&gt; our facts to architectures on which we know they&amp;#8217;ll work.
Thus Facter defines a set of methods like &amp;#8220;confine&amp;#8221; that can be called during the call of Facter#add (just add those outside of the setcode block).  Those methods will modify how the facts collection will be executed later on. It wouldn&amp;#8217;t have been possible to confine our facts if we stored the whole Facter#add block and called it directly at fact resolution, hence the use of this two-steps system.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;And, that&amp;#8217;s all folks for the moment. Next episode will explain types and providers inner workings. I also plan an episode about other Puppet internals, like the parser, catalog evaluation, and/or the indirector system.&lt;/p&gt;

&lt;p&gt;Tell me (though comments here or through my twitter handle &lt;a href="http://twitter.com/#!/_masterzen_"&gt;@&lt;em&gt;masterzen&lt;/em&gt;&lt;/a&gt;) if you&amp;#8217;re interested in this kind of Puppet stuff, or if there are any specific topics you&amp;#8217;d like me to cover :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=kHP_P6basRY:hi2PNXdJBfo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=kHP_P6basRY:hi2PNXdJBfo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=kHP_P6basRY:hi2PNXdJBfo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=kHP_P6basRY:hi2PNXdJBfo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=kHP_P6basRY:hi2PNXdJBfo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=kHP_P6basRY:hi2PNXdJBfo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=kHP_P6basRY:hi2PNXdJBfo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=kHP_P6basRY:hi2PNXdJBfo:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=kHP_P6basRY:hi2PNXdJBfo:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2011/10/29/puppet-extension-points-part-1/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet SSL explained]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/lrU8ibeEyhU/" />
    <updated>2010-11-14T17:57:14+01:00</updated>
    <id>http://www.masterzen.fr/2010/11/14/puppet-ssl-explained</id>
    
    <category term="puppet" />
    
    <category term="crypto" />
    
    <category term="ssl" />
        
    <content type="html">&lt;p&gt;The &lt;a href="http://groups.google.com/group/puppet-users"&gt;puppet-users&lt;/a&gt; or &lt;a href="http://projects.puppetlabs.com/projects/puppet/wiki/IRC_Channel"&gt;#puppet freenode irc channel&lt;/a&gt; is full of questions or people struggling about the &lt;a href="http://projects.puppetlabs.com/projects/puppet/wiki/Certificates_And_Security"&gt;puppet SSL PKI&lt;/a&gt;. To my despair, there are also people wanting to completely get rid of any security.&lt;/p&gt;

&lt;p&gt;While I don&amp;#8217;t advocate the &lt;em&gt;live happy, live without security&lt;/em&gt; motto of some puppet users (and I really think a corporate firewall is only one layer of defense among many, not the ultimate one), I hope this blog post will help them shoot themselves in their foot :)&lt;/p&gt;

&lt;p&gt;I really think SSL or the X509 PKI is simple once you grasped its underlying concept. If you want to know more about SSL, I really think everybody should read Eric Rescola&amp;#8217;s excellent &amp;#8221;&lt;a href="http://www.amazon.com/dp/0201615983"&gt;SSL and TLS: Designing and Building Secure Systems&lt;/a&gt;&amp;#8221;.&lt;/p&gt;

&lt;p&gt;I myself had to deal with SSL internals and X509 PKI while I implemented a java secured network protocol in a previous life, including a cryptographic library.&lt;/p&gt;

&lt;h2&gt;Purpose of Puppet SSL PKI&lt;/h2&gt;

&lt;p&gt;The current puppet security layer has 3 aims:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;authenticate any node to the master (so that no rogue node can get a catalog from your master)&lt;/li&gt;
&lt;li&gt;authenticate the master on any node (so that your nodes are not tricked into getting a catalog from a rogue master).&lt;/li&gt;
&lt;li&gt;prevent communication eavesdropping between master and nodes (so that no rogue users can grab configuration secrets by listening to your traffic, which is useful in the cloud)&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;A notion of PKI&lt;/h2&gt;

&lt;p&gt;PKI means: &lt;a href="http://en.wikipedia.org/wiki/Public_key_infrastructure"&gt;Public Key Infrastructure&lt;/a&gt;. But whats this?&lt;/p&gt;

&lt;p&gt;A PKI is a framework of computer security that allows authentication of individual components based on public key cryptography. The most known system is the &lt;a href="http://en.wikipedia.org/wiki/X.509"&gt;x509&lt;/a&gt; one which is used to protect our current web.&lt;/p&gt;

&lt;p&gt;A public key cryptographic system works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every components of the system has a secret key (known as the &lt;em&gt;private key&lt;/em&gt;) and a &lt;em&gt;public key&lt;/em&gt; (this one can be shared with other participant of the system). The public and private keys are usually bound by a cryptographic algorithm.&lt;/li&gt;
&lt;li&gt;authentication of any component is done with a simple process: a component signs a message with its own private key. The receiver can authenticate the message (ie know the message comes from the original component) by validating the signature. To do this, only the public key is needed.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;There are different public/private key pair cryptosystem, the most known ones are RSA, DSA or those based on Elliptic Curve cryptography.&lt;/p&gt;

&lt;p&gt;Usually it is not good that all participants of the system must know each other to communicate. So most of the current PKI system use a hierarchical validation system, where all the participant in the system must only know one of the parent in the hierarchy to be able to validate each others.&lt;/p&gt;

&lt;h2&gt;X509 PKI&lt;/h2&gt;

&lt;p&gt;X509 is an ITU-T standard of a PKI. It is the base of the SSL protocol authentication that puppet use. This standard specifies certificates, certificate revocation list, authority and so on&amp;#8230;&lt;/p&gt;

&lt;p&gt;A given X509 certificate contains several information like those:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serial number (which is unique for a given CA)&lt;/li&gt;
&lt;li&gt;Issuer (who created this certificate, in puppet this is the CA)&lt;/li&gt;
&lt;li&gt;Subject (who this certificate represents, in puppet this is the node certname or fqdn)&lt;/li&gt;
&lt;li&gt;Validity (valid from, expiration date)&lt;/li&gt;
&lt;li&gt;Public key (and what kind of public key algorithm has been used)&lt;/li&gt;
&lt;li&gt;Various extensions (usually what this certificate can be used for,&amp;#8230;)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You can check &lt;a href="http://www.ietf.org/rfc/rfc1422"&gt;RFC1422&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;The certificate is usually the &lt;a href="http://en.wikipedia.org/wiki/Distinguished_Encoding_Rules"&gt;DER encoding&lt;/a&gt; of the &lt;a href="http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One"&gt;ASN.1&lt;/a&gt; representation of those informations, and is usually stored as &lt;a href="http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail"&gt;PEM&lt;/a&gt; for consumption.&lt;/p&gt;

&lt;p&gt;A given X509 certificate is signed by what we call a &lt;a href="http://en.wikipedia.org/wiki/Certificate_authority"&gt;Certificate Authority&lt;/a&gt; (CA for short). A CA is an infrastructure that can sign new certificates. Anyone sharing the public key of the CA can validate that a given certificate has been validated by the CA.&lt;/p&gt;

&lt;p&gt;Usually X509 certificate embeds a RSA public key with an exponent of 0x100001 (see below).  Along with a certificate, you need a private key (usually also PEM-encoded).&lt;/p&gt;

&lt;p&gt;So basically the X509 system works with the following principle: CA are using their own private keys to sign components certificates, it is the CA role to sign only trusted component certificates. The trust is usually established out-of-bound of the signing request.&lt;/p&gt;

&lt;p&gt;Then every component in the system knows the CA certificate (ie public key). If one component gets a message from another component, it checks the attached message signature with the CA certificate. If that validates, then the component is authenticated. Of course the component should also check the certificate validity, if the certificate has been revoked (from OCSP or a given CRL), and finally that the certificate subject matches who the component pretends to be (usually this is an hostname validation against some part of the certificate &lt;em&gt;Subject&lt;/em&gt;)&lt;/p&gt;

&lt;h2&gt;RSA system&lt;/h2&gt;

&lt;p&gt;Most of X509 certificate are based on the RSA cryptosystem, so let&amp;#8217;s see what it is.&lt;/p&gt;

&lt;p&gt;The RSA cryptosystem is a public key pair system that works like this:&lt;/p&gt;

&lt;h3&gt;Key Generation&lt;/h3&gt;

&lt;p&gt;To generate a RSA key, we chose two prime number &lt;em&gt;p&lt;/em&gt; and &lt;em&gt;q&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We compute &lt;em&gt;n=pq&lt;/em&gt;. We call &lt;em&gt;n&lt;/em&gt; the modulus.&lt;/p&gt;

&lt;p&gt;We compute &lt;em&gt;φ&lt;/em&gt;(&lt;em&gt;pq&lt;/em&gt;) = (&lt;em&gt;p&lt;/em&gt; − 1)(&lt;em&gt;q&lt;/em&gt; − 1).&lt;/p&gt;

&lt;p&gt;We chose e so that e&amp;gt;1 and e&amp;lt;&lt;em&gt;φ&lt;/em&gt;(&lt;em&gt;pq&lt;/em&gt;) (e and &lt;em&gt;φ&lt;/em&gt;(&lt;em&gt;pq&lt;/em&gt;) must be coprime). &lt;em&gt;e&lt;/em&gt; is called the exponent. It usually is 0x10001 because it greatly simplifies the computations later (and you know what I mean if you already implemented this :)).&lt;/p&gt;

&lt;p&gt;Finally we compute &lt;em&gt;d=e&lt;sup&gt;-1&lt;/sup&gt; mod((p-1)(q-1))&lt;/em&gt;. This will be our secret key. Note that it is not possible to get d from only e (and since p and q are never kept after the computation this works).&lt;/p&gt;

&lt;p&gt;In the end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;e&lt;/em&gt; and &lt;em&gt;n&lt;/em&gt; form the public key&lt;/li&gt;
&lt;li&gt;&lt;em&gt;d&lt;/em&gt; is our private key&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Encryption&lt;/h3&gt;

&lt;p&gt;So the usual actors when describing cryptosystems are Alice and Bob. Let&amp;#8217;s use them.&lt;/p&gt;

&lt;p&gt;Alice wants to send a message &lt;em&gt;M&lt;/em&gt; to Bob. Alice knows Bob&amp;#8217;s public key &lt;em&gt;(e,n)&lt;/em&gt;. She transform &lt;em&gt;M&lt;/em&gt; in a number &amp;lt; &lt;em&gt;n &lt;/em&gt;(this is called padding) that we&amp;#8217;ll call &lt;em&gt;m&lt;/em&gt;, then she computes: _c = m&lt;sup&gt;e&lt;/sup&gt; . mod(n) _&lt;/p&gt;

&lt;h3&gt;Decryption&lt;/h3&gt;

&lt;p&gt;When Bob wants to decrypt the message, he computes with his private key &lt;em&gt;d&lt;/em&gt;: &lt;em&gt;m = c&lt;sup&gt;d&lt;/sup&gt; . mod(n)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;Signing message&lt;/h3&gt;

&lt;p&gt;Now if Alice wants to sign a message to Bob. She first computes a hash of her message called &lt;em&gt;H&lt;/em&gt;, then she computes: &lt;em&gt;s = H&lt;sup&gt;(d&lt;/sup&gt; mod n). &lt;/em&gt;So she used her own private key. She sends both the message and the signature.&lt;/p&gt;

&lt;p&gt;Bob, then gets the message computes &lt;em&gt;H &lt;/em&gt;and computes &lt;em&gt;h&amp;#8217; = H&lt;sup&gt;(e&lt;/sup&gt; mod n) &lt;/em&gt;with Alice&amp;#8217;s public key. If &lt;em&gt;h&amp;#8217; = h, &lt;/em&gt;then only Alice could have sent it.&lt;/p&gt;

&lt;h3&gt;Security&lt;/h3&gt;

&lt;p&gt;What makes this scheme work is the fundamental that finding p and q from n is a hard problem (understand for big values of n, it would take far longer than the validity of the message). This operation is called factorization. Current certificate are numbers containing  2048 bits, which roughly makes a 617 digits number to factor.&lt;/p&gt;

&lt;h3&gt;Want to know more?&lt;/h3&gt;

&lt;p&gt;Then there are a couple of books really worth reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://amazon.com/dp/0471117099"&gt;Applied Cryptography&lt;/a&gt; - Bruce Schneier&lt;/li&gt;
&lt;li&gt;&lt;a href="http://amazon.com/dp/0849385237"&gt;Handbook of Applied Cryptography&lt;/a&gt; - Alfred Menezes, Paul van Oorschot, Scott Vanstone&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;How does this fit in SSL?&lt;/h2&gt;

&lt;p&gt;So SSL (which BTW means Secure Socket Layer) and now TLS (SSL successor) is a protocol that aims to provide security of communications between two peers. It is above the transport protocol (usually TCP/IP) in the OSI model. It does this by using &lt;a href="http://en.wikipedia.org/wiki/Symmetric-key_algorithm"&gt;symmetric encryption&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Message_authentication_code"&gt;message authentication code&lt;/a&gt; (MAC for short). The standard is (now) described in &lt;a href="http://tools.ietf.org/html/rfc5246"&gt;RFC5246&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It works by first performing an handshake between peers. Then all the remaining communications are encrypted and tamperproof.&lt;/p&gt;

&lt;p&gt;This handshake contains several phases (some are optional):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client and server finds the best encryption scheme and MAC from the common list supported by both the server and the clients (in fact the server choses).&lt;/li&gt;
&lt;li&gt;The server then sends its certificate and any intermediate CA that the client might need&lt;/li&gt;
&lt;li&gt;The server may ask for the client certificate. The client may send its certificate.&lt;/li&gt;
&lt;li&gt;Both peers may validate those certificates (against a common CA, from the CRL, etc&amp;#8230;)&lt;/li&gt;
&lt;li&gt;They then generate the session keys. The client generates a random number, encrypts it with the server public key. Only the server can decrypt it. From this random number, both peers generate the symmetric key that will be used for encryption and decryption.&lt;/li&gt;
&lt;li&gt;The client may send a signed message of the previous handshake message. This way the server can verify the client knows his private key (this is the client validation). This phase is optional.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;After that, each message is encrypted with the generated session keys using a symmetric cipher, and validated with an agreed on MAC. Usual symmetric ciphers range from RC4 to AES. A symmetric cipher is used because those are usually way faster than any asymmetric systems.&lt;/p&gt;

&lt;h2&gt;Application to Puppet&lt;/h2&gt;

&lt;p&gt;Puppet defines it&amp;#8217;s own Certificate Authority that is usually running on the master (it is possible to run a CA only server, for instance if you have more than one master).&lt;/p&gt;

&lt;p&gt;This CA can be used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate new certificate for a given client out-of-bound&lt;/li&gt;
&lt;li&gt;sign a new node that just sent his Certificate Signing Request&lt;/li&gt;
&lt;li&gt;revoke any signed certificate&lt;/li&gt;
&lt;li&gt;display certificate fingerprints&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;What is important to understand is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every node knows the CA certificate. &lt;em&gt;This allows to check the validity of the master from a node&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The master doesn&amp;#8217;t need the node certificate&lt;/em&gt;, since it&amp;#8217;s sent by the client when connecting. It just need to make sure the client knows the private key and this certificate has been signed by the master CA.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;It is also important to understand that when your master is running behind an Apache proxy (for Passenger setups) or Nginx proxy (ie some mongrel setups):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The proxy is the SSL endpoint. It does all the validation and authentication of the node.&lt;/li&gt;
&lt;li&gt;Traffic between the proxy and the master happens in clear&lt;/li&gt;
&lt;li&gt;The master knows the client has been authenticated because the proxy adds an HTTP header that says so (usually &lt;em&gt;X-Client-Verify &lt;/em&gt;for Apache/Passenger).&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When running with webrick, webrick runs inside the puppetmaster process and does all this internally. Webrick tells the master internally if the node is authenticated or not.&lt;/p&gt;

&lt;p&gt;When the master starts for the 1st time, it generates its own CA certificate and private key, initializes the CRL and generates a special certificate which I will call the &lt;em&gt;server certificate&lt;/em&gt;. This certificate will be the one used in the SSL/TLS communication as the server certificate that is later sent to the client. This certificate subject will be the current master FQDN. If your master is also a client of itself (ie it runs a puppet agent), I recommend using this certificate as the client certificate.&lt;/p&gt;

&lt;p&gt;The more important thing is that this server certificate advertises the following extension:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;X509v3 Subject Alternative Name:
&lt;/span&gt;&lt;span class='line'&gt;                DNS:puppet, DNS:$fqdn, DNS:puppet.$domain&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;What this means is that this certificate will validate if the connection endpoint using it has any name matching puppet, the current fqdn or puppet in the current domain.&lt;/p&gt;

&lt;p&gt;By default a client tries to connect to the &amp;#8221;&lt;em&gt;puppet&lt;/em&gt;&amp;#8221; host (this can be changed with &amp;#8211;server which I don&amp;#8217;t recommend and is usually the source of most SSL trouble).&lt;/p&gt;

&lt;p&gt;If your DNS system is well behaving, the client will connect to &lt;em&gt;puppet.$domain&lt;/em&gt;. If your DNS contains a CNAME for puppet to your &lt;em&gt;real master fqdn&lt;/em&gt;, then when the client will validate the server certificate it will succeed because it will compare &amp;#8220;puppet&amp;#8221; to one of those DNS: entries in the aforementioned certificate extension. BTW, if you need to change this list, you can use the &amp;#8211;certdnsname option (note: this can be done afterward, but requires to re-generate the server certificate).&lt;/p&gt;

&lt;p&gt;The whole client process is the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;if the client runs for the 1st time, it generates a &lt;a href="http://en.wikipedia.org/wiki/Certificate_signing_request"&gt;Certificate Signing Request&lt;/a&gt; and a private key. The former is an x509 certificate that is self-signed.&lt;/li&gt;
&lt;li&gt;the client connects to the master (at this time the client is not authenticated) and sends its CSR, it will also receives the CA certificate and the CRL in return.&lt;/li&gt;
&lt;li&gt;the master stores locally the CSR&lt;/li&gt;
&lt;li&gt;the administrator checks the CSR and can eventually sign it (this process can be automated with autosigning). I strongly suggest verifying certificate fingerprint at this stage.&lt;/li&gt;
&lt;li&gt;the client is then waiting for his signed certificate, which the master ultimately sends&lt;/li&gt;
&lt;li&gt;All next communications will use this client certificate. Both the master and client will authenticate each others by virtue of sharing the same CA.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;Tips and Tricks&lt;/h2&gt;

&lt;h3&gt;Troubleshooting SSL&lt;/h3&gt;

&lt;h4&gt;Certificate content&lt;/h4&gt;

&lt;p&gt;First you can check any certificate content with this:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/700124.js?file=puppet-ssl.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;openssl x509 -text -in /var/lib/puppet/ssl/certs/puppet.pem

Certificate:    Data:
        Version: 3 (0x2)
        Serial Number: 2 (0x2)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: CN=Puppet CA: master.domain.com
        Validity
            Not Before: Nov 13 14:29:23 2010 GMT
            Not After : Nov 12 14:29:23 2015 GMT
        Subject: CN=server.domain.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:be:11:7d:0e:32:4d:c4:da:40:7d:7a:17:30:2c:
                    00:c4:c5:a8:c7:91:31:21:71:50:ef:07:77:79:1a:
                    07:d6:57:d4:4d:e0:01:b3:78:73:ec:84:dd:71:30:
                    62:cd:e5:26:fd:54:46:da:e3:3b:be:3b:05:9a:87:
                    44:9a:5e:b4:41:b7:15:de:20:1d:9d:26:50:44:bc:
                    e6:64:67:d1:93:ee:3f:20:a6:86:0e:11:5c:de:b1:
                    da:e5:fb:b5:f1:e1:e9:2e:14:39:47:f2:b8:a4:40:
                    84:89:18:86:5a:df:3b:68:a4:64:7f:a9:99:93:60:
                    29:e8:fe:d5:a3:e0:6e:ba:4b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            Netscape Comment: 
                Puppet Ruby/OpenSSL Generated Certificate
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier: 
                F4:FA:5A:03:EF:D5:0C:C3:B6:A0:35:47:D1:49:98:74:D4:09:B4:A9
            X509v3 Key Usage: 
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
            X509v3 Subject Alternative Name: 
                DNS:puppet, DNS:puppet.domain.com
    Signature Algorithm: sha1WithRSAEncryption
        70:e3:7c:04:c4:e1:66:07:db:5c:58:d9:64:bb:0a:e7:55:4c:
        93:9d:61:0a:2a:a6:3f:de:aa:98:a9:e5:40:45:40:87:62:78:
        d3:af:a7:01:a7:b9:ca:ee:b2:44:ff:02:be:8b:54:aa:65:45:
        0b:94:2a:56:fa:1d:67:fe:cd:52:09:29:89:bc:2f:4f:6b:30:
        cb:de:6a:01:35:43:74:1e:d6:14:2e:f0:43:ac:38:e9:7c:ec:
        2c:e6:b8:50:8c:15:07:2f:72:35:82:7f:ad:9c:3a:4f:a7:5c:
        d6:e8:87:f9:19:20:1f:8f:2e:2e:28:4c:9f:ea:d7:26:5e:c5:
        18:57&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h4&gt;Simulate a SSL connection&lt;/h4&gt;

&lt;p&gt;You can know more information about a SSL error by simulating a client connection. Log in the trouble node and:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/700124.js?file=connect.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# this simulates how a puppet agent will connect
openssl s_client -host puppet -port 8140 -cert /path/to/ssl/certs/node.domain.com.pem -key /path/to/ssl/private_keys/node.domain.com.pem -CAfile /path/to/ssl/certs/ca.pem

# outputs:

CONNECTED(00000004)
depth=1 /CN=Puppet CA: master.domain.com
verify return:1
depth=0 /CN=macbook.local
verify return:1
---
Certificate chain
 0 s:/CN=macbook.local
   i:/CN=Puppet CA: master.domain.com
 1 s:/CN=Puppet CA: master.domain.com
   i:/CN=Puppet CA: master.domain.com
---
Server certificate
-----BEGIN CERTIFICATE-----
MIICgjCCAeugAwIBAgIBAjANBgkqhkiG9w0BAQUFADAjMSEwHwYDVQQDDBhQdXBw
ZXQgQ0E6IG1hY2Jvb2subG9jYWwwHhcNMTAxMTEzMTQyOTIzWhcNMTUxMTEyMTQy
OTIzWjAYMRYwFAYDVQQDDA1tYWNib29rLmxvY2FsMIGfMA0GCSqGSIb3DQEBAQUA
A4GNADCBiQKBgQC+EX0OMk3E2kB9ehcwLADExajHkTEhcVDvB3d5GgfWV9RN4AGz
eHPshN1xMGLN5Sb9VEba4zu+OwWah0SaXrRBtxXeIB2dJlBEvOZkZ9GT7j8gpoYO
EVzesdrl+7Xx4ekuFDlH8rikQISJGIZa3ztopGR/qZmTYCno/tWj4G66SwIDAQAB
o4HQMIHNMDgGCWCGSAGG+EIBDQQrFilQdXBwZXQgUnVieS9PcGVuU1NMIEdlbmVy
YXRlZCBDZXJ0aWZpY2F0ZTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT0+loD79UM
w7agNUfRSZh01Am0qTALBgNVHQ8EBAMCBaAwJwYDVR0lBCAwHgYIKwYBBQUHAwEG
CCsGAQUFBwMCBggrBgEFBQcDBDAuBgNVHREEJzAlggZwdXBwZXSCDW1hY2Jvb2su
bG9jYWyCDHB1cHBldC5sb2NhbDANBgkqhkiG9w0BAQUFAAOBgQBw43wExOFmB9tc
WNlkuwrnVUyTnWEKKqY/3qqYqeVARUCHYnjTr6cBp7nK7rJE/wK+i1SqZUULlCpW
+h1n/s1SCSmJvC9PazDL3moBNUN0HtYULvBDrDjpfOws5rhQjBUHL3I1gn+tnDpP
p1zW6If5GSAfjy4uKEyf6tcmXsUYVw==
-----END CERTIFICATE-----
subject=/CN=puppet.domain.com
issuer=/CN=Puppet CA: master.domain.com
---
No client certificate CA names sent
---
SSL handshake has read 1794 bytes and written 1656 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 1024 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: DB29414CCB1E094675238999C8C00AF3173F441030C44A67D773648E83D76F75
    Session-ID-ctx: 
    Master-Key: 92430ADC9E52BA22023D5E37DED7D9A274B9E5E461CB46C47F1E9B14BE1956B7615FADC2319D9DA091784EC91ED777B3
    Key-Arg   : None
    Start Time: 1289747911
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Check the last line of the report, it should say &amp;#8220;Verify return code: 0 (ok)&amp;#8221; if both the server and client authenticated each others. Check also the various information bits to see what certificate were sent. In case of error, you can learn about the failure by looking that the verification error message.&lt;/p&gt;

&lt;h4&gt;ssldump&lt;/h4&gt;

&lt;p&gt;Using &lt;a href="http://www.rtfm.com/ssldump/"&gt;ssldump&lt;/a&gt; or &lt;a href="http://www.wireshark.org/"&gt;wireshark&lt;/a&gt; you can also learn more about ssl issues. For this to work, it is usually needed to force the cipher to use a simple cipher like RC4 (and also ssldump needs to know the private keys if you want it to decrypt the application data).&lt;/p&gt;

&lt;h4&gt;Some known issues&lt;/h4&gt;

&lt;p&gt;Also, in case of SSL troubles make sure your master isn&amp;#8217;t using a different $ssldir than what you are thinking. If that happens, it&amp;#8217;s possible your master is using a different dir and has regenerated its CA. If that happens no one node can connect to it anymore. This can happen if you upgrade a master from gem when it was installed first with a package (or the reverse).&lt;/p&gt;

&lt;p&gt;If you regenerate a host, but forgot to remove its cert from the CA (with puppetca &amp;#8211;clean), the master will refuse to sign it. If for any reason you need to fully re-install a given node without changing its fqdn, either use the previous certificate or clean this node certificate (which will automatically revoke the certificate for your own security).&lt;/p&gt;

&lt;h4&gt;Looking to the CRL content:&lt;/h4&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/700124.js?file=crl.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# it is possible to get the content of the CRL:
openssl crl -text -in /var/lib/puppet/ssl/ca/ca_crl.pem 

Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: /CN=Puppet CA: master.domain.com
        Last Update: Nov 14 15:47:42 2010 GMT
        Next Update: Nov 13 15:47:42 2015 GMT
        CRL extensions:
            X509v3 CRL Number: 
                1
Revoked Certificates:
    Serial Number: 03
        Revocation Date: Nov 14 15:47:42 2010 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: 
                Key Compromise
    Signature Algorithm: sha1WithRSAEncryption
        a2:cb:cf:d6:95:34:5d:7e:aa:95:cf:cd:7f:ea:1a:da:b0:f4:
        15:1f:df:03:28:64:b7:e0:a9:2d:53:df:b7:25:05:64:3e:15:
        08:2a:02:6d:42:7f:ad:37:f1:8f:72:66:f5:ed:f0:0b:59:d2:
        9f:16:77:18:eb:dc:dd:2e:f0:c4:ea:80:51:cf:35:43:ed:cd:
        7d:64:c0:43:dc:85:13:0f:5f:e2:88:78:a9:fc:bf:c3:a5:c6:
        e2:0e:8e:9d:95:1e:19:63:03:bb:26:89:9c:52:78:d6:a0:79:
        82:1d:2c:44:15:7d:75:42:52:4e:6a:a8:e5:d7:40:c5:b8:4a:
        24:d2&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Notice how the certificate serial number 3 has been revoked.&lt;/p&gt;

&lt;h3&gt;Fingerprinting&lt;/h3&gt;

&lt;p&gt;Since puppet 2.6.0, it is possible to fingerprint certificates. If you manually sign your node, it is important to make sure you are signing the correct node and not a rogue system trying to pretend it is some genuine node.  To do this you can get the certificate fingerprint of a node by running puppet agent &amp;#8211;fingerprint, and when listing on the master the various CSR, you can make sure both fingerprint match.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/700124.js?file=fingerprint.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# on the node
puppet agent --test --fingerprint               
notice: 14:45:FD:59:F2:CC:83:62:4C:4A:D2:2A:37:4F:12:96

# on the master
puppetca --list node.domain.com --fingerprint
node.domain.com 14:45:FD:59:F2:CC:83:62:4C:4A:D2:2A:37:4F:12:96
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h3&gt;Dirty Trick&lt;/h3&gt;

&lt;p&gt;Earlier I was saying that when running with a reverse proxy in front of Puppet, this one is the SSL endpoint and it propagates the authentication status to Puppet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I strongly don&amp;#8217;t recommend implementing the following. This will compromise your setup security.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can be used to severely remove Puppet security for instance you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make so that every nodes are authenticated for the server by always returning the correct header&lt;/li&gt;
&lt;li&gt;make so that nodes are authenticated based on their IP addresses or fqdn&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You can even combine this with a mono-certificate deployment. The idea is that every node share the same certificate. This can be useful when you need to provision tons of short-lived nodes. Just generate on your master a certificate:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/700124.js?file=generate.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# Generate a certificate and private key to be used for a node
puppetca --generate node.domain.com

notice: node.domain.com has a waiting certificate requestnotice: Signed certificate request for node.domain.com
notice: Removing file Puppet::SSL::CertificateRequest node.domain.com at '/tmp/master/ssl/ca/requests/node.domain.com.pem'
notice: Removing file Puppet::SSL::CertificateRequest node.domain.com at '/tmp/master/ssl/certificate_requests/node.domain.com.pem'
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;You can then use those generated certificate (which will end up in /var/lib/puppet/ssl/certs and /var/lib/puppet/private_keys) in a pre-canned $ssldir, provided you rename it to the local fqdn (or symlink it).  Since this certificate is already signed by the CA, it is valid. The only remaining issue is that the master will serve the catalog of this certificate certname. I proposed a patch to fix this, this patch will be part of 2.6.3. In this case the master will serve the catalog of the given connecting node and not the connecting certname.  Of course you need a relaxed auth.conf:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/700124.js?file=relaxed-auth.conf'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;...
path ~ ^/catalog/([^/]+)$
method find
allow $1
allow node.domain.com
...&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Caveat: I didn&amp;#8217;t try, but it should work. YMMV :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Of course if you follow this and shoot yourself in the foot, I can&amp;#8217;t be held responsible for any reasons, you are warned&lt;/strong&gt;. Think twice and maybe thrice before implementing this.&lt;/p&gt;

&lt;h3&gt;Multiple CA or reusing an existing CA&lt;/h3&gt;

&lt;p&gt;This goes beyond the object of this blog post, and I must admit I never tried this. Please refer to: &lt;a href="http://projects.puppetlabs.com/projects/puppet/wiki/Multiple_Certificate_Authorities"&gt;Managing Multiple Certificate Authorities &lt;/a&gt; and  &lt;a href="http://projects.puppetlabs.com/projects/puppet/wiki/Puppet_Scalability"&gt;Puppet Scalability&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If there is one: &lt;strong&gt;security is necessary when dealing with configuration management&lt;/strong&gt;. We don&amp;#8217;t want any node to trust rogue masters, we don&amp;#8217;t want masters to distribute sensitive configuration data to rogue nodes. We even don&amp;#8217;t want a rogue user sharing the same network to read the configuration traffic. Now that you fully understand SSL, and the X509 PKI, I&amp;#8217;m sure you&amp;#8217;ll be able to design some clever attacks against a Puppet setup :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=lrU8ibeEyhU:jzQyVuPeG48:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=lrU8ibeEyhU:jzQyVuPeG48:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=lrU8ibeEyhU:jzQyVuPeG48:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=lrU8ibeEyhU:jzQyVuPeG48:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=lrU8ibeEyhU:jzQyVuPeG48:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=lrU8ibeEyhU:jzQyVuPeG48:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=lrU8ibeEyhU:jzQyVuPeG48:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=lrU8ibeEyhU:jzQyVuPeG48:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=lrU8ibeEyhU:jzQyVuPeG48:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2010/11/14/puppet-ssl-explained/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Benchmarking puppetmaster stacks]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/tkadiVx4LoM/" />
    <updated>2010-10-18T22:48:49+02:00</updated>
    <id>http://www.masterzen.fr/2010/10/18/benchmarking-puppetmaster-stacks</id>
    
    <category term="puppet" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;It&amp;#8217;s been a long time since my last &lt;a href="http://www.masterzen.fr/2010/03/21/more-puppet-offloading/"&gt;puppet blog post about file content offloading&lt;/a&gt;. Two &lt;a href="http://www.puppetlabs.com/community/puppet-camp/puppet-camp-faq/"&gt;puppetcamps&lt;/a&gt; even passed (more on the last one in a next blog article). A &lt;a href="http://www.puppetlabs.com/misc/download-options/"&gt;new major puppet release &lt;/a&gt;(2.6) was even released, addressing lots of performance issues (including the file streaming patch I contributed).&lt;/p&gt;

&lt;p&gt;In this new major version, I contributed a &lt;em&gt;new 3rd party executable &lt;/em&gt;(available in the ext/ directory in the source tree) that allows to simulate concurrent nodes hammering a puppetmaster. This tool is called &lt;strong&gt;puppet-load&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Rationale&lt;/h2&gt;

&lt;p&gt;I created this tool for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to be able to &lt;em&gt;benchmark and compare several ruby interpreter&lt;/em&gt; (like comparing JRuby against MRI)&lt;/li&gt;
&lt;li&gt;I wanted to be able to &lt;em&gt;benchmark and compare several deployements &lt;/em&gt;solutions (like passenger against mongrel)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;There was already a testing tool (called &lt;em&gt;puppet-test&lt;/em&gt;) that could do that. Unfortunately puppet-test had the following issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No REST support besides some never merged patches I contributed, which render it moot to test 0.25 or 2.6 :(&lt;/li&gt;
&lt;li&gt;based on a forking process models, so simulating many clients is not resource friendly&lt;/li&gt;
&lt;li&gt;it consumes the master response and fully unserializes it creating puppet internals objects, which takes plenty of RAM and time, penalizing the concurrency.&lt;/li&gt;
&lt;li&gt;no useful metrics, except the time the operation took (which was in my test mostly dominated by the unserialization of the response)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Based on those issues, I crafted from scratch a tool that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is able to impose an &lt;em&gt;high concurrency&lt;/em&gt; to a puppetmaster, because it is based on EventMachine (no threads or processes are harmed in this program)&lt;/li&gt;
&lt;li&gt;is &lt;em&gt;lightweight&lt;/em&gt; because it doesn&amp;#8217;t consume puppet responses&lt;/li&gt;
&lt;li&gt;is able to gather some (useful or not) &lt;em&gt;metrics&lt;/em&gt; and &lt;em&gt;aggregates&lt;/em&gt; them&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Caveats&lt;/h2&gt;

&lt;p&gt;For the moment, puppet-load is still very new and only supports catalog compilations for a single node (even though it simulates many clients in parallel requesting this catalog). I just released a patch to support multiple node catalogs. I also plan to support file sourcing in the future.&lt;/p&gt;

&lt;p&gt;So far, since puppet-load exercise a puppetmaster in such a hard way, achieving concurrencies nobody has seen on production puppetmasters, we were able to find and fix half a dozen threading race condition bugs in the puppet code (some have been fixed in 2.6.1 and 2.6.2, the others will soon be fixed).&lt;/p&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;The first thing to do is to generate a certificate and its accompanying private key:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/633063.js?file=GenerateCertificates.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;$ puppet ca --generate puppet-load.domain.com
notice: puppet-load.domain.com has a waiting certificate requestnotice: Signed certificate request for puppet-load.domain.com
notice: Removing file Puppet::SSL::CertificateRequest puppet-load.domain.com at '/var/lib/puppet/ssl/ca/requests/puppet-load.domain.com.pem'
notice: Removing file Puppet::SSL::CertificateRequest puppet-load.domain.com at '/var/lib/puppet/ssl/certificate_requests/puppet-load.domain.com.pem'
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Then modify your auth.conf (or create one if you don&amp;#8217;t have one) to allow puppet-load to compile catalos. Unfortunately until &lt;a href="http://projects.puppetlabs.com/issues/5020"&gt;#5020&lt;/a&gt; is merged, the puppetmaster will use the client certname as the node to compile instead of the given URI. Let&amp;#8217;s pretend your master has the patch #5020 applied (this is a one-liner).&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/633063.js?file=auth.conf%20modification'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;...
path ~ ^/catalog/([^/]+)$
      method find
      allow $1
      allow puppet-load.domain.com

...&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Next, we need the facts of the client we&amp;#8217;ll simulate. Puppet-load will overwrite the &amp;#8216;fqdn&amp;#8217;, &amp;#8216;hostname&amp;#8217; and &amp;#8216;domain&amp;#8217; facts with values inferred from the current node name.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/633063.js?file=facts.yaml'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;--- !ruby/object:Puppet::Node::Facts 
expiration: 2010-04-23 12:49:53.184962 +02:00
name: node1.domain.com
values: 
  processor3: Intel(R) Xeon(R) CPU           L5320  @ 1.86GHz
  kernelmajversion: &amp;quot;2.6&amp;quot;
  kernelversion: 2.6.26
  ipaddress_eth0: 172.16.1.123
  clientversion: 0.25.4
  rubysitedir: /usr/local/lib/site_ruby/1.8
  netmask_eht0: 255.255.255.0
  ps: ps -ef
  lsbdistcodename: lenny
  hardwareisa: unknown
  lsbdistrelease: 5.0.4
  uniqueid: 007f0101
  netmask_bond0_1: 255.255.255.224
  :_timestamp: Fri Apr 23 12:19:53 +0200 2010
  hostname: node1
  kernelrelease: 2.6.26-2-amd64
  kernel: Linux
  uptime_seconds: &amp;quot;2172404&amp;quot;
  facterversion: 1.5.6
  interfaces: eth0
  macaddress_eth0: 00:11:22:33:44:55
  is_virtual: &amp;quot;false&amp;quot;
 &lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Then launch puppet-load against a puppet master:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/633063.js?file=puppet-load-cli.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;$ ./ext/puppet-load.rb --debug --node node1.domain.com --server puppetmaster.domain.com --facts facts.yaml --concurrency 1 --repeat 2 --cert puppet-load.domain.com.pem --key puppet-load.domain.com.pem
debug: reading facts from: facts.yaml
debug: starting client 0 for node1.domain.com
debug: Client 0 finished successfully
debug: starting client 1 for node1.domain.com
debug: Client 1 finished successfully
2 requests finished in 0.489089 s
0 requests failed
Availability: 100.00 %

Time (s):
    min: 0.128136 s
    max: 0.35894 s
    average: 0.243538 s
    median: 0.243538 s

Concurrency: 1.00
Transaction Rate (tps): 4.09 t/s

Received bytes: 1.98 KiB
Throughput: 0.00396 MiB/s
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;If we try with an higher concurrency (here my master is running under webrick with a 1 resource catalog, so compilations are extremely fast):&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/633063.js?file=gistfile4.txt'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;$ ./ext/puppet-load.rb --debug --node node1.domain.com --server puppetmaster.domain.com --facts facts.yaml --concurrency 6 --repeat 1 --cert puppet-load.domain.com.pem --key puppet-load.domain.com.pem
debug: reading facts from: facts.yaml
debug: starting client 0 for node1.domain.com
debug: starting client 1 for node1.domain.com
debug: starting client 2 for node1.domain.com
debug: starting client 3 for node1.domain.com
debug: starting client 4 for node1.domain.com
debug: starting client 5 for node1.domain.com
debug: Client 1 finished successfully
debug: Client 5 finished successfully
debug: Client 4 finished successfully
debug: Client 3 finished successfully
debug: Client 2 finished successfully
debug: Client 0 finished successfully
6 requests finished in 0.654225 s
0 requests failed
Availability: 100.00 %

Time (s):
    min: 0.626235 s
    max: 0.653096 s
    average: 0.639550333333333 s
    median: 0.638822 s

Concurrency: 5.87
Transaction Rate (tps): 9.17 t/s

Received bytes: 5.95 KiB
Throughput: 0.00888 MiB/s
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;It returns a bunch of informations. First if you ran it in debug mode, it would have printed when it would start simulated clients (up to the given concurrency) and when it receives the response.&lt;/p&gt;

&lt;p&gt;Then it displays some important information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;availability %: which is the percent of non-error response it received&lt;/li&gt;
&lt;li&gt;min and max request time&lt;/li&gt;
&lt;li&gt;average and median request time (this can be used to see if the master served clients in a fair way)&lt;/li&gt;
&lt;li&gt;real concurrency: how many clients the master was able to serve in parallel&lt;/li&gt;
&lt;li&gt;transaction rate: how many compilation per seconds the master was able to perform (I expect this number to vary in function of applied concurrency)&lt;/li&gt;
&lt;li&gt;various transfer metrics like throughput and catalog size transferred: this can be useful to understand the amount of information transferred to every clients (hint: puppet 2.6 and puppet-load both support http compression)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;At last puppetcamp, Jason Wright from Google, briefly talked about puppet-load (thanks Jason!). It was apparently already helpful to diagnose performance issues in his External Node Tool classifier.&lt;/p&gt;

&lt;p&gt;If you also use puppet-load, and/or have ideas on how to improve it, please let me know!
If you have interesting results to share like comparison of various puppet master stacks, let me know!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tkadiVx4LoM:ExXV1OutCKg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tkadiVx4LoM:ExXV1OutCKg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=tkadiVx4LoM:ExXV1OutCKg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tkadiVx4LoM:ExXV1OutCKg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tkadiVx4LoM:ExXV1OutCKg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tkadiVx4LoM:ExXV1OutCKg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=tkadiVx4LoM:ExXV1OutCKg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tkadiVx4LoM:ExXV1OutCKg:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=tkadiVx4LoM:ExXV1OutCKg:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2010/10/18/benchmarking-puppetmaster-stacks/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[The definitive recipe for Wordpress Gengo to WPML conversion]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/8-djGIxqfuA/" />
    <updated>2010-07-31T16:48:27+02:00</updated>
    <id>http://www.masterzen.fr/2010/07/31/the-definitive-recipe-for-wordpress-gengo-to-wpml-conversion</id>
    
    <category term="sql" />
    
    <category term="system administration" />
    
    <category term="war stories" />
    
    <category term="wordpress" />
        
    <content type="html">&lt;p&gt;The &lt;a href="http://blog.daysofwonder.com"&gt;Days of Wonder News Center&lt;/a&gt; is running &lt;a href="http://wordpress.org"&gt;Wordpress&lt;/a&gt; which until a couple of days used &lt;a href="http://wordpress.org/extend/plugins/gengo/"&gt;Gengo&lt;/a&gt; for multilingual stuff. Back when we started using &lt;em&gt;Wordpress&lt;/em&gt; for our news, we wanted to be able to have those in three (and maybe more) languages.&lt;/p&gt;

&lt;p&gt;At that time (in 2007, wordpress 2.3), only &lt;em&gt;Gengo&lt;/em&gt; was available.
During the last years, &lt;em&gt;Gengo&lt;/em&gt; was unfortunately not maintained anymore, and it was difficult to upgrade Wordpress to new versions.&lt;/p&gt;

&lt;p&gt;Recently we took the decision to upgrade our &lt;em&gt;Wordpress&lt;/em&gt; installation, and at the same time ditch &lt;em&gt;Gengo&lt;/em&gt; and start over using &lt;a href="http://wpml.org/"&gt;WPML&lt;/a&gt;, which is actively maintained (and looks superior to Gengo).&lt;/p&gt;

&lt;p&gt;So, I started thinking about the conversion, then looked on the web and  found how to convert posts, with the help of those two blog posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.bernawebdesign.ch/byteblog/2009/08/15/migrating-wordpress-from-gengo-to-wpml/"&gt;Migrating Wordpress from Gengo to WPML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.pietvanoostrum.com/en/wordpress/converting-wordpress-from-gengo-to-wpml/"&gt;Converting Wordpress from Gengo to WPML - part 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Those two posts were invaluable for the conversion of posts, but unfortunately nobody solved the conversion of translated categories&amp;#8230; until I did :)&lt;/p&gt;

&lt;p&gt;So here is the most complete recipe to convert from Gengo 2.5 to WPML 1.8, with updated and working SQL requests.&lt;/p&gt;

&lt;h2&gt;Pre-requisites&lt;/h2&gt;

&lt;p&gt;You might want to stop the traffic to your blog during all this procedure. One way to do that is to return an HTTP error code 503 by modifying your Apache/Nginx/Whatever configuration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log-in as an administrator in the Wordpress back-end, and deactivate Gengo.&lt;/li&gt;
&lt;li&gt;Install WPML 1.8, and activates it to create the necessary tables. I had to massage WPML a little bit to let it create the tables, YMMV.&lt;/li&gt;
&lt;li&gt;In the WPML settings, define the same languages as in Gengo (in my case English (primary), French and German)&lt;/li&gt;
&lt;li&gt;Finish the WPML configuration.&lt;/li&gt;
&lt;li&gt;If you had a define(WP_LANG,&amp;#8230;) in your wordpress config, get &lt;em&gt;rid of it&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;Converting Posts&lt;/h2&gt;

&lt;p&gt;Connect to your MySQL server and issue the following revised SQL requests (thanks for the above blog posts for them):&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/502282.js?file=Convert%20Posts%20from%20Gengo%20to%20WPML.sql'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;-- update translations of posts
UPDATE wp_icl_translations wpml
LEFT JOIN wp_post2lang gengo ON gengo.post_id = wpml.element_id
LEFT JOIN wp_languages gengoLang ON gengoLang.language_id = gengo.language_id
SET wpml.language_code = gengoLang.code
WHERE wpml.element_type ='post_post';

-- Link translations to their primary posts
CREATE TEMPORARY TABLE wpml2 SELECT * FROM wp_icl_translations WHERE element_type = 'post_post';

UPDATE wp_icl_translations AS wpml
SET trid = (
    SELECT MIN(wpml2.trid)
    FROM wpml2, wp_post2lang AS gengo, wp_post2lang AS gengo2
    WHERE wpml.element_id = gengo.post_id
    AND gengo.translation_group = gengo2.translation_group
    AND wpml2.element_id = gengo2.post_id
   )
WHERE element_type = 'post_post'
   AND (SELECT translation_group
            FROM wp_post2lang AS gengo
            WHERE gengo.post_id = element_id) &amp;gt; 0;
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Converting Pages&lt;/h2&gt;

&lt;p&gt;This is the same procedure, except we track &amp;#8216;post_page&amp;#8217; instead of &amp;#8216;post_post&amp;#8217;:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/502282.js?file=Convert%20Pages%20from%20Gengo%20to%20WPML.sql'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;-- add page translations
UPDATE wp_icl_translations wpml
LEFT JOIN wp_post2lang gengo ON gengo.post_id = wpml.element_id
LEFT JOIN wp_languages gengoLang ON gengoLang.language_id = gengo.language_id
SET wpml.language_code = gengoLang.code
WHERE wpml.element_type ='post_page';

-- link translation to the primary page
CREATE TEMPORARY TABLE wpml3 SELECT * FROM wp_icl_translations WHERE element_type = 'post_page';

UPDATE wp_icl_translations AS wpml
SET trid = (
    SELECT MIN(wpml3.trid)
    FROM wpml3, wp_post2lang AS gengo, wp_post2lang AS gengo2
    WHERE wpml.element_id = gengo.post_id
    AND gengo.translation_group = gengo2.translation_group
    AND wpml3.element_id = gengo2.post_id
   )
WHERE element_type = 'post_page'
   AND (SELECT translation_group
            FROM wp_post2lang AS gengo
            WHERE gengo.post_id = element_id) &amp;gt; 0;
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Category conversion&lt;/h2&gt;

&lt;p&gt;This part is a little bit tricky. In Gengo, we translated the categories without creating new categories, but in WPML we have to create new categories that would be translations of a primary category.
To do this, I created the following SQL procedure that simplifies the creation of a translated category:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/502282.js?file=SQL%20Procedure%20to%20create%20a%20translated%20category.sql'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;-- note: you might have to increase MySQL thread stack
-- to allow this procedure to run

delimiter //
CREATE PROCEDURE transcat(cat VARCHAR(255), slug VARCHAR(255), lang CHAR(2), orig VARCHAR(255), parentid INT)
BEGIN

  -- insert a new term
  INSERT INTO `wp_terms` (`name`,`slug`,`term_group`) VALUES (cat,slug,'0');
  SELECT @lid:=LAST_INSERT_ID();

  -- insert a new taxonomy
  IF parentid THEN
    INSERT INTO `wp_term_taxonomy` (`term_id`,`taxonomy`,`description`,`parent`,`count`) VALUES (@lid,'category','',parentid,'0');
  ELSE
    INSERT INTO `wp_term_taxonomy` (`term_id`,`taxonomy`,`description`,`parent`,`count`) VALUES (@lid,'category','','0','0');
  END IF;

  SELECT @taxid:=LAST_INSERT_ID();

  -- find the category in the primary language (orig)
  SELECT @groupid:=trid 
  FROM wp_icl_translations tr 
  INNER JOIN wp_term_taxonomy tax ON tax.term_taxonomy_id=tr.element_id 
  INNER JOIN wp_terms t ON t.term_id = tax.term_id 
  WHERE tr.element_type='tax_category' and t.name=orig;

  -- finally insert the translation
  INSERT INTO 
  `wp_icl_translations`(`trid`,`element_type`,`element_id`,`language_code`,`source_language_code`)
   VALUES (@groupid,'tax_category',@taxid,lang,'en');

END
//
-- back to normal delimiter
delimiter ;

&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Then we need to create translated categories with this procedure (this can be done with the Wordpress admin interface, but if you have many categories it is simpler to do this with a bunch of SQL statements):&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/502282.js?file=Convert%20some%20categories.sql'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;-- make sure we use utf8
set names 'utf8';

-- let's insert French categories

-- let's insert the 'Wordpress' category which is a translation of the 'Wordpress' english one:
call transcat(&amp;quot;Wordpress @fr&amp;quot;, &amp;quot;wordpress-fr&amp;quot;, &amp;quot;fr&amp;quot;, &amp;quot;Wordpress&amp;quot;, NULL);

-- let's insert a translation of a category
call transcat(&amp;quot;Programmation&amp;quot;, &amp;quot;programmation&amp;quot;, &amp;quot;fr&amp;quot;, &amp;quot;Programming&amp;quot;, NULL);

-- Get the id of this translation to translate child categories
SELECT @progid:=term_id FROM wp_terms WHERE name='Programmation';

-- Insert a child translation
call transcat(&amp;quot;Tests Unitaires&amp;quot;, &amp;quot;test-unitaires&amp;quot;, &amp;quot;fr&amp;quot;, &amp;quot;Unit Testing&amp;quot;, @progid);

&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Bind translated categories to translated posts&lt;/h2&gt;

&lt;p&gt;And this is the last step, we need to make sure our posts translations have the correct translated categories (for the moment they use the English primary categories).&lt;/p&gt;

&lt;p&gt;To do this, I created the following SQL request:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/502282.js?file=Bind%20French%20posts%20translations%20to%20French%20categories.sql'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;-- Get the list of French posts translations
SELECT group_concat(p.id) 
FROM wp_posts p 
INNER JOIN wp_icl_translations tr ON tr.element_id=p.id 
WHERE tr.element_type='post_post' 
AND tr.language_code='fr' 
GROUP BY element_type;

-- this produces this kind of output:
-- 9,13,14,22,24,34,35,42,47,52

-- Bind french translations to french categories
UPDATE wp_term_relationships tr
INNER JOIN wp_term_taxonomy tax ON tax.term_taxonomy_id=tr.term_taxonomy_id
INNER JOIN wp_terms t ON t.term_id = tax.term_id
INNER JOIN wp_icl_translations tra ON tra.element_id = tax.term_taxonomy_id
INNER JOIN wp_icl_translations tra2 ON tra.trid = tra2.trid AND tra2.language_code = 'fr'
INNER JOIN wp_term_taxonomy tax2 ON tax2.term_taxonomy_id=tra2.element_id
INNER JOIN wp_terms t2 ON t2.term_id = tax2.term_id
SET tr.term_taxonomy_id=tax2.term_taxonomy_id, tax2.count=tax2.count+1
WHERE tra.element_type='tax_category' 
AND tax.taxonomy = 'category' 
AND tr.object_id in (9,13,14,22,24,34,35,42,47,52);

&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;The request is in two parts. The first one will list all the French translations posts IDs that we will report in the second request to update the categories links.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=8-djGIxqfuA:8RlO8TZK4HM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=8-djGIxqfuA:8RlO8TZK4HM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=8-djGIxqfuA:8RlO8TZK4HM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=8-djGIxqfuA:8RlO8TZK4HM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=8-djGIxqfuA:8RlO8TZK4HM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=8-djGIxqfuA:8RlO8TZK4HM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=8-djGIxqfuA:8RlO8TZK4HM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=8-djGIxqfuA:8RlO8TZK4HM:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=8-djGIxqfuA:8RlO8TZK4HM:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2010/07/31/the-definitive-recipe-for-wordpress-gengo-to-wpml-conversion/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[More Puppet Offloading]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/x71qXOlDfyU/" />
    <updated>2010-03-21T16:56:58+01:00</updated>
    <id>http://www.masterzen.fr/2010/03/21/more-puppet-offloading</id>
    
    <category term="nginx" />
    
    <category term="programming" />
    
    <category term="puppet" />
    
    <category term="ruby" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;&lt;a href="http://reductivelabs.com/products/puppet/"&gt;Puppet&lt;/a&gt; really shines at configuration management, but there are some things it is not good at, for instance file sourcing of large files, or managing deep hierarchies.&lt;/p&gt;

&lt;p&gt;Fortunately, most of this efficiency issues will be addressed in a subsequent major version (thanks to &lt;a href="http://projects.reductivelabs.com/issues/3396"&gt;some of&lt;/a&gt; &lt;a href="http://projects.reductivelabs.com/issues/2929"&gt;my patches&lt;/a&gt; and other refactorings).&lt;/p&gt;

&lt;p&gt;Meanwhile it is interesting to work-around those bugs. Since most of us are running our masters as part of a more complete stack and not isolated, we can leverage the power of this stack to address some of the issues.&lt;/p&gt;

&lt;p&gt;In this article, I&amp;#8217;ll expose two techniques to help your overloaded masters to serve more and more clients.&lt;/p&gt;

&lt;h2&gt;Offloading file sourcing&lt;/h2&gt;

&lt;p&gt;I already talked about offloading file sourcing in a &lt;a href="http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality/"&gt;previous blog post about puppet memory consumption&lt;/a&gt;. Here the idea is to prevent our puppetmasters to read the whole content of files in memory at once to serve them. Most of the installation of puppetmasterd out there are behind an http reverse proxy of some sort (ie Apache or Nginx).&lt;/p&gt;

&lt;p&gt;The idea is that file serving is an activity that a small static server is better placed to do than puppet itself (that might change when &lt;a href="http://projects.reductivelabs.com/issues/3373"&gt;#3373&lt;/a&gt; will be fully addressed). Note: I produced &lt;a href="http://groups.google.com/group/puppet-dev/t/f9ffe87357c2ba38"&gt;an experimental patch pending review&lt;/a&gt; to stream puppet file sourcing on the client side, which this tip doesn&amp;#8217;t address.&lt;/p&gt;

&lt;p&gt;So I did implement this in &lt;a href="http://www.nginx.org"&gt;Nginx&lt;/a&gt; (which is my favorite http server of course, but that can be ported to any other webserver quite easily, which is an exercise left to the reader):&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/339342.js?file='&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;server {
    listen 8140;

    ssl                     on;
    ssl_session_timeout     5m;
    ssl_certificate         /var/lib/puppet/ssl/certs/master.pem;
    ssl_certificate_key     /var/lib/puppet/ssl/private_keys/master.pem;
    ssl_client_certificate  /var/lib/puppet/ssl/ca/ca_crt.pem;
    ssl_crl                 /var/lib/puppet/ssl/ca/ca_crl.pem;
    ssl_verify_client       optional;

    root                    /etc/puppet;

    # make sure we serve everything
    # as raw
    types { }
    default_type application/x-raw;

    # those locations are for the &amp;quot;production&amp;quot; environment
    # update according to your configuration

    # serve static file for the [files] mountpoint
    location /production/file_content/files/ {
        # it is advisable to have some access rules here
        allow   172.16.0.0/16;
        deny    all;

        alias /etc/puppet/files/;
    }

    # serve modules files sections
    location ~ /production/file_content/[^/]+/files/ {
        # it is advisable to have some access rules here
        allow   172.16.0.0/16;
        deny    all;

        root /etc/puppet/modules;

        # rewrite /production/file_content/module/files/file.txt
        # to /module/file.text
        rewrite ^/production/file_content/([^/]+)/files/(.+)$  $1/$2 break;
    }

    # ask the puppetmaster for everything else
    location / {
        proxy_pass          http://puppet-production;
        proxy_redirect      off;
        proxy_set_header    Host             $host;
        proxy_set_header    X-Real-IP        $remote_addr;
        proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header    X-Client-Verify  $ssl_client_verify;
        proxy_set_header    X-SSL-Subject    $ssl_client_s_dn;
        proxy_set_header    X-SSL-Issuer     $ssl_client_i_dn;
        proxy_buffer_size   16k;
        proxy_buffers       8 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
        proxy_read_timeout  65;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;And if you use multiple module paths (for instance to separate common modules to other modules), it is still possible to use this trick with some use of &lt;a href="http://wiki.nginx.org/NginxHttpCoreModule#try_files"&gt;nginx try_files&lt;/a&gt; directive.&lt;/p&gt;

&lt;p&gt;The try_files directive allows puppet to try several physical path (the first matching one will be served), and if none match you can use the generic location that proxies to the master which certainly will know what to do.&lt;/p&gt;

&lt;p&gt;Something that can be useful would be to create a small script to generate the nginx config from your fileserver.conf and puppet.conf. Since mine is pretty easy, I did it manually.&lt;/p&gt;

&lt;h2&gt;Optimize Catalog Compilation&lt;/h2&gt;

&lt;p&gt;The normal process of puppet is to contact the &lt;em&gt;puppetmaster&lt;/em&gt; at some time interval asking for a catalog. The catalog is a byproduct of the compilation of the parsed manifests in which are injected the node facts. This operation takes some times depending on the manifest complexity and the server capacity or current load.&lt;/p&gt;

&lt;p&gt;Most of the time an host requires a catalog while the &lt;em&gt;manifests didn&amp;#8217;t change at all&lt;/em&gt;. In my own infrastructure I rarely change my manifests once a kind of host become stable (I might do a change every week at most when in production).&lt;/p&gt;

&lt;p&gt;Since 0.25, puppet is now fully &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;RESTful&lt;/a&gt;, that means to get a catalog &lt;em&gt;puppetd&lt;/em&gt; contacts the master under its SSL protected links and asks for this url:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/339348.js?file='&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;GET /production/catalog/node.daysofwonder.com?facts=&amp;lt;encodedfacts&amp;gt;&amp;amp;facts_format=b64_zlib_yaml HTTP/1.1&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;In return the puppetmaster responds by a json-encoded catalog.
The actual compilation of a catalog for one of my largest host takes about 4s (excluding storeconfigs). During this 4s one ruby thread inside the master is using the CPU. And this is done once every 30 minutes, even if the manifests don&amp;#8217;t change.&lt;/p&gt;

&lt;p&gt;What if we could compile only when something changes? This would really free our masters!&lt;/p&gt;

&lt;p&gt;Since puppet uses HTTP, it is easy to add a front-most HTTP cache in front of our master to actually cache the catalog the first time it is compiled and serve this one on the subsequent requests.&lt;/p&gt;

&lt;p&gt;Although we can do it with any HTTP Cache (ie Varnish), this is really easy to add this with Nginx (which is already running in my own stack):&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/339353.js?file='&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;
# define a proxy cache called 'puppetcache'
# with an in-memory zone of 10MiB (increase this number if you want to be able to cache
# more keys)
# the cache disk path should be on the same filesystem as the proxy_temp_path
proxy_cache_path  /var/cache/nginx/cache  levels=1:2   keys_zone=puppetcache:10m;

server {

    ... normal nginx for puppet config...

    proxy_set_header    Host             $host;
    proxy_set_header    X-Real-IP        $remote_addr;
    proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header    X-Client-Verify  $ssl_client_verify;
    proxy_set_header    X-SSL-Subject    $ssl_client_s_dn;
    proxy_set_header    X-SSL-Issuer     $ssl_client_i_dn;
    proxy_buffer_size   16k;
    proxy_buffers       8 32k;
    proxy_busy_buffers_size    64k;
    proxy_temp_file_write_size 64k;
    proxy_read_timeout  65;

    # we handle catalog differently
    # because we want to cache them
    location /production/catalog {
        proxy_pass          http://puppet-production;
        proxy_redirect      off;

        # it is a good thing to actually restrict who
        # can ask for a catalog (especially for cached
        # catalogs)
        allow 172.16.10.0/24;
        allow 127.0.0.0/8;
        deny all;

        # where to cache contents
        proxy_cache         puppetcache;
    
        # we cache content by catalog host
        # we could also use $args to take into account request
        # facts, but those change too often (ie uptime or memory)
        # to be really usefull
        proxy_cache_key     $uri;

        # define how long to cache response
    
        # normal catalogs will be cached 2 weeks
        proxy_cache_valid  200 302 301 2w;

        # errors are not cached long
        proxy_cache_valid  500 403 1m;
    
        # the rest is cached a little bit
        proxy_cache_valid  any 30m;             
    }

    # catch all location for other terminii
    location / {
        proxy_pass          http://puppet-production;
        proxy_redirect      off;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Puppet currently doesn&amp;#8217;t return any http caching headers (ie Cache-Control or Expires), so we use nginx ability to cache despite it (see proxy_cache_valid). Of course I have a &lt;a href="http://github.com/masterzen/puppet/tree/features/http-catalog-cache"&gt;custom puppet branch&lt;/a&gt; that introduces a new parameter called &lt;em&gt;&amp;#8211;catalog_ttl&lt;/em&gt; which allows puppet to set those cache headers.&lt;/p&gt;

&lt;p&gt;One thing to note is that the &lt;em&gt;cache expiration won&amp;#8217;t coincide with when you change your manifests&lt;/em&gt;. So we need some ways to purge the cache when you deploy new manifests.&lt;/p&gt;

&lt;p&gt;With Nginx this can be done with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removing the nginx cache directory: rm -rf /var/cache/nginx/cache &amp;amp;&amp;amp; killall -HUP nginx&lt;/li&gt;
&lt;li&gt;selectively purge with: the &lt;a href="http://github.com/FRiCKLE/ngx_cache_purge"&gt;Nginx proxy cache purge module&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;It&amp;#8217;s easy to actually add one of those methods to any &lt;em&gt;svn hook&lt;/em&gt; or &lt;em&gt;git post-receive hook&lt;/em&gt; so that deploying manifests actually purge the cache.&lt;/p&gt;

&lt;p&gt;Note: I think that ReductiveLabs has some plan to add catalog compilation caching directly to Puppet (which would make sense). This method is the way to go before this features gets added to Puppet. I have no doubt that caching inside Puppet will be much better than outside caching, mainly because Puppet would be able to expire the cache when the manifests change.&lt;/p&gt;

&lt;p&gt;There a few caveats to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;any host with a valid certificate can request another cached catalog, unlike with the normal puppetmaster which makes sure to serve catalogs only to the correct host. It&amp;#8217;s something that can be a problem for some configurations&lt;/li&gt;
&lt;li&gt;if your manifests rely on &amp;#8220;dynamic&amp;#8221; facts (like uptime or free memory), obviously you shouldn&amp;#8217;t cache the catalog at all.&lt;/li&gt;
&lt;li&gt;the above nginx configuration doesn&amp;#8217;t include the facts as part of the cache key. That means the catalog won&amp;#8217;t be re-generated when any facts change and the cached catalog will always be served. If that&amp;#8217;s an issue, you need to purge the cache when the host itself change.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I should also mention that caching is certainly not the panacea of reducing the master load.&lt;/p&gt;

&lt;p&gt;Some other people are using clever methods to smooth out master load. One notable example is the &lt;a href="http://www.devco.net/archives/2010/03/17/scheduling_puppet_with_mcollective.php"&gt;MCollective puppet scheduler&lt;/a&gt;, &lt;a href="http://twitter.com/ripienaar"&gt;R.I Pienaar&lt;/a&gt; has written. In essence he wrote a &lt;em&gt;puppet run scheduler&lt;/em&gt; running on top of &lt;a href="http://code.google.com/p/mcollective/"&gt;MCollective&lt;/a&gt; that schedule puppet runs (triggered through MCollective) when the master load is appropriate. This allows for the best use of the host running the master.&lt;/p&gt;

&lt;p&gt;If you also have some tricks or tips for running puppet, do not hesitate to contact me (I&amp;#8217;m masterzen on freenode&amp;#8217;s #puppet or &lt;a href="http://twitter.com/_masterzen_"&gt;@&lt;em&gt;masterzen&lt;/em&gt;&lt;/a&gt; on twitter).&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=x71qXOlDfyU:5t1OXC0i-1A:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=x71qXOlDfyU:5t1OXC0i-1A:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=x71qXOlDfyU:5t1OXC0i-1A:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=x71qXOlDfyU:5t1OXC0i-1A:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=x71qXOlDfyU:5t1OXC0i-1A:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=x71qXOlDfyU:5t1OXC0i-1A:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=x71qXOlDfyU:5t1OXC0i-1A:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=x71qXOlDfyU:5t1OXC0i-1A:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=x71qXOlDfyU:5t1OXC0i-1A:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2010/03/21/more-puppet-offloading/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Puppet Memory Usage - not a fatality]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/tVVnJ3c2cc0/" />
    <updated>2010-01-28T22:43:33+01:00</updated>
    <id>http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality</id>
    
    <category term="nginx" />
    
    <category term="programming" />
    
    <category term="puppet" />
    
    <category term="ruby" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;As every reader of this blog certainly know, I&amp;#8217;m a big fan of &lt;a href="http://reductivelabs.com/products/"&gt;Puppet,&lt;/a&gt; using it in production on &lt;a href="http://www.daysofwonder.com"&gt;Days of Wonder&lt;/a&gt; servers, up to the point I used to contribute regularly bug fixes and new features (not that I stopped, it&amp;#8217;s just that my spare time is a scarce resource nowadays).&lt;/p&gt;

&lt;p&gt;Still, I think there are some issues in term of scalability or resource consumption (CPU or memory), for which we can find some workarounds or even fixes. Those issues are not a symptom bad programming or bad design. No, most of the issues come either from ruby itself or some random library issues.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s review the things I have been thinking about lately.&lt;/p&gt;

&lt;h2&gt;Memory consumption&lt;/h2&gt;

&lt;p&gt;This is by far one of the most seen issues both on the client side and the server side. I&amp;#8217;ve mainly seen this problem on the client side, up to the point that most people recommend running puppetd as cronjobs, instead of being a long lived process.&lt;/p&gt;

&lt;h3&gt;Ruby allocator&lt;/h3&gt;

&lt;p&gt;All boils down to the ruby (at least the the MRI 1.8.x version) allocator. This is the part in the ruby interpreter that deals with memory allocations. Like in many dynamic languages, the allocator manages a memory pool that is called a&lt;a href="http://en.wikipedia.org/wiki/Dynamic_memory_allocation"&gt; heap&lt;/a&gt;. And like some other languages (among them Java), this heap can &lt;strong&gt;never shrink and always grows&lt;/strong&gt; when more memory is needed. This is done this way because it is simpler and way faster. Usually applications ends using their nominal part of memory and no more memory has to be allocated by the kernel to the process, which gives faster applications.&lt;/p&gt;

&lt;p&gt;The problem is that if the application needs transiently a high amount of memory that will be trashed a couple of millisecond after, the process will pay this penalty all its life, even though say 80% of the memory used by the process is free but not reclaimed by the OS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And it&amp;#8217;s even worst&lt;/em&gt;. The ruby interpreter when it grows the heap, instead of allocating bytes per bytes (which would be really slow) does this by chunk. The whole question is what is the proper size of a chunk?&lt;/p&gt;

&lt;p&gt;In the default implementation of MRI 1.8.x, a chunk is the size of the previous heap times 1.8. That means at worst a ruby process might end up allocating 1.8 times more than what it really needs at a given time. (This is a gross simplification, read the code if you want to know more).&lt;/p&gt;

&lt;h3&gt;Yes but what happens in Puppet?&lt;/h3&gt;

&lt;p&gt;So how does it apply to &lt;em&gt;puppetd&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s easy, &lt;em&gt;puppetd&lt;/em&gt; uses memory for two things (beside maintaining some core data to be able to run):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the &lt;strong&gt;catalog&lt;/strong&gt; (which contains all resources, along with all templates) as shipped by the &lt;em&gt;puppetmaster&lt;/em&gt; (i.e. serialized) and live as ruby objects.&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;content of the sourced&lt;/strong&gt; files (one at a time, so it&amp;#8217;s the biggest transmitted file that imposes it&amp;#8217;s high watermark for &lt;em&gt;puppetd&lt;/em&gt;). Of course this is still better than in 0.24 where the content was transmitted encoded in XMLRPC adding the penalty of escaping everything&amp;#8230;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Hopefully, &lt;strong&gt;nobody distributes large files with Puppet :-)&lt;/strong&gt; If you&amp;#8217;re tempted to do so, see below&amp;#8230;&lt;/p&gt;

&lt;p&gt;But again there&amp;#8217;s more, as &lt;em&gt;Peter Meier&lt;/em&gt; (known as duritong in the community) &lt;a href="http://groups.google.com/group/puppet-dev/browse_thread/thread/17e901f2613b9c27/552469109dac1f91?lnk=gst&amp;amp;q=+Possible+workaround+for+%232824#552469109dac1f91"&gt;discovered a couple of month ago&lt;/a&gt;: when &lt;em&gt;puppetd&lt;/em&gt; gets its &lt;em&gt;catalog&lt;/em&gt; (which by the way is transmitted in &lt;a href="http://www.json.org/"&gt;json&lt;/a&gt; nowadays), it also stores it as a local cache to be able to run if it can&amp;#8217;t contact the master for a subsequent run. This operation is done by unserializing the catalog from json to ruby live objects, and then serializing the laters to &lt;a href="http://www.yaml.org/"&gt;YAML&lt;/a&gt;. Beside the &lt;strong&gt;evident loss of time&lt;/strong&gt; to do that on large catalog, YAML is a real memory hog. Peter&amp;#8217;s experience showed that about 200MB of live memory his &lt;em&gt;puppetd&lt;/em&gt; process was using came from this final serialization!&lt;/p&gt;

&lt;p&gt;So I had the following idea: why not store the serialized version of the catalog (the json one) since we already have it in a serialized form when we receive it from the master (it&amp;#8217;s a little bit more complex than that of course). This way no need to serialize it again in YAML. This is what &lt;a href="http://projects.reductivelabs.com/issues/2892"&gt;ticket #2892 is all about.&lt;/a&gt; &lt;a href="http://www.madstop.com/"&gt;Luke&lt;/a&gt; is committed to have this enhancement in Rowlf, so there&amp;#8217;s good hope!&lt;/p&gt;

&lt;h3&gt;Some puppet solutions?&lt;/h3&gt;

&lt;p&gt;So what can we do to help puppet not consume that many memory?&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;theory we could play on several factors&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transmit smaller catalogs&lt;/strong&gt;. For instance get rid of all those templates you love (ok that&amp;#8217;s not a solution)&lt;/li&gt;
&lt;li&gt;Stream the serialization/deserialization with something like &lt;a href="http://github.com/brianmario/yajl-ruby"&gt;Yajl-Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use another &lt;strong&gt;ruby interpreter with a better allocator&lt;/strong&gt; (like for instance JRuby)&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;different constant for resizing the heap&lt;/strong&gt; (ie replace this 1.8 by 1.0 or less on line 410 of gc.c). This can be done easily when using Rails machine GC patches or Ruby Enterprise Edition, in which case setting the environment variable  &lt;strong&gt;&lt;tt&gt;RUBY_HEAP_SLOTS_GROWTH_FACTOR&lt;/tt&gt;&lt;/strong&gt; is enough. Check the &lt;a href="http://www.rubyenterpriseedition.com/documentation.html#_garbage_collector_performance_tuning"&gt;documentation for more information&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stream the sourced file on the server and the client&lt;/strong&gt; (this way only a small buffer is used, and the total size of the file is never allocated). This one is hard.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Note that the same issues apply to the master too (especially for the file serving part). But it&amp;#8217;s usually easier to run a different ruby interpreter (like REE) on the master than on all your clients.&lt;/p&gt;

&lt;p&gt;Streaming HTTP requests is promising but unfortunately would require large change to how Puppet deals with HTTP. Maybe it can be done only for file content requests&amp;#8230; This is something I&amp;#8217;ll definitely explore.&lt;/p&gt;

&lt;p&gt;This file serving thing let me think about the following which I already discussed several time with Peter&amp;#8230;&lt;/p&gt;

&lt;h2&gt;File serving offloading&lt;/h2&gt;

&lt;p&gt;One of the mission of the &lt;em&gt;puppetmaster&lt;/em&gt; is to serve sourced file to its clients. We saw in the previous section that to do that the master has to read the file in memory. That&amp;#8217;s &lt;a href="http://reductivelabs.com/trac/puppet/wiki/PuppetScalability."&gt;one reason it is recommended&lt;/a&gt; to use a dedicated puppetmaster server to act as a &lt;strong&gt;pure fileserver&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;there&amp;#8217;s a better way&lt;/strong&gt;, provided you run puppet behind &lt;a href="http://nginx.org/en/"&gt;nginx&lt;/a&gt; or &lt;a href="http://httpd.apache.org/"&gt;apache&lt;/a&gt;. Those two proxies are also static file servers: why not leverage what they do best to serve the sourced files and thus offload our puppetmaster?&lt;/p&gt;

&lt;p&gt;This has some advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it frees lots of resources on the puppetmaster, so that they can serve more catalogs by unit time&lt;/li&gt;
&lt;li&gt;the job will be done faster and by using less resources. Those static servers have been created to spoon-feed our puppet clients&amp;#8230;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In fact it was impossible in 0.24.x, but now that file content serving is &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;RESTful&lt;/a&gt; it becomes trivial.&lt;/p&gt;

&lt;p&gt;Of course offloading would give its best if your clients requires lots of sourced files that change often, or if you provision lots of new hosts at the same time because we&amp;#8217;re offloading only content, not file metadata. File content is served only if the client hasn&amp;#8217;t the file or the file checksum on the client is different.&lt;/p&gt;

&lt;h3&gt;An example is better than thousand words&lt;/h3&gt;

&lt;p&gt;Imagine we have a standard manifest layout with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;some globally sourced files under /etc/puppet/files and&lt;/li&gt;
&lt;li&gt;some modules files under /etc/puppet/modules/&lt;modulename&gt;/files.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Here is what would be the &lt;em&gt;nginx configuration&lt;/em&gt; for such scheme:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;span class='line-number'&gt;33&lt;/span&gt;
&lt;span class='line-number'&gt;34&lt;/span&gt;
&lt;span class='line-number'&gt;35&lt;/span&gt;
&lt;span class='line-number'&gt;36&lt;/span&gt;
&lt;span class='line-number'&gt;37&lt;/span&gt;
&lt;span class='line-number'&gt;38&lt;/span&gt;
&lt;span class='line-number'&gt;39&lt;/span&gt;
&lt;span class='line-number'&gt;40&lt;/span&gt;
&lt;span class='line-number'&gt;41&lt;/span&gt;
&lt;span class='line-number'&gt;42&lt;/span&gt;
&lt;span class='line-number'&gt;43&lt;/span&gt;
&lt;span class='line-number'&gt;44&lt;/span&gt;
&lt;span class='line-number'&gt;45&lt;/span&gt;
&lt;span class='line-number'&gt;46&lt;/span&gt;
&lt;span class='line-number'&gt;47&lt;/span&gt;
&lt;span class='line-number'&gt;48&lt;/span&gt;
&lt;span class='line-number'&gt;49&lt;/span&gt;
&lt;span class='line-number'&gt;50&lt;/span&gt;
&lt;span class='line-number'&gt;51&lt;/span&gt;
&lt;span class='line-number'&gt;52&lt;/span&gt;
&lt;span class='line-number'&gt;53&lt;/span&gt;
&lt;span class='line-number'&gt;54&lt;/span&gt;
&lt;span class='line-number'&gt;55&lt;/span&gt;
&lt;span class='line-number'&gt;56&lt;/span&gt;
&lt;span class='line-number'&gt;57&lt;/span&gt;
&lt;span class='line-number'&gt;58&lt;/span&gt;
&lt;span class='line-number'&gt;59&lt;/span&gt;
&lt;span class='line-number'&gt;60&lt;/span&gt;
&lt;span class='line-number'&gt;61&lt;/span&gt;
&lt;span class='line-number'&gt;62&lt;/span&gt;
&lt;span class='line-number'&gt;63&lt;/span&gt;
&lt;span class='line-number'&gt;64&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='line'&gt;server &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    listen 8140;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    ssl                     on;
&lt;/span&gt;&lt;span class='line'&gt;    ssl_session_timeout     5m;
&lt;/span&gt;&lt;span class='line'&gt;    ssl_certificate         /var/lib/puppet/ssl/certs/master.pem;
&lt;/span&gt;&lt;span class='line'&gt;    ssl_certificate_key     /var/lib/puppet/ssl/private_keys/master.pem;
&lt;/span&gt;&lt;span class='line'&gt;    ssl_client_certificate  /var/lib/puppet/ssl/ca/ca_crt.pem;
&lt;/span&gt;&lt;span class='line'&gt;    ssl_crl                 /var/lib/puppet/ssl/ca/ca_crl.pem;
&lt;/span&gt;&lt;span class='line'&gt;    ssl_verify_client       optional;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    root                    /etc/puppet;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c"&gt;# those locations are for the &amp;quot;production&amp;quot; environment&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c"&gt;# update according to your configuration&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c"&gt;# serve static file for the [files] mountpoint&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    location /production/file_content/files/ &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# it is advisable to have some access rules here&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        allow   172.16.0.0/16;
&lt;/span&gt;&lt;span class='line'&gt;        deny    all;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# make sure we serve everything&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# as raw&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        types &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        default_type application/x-raw;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nb"&gt;alias&lt;/span&gt; /etc/puppet/files/;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c"&gt;# serve modules files sections&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    location ~ /production/file_content/&lt;span class="o"&gt;[&lt;/span&gt;^/&lt;span class="o"&gt;]&lt;/span&gt;+/files/ &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# it is advisable to have some access rules here&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        allow   172.16.0.0/16;
&lt;/span&gt;&lt;span class='line'&gt;        deny    all;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# make sure we serve everything&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# as raw&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        types &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        default_type application/x-raw;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        root /etc/puppet/modules;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# rewrite /production/file_content/module/files/file.txt&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="c"&gt;# to /module/file.text&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        rewrite ^/production/file_content/&lt;span class="o"&gt;([&lt;/span&gt;^/&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;/files/&lt;span class="o"&gt;(&lt;/span&gt;.+&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;/&lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c"&gt;# ask the puppetmaster for everything else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    location / &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_pass          http://puppet-production;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_redirect      off;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_set_header    Host             &lt;span class="nv"&gt;$host&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_set_header    X-Real-IP        &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_set_header    X-Forwarded-For  &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_set_header    X-Client-Verify  &lt;span class="nv"&gt;$ssl_client_verify&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_set_header    X-SSL-Subject    &lt;span class="nv"&gt;$ssl_client_s_dn&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_set_header    X-SSL-Issuer     &lt;span class="nv"&gt;$ssl_client_i_dn&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_buffer_size   16k;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_buffers       8 32k;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_busy_buffers_size    64k;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_temp_file_write_size 64k;
&lt;/span&gt;&lt;span class='line'&gt;        proxy_read_timeout  65;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; the above configuration was missing the only content-type that nginx can return for Puppet to be able to actually receive the file content (that is raw).&lt;/p&gt;

&lt;p&gt;I leave as an exercise to the reader the apache configuration.&lt;/p&gt;

&lt;p&gt;It would also be possible to write some ruby/sh/whatever to generate the nginx configuration from the puppet fileserver.conf file.&lt;/p&gt;

&lt;p&gt;And that&amp;#8217;s all folks, stay tuned for more Puppet (or even different) content.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tVVnJ3c2cc0:CaYt01qKq9g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tVVnJ3c2cc0:CaYt01qKq9g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=tVVnJ3c2cc0:CaYt01qKq9g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tVVnJ3c2cc0:CaYt01qKq9g:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tVVnJ3c2cc0:CaYt01qKq9g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tVVnJ3c2cc0:CaYt01qKq9g:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=tVVnJ3c2cc0:CaYt01qKq9g:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=tVVnJ3c2cc0:CaYt01qKq9g:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=tVVnJ3c2cc0:CaYt01qKq9g:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[mysql-snmp 1.0 - SNMP monitoring for MySQL]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/vmgzAf77PHg/" />
    <updated>2010-01-10T18:14:06+01:00</updated>
    <id>http://www.masterzen.fr/2010/01/10/mysql-snmp-10-snmp-monitoring-for-mysql</id>
    
    <category term="monitoring" />
    
    <category term="mysql" />
    
    <category term="perl" />
    
    <category term="programming" />
    
    <category term="system administration" />
    
    <category term="snmp" />
        
    <content type="html">&lt;p&gt;I&amp;#8217;m really proud to announce the release of the version 1.0 of &lt;a href="http://www.masterzen.fr/software-contributions/mysql-snmp-monitor-mysql-with-snmp/"&gt;mysql-snmp&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;What is mysql-snmp?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;mysql-snmp&lt;/strong&gt; is a mix between the excellent &lt;a href="http://code.google.com/p/mysql-cacti-templates/"&gt;MySQL Cacti Templates&lt;/a&gt; and a &lt;a href="http://www.net-snmp.org/"&gt;Net-SNMP agent&lt;/a&gt;. The idea is that combining the power of the &lt;em&gt;MySQL Cacti Templates&lt;/em&gt; and any SNMP based monitoring would unleash a powerful mysql monitoring system. Of course this project favorite monitoring system is &lt;a href="http://www.opennms.org/wiki/Main_Page"&gt;OpenNMS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mysql-snmp&lt;/strong&gt; is shipped with the necessary &lt;a href="http://www.opennms.org/wiki/Main_Page"&gt;OpenNMS&lt;/a&gt; configuration files, but any other SNMP monitoring software can be used (provided you configure it).&lt;/p&gt;

&lt;p&gt;To get there, you need to run a SNMP agent on each MySQL server, along with &lt;strong&gt;mysql-snmp&lt;/strong&gt;. Then OpenNMS (or any SNMP monitoring software) will contact it and fetch the various values.&lt;/p&gt;

&lt;p&gt;Mysql-snmp exposes a lot of useful values including but not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SHOW STATUS values&lt;/li&gt;
&lt;li&gt;SHOW ENGINE INNODB STATUS parsed values (MySQL 5.0, 5.1, XtraDB or Innodb plugin are supported)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Here are some graph examples produced with OpenNMS 1.6.5 and mysql-snmp 1.0 on one of Days of Wonder MySQL server (running a MySQL 5.0 Percona build):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.masterzen.fr/wp-content/uploads/2010/01/commands.jpg"&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2010/01/commands-300x187.jpg" title="MySQL command counters" alt="commands" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.masterzen.fr/wp-content/uploads/2010/01/mem.jpg"&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2010/01/mem-300x163.jpg" title="Innodb Memory Usage" alt="mem" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.masterzen.fr/images/uploads/2010/01/tmp.jpg"&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2010/01/tmp-300x145.jpg" title="tmp" alt="tmp" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.masterzen.fr/wp-content/uploads/2010/01/innodbwrites.jpg"&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2010/01/innodbwrites-300x145.jpg" title="innodbwrites" alt="innodbwrites" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.masterzen.fr/wp-content/uploads/2010/01/graph.jpg"&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2010/01/graph-300x145.jpg" title="graph" alt="graph" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.masterzen.fr/wp-content/uploads/2010/01/tablelocks.jpg"&gt;&lt;img src="http://www.masterzen.fr/images/uploads/2010/01/tablelocks-300x145.jpg" title="tablelocks" alt="tablelocks" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Where to get it&lt;/h2&gt;

&lt;p&gt;mysql-snmp is available in my &lt;a href="http://github.com/masterzen/mysql-snmp"&gt;github repository&lt;/a&gt;. The repository contains a spec file to build a RPM and what is needed to build a Debian package. Refer to the &lt;a href="http://github.com/masterzen/mysql-snmp/blob/master/README"&gt;README&lt;/a&gt; or the &lt;a href="http://www.masterzen.fr/software-contributions/mysql-snmp-monitor-mysql-with-snmp/"&gt;mysql-snmp page &lt;/a&gt;for more information.&lt;/p&gt;

&lt;p&gt;Thanks to gihub, it is possible to download the tarball instead of using Git:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/masterzen/mysql-snmp/tarball/v1.0"&gt;Mysql-snmp v1.0 tarball&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Changelog&lt;/h2&gt;

&lt;p&gt;This lists all new features/options from the initial version v0.6:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spec file to build RPM&lt;/li&gt;
&lt;li&gt;Use of configuration file for storing mysql password&lt;/li&gt;
&lt;li&gt;Fix of slave handling&lt;/li&gt;
&lt;li&gt;Fix for mk-heartbeat slave lag&lt;/li&gt;
&lt;li&gt;Support of InnoDB plugin and Percona XtraDB&lt;/li&gt;
&lt;li&gt;Automated testing of InnoDB parsing&lt;/li&gt;
&lt;li&gt;Removed some false positive errors&lt;/li&gt;
&lt;li&gt;OpenNMS configuration generation from MySQL Cacti Templates core files&lt;/li&gt;
&lt;li&gt;64 bits computation done in Perl instead of (ab)using MySQL&lt;/li&gt;
&lt;li&gt;More InnoDB values (memory, locked tables, &amp;#8230;)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Reporting Issues&lt;/h2&gt;

&lt;p&gt;Please use &lt;a href="http://github.com/masterzen/mysql-snmp/issues"&gt;Github issue system&lt;/a&gt; to report any issues.&lt;/p&gt;

&lt;h2&gt;Requirements&lt;/h2&gt;

&lt;p&gt;There is a little issue here. &lt;strong&gt;mysql-snmp&lt;/strong&gt; uses Net-Snmp. Not all versions of Net-Snmp are supported as some older versions have some bug for dealing with Counter64. &lt;a href="http://sourceforge.net/projects/net-snmp/files/"&gt;Version 5.4.2.1&lt;/a&gt; with &lt;a href="http://sourceforge.net/tracker/?func=detail&amp;amp;aid=2890931&amp;amp;group_id=12694&amp;amp;atid=312694"&gt;this patch&lt;/a&gt; is known to work fine.&lt;/p&gt;

&lt;p&gt;Also note that this project uses some Counter64, so make sure you configure your SNMP monitoring software to use SNMP v2c or v3 (SNMP v1 doesn&amp;#8217;t support 64 bits values).&lt;/p&gt;

&lt;h2&gt;Final words!&lt;/h2&gt;

&lt;p&gt;I wish everybody an happy new year. Consider this new version as my Christmas present to the community :-)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=vmgzAf77PHg:ihbkmv-nYdI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=vmgzAf77PHg:ihbkmv-nYdI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=vmgzAf77PHg:ihbkmv-nYdI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=vmgzAf77PHg:ihbkmv-nYdI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=vmgzAf77PHg:ihbkmv-nYdI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=vmgzAf77PHg:ihbkmv-nYdI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=vmgzAf77PHg:ihbkmv-nYdI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=vmgzAf77PHg:ihbkmv-nYdI:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=vmgzAf77PHg:ihbkmv-nYdI:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2010/01/10/mysql-snmp-10-snmp-monitoring-for-mysql/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Nginx upload progress module v0.8!]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/Hy6zV9cJNbU/" />
    <updated>2009-12-19T20:46:42+01:00</updated>
    <id>http://www.masterzen.fr/2009/12/19/nginx-upload-progress-module-v08</id>
    
    <category term="c" />
    
    <category term="nginx" />
    
    <category term="programming" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;Yes, I know&amp;#8230; I &lt;a href="http://www.masterzen.fr/2009/11/22/nginx-upload-progress-module-v07/"&gt;released v0.7 less than a month ago&lt;/a&gt;. But this release was &lt;a href="http://github.com/masterzen/nginx-upload-progress-module/issues/closed/#issue/2"&gt;crippled by a crash&lt;/a&gt; that could happen at start or reload.&lt;/p&gt;

&lt;h2&gt;Changes&lt;/h2&gt;

&lt;p&gt;Bonus in this new version, brought to you by &lt;a href="http://github.com/tizoc"&gt;Tizoc&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSONP support&lt;/li&gt;
&lt;li&gt;Long awaited fix for X-Progress-ID to be the last parameter in the request parameter&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If you wonder what JSONP is (as I did when I got the merge request), you can check &lt;a href="http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/"&gt;the original blog post that lead to it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To activate JSONP you need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;to use the upload_progress_jsonp_output in the progress probe location&lt;/li&gt;
&lt;li&gt;declare the JSONP parameter with the upload_progress_jsonp_parameter&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;This version has been tested with 0.7.64 and 0.8.30.&lt;/p&gt;

&lt;h2&gt;How do you get it?&lt;/h2&gt;

&lt;p&gt;Easy, download the tarball from the &lt;a href="http://github.com/masterzen/nginx-upload-progress-module/downloads"&gt;nginx upload progress module github repository download section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to report a bug, please use the &lt;a href="http://github.com/masterzen/nginx-upload-progress-module/issues"&gt;Github issue section&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Hy6zV9cJNbU:KWGIlsiBxtg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Hy6zV9cJNbU:KWGIlsiBxtg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=Hy6zV9cJNbU:KWGIlsiBxtg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Hy6zV9cJNbU:KWGIlsiBxtg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Hy6zV9cJNbU:KWGIlsiBxtg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Hy6zV9cJNbU:KWGIlsiBxtg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=Hy6zV9cJNbU:KWGIlsiBxtg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=Hy6zV9cJNbU:KWGIlsiBxtg:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=Hy6zV9cJNbU:KWGIlsiBxtg:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2009/12/19/nginx-upload-progress-module-v08/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Nginx upload progress module v0.7!]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/I9Q0mZTchPc/" />
    <updated>2009-11-22T12:24:50+01:00</updated>
    <id>http://www.masterzen.fr/2009/11/22/nginx-upload-progress-module-v07</id>
    
    <category term="c" />
    
    <category term="nginx" />
    
    <category term="programming" />
    
    <category term="system administration" />
        
    <content type="html">&lt;p&gt;I&amp;#8217;m proud to announce the release of &lt;a href="http://github.com/masterzen/nginx-upload-progress-module"&gt;Nginx Upload Progress&lt;/a&gt; module v0.7&lt;/p&gt;

&lt;p&gt;This version sees a crash fix and various new features implemented by Valery Kholodkov (the author of the famous &lt;a href="http://www.grid.net.ru/nginx/upload.en.html"&gt;Nginx Upload Module&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This version has been tested with Nginx 0.7.64.&lt;/p&gt;

&lt;h2&gt;Changes&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;fixed segfault when uploads are aborted (thanks to Markus  Doppelbauer for his bug report)&lt;/li&gt;
&lt;li&gt;session ID header name is now configurable (thanks to Valery Kholodkov)&lt;/li&gt;
&lt;li&gt;Added directive to format output as pure json (thanks to Valery  Kholodkov)&lt;/li&gt;
&lt;li&gt;Added directive to format output with configurable template (thanks  to Valery Kholodkov)&lt;/li&gt;
&lt;li&gt;Added directive to set a probe response content-type (thanks to  Valery Kholodkov)&lt;/li&gt;
&lt;li&gt;Added upload status variables (needs a status patch) (thanks to  Valery Kholodkov)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;What&amp;#8217;s now cool!&lt;/h2&gt;

&lt;p&gt;What is cool is that now with only one directive (upload_progress_json_output) the responses are sent in pure Json and not in javascript mix as it was before.&lt;/p&gt;

&lt;p&gt;Another cool feature is the possibility to use templates to send progress information. That means with a simple configuration change nginx can now return XML:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='line'&gt;upload_progress_content_type &lt;span class="s1"&gt;&amp;#39;text/xml&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;upload_progress_template starting &lt;span class="s1"&gt;&amp;#39;&amp;lt;upload&amp;gt;&amp;lt;state&amp;gt;starting&amp;lt;/state&amp;gt;&amp;lt;/upload&amp;gt;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;upload_progress_template uploading &lt;span class="s1"&gt;&amp;#39;&amp;lt;upload&amp;gt;&amp;lt;state&amp;gt;uploading&amp;lt;/state&amp;gt;&amp;lt;size&amp;gt;$uploadprogress_length&amp;lt;/size&amp;gt;&amp;lt;uploaded&amp;gt;$uploadprogress_received&amp;lt;/uploaded&amp;gt;&amp;lt;/upload&amp;gt;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;upload_progress_template &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;upload&amp;gt;&amp;lt;state&amp;gt;done&amp;lt;/state&amp;gt;&amp;lt;/upload&amp;gt;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;span class='line'&gt;upload_progress_template error &lt;span class="s1"&gt;&amp;#39;&amp;lt;upload&amp;gt;&amp;lt;state&amp;gt;error&amp;lt;/state&amp;gt;&amp;lt;/upload&amp;gt;``$uploadprogress_status``&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Refer to the README in the distribution for more information.&lt;/p&gt;

&lt;h2&gt;How do you get it?&lt;/h2&gt;

&lt;p&gt;Easy, download the tarball from the &lt;a href="http://github.com/masterzen/nginx-upload-progress-module/downloads"&gt;nginx upload progress module github repository download section&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;How can I use it?&lt;/h2&gt;

&lt;p&gt;Normally you have to use your own client code to display the progress bar and contact nginx to get the progress information.&lt;/p&gt;

&lt;p&gt;But some nice people have created various javascript libraries doing this for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://github.com/drogus/jquery-upload-progress"&gt;JQuery upload progress module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/edgarjs/prototype-nginx-upload-progress"&gt;Protoype upload progress module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Happy uploads!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=I9Q0mZTchPc:Vwx1MCE_034:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=I9Q0mZTchPc:Vwx1MCE_034:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=I9Q0mZTchPc:Vwx1MCE_034:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=I9Q0mZTchPc:Vwx1MCE_034:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=I9Q0mZTchPc:Vwx1MCE_034:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=I9Q0mZTchPc:Vwx1MCE_034:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=I9Q0mZTchPc:Vwx1MCE_034:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=I9Q0mZTchPc:Vwx1MCE_034:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=I9Q0mZTchPc:Vwx1MCE_034:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2009/11/22/nginx-upload-progress-module-v07/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[MySQL InnoDB and table renaming don't play well...]]></title>
    <link href="http://feedproxy.google.com/~r/masterzen/~3/rp6dRg5sCcs/" />
    <updated>2009-10-15T06:52:08+02:00</updated>
    <id>http://www.masterzen.fr/2009/10/15/mysql-innodb-and-table-renaming-dont-play-well</id>
    
    <category term="mysql" />
    
    <category term="system administration" />
    
    <category term="war stories" />
        
    <content type="html">&lt;p&gt;At Days of Wonder we are huge fans of &lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt; (and since about a year of the various &lt;a href="http://ourdelta.org/"&gt;Open Query&lt;/a&gt;, &lt;a href="http://www.percona.com/percona-lab.html"&gt;Percona&lt;/a&gt;, &lt;a href="http://code.google.com/p/google-mysql-tools/wiki/Mysql5Patches"&gt;Google&lt;/a&gt; or other community patches), up to the point we&amp;#8217;re using MySQL for about everything in production.&lt;/p&gt;

&lt;p&gt;But since we moved to 5.0, back 3 years ago our production databases which hold our website and online game systems has a unique issue: the mysqld process uses more and more RAM, up to the point where the &lt;a href="http://linux-mm.org/OOM_Killer"&gt;kernel OOM&lt;/a&gt; decide to kill the process.&lt;/p&gt;

&lt;p&gt;You&amp;#8217;d certainly think we are complete morons because we didn&amp;#8217;t do anything in the last 3 years to fix the issue :-)&lt;/p&gt;

&lt;p&gt;Unfortunately, I never couldn&amp;#8217;t replicate the issue in the lab, mainly because it is difficult to replicate the exact same load the production server sees (mainly because of the online games activity).&lt;/p&gt;

&lt;p&gt;During those 3 years, I tried everything I could, from using other allocators, valgrind, debug builds and so on, without any success.&lt;/p&gt;

&lt;p&gt;What is nice, is that we moved to an &lt;a href="http://ourdelta.org/"&gt;OurDelta&lt;/a&gt; build about a year ago, where &lt;a href="http://www.innodb.com/"&gt;InnoDB&lt;/a&gt; is able to print more memory statistics than the default MySQL version.&lt;/p&gt;

&lt;p&gt;For instance it shows&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;Internal hash tables (constant factor + variable factor)
&lt;/span&gt;&lt;span class='line'&gt;    Adaptive hash index 1455381240      (118999688 + 1336381552)
&lt;/span&gt;&lt;span class='line'&gt;    Page hash           7438328
&lt;/span&gt;&lt;span class='line'&gt;    Dictionary cache    281544240       (89251896 + 192292344)
&lt;/span&gt;&lt;span class='line'&gt;    File system         254712  (82672 + 172040)
&lt;/span&gt;&lt;span class='line'&gt;    Lock system         18597112        (18594536 + 2576)
&lt;/span&gt;&lt;span class='line'&gt;    Recovery system     0       (0 + 0)
&lt;/span&gt;&lt;span class='line'&gt;    Threads             408056  (406936 + 1120)
&lt;/span&gt;&lt;span class='line'&gt;    innodb_io_pattern   0       (0 + 0)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Back several month ago, I analyzed this output just to see what figures were growing, and found that the &lt;em&gt;Dictionary Cache variable part &lt;/em&gt;was increasing (slowly but definitely).&lt;/p&gt;

&lt;p&gt;Sure fine MySQL experts would have been able to tell me exactly what, when and where the problem was, but since I&amp;#8217;m not familiar with the code-base, I looked up what this number was and where it was increased (all in &lt;em&gt;dict0dict.c&lt;/em&gt;) and added some logs each time it was increased.&lt;/p&gt;

&lt;p&gt;I then installed this version for a quite long time (just to check it wouldn&amp;#8217;t crash on production) on a slave server. But this server didn&amp;#8217;t print anything interesting because it doesn&amp;#8217;t see the exact same load the production masters.&lt;/p&gt;

&lt;p&gt;A couple of months after that, I moved this code to one of the master and bingo! I found the operation and the tables exhibiting an increase:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='bash'&gt;&lt;span class='line'&gt;mysqld&lt;span class="o"&gt;[&lt;/span&gt;8131&lt;span class="o"&gt;]&lt;/span&gt;: InnoDB: dict_table_rename_in_cache production/rank_tmp2 193330680 + 8112
&lt;/span&gt;&lt;span class='line'&gt;mysqld&lt;span class="o"&gt;[&lt;/span&gt;8131&lt;span class="o"&gt;]&lt;/span&gt;: InnoDB: dict_table_rename_in_cache production/rank 193338792 + 8112
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;As soon as I saw the operation and table (ie rank), I found what the culprit is. We have a daemon that every 10s computes the player ranks for our online games.&lt;/p&gt;

&lt;p&gt;To do this, we&amp;#8217;re using the following pattern:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sql'&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;-- compute the ranks&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;playerID&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;game_score&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rankscore&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;OUTFILE&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;/tmp/rank_tmp.tmp&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;-- load back the scores&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;LOAD&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;INFILE&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;/tmp/rank_tmp.tmp&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;rank_tmp&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;-- swap tables so that clients see new ranks atomatically&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;rank_tmp2&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rank_tmp&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rank_tmp2&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;rank_tmp&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;-- truncate the old ranks for a new pass&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;rank_tmp&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;-- go back to the select above&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;You might ask why I&amp;#8217;m doing a so much convoluted system, especially the SELECT INTO OUTFILE and the LOAD DATA. It&amp;#8217;s just because INSERT &amp;#8230; SELECT with innodb and binlog enabled can produce transactions abort (which we were getting tons of).&lt;/p&gt;

&lt;p&gt;Back to the original issue, apparently the issue lies in the RENAME part of the daemon.&lt;/p&gt;

&lt;p&gt;Looking at the &lt;em&gt;dict0dict.c dict_table_rename_in_cache&lt;/em&gt; function we see:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='c'&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;ibool&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;dict_table_rename_in_cache&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;old_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem_heap_strdup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem_heap_strdup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Looking to &lt;em&gt;mem_heap&lt;/em&gt; stuff, I discovered that each table has a heap associated in which InnoDB allocates various things. This heap can only grow (by block of 8112 bytes it seems), since the allocator is not a real one. This is done for performance reasons.&lt;/p&gt;

&lt;p&gt;So each time we rename a table, the old name (why? since it is already allocated) is duplicated, along with the new name. Each time.&lt;/p&gt;

&lt;p&gt;This heap is freed when the table is dropped, so there is a possibility to reclaim the used memory. That means this issue is not a memory leak per-se.&lt;/p&gt;

&lt;p&gt;By the way, I&amp;#8217;ve &lt;a href="http://bugs.mysql.com/?id=47991"&gt;filed this bug on mysql bug system&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One work-around, beside fixing the code itself, would be to drop the rank table instead of truncating it. The issue with dropping/creating InnoDB table on a fast pace is that the dictionary cache itself will grow, because it can only grow as there is no way to purge it from old tables (except running one of the Percona patches). So the more tables we create the more we&amp;#8217;ll use memory - back to square 0, but worst.&lt;/p&gt;

&lt;p&gt;So right now, I don&amp;#8217;t really have any idea on how to really fix the issue. Anyone having an idea, please do not hesitate to comment on this blog post :-)&lt;/p&gt;

&lt;p&gt;And please, don&amp;#8217;t tell me to move to MyISAM&amp;#8230;&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=rp6dRg5sCcs:yyGcOCqAxOE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=rp6dRg5sCcs:yyGcOCqAxOE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=rp6dRg5sCcs:yyGcOCqAxOE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=rp6dRg5sCcs:yyGcOCqAxOE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=rp6dRg5sCcs:yyGcOCqAxOE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=rp6dRg5sCcs:yyGcOCqAxOE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=rp6dRg5sCcs:yyGcOCqAxOE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/masterzen?a=rp6dRg5sCcs:yyGcOCqAxOE:Zr3FfEXbzIM"&gt;&lt;img src="http://feeds.feedburner.com/~ff/masterzen?i=rp6dRg5sCcs:yyGcOCqAxOE:Zr3FfEXbzIM" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</content>
  <feedburner:origLink>http://www.masterzen.fr/2009/10/15/mysql-innodb-and-table-renaming-dont-play-well/</feedburner:origLink></entry>
  
</feed>
