<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>www.devco.net</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/" />
    <link rel="self" type="application/atom+xml" href="http://www.devco.net/tag-puppet-rss2.0.xml" />
    <id>tag:www.devco.net,2008-08-23://1</id>
    <updated>2009-10-07T22:06:30Z</updated>
    
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 4.32-en</generator>

<entry>
    <title>Flashpolicyd moved to Google Code</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/10/07/flashpolicyd_moved_to_google_code.php" />
    <id>tag:www.devco.net,2009://1.476</id>

    <published>2009-10-07T22:01:13Z</published>
    <updated>2009-10-07T22:06:30Z</updated>

    <summary>Long time readers might know I have a Ruby based Flash Policy Server called flashpolicyd - I blogged about this in the past.Since I&apos;ve been very happy with Google Code for hosting ruby-pdns I thought I&apos;d start moving some other projects there too, in the process I&apos;ll probably also release some other bits and bobs that I did not previous share the code of.The new home for flashpolicyd is then: http://code.google.com/p/flashpolicyd/This was my first major bit of Ruby code and looking back at it now I&apos;m still fairly happy with it but the real proof is in the pudding; my production copy of this code is up over 300 days now, does not leak memory or threads and have served 10s of millions of requests without any problems.</summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="flashpolicyd" label="flashpolicyd" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[Long time readers might know I have a Ruby based Flash Policy Server called flashpolicyd - <a href="http://www.devco.net/archives/2008/06/27/flashpolicyd_20.php">I blogged about this in the past</a>.<br /><br />Since I've been very happy with Google Code for hosting <a href="http://code.google.com/p/ruby-pdns/">ruby-pdns</a> I thought I'd start moving some other projects there too, in the process I'll probably also release some other bits and bobs that I did not previous share the code of.<br /><br />The new home for flashpolicyd is then: <a href="http://code.google.com/p/flashpolicyd/">http://code.google.com/p/flashpolicyd/</a><br /><br />This was my first major bit of Ruby code and looking back at it now I'm still fairly happy with it but the real proof is in the pudding; my production copy of this code is up over 300 days now, does not leak memory or threads and have served 10s of millions of requests without any problems.<br /><br />]]>
        
    </content>
</entry>

<entry>
    <title>Ruby PDNS External Data</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/10/06/ruby_pdns_external_data.php" />
    <id>tag:www.devco.net,2009://1.475</id>

    <published>2009-10-06T22:25:24Z</published>
    <updated>2009-10-06T22:39:24Z</updated>

    <summary><![CDATA[I develop ruby-pdns, so far things have been very static - you give it code and it serves your code, simple stuff.But this is not what I want, I want a full API enabled DNS server where I can both code the logic for records as well as send environmental data into the DNS server to adjust it's behavior.&nbsp; I think there's a big gap in the various cloud offerings today wrt to DNS, people end up hosting their DNS and make manual config changes, but this is not what clouds are about, you want to code the entire infrastructure, and it should adjust based on demand.&nbsp; This does not sit well with todays slow and generally crappy DNS.&nbsp; Combined with the fact that clouds don't let you follow the time tested methods for load balancing etc. we need to get creative.My end game then is to scratch this itch, you should be able to host DNS servers and interact with them fully both in how they decide how to answer requests but also send data to them and adjust the behaviors on the fly.&nbsp; Version 1 of Ruby PDNS will hopefully achieve this, here is a video of what you might expect to be able to do - the video sends data to the DNS server telling it to take a machine out of the pool for maintenance.&nbsp;&nbsp; There will be REST based APIs that will enable any language to do this, if you want to play the code that you'll see in the video is all in SVN.I still have some ways to go before version 1, consider this a very early preview after 1 nights hacking on the feature.]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rubypdns" label="ruby-pdns" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[I develop <a href="http://code.google.com/p/ruby-pdns/">ruby-pdns</a>, so far things have been very static - you give it code and it serves your code, simple stuff.<br /><br />But this is not what I want, I want a full API enabled DNS server where I can both code the logic for records as well as send environmental data into the DNS server to adjust it's behavior.&nbsp; <br /><br />I think there's a big gap in the various cloud offerings today wrt to DNS, people end up hosting their DNS and make manual config changes, but this is not what clouds are about, you want to code the entire infrastructure, and it should adjust based on demand.&nbsp; This does not sit well with todays slow and generally crappy DNS.&nbsp; Combined with the fact that clouds don't let you follow the time tested methods for load balancing etc. we need to get creative.<br /><br />My end game then is to scratch this itch, you should be able to host DNS servers and interact with them fully both in how they decide how to answer requests but also send data to them and adjust the behaviors on the fly.&nbsp; <br /><br />Version 1 of Ruby PDNS will hopefully achieve this, <a href="http://screenr.com/50H">here is a video of what you might expect</a> to be able to do - the video sends data to the DNS server telling it to take a machine out of the pool for maintenance.&nbsp;&nbsp; There will be REST based APIs that will enable any language to do this, if you want to play the code that you'll see in the video is all in SVN.<br /><br />I still have some ways to go before version 1, consider this a very early preview after 1 nights hacking on the feature.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Ruby PDNS Version 0.5</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/10/05/ruby_pdns_version_05.php" />
    <id>tag:www.devco.net,2009://1.474</id>

    <published>2009-10-05T22:11:20Z</published>
    <updated>2009-10-05T22:51:59Z</updated>

    <summary><![CDATA[I just released version 0.5 of Ruby PDNS.&nbsp; This mainly introduces a full statistics capability and fixes a minor bug or two.I didn't intend on releasing another version before 1.0 but it turns out the stats feature were quite a big job worthy of a release.The stats is complicated by the fact that PDNS runs multiple instances of my code sharing the load between them, this makes keeping stats pretty hard because I have no shared memory space like you would in a multi threaded app.&nbsp; So I'd somehow need to dump per process stats and calculate totals.&nbsp; Everyone I asked suggested writing a log and summarizing the log data but this just never felt right to me - there would be issues with log rotation and knowing till when you last counted stats and so forth - so I kept looking for a solution.I struggled to come up with a good approach for a long time, finally settling on each process writing a YAML file with it's stats and a cron job to aggregate it regularly.&nbsp; This turned out to be quite elegant - much more so than processing logs for example - as the cron job cleans up files it already processed and also ignore too old ones etc.&nbsp; If the cron job keeps running it should be zero maintenance.I added a script - pdns-get-stats.rb - to query the aggregated stats:$ pdns-get-stats.rb --usec --record puppetusagecount:896 totaltime:165844 averagetime:185$ pdns-get-stats.rb --usec --record ruby-pdns-totalsusagecount:68705 totaltime:11056529 averagetime:160The puppet record is using GeoIP so you'd expect it to be a bit slower than ones that just shuffle records and this is clear above, though I am quite pleased with the performance of the GeoIP code.&nbsp; Times are in microseconds.Those who know Cacti will recognise the format as being compatible with the Cacti plugin standard - if there is demand I can easily add Munin, Collectd etc query toolsUsing the details I was able to draw a quick little graph below using CactiThe release is available in RPM, tgz and gem at the project site.The next major step is to write a API that is externally accessible, perhaps using REST over HTTP, this means you could send data from your monitoring or CMDB and adjust weights or really anything you can code using external data.]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rubypdns" label="ruby-pdns" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[I just released <a href="http://code.google.com/p/ruby-pdns/wiki/ReleaseNotes_0_5">version 0.5</a> of <a href="http://code.google.com/p/ruby-pdns/">Ruby PDNS</a>.&nbsp; This mainly introduces a full statistics capability and fixes a minor bug or two.<br /><br />I didn't intend on releasing another version before 1.0 but it turns out the stats feature were quite a big job worthy of a release.<br /><br />The stats is complicated by the fact that PDNS runs multiple instances of my code sharing the load between them, this makes keeping stats pretty hard because I have no shared memory space like you would in a multi threaded app.&nbsp; So I'd somehow need to dump per process stats and calculate totals.&nbsp; Everyone I asked suggested writing a log and summarizing the log data but this just never felt right to me - there would be issues with log rotation and knowing till when you last counted stats and so forth - so I kept looking for a solution.<br /><br />I struggled to come up with a good approach for a long time, finally settling on each process writing a YAML file with it's stats and a cron job to aggregate it regularly.&nbsp; This turned out to be quite elegant - much more so than processing logs for example - as the cron job cleans up files it already processed and also ignore too old ones etc.&nbsp; If the cron job keeps running it should be zero maintenance.<br /><br />I added a script - pdns-get-stats.rb - to query the aggregated stats:<br /><br /><blockquote>$ pdns-get-stats.rb --usec --record puppet<br />usagecount:896 totaltime:165844 averagetime:185<br /><br />$ pdns-get-stats.rb --usec --record ruby-pdns-totals<br />usagecount:68705 totaltime:11056529 averagetime:160<br /></blockquote><br />The puppet record is using GeoIP so you'd expect it to be a bit slower than ones that just shuffle records and this is clear above, though I am quite pleased with the performance of the GeoIP code.&nbsp; Times are in microseconds.<br /><br />Those who know <a href="http://www.cacti.net/">Cacti</a> will recognise the format as being compatible with the Cacti plugin standard - if there is demand I can easily add Munin, Collectd etc query tools<br /><br />Using the details I was able to draw a quick little graph below using Cacti<br /><br /><center><img src="http://www.devco.net/images/pdns-time-graph.png" /></center><br /><br />The release is available in RPM, tgz and gem <a href="http://code.google.com/p/ruby-pdns/downloads/list">at the project site</a>.<br /><br />The next major step is to write a API that is externally accessible, perhaps using REST over HTTP, this means you could send data from your monitoring or CMDB and adjust weights or really anything you can code using external data.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Simple Puppet Module Structure</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/09/28/simple_puppet_module_structure.php" />
    <id>tag:www.devco.net,2009://1.473</id>

    <published>2009-09-28T20:31:57Z</published>
    <updated>2009-09-28T22:26:20Z</updated>

    <summary><![CDATA[Puppet supports something called Modules, it's a convenient collection of templates, files, classes, types and facts all related to one thing.&nbsp; I find it makes it easy to think about a problem in an isolated manner and it should be the default structure everyone use.Usually though the problem comes with how to lay out modules and how to really use the Puppet classes system, below a simple module layout that can grow with you as your needs extend.&nbsp; I should emphasis the simple here - this example does not cater well for environments with mix of Operating Systems, or weird multi site setups with overrides per environment, but once you understand the design behind this you should be able to grow it to cater for your needs.&nbsp; If you have more complex needs, see this post as a primer on using the class relationships effectively.For the most part I find I make modules for each building block - apache, syslog, php, mysql - usually the kind of thing that has:One or more&nbsp; packages - or at least some install process.One or more config files or config actionsA service or something that you do to start it.You can see there's an implied dependency tree here, the config steps require the package to be installed and the service step require the config to be done.&nbsp; Similarly if any config changes the services might need to restart.&nbsp; We'd want to cater for the case where you could have 10 config files, or 10 packages, or 100s of files needed to install the service in question.&nbsp; You really wouldn't want to create a big array of require =&gt; resources to control the relationships or to do the notifications, this would be a maintenance nightmare. We're going to create a class for each of the major parts of the module - package, config and service.&nbsp; And we'll use the class systems and relationships, notifies etc to ensure the ordering, requires and notifies are kept simple - especially so that future changes can be done in one place only without editing everywhere else that has requires.class ntp::install {&nbsp;&nbsp;&nbsp; package{"ntpd":&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ensure =&gt; latest&nbsp;&nbsp;&nbsp; }}class ntp::config {&nbsp;&nbsp; File{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; require =&gt; Class["ntp::install"],&nbsp; &nbsp; &nbsp; notify =&gt; Class["ntp::service"],&nbsp; &nbsp; &nbsp; owner =&gt; "root",&nbsp; &nbsp; &nbsp; group =&gt; "root",&nbsp; &nbsp; &nbsp; mode =&gt; 644 &nbsp;&nbsp; }&nbsp;&nbsp; file{"/etc/ntp.conf":&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; source =&gt; "puppet:///ntp/ntp.conf";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "/etc/ntp/step-tickers":&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; source =&gt; "puppet:///ntp/step-tickers";&nbsp;&nbsp; }&nbsp;}}class ntp::service {&nbsp;&nbsp; service{"ntp":&nbsp; &nbsp; &nbsp; ensure =&gt; running,&nbsp; &nbsp; &nbsp; enable =&gt; true,&nbsp; &nbsp; &nbsp; require =&gt; Class["ntp::config"], &nbsp;&nbsp; }}&nbsp;&nbsp;&nbsp;&nbsp; class ntp { &nbsp;&nbsp; include ntp::install, ntp::config, ntp::service}If we now just do include ntp everything will happen cleanly and in order.There's a few things you should notice here.&nbsp; We're using the following syntax in a few places:require =&gt; Class["ntp::something"]notify =&gt; Class["ntp::service"]before =&gt; Class["ntp::something"]subscribe =&gt; Class["ntp::config"]All of the usual relationship meta parameters can operate on classes, so what this means is if you require Class["ntp::config"] then both ntp.conf and step-tickers will be created before the service starts without having to list the files in your relationships.&nbsp; Similarly if you notify a class then all resources in that class gets notifies.The preceding paragraph is very important to grasp, typically we'd try to maintain arrays of files in require trees and later if we add a new config file we have to go hunt everywhere that needs to require it and update those places, by using this organization approach we never have to update anything. &nbsp; If I add 10 services and 100 files into the config and service classes everything else that requires ntp::service will still work as expected without needing updates.&nbsp; This is a very important decoupling of class contents with class function.&nbsp; Your other classes should never be concerned with class contents only class function.As a convention this layout is a good sample, if all your daemons gets installed in the daemon::install class you'll have no problem knowing where to find the code to adjust the install behavior - perhaps to upgrade to the next version - you should try to come up with a set of module naming conventions that works for you and stick to it throughout your manifests - I have a rake task to setup my usual module structure, they're all the same and the learning curve for new people is small.By building up small classes that focus on their task or subtask you can build up ever more powerful modules.&nbsp; We have a simple example here, but you can see how you could create ntp::master and ntp::client wrapper classes that would simply reuse the normal ntp::install and ntp::service classes.As I mentioned this is a simple example for simple needs, the real take away here is to use class relationships and decouple the contents of those classes with their intent this should give you a more maintainable code set.Ultimately conventions such as these will be needed to get us all one step closer to being able to share modules amongst each other as the specifics of making Apache on different distributions work will not matter - we'd just know that we want Class["apache::service"] for example.]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="puppet" label="puppet" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[<a href="http://reductivelabs.com/trac/puppet/wiki/AboutPuppet">Puppet</a> supports something called <a href="http://reductivelabs.com/trac/puppet/wiki/ModuleOrganisation">Modules</a>, it's a convenient collection of templates, files, classes, types and facts all related to one thing.&nbsp; I find it makes it easy to think about a problem in an isolated manner and it should be the default structure everyone use.<br /><br />Usually though the problem comes with how to lay out modules and how to really use the Puppet classes system, below a simple module layout that can grow with you as your needs extend.&nbsp; I should emphasis the simple here - this example does not cater well for environments with mix of Operating Systems, or weird multi site setups with overrides per environment, but once you understand the design behind this you should be able to grow it to cater for your needs.&nbsp; If you have more complex needs, see this post as a primer on using the class relationships effectively.<br /><br />For the most part I find I make modules for each building block - apache, syslog, php, mysql - usually the kind of thing that has:<br /><br /><ul><li>One or more&nbsp; packages - or at least some install process.<br /></li><li>One or more config files or config actions</li><li>A service or something that you do to start it.</li></ul>You can see there's an implied dependency tree here, the config steps require the package to be installed and the service step require the config to be done.&nbsp; Similarly if any config changes the services might need to restart.&nbsp; <br /><br />We'd want to cater for the case where you could have 10 config files, or 10 packages, or 100s of files needed to install the service in question.&nbsp; You really wouldn't want to create a big array of <i>require =&gt;</i> resources to control the relationships or to do the notifications, this would be a maintenance nightmare. <br /><br />We're going to create a class for each of the major parts of the module - package, config and service.&nbsp; And we'll use the class systems and relationships, notifies etc to ensure the ordering, requires and notifies are kept simple - especially so that future changes can be done in one place only without editing everywhere else that has requires.<br /><br /><blockquote>class ntp::install {<br />&nbsp;&nbsp;&nbsp; package{"ntpd":<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ensure =&gt; latest<br />&nbsp;&nbsp;&nbsp; }<br />}<br /><br />class ntp::config {<br />&nbsp;&nbsp; File{ <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; require =&gt; Class["ntp::install"],<br />&nbsp; &nbsp; &nbsp; notify =&gt; Class["ntp::service"],<br />&nbsp; &nbsp; &nbsp; owner =&gt; "root",<br />&nbsp; &nbsp; &nbsp; group =&gt; "root",<br />&nbsp; &nbsp; &nbsp; mode =&gt; 644 <br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; file{"/etc/ntp.conf":<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; source =&gt; "puppet:///ntp/ntp.conf";<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "/etc/ntp/step-tickers":<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; source =&gt; "puppet:///ntp/step-tickers";<br />&nbsp;&nbsp; }<br />&nbsp;}<br />}<br /><br />class ntp::service {<br />&nbsp;&nbsp; service{"ntp":<br />&nbsp; &nbsp; &nbsp; ensure =&gt; running,<br />&nbsp; &nbsp; &nbsp; enable =&gt; true,<br />&nbsp; &nbsp; &nbsp; require =&gt; Class["ntp::config"], <br />&nbsp;&nbsp; }<br />}<br />&nbsp;&nbsp;&nbsp;&nbsp; <br />class ntp { <br />&nbsp;&nbsp; include ntp::install, ntp::config, ntp::service<br />}</blockquote><br />If we now just do <i>include ntp</i> everything will happen cleanly and in order.<br /><br />There's a few things you should notice here.&nbsp; We're using the following syntax in a few places:<br /><br /><ul><li>require =&gt; Class["ntp::something"]</li><li>notify =&gt; Class["ntp::service"]</li><li>before =&gt; Class["ntp::something"]</li><li>subscribe =&gt; Class["ntp::config"]<br /></li></ul>All of the usual relationship meta parameters can operate on classes, so what this means is if you require Class["ntp::config"] then both <i>ntp.conf</i> and <i>step-tickers</i> will be created before the service starts without having to list the files in your relationships<i></i>.&nbsp; Similarly if you notify a class then all resources in that class gets notifies.<br /><br />The preceding paragraph is very important to grasp, typically we'd try to maintain arrays of files in require trees and later if we add a new config file we have to go hunt everywhere that needs to require it and update those places, by using this organization approach we never have to update anything. &nbsp; If I add 10 services and 100 files into the config and service classes everything else that requires <i>ntp::service</i> will still work as expected without needing updates.&nbsp; <br /><br />This is a very important decoupling of class contents with class function.&nbsp; Your other classes should never be concerned with class contents only class function.<br /><br />As a convention this layout is a good sample, if all your daemons gets installed in the <i>daemon::install </i>class you'll have no problem knowing where to find the code to adjust the install behavior - perhaps to upgrade to the next version - you should try to come up with a set of module naming conventions that works for you and stick to it throughout your manifests - I have a rake task to setup my usual module structure, they're all the same and the learning curve for new people is small.<br /><br />By building up small classes that focus on their task or subtask you can build up ever more powerful modules.&nbsp; We have a simple example here, but you can see how you could create <i>ntp::master</i> and <i>ntp::client </i>wrapper classes that would simply reuse the normal <i>ntp::install</i> and <i>ntp::service</i> classes.<br /><br />As I mentioned this is a simple example for simple needs, the real take away here is to use class relationships and decouple the contents of those classes with their intent this should give you a more maintainable code set.<br /><br />Ultimately conventions such as these will be needed to get us all one step closer to being able to share modules amongst each other as the specifics of making Apache on different distributions work will not matter - we'd just know that we want <i>Class["apache::service"]</i> for example.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Vim and Puppet</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/09/22/vim_and_puppet.php" />
    <id>tag:www.devco.net,2009://1.472</id>

    <published>2009-09-22T13:38:25Z</published>
    <updated>2009-09-22T18:00:35Z</updated>

    <summary><![CDATA[I use vim for almost all my editing needs, till now I often thought I should give using TextMate another try due to the nice Bundle that Masterzen made but there's just something about desktop editing that puts me off.I've also been renewing my efforts to better my VIM use and came across the excellent snipMate that enables you to do almost the same thing as textmate - the syntax for the snippets is even roughly the same.I made a quick snippet file this morning for some common used bits that you can find here, see it in action below or view the movie better here:Each time you see the input cursor jump between fields in the templates I'm simply pressing tab, shift-tab would cycle backward.There are some nice things here you might not notice, for example editing a file test.pp and adding a class it will automatically fill in the class name as test.&nbsp; Same for nodes, it's really very powerful and the snippets are trivial to write.I am using the vim syntax file that comes in the puppet tarball and the adaryn color scheme that ships with vim. ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="puppet" label="puppet" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[I use vim for almost all my editing needs, till now I often thought I should give using TextMate another try due to the nice <a href="http://github.com/masterzen/puppet-textmate-bundle">Bundle</a> that <a href="http://www.masterzen.fr/">Masterzen</a> made but there's just something about desktop editing that puts me off.<br /><br />I've also been renewing my efforts to better my VIM use and came across the excellent <a href="http://www.vim.org/scripts/script.php?script_id=2540">snipMate</a> that enables you to do almost the same thing as textmate - the syntax for the snippets is even roughly the same.<br /><br />I made a quick snippet file this morning for some common used bits that you can find <a href="http://www.devco.net/code/puppet.snippets">here</a>, see it in action below or <a href="http://screenr.com/su7">view the movie better here</a>:<br /><br /><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0" height="345" width="560"><param name="movie" value="http://screenr.com/Content/assets/screenr_0817090731.swf" /><param name="flashvars" value="i=13889" /><param name="allowFullScreen" value="true" /><embed src="http://screenr.com/Content/assets/screenr_0817090731.swf" flashvars="i=13889" allowfullscreen="true" pluginspage="http://www.macromedia.com/go/getflashplayer" height="335" width="540"><a style="left: 511px ! important; top: 168.3px ! important;" title="Click here to block this object with Adblock Plus" class="lpxjcpxmlxqbbhzkofxr wrklgcbpyezztalkfyxf" href="http://screenr.com/Content/assets/screenr_0817090731.swf"></a></object><br /><br />Each time you see the input cursor jump between fields in the templates I'm simply pressing <i>tab</i>, <i>shift-tab</i> would cycle backward.<br /><br />There are some nice things here you might not notice, for example editing a file <i>test.pp</i> and adding a class it will automatically fill in the class name as <i>test</i>.&nbsp; Same for nodes, it's really very powerful and the snippets are trivial to write.<br /><br />I am using the vim syntax file that comes in the puppet tarball and the <i>adaryn</i> color scheme that ships with vim.<br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Snappy Snaps 120 Development</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/09/14/snappy_snaps_120_development.php" />
    <id>tag:www.devco.net,2009://1.471</id>

    <published>2009-09-14T08:21:28Z</published>
    <updated>2009-09-14T08:28:36Z</updated>

    <summary><![CDATA[Since moving into my current flat I've not really had a convenient place to develop film so have been putting the black and white shooting on the backburner.During the last week though I for some other reason thought to ask the Snappy Snaps near me and sure enough they did do 120 black and white.&nbsp; Problem is they wanted £18/roll developed and scanned and want 3 days to do it in.So I asked around and found out that the Snappy Snaps in Wardour Street London does it for £10/roll scanned on a standard 1 hour wait, that's very good.Below a scan direct from their scanner, I didn't touch it in any way (click for full size): You can see some more from this roll here it was taken on Ilford FP4 with my Bronica SQA, this is also the first time in over a year that I touched this camera so was fumbling around a bit, will get back into it now I think especially with a quick development place just 5 minutes from my office.]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Photography" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="bronica" label="bronica" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="photography" label="photography" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[Since moving into my current flat I've not really had a convenient place to develop film so have been putting the black and white shooting on the backburner.<br /><br />During the last week though I for some other reason thought to ask the Snappy Snaps near me and sure enough they did do 120 black and white.&nbsp; Problem is they wanted £18/roll developed and scanned and want 3 days to do it in.<br /><br />So I asked around and found out that the Snappy Snaps in Wardour Street London does it for £10/roll scanned on a standard 1 hour wait, that's very good.<br /><br />Below a scan direct from their scanner, I didn't touch it in any way (click for full size):<br /><br /><center><a href="http://www.devco.net/images/3912931374_3ceb381694_o.jpg"><img src="http://www.devco.net/images/3912931374_b3ca9245a9_m.jpg" border="0" /></a> </center><br /><br />You can see some more from this roll <a href="http://www.flickr.com/photos/ripienaar/tags/roll104/">here</a> it was taken on Ilford FP4 with my Bronica SQA, this is also the first time in over a year that I touched this camera so was fumbling around a bit, will get back into it now I think especially with a quick development place just 5 minutes from my office.<br />]]>
        
    </content>
</entry>

<entry>
    <title>More on Puppet 0.25 upgrade</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/09/11/more_on_puppet_025_upgrade.php" />
    <id>tag:www.devco.net,2009://1.470</id>

    <published>2009-09-11T21:25:06Z</published>
    <updated>2009-09-11T21:51:07Z</updated>

    <summary><![CDATA[Previously I posted a quick bit of information about Puppet 0.25 upgrade and how it sped up file transfers, below some more information.First some metrics from the puppetmaster before and after, this particular master handles around 50 clients on a 30 minute interval.&nbsp; The master is a Mongrel + Apache master with 4 worker processes running on a 64bit CentOS 5.3 VM with 1GB RAM First the CPU graph, blue is user time red is system time:The next is Load Average, it's a stack of 5, 10 and 15 minute averages:And finally here is a bandwidth of the master blue is outbound and green is incoming:So this is the master, it's very interesting that the master is a lot busier than before, even with just 50 nodes this is a significant CPU increase, I do not know how this will map to say 500 or 600 nodes but I think larger sites will want to be pretty careful about updating a large chunk of machines without testing the impact.Finally here is a graph from a node, the node has 450 file resources and doesn't do anything but run Puppet all day - at night in this graph for a short period it did backups after that it idled again.&nbsp; In this case combined with the massive drop in run time the cpu time is also way down, I think this is a massive win - you can always add more masters easily but suffering on all your nodes under puppetd is pretty bad.&nbsp; This on its own for me is a pretty big win for upgrading to Puppet 0.25.This graph is obviously taken some hours later but it's the same basic scale.I did not see a noticeable change in memory profile on either master of nodes so no graphs included here for that.Overall I think this is a big win, but be careful of what happens on your masters when you upgrade. ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="puppet" label="puppet" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[Previously I posted a <a href="http://www.devco.net/archives/2009/09/09/puppet_0250_upgrade.php">quick bit of information about Puppet 0.25 upgrade</a> and how it sped up file transfers, below some more information.<br /><br />First some metrics from the puppetmaster before and after, this particular master handles around 50 clients on a 30 minute interval.&nbsp; The master is a Mongrel + Apache master with 4 worker processes running on a 64bit CentOS 5.3 VM with 1GB RAM <br /><br />First the CPU graph, blue is user time red is system time:<br /><br /><center><img src="http://www.devco.net/images/0_24_vs_0_25/master_cpu.png" /></center><br />The next is Load Average, it's a stack of 5, 10 and 15 minute averages:<br /><br /><center><img src="http://www.devco.net/images/0_24_vs_0_25/master_load.png" /></center><br /><br />And finally here is a bandwidth of the master blue is outbound and green is incoming:<br /><br /><center><img src="http://www.devco.net/images/0_24_vs_0_25/master_bandwidth.png" /></center><br /><br />So this is the master, it's very interesting that the master is a lot busier than before, even with just 50 nodes this is a significant CPU increase, I do not know how this will map to say 500 or 600 nodes but I think larger sites will want to be pretty careful about updating a large chunk of machines without testing the impact.<br /><br />Finally here is a graph from a node, the node has 450 file resources and doesn't do anything but run Puppet all day - at night in this graph for a short period it did backups after that it idled again.&nbsp; In this case combined with the massive drop in run time the cpu time is also way down, I think this is a massive win - you can always add more masters easily but suffering on all your nodes under puppetd is pretty bad.&nbsp; This on its own for me is a pretty big win for upgrading to Puppet 0.25.<br /><br />This graph is obviously taken some hours later but it's the same basic scale.<br /><br /><center><img src="http://www.devco.net/images/0_24_vs_0_25/node_cpu.png" /></center><br /><br />I did not see a noticeable change in memory profile on either master of nodes so no graphs included here for that.<br /><br />Overall I think this is a big win, but be careful of what happens on your masters when you upgrade.<br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Puppet 0.25.0 upgrade</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/09/09/puppet_0250_upgrade.php" />
    <id>tag:www.devco.net,2009://1.469</id>

    <published>2009-09-09T17:39:46Z</published>
    <updated>2009-09-09T17:44:02Z</updated>

    <summary>Reductive Labs released version 0.25.0 of Puppet recently, there are some teething problems but I got most of it sorted out by upgrading Mongrel.Today I decided to upgrade 60 of my nodes and they&apos;ve slowly been running in a for loop all day, the results on machines with many file resources are staggering.One node that uses snippets heavily has 450 file resources and used to take a while to run, here&apos;s some before and after stats:Finished catalog run in 235.63 secondsFinished catalog run in 231.43 secondsFinished catalog run in 42.74 secondsFinished catalog run in 32.14 secondsVery impressed. </summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="puppet" label="puppet" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[Reductive Labs <a href="http://reductivelabs.com/trac/puppet/wiki/ReleaseNotes#id1">released version 0.25.0 of Puppet</a> recently, there are some teething problems but I got most of it sorted out by upgrading Mongrel.<br /><br />Today I decided to upgrade 60 of my nodes and they've slowly been running in a for loop all day, the results on machines with many file resources are staggering.<br /><br />One node that uses snippets heavily has 450 file resources and used to take a while to run, here's some before and after stats:<br /><br /><blockquote>Finished catalog run in 235.63 seconds<br />Finished catalog run in 231.43 seconds<br />Finished catalog run in 42.74 seconds<br />Finished catalog run in 32.14 seconds<br /></blockquote><br />Very impressed.<br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Snow Leopard and Samba</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/09/01/snow_leopard_and_samba.php" />
    <id>tag:www.devco.net,2009://1.468</id>

    <published>2009-09-01T21:34:19Z</published>
    <updated>2009-09-01T21:38:25Z</updated>

    <summary><![CDATA[I did a fresh install of Snow Leopard on my Macbook and soon realized my Samba shares were broken through finder - but still worked from the CLI.Worse still once I tried to access the shares Finder would basically be dead, you'd need to Force Quit it to make it work again.Eventually I reached for tcpdump and wireshark and found it's the pesky .DS_Store files again, seems my QNAP is denying access to them, Finder did not cope well with this.A quick bit of hackery of my smb.conf solved it:&nbsp;veto files = /.AppleDB/.AppleDouble/.AppleDesktop/.DS_Store/:2eDS_Store/Network Trash Folder/Temporary Items/TheVolumeSettingsFolder/.@__thumb/.@__desc/delete veto files = yesOnce I got this removed and samba restarted my shares were working again in Snow Leopard.&nbsp; A bit annoying but not too hard in the end. ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="apple" label="apple" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[I did a fresh install of Snow Leopard on my Macbook and soon realized my Samba shares were broken through finder - but still worked from the CLI.<br /><br />Worse still once I tried to access the shares Finder would basically be dead, you'd need to Force Quit it to make it work again.<br /><br />Eventually I reached for tcpdump and wireshark and found it's the pesky .DS_Store files again, seems my QNAP is denying access to them, Finder did not cope well with this.<br /><br />A quick bit of hackery of my smb.conf solved it:<br /><br /><blockquote>&nbsp;veto files = /.AppleDB/.AppleDouble/.AppleDesktop/.DS_Store/:2eDS_Store/Network Trash Folder/Temporary Items/TheVolumeSettingsFolder/.@__thumb/.@__desc/<br />delete veto files = yes<br /><br /></blockquote>Once I got this removed and samba restarted my shares were working again in Snow Leopard.&nbsp; A bit annoying but not too hard in the end.<br /><br /><br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Complex data and Puppet</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/08/31/complex_data_and_puppet.php" />
    <id>tag:www.devco.net,2009://1.467</id>

    <published>2009-08-31T20:29:58Z</published>
    <updated>2009-08-31T22:07:36Z</updated>

    <summary><![CDATA[Often while writing Puppet manifests you find yourself needing data, things like the local resolver, SMTP relay, SNMP Contact, Root Aliases etc. once you start thinking about it the amount of data you deal with is quite staggering.It's strange then that Puppet provides no way to work with this in a flexible way.&nbsp; By flexible I mean:A way to easily retrieve itA way to choose data per host, domain, location, data center or any other criteria you could possibly wishA way to provide defaults that allow your code to degrade gracefullyA way to make it a critical error should expected data not existA way that works with LDAP nodes, External Nodes or normal node{} blocksThis is quite a list of requirements, and in vanilla puppet you'd need to use case statements, if statements etc.For example, here's a use case, set SNMP Contact and root user alias.&nbsp; Some machines for a specific client should have different contact details than other, indeed even some machines should have different contact details.&nbsp; There should be a fall back default value should nothing be set specifically for a host.You might attempt to do this with case and if statements:class snmp::config {&nbsp;&nbsp; if $fqdn == "some.box.your.com" {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "Foo Sysadmin"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "sysadmin@foo.com"&nbsp;&nbsp; }&nbsp;&nbsp; if $domain == "bar.com" {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "Bar Sysadmin"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "sysadmin@bar.com"&nbsp;&nbsp; }&nbsp;&nbsp; if $location == "ldn_dc" &amp;&amp; (! $contactname &amp;&amp; ! $contactemail) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "London Sysadmin"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "ldnops@your.com"&nbsp;&nbsp; }&nbsp;&nbsp; if (! $contactname &amp;&amp; ! $contactemail) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "Sysadmin"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "sysadmin@you.com"&nbsp;&nbsp; }} &nbsp; You can see that this might work, but it's very unwieldy and your data is all over the code and soon enough you'll be nesting selectors in case statements inside if statements, it's totally unwieldy not to mention not reusable throughout your code.&nbsp;&nbsp; Not only is it unwieldy but if you wish to add more specifics in the future you will need to use tools like grep, find etc to find all the cases in your code where you use this and update them all.&nbsp; You could of course come up with one file that contains all this logic but it would be aweful, I've tried it's not viable.What we really want to do is just this, and it should take care of all the code above, you should be able to call this wherever you want with complete disregard for the specifics of the overrides in data:$contactname = extlookup("contactname")$contactemail = extlookup("contactemail")I've battled for ages with ways to deal with this and have come up with something that fits the bill perfectly, been using it and promoting it for almost a year now and so far found it to be totally life saver.Sticking with the example above, first we should configure a lookup order that will work for us, here is my actual use:$extlookup_precedence = ["%{fqdn}", "location_%{location}", "domain_%{domain}", "country_%{country}", "common"]This sets up the lookup code to first look for data specified for the host, then the location the host is hosted at, then the domain, country and eventually a set of defaults.My current version of this code uses CSV files to store the data simply because it was convenient and universally available with no barrier to entry.&nbsp; It would be trivial to extend the code to use a database, LDAP or other system like that.For my example if I put into the file some.box.your.com.csv the following:contactemail,sysadmin@foo.comcontactname,Foo SysadminAnd in common.csv if I put:contactemail,sysadmin@you.comcontactname,SysadminThe lookup code will use this data whenever extlookup("contactemail") gets called on that machine, but will use the default when called from other hosts.&nbsp; If you follow the logic above you'll see this completely replace the case statement above with simple data files.&nbsp; &nbsp; Using a system like this you can model all your data needs and deal with the data and your location, machine, domain etc specific data outside of your manifests.The code is very flexible, you can reuse existing variables in your code inside your data, for example:ntpservers,1.pool.%{country}.ntp.org,2.pool.%{country}.ntp.orgIn this case if you have $country defined in your manifest the code will use this variable and put it into the answer.&nbsp; This snippet of data also shows that it supports arrays.Here is another use case:package{"screen":&nbsp;&nbsp; ensure =&gt; extlookup("pkg_screen", "absent")}This code will ensure that, unless otherwise specified, I do not want to have screen installed on any of my servers.&nbsp; I could now though decide that all machines in a domain, or all machines in a location, country or specific hosts could have screen installed by simply setting them to present in the data file.&nbsp; This makes the code not only configurable but configurable in a way that suits every possible case as it depends on the precedence defined above.&nbsp; If your use case does not rely on countries for example you can just replace the country ordering with whatever works for you.I use this code in all my manifests and it's helped me to make an extremely configurable set of manifests.&nbsp; It has proven to be very flexible as I can use the same code for different clients in different industries and with different needs and network layouts without changing the code.The code as it stands is available here: http://www.devco.net/code/extlookup.rb Follow the comments in the script for install instructions and full usage guides.]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="puppet" label="puppet" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[Often while writing <a href="http://reductivelabs.com/products/puppet/">Puppet</a> manifests you find yourself needing data, things like the local resolver, SMTP relay, SNMP Contact, Root Aliases etc. once you start thinking about it the amount of data you deal with is quite staggering.<br /><br />It's strange then that Puppet provides no way to work with this in a flexible way.&nbsp; By flexible I mean:<br /><br /><ul><li>A way to easily retrieve it</li><li>A way to choose data per host, domain, location, data center or any other criteria you could possibly wish</li><li>A way to provide defaults that allow your code to degrade gracefully</li><li>A way to make it a critical error should expected data not exist</li><li>A way that works with LDAP nodes, External Nodes or normal node{} blocks</li></ul>This is quite a list of requirements, and in vanilla puppet you'd need to use case statements, if statements etc.<br /><br />For example, here's a use case, set SNMP Contact and root user alias.&nbsp; Some machines for a specific client should have different contact details than other, indeed even some machines should have different contact details.&nbsp; There should be a fall back default value should nothing be set specifically for a host.<br /><br />You might attempt to do this with case and if statements:<br /><br /><blockquote>class snmp::config {<br />&nbsp;&nbsp; if $fqdn == "some.box.your.com" {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "Foo Sysadmin"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "sysadmin@foo.com"<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; if $domain == "bar.com" {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "Bar Sysadmin"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "sysadmin@bar.com"<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; if $location == "ldn_dc" &amp;&amp; (! $contactname &amp;&amp; ! $contactemail) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "London Sysadmin"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "ldnops@your.com"<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; if (! $contactname &amp;&amp; ! $contactemail) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactname = "Sysadmin"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $contactemail = "sysadmin@you.com"<br />&nbsp;&nbsp; }<br />} <br /></blockquote>&nbsp; <br />You can see that this might work, but it's very unwieldy and your data is all over the code and soon enough you'll be nesting selectors in case statements inside if statements, it's totally unwieldy not to mention not reusable throughout your code.&nbsp;&nbsp; <br /><br />Not only is it unwieldy but if you wish to add more specifics in the future you will need to use tools like grep, find etc to find all the cases in your code where you use this and update them all.&nbsp; You could of course come up with one file that contains all this logic but it would be aweful, I've tried it's not viable.<br /><br />What we really want to do is just this, and it should take care of all the code above, you should be able to call this wherever you want with complete disregard for the specifics of the overrides in data:<br /><br /><blockquote>$contactname = extlookup("contactname")<br />$contactemail = extlookup("contactemail")<br /></blockquote>I've battled for ages with ways to deal with this and have come up with something that fits the bill perfectly, been using it and promoting it for almost a year now and so far found it to be totally life saver.<br /><br />Sticking with the example above, first we should configure a lookup order that will work for us, here is my actual use:<br /><br /><blockquote>$extlookup_precedence = ["%{fqdn}", "location_%{location}", "domain_%{domain}", "country_%{country}", "common"]<br /></blockquote><br />This sets up the lookup code to first look for data specified for the host, then the location the host is hosted at, then the domain, country and eventually a set of defaults.<br /><br />My current version of this code uses CSV files to store the data simply because it was convenient and universally available with no barrier to entry.&nbsp; It would be trivial to extend the code to use a database, LDAP or other system like that.<br /><br />For my example if I put into the file <i>some.box.your.com.csv</i> the following:<br /><br /><blockquote>contactemail,sysadmin@foo.com<br />contactname,Foo Sysadmin<br /><br /></blockquote>And in <i>common.csv</i> if I put:<br /><br /><blockquote>contactemail,sysadmin@you.com<br />contactname,Sysadmin<br /></blockquote><br />The lookup code will use this data whenever <i>extlookup("contactemail")</i> gets called on that machine, but will use the default when called from other hosts.&nbsp; If you follow the logic above you'll see this completely replace the case statement above with simple data files.&nbsp; <br />&nbsp; <br />Using a system like this you can model all your data needs and deal with the data and your location, machine, domain etc specific data outside of your manifests.<br /><br />The code is very flexible, you can reuse existing variables in your code inside your data, for example:<br /><br /><blockquote>ntpservers,1.pool.%{country}.ntp.org,2.pool.%{country}.ntp.org<br /><br /></blockquote>In this case if you have $country defined in your manifest the code will use this variable and put it into the answer.&nbsp; This snippet of data also shows that it supports arrays.<br /><br />Here is another use case:<br /><br /><blockquote>package{"screen":<br />&nbsp;&nbsp; ensure =&gt; extlookup("pkg_screen", "absent")<br />}<br /></blockquote><br />This code will ensure that, unless otherwise specified, I do not want to have <i>screen</i> installed on any of my servers.&nbsp; I could now though decide that all machines in a domain, or all machines in a location, country or specific hosts could have screen installed by simply setting them to <i>present</i> in the data file.&nbsp; <br /><br />This makes the code not only configurable but configurable in a way that suits every possible case as it depends on the precedence defined above.&nbsp; If your use case does not rely on countries for example you can just replace the country ordering with whatever works for you.<br /><br />I use this code in all my manifests and it's helped me to make an extremely configurable set of manifests.&nbsp; It has proven to be very flexible as I can use the same code for different clients in different industries and with different needs and network layouts without changing the code.<br /><br />The code as it stands is available here: <a href="http://www.devco.net/code/extlookup.rb">http://www.devco.net/code/extlookup.rb </a><br /><br />Follow the comments in the script for install instructions and full usage guides.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Ruby PDNS 0.4</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/08/31/ruby_pdns_04.php" />
    <id>tag:www.devco.net,2009://1.466</id>

    <published>2009-08-31T17:51:08Z</published>
    <updated>2009-08-31T17:58:49Z</updated>

    <summary><![CDATA[Yesterday I released version 0.4 of my Ruby PowerDNS development framework.&nbsp; Version 0.3 was a feature complete version but lacked in some decent error handling in all cases which resulted in weird unexplained crashes when things didn't work as hoped, for example syntax errors in records could kill the whole thing.Version 0.4 is a big push in stability, I've added tons of exception handling there should now be very few cases of unexpected terminations, I know of only one case and that's when the log can't be written too, all other cases should be logged and recovered from in some hopefully sane way.I've written loads of unit tests using Test::Unit and have created a little testing harness that can be used to test your records without putting them on the server, using this for example you can test GeoIP based records easily since you can specify any source address.Overall I think this is a production ready release, it would be a 1.0 release was it not for some features I wish to add before calling it 1.0.&nbsp; The features are about logging stats and about consuming data from external sources, these will be my next priorities. ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="dns" label="dns" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rubypdns" label="ruby-pdns" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[Yesterday I released <a href="http://groups.google.com/group/ruby-pdns-users/browse_thread/thread/17c84d0ff70fec0c">version 0.4</a> of my Ruby PowerDNS development framework.&nbsp; <br /><br />Version 0.3 was a feature complete version but lacked in some decent error handling in all cases which resulted in weird unexplained crashes when things didn't work as hoped, for example syntax errors in records could kill the whole thing.<br /><br />Version 0.4 is a big push in stability, I've added tons of exception handling there should now be very few cases of unexpected terminations, I know of only one case and that's when the log can't be written too, all other cases should be logged and recovered from in some hopefully sane way.<br /><br />I've written loads of unit tests using Test::Unit and have created a little testing harness that can be used to test your records without putting them on the server, using this for example you can test GeoIP based records easily since you can specify any source address.<br /><br />Overall I think this is a production ready release, it would be a 1.0 release was it not for some features I wish to add before calling it 1.0.&nbsp; The features are about logging stats and about consuming data from external sources, these will be my next priorities.<br /> ]]>
        
    </content>
</entry>

<entry>
    <title>hetzner.de hardware policies</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/08/24/hetznerde_hardware_policies.php" />
    <id>tag:www.devco.net,2009://1.465</id>

    <published>2009-08-24T17:02:46Z</published>
    <updated>2009-08-24T17:14:59Z</updated>

    <summary><![CDATA[So I use Hetzner a lot for my machines, I've about 10 to 15 of their machines now across various clients and am mostly quite happy with them.&nbsp; They provide a service that matches the price - ie. good enough.One area of their service though really grates me, they give you old machines, when those machines fail they replace them with other old machines similarly for drives etc.On more than one occasion now have I had hard drives fail only to see them replaced with other shitty drives.&nbsp; Each time they claim the drives are well tested and each time they pull the old 'it could be the cable' trick and then replace the machine and the drive.Since this has happened to me every single time I've changed a disk so far I have to wonder if this is everyones experience?&nbsp; From where I sit its simple.&nbsp; They made a choice to take out drives reported broken by someone, they then test it and put it back when their tests fail to find any problem, they do this to save them money knowing full well that drives will fail and all they're doing is shifting the risk onto their clients, while the clients keep subsidizing their expansion.&nbsp; So given this is the quality of service they're aiming at, surely once this policy bites a good long standing user offering some kind of payback for the inconvenience would be good business practice?&nbsp; Apparently not.This is pretty poor, even after complaining to them they swapped my chassis and again put a disk with &gt; 6000 hours under its belt in my machine.So I guess you need to be pretty sure your softraids are setup properly when you want to use this company, their support stand is clear:I'm sorry but we don't promise anywhere that we built always new hardware into our servers. I can only ensure you that all hardware is always well tested and without any problem before we build it into a server.Ie., screw you, we don't care for any evidence and repeated failures, and we take zero responsibility for our equipment, we'll just keep taking your money. ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Rantings" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="hetzner" label="hetzner" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="hosting" label="hosting" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[So I use <a href="http://www.hetzner.de/">Hetzner</a> a lot for my machines, I've about 10 to 15 of their machines now across various clients and am mostly quite happy with them.&nbsp; They provide a service that matches the price - ie. good enough.<br /><br />One area of their service though really grates me, they give you old machines, when those machines fail they replace them with other old machines similarly for drives etc.<br /><br />On more than one occasion now have I had hard drives fail only to see them replaced with other shitty drives.&nbsp; Each time they claim the drives are well tested and each time they pull the old 'it could be the cable' trick and then replace the machine and the drive.<br /><br />Since this has happened to me every single time I've changed a disk so far I have to wonder if this is everyones experience?&nbsp; <br /><br />From where I sit its simple.&nbsp; They made a choice to take out drives reported broken by someone, they then test it and put it back when their tests fail to find any problem, they do this to save them money knowing full well that drives will fail and all they're doing is shifting the risk onto their clients, while the clients keep subsidizing their expansion.&nbsp; <br /><br />So given this is the quality of service they're aiming at, surely once this policy bites a good long standing user offering some kind of payback for the inconvenience would be good business practice?&nbsp; Apparently not.<br /><br />This is pretty poor, even after complaining to them they swapped my chassis and again put a disk with &gt; 6000 hours under its belt in my machine.<br /><br />So I guess you need to be pretty sure your softraids are setup properly when you want to use this company, their support stand is clear:<br /><br /><blockquote>I'm sorry but we don't promise anywhere that we built always new hardware into our servers. I can only ensure you that all hardware is always well tested and without any problem before we build it into a server.<br /></blockquote>Ie., screw you, we don't care for any evidence and repeated failures, and we take zero responsibility for our equipment, we'll just keep taking your money.<br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Tips and Tricks for puppet debugging</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/08/19/tips_and_tricks_for_puppet_debugging.php" />
    <id>tag:www.devco.net,2009://1.464</id>

    <published>2009-08-19T17:16:57Z</published>
    <updated>2009-08-27T08:23:17Z</updated>

    <summary><![CDATA[We often have people asking 'will this work...' or 'how do I...' type questions on IRC, usually because it seems like such a big deal to upload a bit of code to your master just to test.Here are a quick few tips and tricks for testing out bits of puppet code to get the feel for things, I'll show you how to test code without using your puppetmaster or needing root, so it's ideal for just playing around on your shell, exploring the language structure and syntax..Getting more infoMost people know this one, but just running puppetd --test
will highlight all the various steps puppet is taking, any actions its
performing etc in a nice colored display, handy to just do one-offs and
see what is happening.Testing small bits of code:Often you're not sure if you've
got the right syntax, especially for case statements, selectors and
such, or you just want to test out some scenarios, you can't just dump
the stuff into your master because it might not even compile.Puppet
comes with a executable called 'puppet' that's perfect for this task,
simply puppet some manifest into a file called test.pp and run it:puppet --debug --verbose test.ppThis
will run through your code in test.pp and execute it.&nbsp; You should be
aware that you couldn't fetch files from the master in this case since
it's purely local but see the File types reference for a explanation of
how the behavior changes - you can still copy files, just not from the
master.You can do anything that puppet code can do, make classes, do defines, make sub classes, install packages, this is great for testing out small concepts in a safe way.&nbsp; Everything you see in this article was done in my shell like this.What is the value of a variable?You've set a variable in some class but you're not sure if it's set to what you're expecting, maybe you don't know the scoping rules just yet or you just want to log some state back to the node or master log.In a usual scripting language you'd add some debug prints, puppet is the same.&nbsp; You can print simple values in the master log by doing this in your manifest:notice("The value is: ${yourvar}")Now when you run your node you should see this being printed to the syslog (by default) on your master.To log something to your client, do this:notify{"The value is: ${yourvar}": }Now your puppet logs - syslog usually - will show lines on the client or you could just do puppetd --test on the client to see it run and see your debug bits.What is in an array?You've made an array, maybe from some external function, you want to know what is in it?&nbsp; This really is an extension to the above hint that would print garbage when passed arrays.Building on the example above and the fact that puppet loops using resources, lets make a simple defined type that prints each member of an array out to either the master or the client (use the technique above to choose)$arr = [1, 2, 3]define print() {&nbsp;&nbsp; notice("The value is: '${name}'")}print{$arr: }This will print one line for each member in the array - or just one if $arr isn't an array at all.$ puppet test.ppnotice: Scope(Print[1]): The value is: '1'notice: Scope(Print[3]): The value is: '3'notice: Scope(Print[2]): The value is: '2'Writing shell scripts with puppet?Puppets a great little language, you might even want to replace some general shell scripts with puppet manifest code, here's a simple hello world:#!/usr/bin/puppetnotice("Hello world from puppet!")notice("This is the host ${fqdn}")If we run this we get the predictable output:$ ./test.ppnotice: Scope(Class[main]): Hello world from puppet!notice: Scope(Class[main]): This is the host your.box.comAnd note that you can access facts and everything from within this shell script language, really nifty!Did I get the syntax right?If I introduce a deliberate error in the code above - remove the last " - it would blow up, you can test puppet syntax using puppet itself:$ puppet --parseonly test.pperr: Could not parse for environment production: Unclosed quote after '' in 'Hello world from puppet!)' at /home/rip/test.pp:1You can combine this with a pre-commit hook on your SCM to make sure you don't check in bogus stuff.How should I specify the package version? or user properties?Often you've added a package, but not 100% sure how to pass the version string&nbsp; to ensure =&gt; or you're not sure how to specify password hashes etc, puppet comes with something called ralsh that can interrogate a running system, some samples below:% ralsh package httpdpackage { 'httpd':&nbsp;&nbsp;&nbsp; ensure =&gt; '2.2.3-22.el5.centos.2'}% ralsh package httpd.i386package { 'httpd.i386':&nbsp;&nbsp;&nbsp; ensure =&gt; '2.2.3-22.el5.centos.2'}# ralsh user apacheuser { 'apache':&nbsp;&nbsp;&nbsp; password =&gt; '!!',&nbsp;&nbsp;&nbsp; uid =&gt; '48',&nbsp;&nbsp;&nbsp; comment =&gt; 'Apache',&nbsp;&nbsp;&nbsp; home =&gt; '/var/www',&nbsp;&nbsp;&nbsp; gid =&gt; '48',&nbsp;&nbsp;&nbsp; ensure =&gt; 'present',&nbsp;&nbsp;&nbsp; shell =&gt; '/sbin/nologin'}Note in the 2nd case I ran it as root, puppet needs to be able to read shadow and so forth.&nbsp; The code it's outputting is valid puppet code that you can put in manifests.Will Puppet destroy my machine?Maybe you're just getting ready to run puppet on a host for the first time or you're testing some new code and you want to be sure nothing terrible will happen, puppetd has a no-op option to make it just print what will happen, just run puppetd --test --noopWhat files etc are being managed by puppet?See my previous post for a script that can tell you what puppet is managing on your machine, note this does not yet work on 0.25.x branch of code.Is my config changes taking effect?Often people make puppet.conf changes and it just isn't working, perhaps they put them in the wrong [section] in the config file, a simple way to test is to run puppetd --genconfig this will dump all the active configuration options.Be careful though don't just dump this file over your puppet.conf thinking you'll have a nice commented file, this wont work as the option for genconfig will be set to true in the file and you'll end up with a broken configuration.&nbsp; In general I recommend keeping puppet.conf simple and short only showing the things you're changing away from defaults, that makes it much easier to see what differs from standard behavior when asking for help.Getting further helpPuppet has a irc channel #puppet on freenode, we try and be helpful and welcoming to newcomers, pop by if you have questions.As always you need to have these wiki pages bookmarked and they should be your daily companions:Language TutorialType ReferenceFunction ReferenceConfiguration ReferenceCommon MisconceptionsFAQ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Usefull Things" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="puppet" label="puppet" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[We often have people asking 'will this work...' or 'how do I...' type questions on IRC, usually because it seems like such a big deal to upload a bit of code to your master just to test.<br /><br />Here are a quick few tips and tricks for testing out bits of puppet code to get the feel for things, I'll show you how to test code without using your puppetmaster or needing root, so it's ideal for just playing around on your shell, exploring the language structure and syntax..<br /><br /><b>Getting more info<br /></b>Most people know this one, but just running <i>puppetd --test</i>
will highlight all the various steps puppet is taking, any actions its
performing etc in a nice colored display, handy to just do one-offs and
see what is happening.<br /><br /><b>Testing small bits of code:</b><br />Often you're not sure if you've
got the right syntax, especially for case statements, selectors and
such, or you just want to test out some scenarios, you can't just dump
the stuff into your master because it might not even compile.<br /><br />Puppet
comes with a executable called 'puppet' that's perfect for this task,
simply puppet some manifest into a file called test.pp and run it:<br /><br /><blockquote>puppet --debug --verbose test.pp<br /></blockquote>This
will run through your code in test.pp and execute it.&nbsp; You should be
aware that you couldn't fetch files from the master in this case since
it's purely local but see the File types reference for a explanation of
how the behavior changes - you can still copy files, just not from the
master.<br /><br />You can do anything that puppet code can do, make classes, do defines, make sub classes, install packages, this is great for testing out small concepts in a safe way.&nbsp; Everything you see in this article was done in my shell like this.<br /><br /><b>What is the value of a variable?</b><br />You've set a variable in some class but you're not sure if it's set to what you're expecting, maybe you don't know the scoping rules just yet or you just want to log some state back to the node or master log.<br /><br />In a usual scripting language you'd add some debug prints, puppet is the same.&nbsp; You can print simple values in the master log by doing this in your manifest:<br /><br /><blockquote>notice("The value is: ${yourvar}")<br /></blockquote>Now when you run your node you should see this being printed to the syslog (by default) on your master.<br /><br />To log something to your client, do this:<br /><br /><blockquote>notify{"The value is: ${yourvar}": }<br /></blockquote><br />Now your puppet logs - syslog usually - will show lines on the client or you could just do <i>puppetd --test</i> on the client to see it run and see your debug bits.<br /><br /><b>What is in an array?</b><br />You've made an array, maybe from some external function, you want to know what is in it?&nbsp; This really is an extension to the above hint that would print garbage when passed arrays.<br /><br />Building on the example above and the fact that puppet loops using resources, lets make a simple defined type that prints each member of an array out to either the master or the client (use the technique above to choose)<br /><br /><blockquote>$arr = [1, 2, 3]<br /><br />define print() {<br />&nbsp;&nbsp; notice("The value is: '${name}'")<br />}<br /><br />print{$arr: }<br /></blockquote>This will print one line for each member in the array - or just one if $arr isn't an array at all.<br /><br /><blockquote>$ puppet test.pp<br />notice: Scope(Print[1]): The value is: '1'<br />notice: Scope(Print[3]): The value is: '3'<br />notice: Scope(Print[2]): The value is: '2'<br /><br /></blockquote><b>Writing shell scripts with puppet?</b><br />Puppets a great little language, you might even want to replace some general shell scripts with puppet manifest code, here's a simple hello world:<br /><br /><blockquote>#!/usr/bin/puppet<br /><br />notice("Hello world from puppet!")<br />notice("This is the host ${fqdn}")<br /></blockquote><br />If we run this we get the predictable output:<br /><br /><blockquote>$ ./test.pp<br />notice: Scope(Class[main]): Hello world from puppet!<br />notice: Scope(Class[main]): This is the host your.box.com<br /></blockquote>And note that you can access facts and everything from within this shell script language, really nifty!<br /><br /><b>Did I get the syntax right?<br /></b>If I introduce a deliberate error in the code above - remove the last " - it would blow up, you can test puppet syntax using puppet itself:<br /><br /><blockquote>$ puppet --parseonly test.pp<br />err: Could not parse for environment production: Unclosed quote after '' in 'Hello world from puppet!)<br />' at /home/rip/test.pp:1<br /></blockquote>You can combine this with a pre-commit hook on your SCM to make sure you don't check in bogus stuff.<br /><br /><b>How should I specify the package version? or user properties?</b><br />Often you've added a package, but not 100% sure how to pass the version string&nbsp; to <i>ensure =&gt;</i> or you're not sure how to specify password hashes etc, puppet comes with something called ralsh that can interrogate a running system, some samples below:<br /><br /><blockquote>% ralsh package httpd<br />package { 'httpd':<br />&nbsp;&nbsp;&nbsp; ensure =&gt; '2.2.3-22.el5.centos.2'<br />}<br /><br />% ralsh package httpd.i386<br />package { 'httpd.i386':<br />&nbsp;&nbsp;&nbsp; ensure =&gt; '2.2.3-22.el5.centos.2'<br />}<br /><br /># ralsh user apache<br />user { 'apache':<br />&nbsp;&nbsp;&nbsp; password =&gt; '!!',<br />&nbsp;&nbsp;&nbsp; uid =&gt; '48',<br />&nbsp;&nbsp;&nbsp; comment =&gt; 'Apache',<br />&nbsp;&nbsp;&nbsp; home =&gt; '/var/www',<br />&nbsp;&nbsp;&nbsp; gid =&gt; '48',<br />&nbsp;&nbsp;&nbsp; ensure =&gt; 'present',<br />&nbsp;&nbsp;&nbsp; shell =&gt; '/sbin/nologin'<br />}<br /><br /></blockquote>Note in the 2nd case I ran it as root, puppet needs to be able to read shadow and so forth.&nbsp; The code it's outputting is valid puppet code that you can put in manifests.<br /><br /><b>Will Puppet destroy my machine?</b><br />Maybe you're just getting ready to run puppet on a host for the first time or you're testing some new code and you want to be sure nothing terrible will happen, puppetd has a no-op option to make it just print what will happen, just run <i>puppetd --test --noop</i><br /><br /><b>What files etc are being managed by puppet?</b><br />See <a href="http://www.devco.net/archives/2009/07/30/what_does_puppet_manage_on_a_node.php">my previous post for a script that can tell you</a> what puppet is managing on your machine, note this does not yet work on 0.25.x branch of code.<br /><br /><b>Is my config changes taking effect?</b><br />Often people make <i>puppet.conf</i> changes and it just isn't working, perhaps they put them in the wrong [section] in the config file, a simple way to test is to run <i>puppetd --genconfig</i> this will dump all the active configuration options.<br /><br />Be careful though don't just dump this file over your <i>puppet.conf</i> thinking you'll have a nice commented file, this wont work as the option for <i>genconfig</i> will be set to true in the file and you'll end up with a broken configuration.&nbsp; In general I recommend keeping <i>puppet.conf</i> simple and short only showing the things you're changing away from defaults, that makes it much easier to see what differs from standard behavior when asking for help.<br /><br /><b>Getting further help</b><br />Puppet has a irc channel #puppet on freenode, we try and be helpful and welcoming to newcomers, pop by if you have questions.<br /><br />As always you need to have these wiki pages bookmarked and they should be your daily companions:<br /><br /><ul><li><a href="http://reductivelabs.com/trac/puppet/wiki/LanguageTutorial">Language Tutorial</a></li><li><a href="http://reductivelabs.com/trac/puppet/wiki/TypeReference">Type Reference</a></li><li><a href="http://reductivelabs.com/trac/puppet/wiki/FunctionReference">Function Reference</a></li><li><a href="http://reductivelabs.com/trac/puppet/wiki/ConfigurationReference">Configuration Reference</a><br /></li><li><a href="http://reductivelabs.com/trac/puppet/wiki/CommonMisconceptions">Common Misconceptions</a></li><li><a href="http://reductivelabs.com/trac/puppet/wiki/FrequentlyAskedQuestions">FAQ</a></li></ul>]]>
        
    </content>
</entry>

<entry>
    <title>Managing web traffic with ruby-pdns</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/08/17/managing_web_traffic_with_ruby-pdns.php" />
    <id>tag:www.devco.net,2009://1.463</id>

    <published>2009-08-17T21:43:39Z</published>
    <updated>2009-08-18T06:54:19Z</updated>

    <summary><![CDATA[A short while ago I wrote about releasing a Ruby Development framework for PowerDNS the release is still early days, feature complete but needs some robustness tweaks and a new release will be out in a week or so to address that.I wanted though to highlight some success that I've had using it.&nbsp; I have a small static farm for a client that handles around 2MiB/sec of 200x200 jpg files, this setup is for a startup so out of necessity its all built to be cheap, I host on networks I don't own yet I need pretty good control over it, what IPs will be used to serve traffic and so forth.The graph above shows the case before caused by the windows DNS bug, you'll see the bottom host is working pretty hard getting a large chunk of the bandwidth.This is a problem because come mid month this poor machine has already used up its allocation of 2.5TiB of transfer and I need to move it from the pool.So my goal was to shift the traffic to the yellow and green machines and just generally balance things out a bit.&nbsp;&nbsp; I used the Weighted Round Robin feature of ruby-pdns to adjust the biases, it took a bit of fiddling because for some other reason even when this machine gets fewer requests per second it still seems to manage more in terms of bandwidth, this is the eventual code snippet:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ips = ["213.x.x.232",&nbsp;&nbsp;&nbsp;&nbsp; # dark blue&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "88.x.x.201",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # lighter blue&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "82.x.x.180",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # yellow&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "82.x.x.181"].randomize([1,2,2,3])&nbsp;&nbsp; # green&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.ttl 300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.shuffle false&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.content [:A, ips[0]]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.content [:A, ips[1]]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.content [:A, ips[2]]The thresholds seems odd but that's what worked after some fiddling, see the graph below.This is much nicer balanced, it's not perfect and I doubt I will get it perfect with just 4 machines to play with but I believe it's already at the point where it means I can use all my machines for the entire month without hitting any limits.Here's another graph over the week showing things side by side:The improvement is very obvious in this graph and you can see I've not lost anything in performance between first day and last day on the graph in terms of throughput (the lower days were days where lower traffic is expected).If I look at my actual transfer used it's better balanced now, first lets see the 12th:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp; 12.67 GiB |&nbsp;&nbsp; 46.42 GiB |&nbsp;&nbsp; 59.09 GiB |&nbsp;&nbsp;&nbsp; 5.74 Mbit/s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.71 GiB |&nbsp;&nbsp; 21.32 GiB |&nbsp;&nbsp; 29.04 GiB |&nbsp;&nbsp;&nbsp; 2.82 Mbit/s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9.05 GiB |&nbsp;&nbsp; 23.05 GiB |&nbsp;&nbsp; 32.10 GiB |&nbsp;&nbsp;&nbsp; 3.12 Mbit/s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.94 GiB |&nbsp;&nbsp; 16.56 GiB |&nbsp;&nbsp; 23.50 GiB |&nbsp;&nbsp;&nbsp; 2.28 Mbit/sAgain the skew is very clear with a 23GiB on the lowest compared to 59GiB on the highest use machine, on the 17th it looked a lot better:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.84 GiB |&nbsp;&nbsp; 28.55 GiB |&nbsp;&nbsp; 36.39 GiB |&nbsp;&nbsp;&nbsp; 3.53 Mbit/s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8.46 GiB |&nbsp;&nbsp; 25.66 GiB |&nbsp;&nbsp; 34.12 GiB |&nbsp;&nbsp;&nbsp; 3.31 Mbit/s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp; 11.21 GiB |&nbsp;&nbsp; 30.70 GiB |&nbsp;&nbsp; 41.91 GiB |&nbsp;&nbsp;&nbsp; 4.07 Mbit/s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp; 10.25 GiB |&nbsp;&nbsp; 28.20 GiB |&nbsp;&nbsp; 38.46 GiB |&nbsp;&nbsp;&nbsp; 3.73 Mbit/sObviously much better when looking at the 2nd to last column.&nbsp; The first column is received the increase in those is down to a slightly lower hit ratio on the caching proxy on these machines meaning it's fetching more files from origin than the others.Overall I am extremely pleased with this solution, I agree one should not be using DNS as a hammer to all your nails but for startups and cloud based people who do not have control over networks, BGP tables and so forth this really does represent a viable option to what would otherwise be an extremely expensive problem to solve.]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Code" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="dns" label="dns" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="networking" label="networking" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="programming" label="programming" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rubypdns" label="ruby-pdns" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[A short while ago I wrote about releasing a <a href="http://www.devco.net/archives/2009/08/10/ruby_powerdns_framework.php">Ruby Development framework for PowerDNS</a> the release is still early days, feature complete but needs some robustness tweaks and a new release will be out in a week or so to address that.<br /><br />I wanted though to highlight some success that I've had using it.&nbsp; I have a small static farm for a client that handles around 2MiB/sec of 200x200 jpg files, this setup is for a startup so out of necessity its all built to be cheap, I host on networks I don't own yet I need pretty good control over it, what IPs will be used to serve traffic and so forth.<br /><br /><center><img src="http://www.devco.net/images/static-balance-3.png" /></center><br />The graph above shows the case before caused by <a href="http://blogs.technet.com/networking/archive/2009/04/17/dns-round-robin-and-destination-ip-address-selection.aspx">the windows DNS bug</a>, you'll see the bottom host is working pretty hard getting a large chunk of the bandwidth.<br /><br />This is a problem because come mid month this poor machine has already used up its allocation of 2.5TiB of transfer and I need to move it from the pool.<br /><br />So my goal was to shift the traffic to the yellow and green machines and just generally balance things out a bit.&nbsp;&nbsp; I used the <a href="http://code.google.com/p/ruby-pdns/wiki/RecipeWeightedRoundRobin">Weighted Round Robin</a> feature of ruby-pdns to adjust the biases, it took a bit of fiddling because for some other reason even when this machine gets fewer requests per second it still seems to manage more in terms of bandwidth, this is the eventual code snippet:<br /><br /><blockquote>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ips = ["213.x.x.232",&nbsp;&nbsp;&nbsp;&nbsp; # dark blue<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "88.x.x.201",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # lighter blue<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "82.x.x.180",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # yellow<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "82.x.x.181"].randomize([1,2,2,3])&nbsp;&nbsp; # green<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.ttl 300<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.shuffle false<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.content [:A, ips[0]]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.content [:A, ips[1]]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; answer.content [:A, ips[2]]<br /><br /></blockquote>The thresholds seems odd but that's what worked after some fiddling, see the graph below.<br /><br /><br /><center><img src="http://www.devco.net/images/static-balance-2.png" /></center><br />This is much nicer balanced, it's not perfect and I doubt I will get it perfect with just 4 machines to play with but I believe it's already at the point where it means I can use all my machines for the entire month without hitting any limits.<br /><br />Here's another graph over the week showing things side by side:<br /><br /><center><img src="http://www.devco.net/images/static-balance-1.png" /></center><br />The improvement is very obvious in this graph and you can see I've not lost anything in performance between first day and last day on the graph in terms of throughput (the lower days were days where lower traffic is expected).<br /><br />If I look at my actual transfer used it's better balanced now, first lets see the 12th:<br /><br /><blockquote>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp; 12.67 GiB |&nbsp;&nbsp; 46.42 GiB |&nbsp;&nbsp; 59.09 GiB |&nbsp;&nbsp;&nbsp; 5.74 Mbit/s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.71 GiB |&nbsp;&nbsp; 21.32 GiB |&nbsp;&nbsp; 29.04 GiB |&nbsp;&nbsp;&nbsp; 2.82 Mbit/s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9.05 GiB |&nbsp;&nbsp; 23.05 GiB |&nbsp;&nbsp; 32.10 GiB |&nbsp;&nbsp;&nbsp; 3.12 Mbit/s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/12/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.94 GiB |&nbsp;&nbsp; 16.56 GiB |&nbsp;&nbsp; 23.50 GiB |&nbsp;&nbsp;&nbsp; 2.28 Mbit/s<br /></blockquote>Again the skew is very clear with a 23GiB on the lowest compared to 59GiB on the highest use machine, on the 17th it looked a lot better:<br /><br /><blockquote>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.84 GiB |&nbsp;&nbsp; 28.55 GiB |&nbsp;&nbsp; 36.39 GiB |&nbsp;&nbsp;&nbsp; 3.53 Mbit/s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8.46 GiB |&nbsp;&nbsp; 25.66 GiB |&nbsp;&nbsp; 34.12 GiB |&nbsp;&nbsp;&nbsp; 3.31 Mbit/s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp; 11.21 GiB |&nbsp;&nbsp; 30.70 GiB |&nbsp;&nbsp; 41.91 GiB |&nbsp;&nbsp;&nbsp; 4.07 Mbit/s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 08/17/09&nbsp;&nbsp;&nbsp;&nbsp; 10.25 GiB |&nbsp;&nbsp; 28.20 GiB |&nbsp;&nbsp; 38.46 GiB |&nbsp;&nbsp;&nbsp; 3.73 Mbit/s<br /></blockquote>Obviously much better when looking at the 2nd to last column.&nbsp; The first column is received the increase in those is down to a slightly lower hit ratio on the caching proxy on these machines meaning it's fetching more files from origin than the others.<br /><br />Overall I am extremely pleased with this solution, I agree one should not be using DNS as a hammer to all your nails but for startups and cloud based people who do not have control over networks, BGP tables and so forth this really does represent a viable option to what would otherwise be an extremely expensive problem to solve.<br />]]>
        
    </content>
</entry>

<entry>
    <title>News from SA</title>
    <link rel="alternate" type="text/html" href="http://www.devco.net/archives/2009/08/13/news_from_sa.php" />
    <id>tag:www.devco.net,2009://1.462</id>

    <published>2009-08-13T21:05:56Z</published>
    <updated>2009-08-14T08:47:09Z</updated>

    <summary><![CDATA[So today I decided to follow @iol on my twitter client to try and keep in touch with things back home.These are some of the messages I got in the first 11 hours:Still no trace of missing baby http://bit.ly/cUafy'I could not clear away images of the dead' http://bit.ly/NjQxqWoman forgives parents for killing her lover http://bit.ly/xd2P7'Mass hysteria' sweeps six E Cape schools http://bit.ly/yE30RBona magazine editor killed http://bit.ly/13JN08Suspect shoots himself in groin http://bit.ly/29UNDMBaby's body found in car boot http://bit.ly/349oqChabaan guilty of assault http://bit.ly/XpKZsHammer attack hit straight to teacher's core http://bit.ly/YbdD6MJC rejects the slaughter of chickens claim http://bit.ly/23igtE'I just can't wait till this is all over' http://bit.ly/2mWbgJudge's death: Unanswered questions remain http://bit.ly/4aqPWSlain editor: Family believes he knew killers http://bit.ly/anMLtThat all in 11 hours of headlines.&nbsp; Shocking. ]]></summary>
    <author>
        <name>R.I.Pienaar</name>
        <uri>http://www.devco.net/</uri>
    </author>
    
        <category term="Front Page" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="news" label="news" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="southafrica" label="south africa" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.devco.net/">
        <![CDATA[So today I decided to follow @iol on my twitter client to try and keep in touch with things back home.<br /><br />These are some of the messages I got in the first 11 hours:<br /><br /><ul><li>Still no trace of missing baby http://bit.ly/cUafy</li><li>'I could not clear away images of the dead' http://bit.ly/NjQxq</li><li>Woman forgives parents for killing her lover http://bit.ly/xd2P7</li><li>'Mass hysteria' sweeps six E Cape schools http://bit.ly/yE30R</li><li>Bona magazine editor killed http://bit.ly/13JN08</li><li>Suspect shoots himself in groin http://bit.ly/29UNDM</li><li>Baby's body found in car boot http://bit.ly/349oq</li><li>Chabaan guilty of assault http://bit.ly/XpKZs</li><li>Hammer attack hit straight to teacher's core http://bit.ly/YbdD6</li><li>MJC rejects the slaughter of chickens claim http://bit.ly/23igtE</li><li>'I just can't wait till this is all over' http://bit.ly/2mWbg</li><li>Judge's death: Unanswered questions remain http://bit.ly/4aqPW</li><li>Slain editor: Family believes he knew killers http://bit.ly/anMLt</li></ul>That all in 11 hours of headlines.&nbsp; Shocking.<br /> ]]>
        
    </content>
</entry>

</feed>
