<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Terrarum</title>
	
	<link>http://terrarum.net</link>
	<description>System Administration and Development</description>
	<lastBuildDate>Tue, 23 Apr 2013 05:18:20 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/JoeTopjian" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="joetopjian" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Puppet Infrastructure with Puppet</title>
		<link>http://terrarum.net/administration/puppet-infrastructure-with-puppet.html</link>
		<comments>http://terrarum.net/administration/puppet-infrastructure-with-puppet.html#comments</comments>
		<pubDate>Sat, 08 Dec 2012 06:45:21 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[administration]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=423</guid>
		<description><![CDATA[Introduction Managing your Puppet infrastructure with Puppet is a bit of a chicken-and-egg scenario. This article describes how I bootstrap a server into becoming a Puppet Master server. Table of Contents A Note About the Puppet Module The Puppet Master First Steps Bootstrap Files Agent Bootstrapping A Note About the Puppet Module I used to [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>Managing your Puppet infrastructure with Puppet is a bit of a chicken-and-egg scenario. This article describes how I bootstrap a server into becoming a Puppet Master server.</p>

<p><span id="more-423"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#note">A Note About the Puppet Module</a></li>
<li><a href="#puppet-master">The Puppet Master</a>

<ul>
<li><a href="#first-steps">First Steps</a></li>
<li><a href="#boostrap-files">Bootstrap Files</a></li>
</ul></li>
<li><a href="#agent-bootstrap">Agent Bootstrapping</a></li>
</ul>
</p>

<h2 id="note">A Note About the Puppet Module</h2>

<p>I used to use <a href="https://github.com/jtopjian/puppetlabs-puppet/tree/jtopjian-mods">my own fork</a> of the <a href="https://github.com/puppetlabs/puppetlabs-puppet">puppetlabs/puppet</a> module but have recently discovered the <a href="https://github.com/puppetlabs-operations/puppet-puppet">puppetlabs-operations/puppet</a> module. This article uses <a href="https://github.com/jtopjian/puppet-puppet">my own fork</a> of the operations module. Since this module is fairly new to me, there might be errors. Please let me know if you notice any.</p>

<h2 id="puppet-master">The Puppet Master</h2>

<p>This section will describe the creation of the Puppet Master server. The Puppet Master will be hosted on Ubuntu 12.04. It will use <a href="http://docs.puppetlabs.com/puppetdb/1/index.html">PuppetDB</a> for stored configuration and Apache/Passenger for the web service.</p>

<h3 id="first-steps">First Steps</h3>

<p>If you&#8217;re at a command prompt on a recently provisioned server, first ensure all packages are updated:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ <span style="color: #c20cb9; font-weight: bold;">apt-get update</span><br />
$ <span style="color: #c20cb9; font-weight: bold;">apt-get dist-upgrade</span></div></td></tr></tbody></table></div>

<p>Next, install some basic components:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">apt-get install</span> <span style="color: #660033;">-y</span> puppet <span style="color: #c20cb9; font-weight: bold;">git</span> rubygems</div></td></tr></tbody></table></div>

<p>Once this is done, you&#8217;ll have access to Puppet 2.7.11. Upgrading to a newer version will be covered later on.</p>

<p>Next clone the <code>puppet</code> module:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ <span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>puppet<span style="color: #000000; font-weight: bold;">/</span>modules<br />
$ <span style="color: #c20cb9; font-weight: bold;">git clone</span> https:<span style="color: #000000; font-weight: bold;">//</span>github.com<span style="color: #000000; font-weight: bold;">/</span>jtopjian<span style="color: #000000; font-weight: bold;">/</span>puppet-puppet puppet</div></td></tr></tbody></table></div>

<h3 id="boostrap-files">Bootstrap Files</h3>

<p>Once the repo is cloned, you can utilize a few supporting scripts to help with boostrapping. These scripts can be found <a href="https://gist.github.com/jtopjian/5099948">here</a>.</p>

<p>The first bootstrap script to run is <code>bootstrap.sh</code>. If you look at the contents of this file, it will simply checkout a collection of other Puppet modules as well as make a <code>production</code> and <code>development</code> directory structure for manifests of those environments.</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>.<span style="color: #000000; font-weight: bold;">/</span>bootstrap.sh</div></td></tr></tbody></table></div>

<p>Once <code>bootstrap.sh</code> has finished, you can optionally apply another manifest to gain access to updated versions of Puppet by way of a custom apt repository:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">apt::source <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'puppet'</span>:<br />
&nbsp; location &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'http://apt.example.com/'</span>,<br />
&nbsp; release &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> $::lsbdistcodename,<br />
&nbsp; repos &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'main'</span>,<br />
&nbsp; key &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'ABCD1234'</span>,<br />
&nbsp; key_server &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'subkeys.pgp.net'</span>,<br />
&nbsp; include_src <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">false</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>Creating your own apt repository is covered in <a href="http://terrarum.net/administration/apt-infrastructure-with-puppet.html">this article</a>.</p>

<p>Next, open <code>pmaster.pp</code> in an editor. Review the settings and change anything necessary. If you require more configuration than what is given, please review the <code>puppet</code> module.</p>

<p>Once edited to suit your environment, apply it:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet apply <span style="color: #660033;">--verbose</span> pmaster.pp</div></td></tr></tbody></table></div>

<p>This manifest should fail at the very end due to Apache being unable to start. This is because Ubuntu will automatically start the <code>puppetmaster</code> server as soon as the package is installed. In my opinion, it&#8217;s much easier to just stop the <code>puppetmaster</code> service and then re-apply the manifest to check for any more errors:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>puppetmaster stop<br />
$ puppet apply <span style="color: #660033;">--verbose</span> pmaster.pp</div></td></tr></tbody></table></div>

<p>(<strong>Note:</strong> the <code>puppet</code> module disables the <code>puppetmaster</code> service from starting at boot by editing the <code>/etc/default/puppetmaster</code> file)</p>

<p>Once that&#8217;s done, install and configure PuppetDB:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet apply <span style="color: #660033;">--verbose</span> pmaster-puppetdb.pp</div></td></tr></tbody></table></div>

<p>I&#8217;ve been unable to figure out the right logic to install both <code>puppetmaster</code> and <code>puppetdb</code> in a single manifest and single puppet run. It&#8217;s easy enough to just apply two manifests and be done with it.</p>

<p><code>pmaster-puppetdb.pp</code> might fail at the end. This is most likely because PuppetDB takes a minute or two to fully launch. If <code>pmaster-puppetdb.pp</code> fails, wait a minute and try again &#8212; chances are it will work the second time.</p>

<p>That&#8217;s it. You should now have a fully functional Puppet Master service. You can verify this by running:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet agent <span style="color: #660033;">--verbose</span> <span style="color: #660033;">--onetime</span> <span style="color: #660033;">--no-daemon</span></div></td></tr></tbody></table></div>

<p>and not get any errors.</p>

<h2 id="agent-bootstrap">Agent Bootstrapping</h2>

<p>There are a few different ways to bootstrap an agent. If you&#8217;re running in a virtual environment, you can make a VM image that has the Puppet agent installed and configured. By using this image, all of your new VMs will launch Puppet on boot and connect to the configured Puppet Master service.</p>

<p>Another way, and this works with bare-metal provisioning, is to install the Puppet agent as a post-install step. For example, using Debian/Ubuntu preseed, this might look like:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">in-target <span style="color: #c20cb9; font-weight: bold;">apt-get install</span> puppet; in-target <span style="color: #c20cb9; font-weight: bold;">wget</span> http:<span style="color: #000000; font-weight: bold;">//</span>puppet.example.com<span style="color: #000000; font-weight: bold;">/</span>puppet.conf <span style="color: #660033;">-O</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>puppet<span style="color: #000000; font-weight: bold;">/</span>puppet.conf</div></td></tr></tbody></table></div>

<p><code>puppet.conf</code> is a generic Puppet configuration file that you can make publicly accessible.</p>

<h2>Conclusion</h2>

<p>Even though you&#8217;ll rarely create a Puppet Master service once you have one up and running, it&#8217;s good practice to have a stable, repeatable procedure to ensure it&#8217;s done correctly. Puppet can be used to create this procedure, but it takes a bit of CLI bootstrapping, too.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/administration/puppet-infrastructure-with-puppet.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Puppet, OpenStack, and rsyslog</title>
		<link>http://terrarum.net/administration/puppet-openstack-and-rsyslog.html</link>
		<comments>http://terrarum.net/administration/puppet-openstack-and-rsyslog.html#comments</comments>
		<pubDate>Thu, 06 Dec 2012 04:22:02 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[openstack]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[rsyslog]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=414</guid>
		<description><![CDATA[Introduction Sébastien Han posted a good article on how to use rsyslog with OpenStack. Coincidentally, I recently configured rsyslog for an OpenStack project and figured I&#8217;d publish my configuration, too. Table of Contents Configuring rsyslog Configuring the Server Configuring the Client OpenStack and syslog Sorting and Organizing Configuring rsyslog I have three puppet manifests that [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>Sébastien Han posted a <a href="http://www.sebastien-han.fr/blog/2012/12/05/openstack-and-rsyslog/">good article</a> on how to use <code>rsyslog</code> with OpenStack. Coincidentally, I recently configured <code>rsyslog</code> for an OpenStack project and figured I&#8217;d publish my configuration, too.</p>

<p><span id="more-414"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#configuring-rsyslog">Configuring rsyslog</a>

<ul>
<li><a href="#configuring-server">Configuring the Server</a></li>
<li><a href="#configuring-client">Configuring the Client</a></li>
</ul></li>
<li><a href="#openstack-syslog">OpenStack and syslog</a></li>
<li><a href="#sorting-and-organizing">Sorting and Organizing</a></li>
</ul>
</p>

<h2 id="configuring-rsyslog">Configuring rsyslog</h2>

<p>I have <a href="https://github.com/jtopjian/puppet-admin/tree/dair/manifests/rsyslog">three puppet manifests</a> that I use to configure <code>rsyslog</code>:</p>

<ul>
<li><code>base.pp</code></li>
<li><code>client.pp</code></li>
<li><code>server.pp</code></li>
</ul>

<p><code>base.pp</code> is simply declared in both <code>client.pp</code> and <code>server.pp</code>.</p>

<h3 id="configuring-server">Configuring the Server</h3>

<p><a href="https://github.com/jtopjian/puppet-admin/blob/dair/manifests/rsyslog/server.pp">server.pp</a> is used to configure a server to be an <code>rsyslog</code>-server. To apply it, simply do:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'admin::rsyslog::server'</span>: <span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>Although that seems simple enough, the heavy work is in the two files that the manifest uses:</p>

<ul>
<li><a href="https://github.com/jtopjian/puppet-admin/blob/dair/files/rsyslog/cloud-rsyslog.logrotate">cloud-rsyslog.logrotate</a> is a script used by <code>logrotate</code> to, of course, rotate the logs.</li>
<li><a href="https://github.com/jtopjian/puppet-admin/blob/dair/templates/rsyslog/server.conf.erb">server.conf.erb</a> is an <code>rsyslog</code> configuration file that has a tremendous amount of stuff going on. I&#8217;ll describe this later on.</li>
</ul>

<h3 id="configuring-client">Configuring the Client</h3>

<p>Client configuration is just a simple as server configuration:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'admin::rsyslog::client'</span>:<br />
&nbsp; server <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'rsyslog.example.com'</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<h2 id="openstack-syslog">OpenStack and syslog</h2>

<p>As Sébastien described, each OpenStack component can be configured to send their logs to syslog. You can see this being done in the following manifests (search for &#8220;syslog&#8221;):</p>

<ul>
<li><a href="https://github.com/jtopjian/puppet-admin/blob/dair/manifests/openstack/compute/node.pp">compute node</a></li>
<li><a href="https://github.com/jtopjian/puppet-admin/blob/dair/manifests/openstack/controller/base.pp">cloud controller</a></li>
</ul>

<p>For Swift, the <code>proxy-logging</code> middleware sends the logs to syslog.</p>

<h2 id="sorting-and-organizing">Sorting and Organizing</h2>

<p>When an OpenStack component sends a log to the <code>rsyslog</code> server, the log is passed through the filters in the <code>server.conf</code> file. Based on if the log is from Swift (special case), the log facility, and the hostname, the log is directed to a specific log file in a specific directory.</p>

<p>I have logs going into the following directories:</p>

<ul>
<li><code>/var/log/rsyslog/%HOSTNAME%/nova.log</code>: all logs related to <code>nova</code> from that host.</li>
<li><code>/var/log/rsyslog/%HOSTNAME%/glance.log</code>: all logs related to <code>glance</code> from that host.</li>
<li><code>/var/log/rsyslog/%HOSTNAME%/cinder.log</code>: all logs related to <code>cinder</code> from that host.</li>
<li><code>/var/log/rsyslog/%HOSTNAME%/keystone.log</code>: all logs related to <code>keystone</code> from that host.</li>
<li><code>/var/log/rsyslog/%HOSTNAME%/swift.log</code>: all logs related to <code>nova</code> from that host.</li>
<li><code>/var/log/rsyslog/%HOSTNAME%/syslog.log</code>: all other logs from that host.</li>
<li><code>/var/log/rsyslog/nova.log</code>: all nova logs from all hosts.</li>
<li><code>/var/log/rsyslog/swift.log</code>: all swift logs from all hosts.</li>
<li><code>/var/log/rsyslog/cinder.log</code>: all cinder logs from all hosts.</li>
</ul>

<h2>Conclusion</h2>

<p><code>rsyslog</code> provides a great deal of flexibility when it comes to logging. Puppet, as usual, comes to the rescue in making the complexities of such services manageable.</p>

<p>While this solution works for me, I have plans to revisit OpenStack logs in combination with <a href="http://logstash.net/">logstash</a> in the future.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/administration/puppet-openstack-and-rsyslog.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>apt Infrastructure with Puppet</title>
		<link>http://terrarum.net/administration/apt-infrastructure-with-puppet.html</link>
		<comments>http://terrarum.net/administration/apt-infrastructure-with-puppet.html#comments</comments>
		<pubDate>Sat, 01 Dec 2012 06:53:16 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[administration]]></category>
		<category><![CDATA[apt]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[Puppet]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=392</guid>
		<description><![CDATA[Introduction This article will explain various methods of controlling apt (the Debian and Ubuntu package manager) across several servers with Puppet and the benefits of doing so. Table of Contents Caching apt Packages apt-cacher-ng Mirroring apt Repositories apt-mirror Creating Your Own apt Repository reprepro Making Your Repository Available Use-case: Puppet 2.7.x Conclusion Caching apt Packages [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>This article will explain various methods of controlling <code>apt</code> (the Debian and Ubuntu package manager) across several servers with Puppet and the benefits of doing so.</p>

<p><span id="more-392"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#caching-packages">Caching apt Packages</a>

<ul>
<li><a href="#acng">apt-cacher-ng</a></li>
</ul></li>
<li><a href="#mirroring">Mirroring apt Repositories</a>

<ul>
<li><a href="#apt-mirror">apt-mirror</a></li>
</ul></li>
<li><a href="#custom-repo">Creating Your Own apt Repository</a>

<ul>
<li><a href="#reprepro">reprepro</a></li>
<li><a href="#repo-avail">Making Your Repository Available</a></li>
</ul></li>
<li><a href="#use-case">Use-case: Puppet 2.7.x</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</p>

<h2 id="caching-packages">Caching apt Packages</h2>

<p>Each time <code>apt</code> downloads a package, it is locally cached on the server until you explicitly clear the cache. This gives you the ability to reinstall the package without having <code>apt</code> download it again.</p>

<p>On individual servers, this works fine. But what about groups of several servers? If, for example, you ran the following on 15 different servers to install <code>tmux</code>:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">apt-get install</span> tmux</div></td></tr></tbody></table></div>

<p><code>tmux</code> would have been downloaded from a public server 15 times. This is very inefficient &#8212; especially if the package was significantly large.</p>

<p>A solution to this is to designate a server to be an apt proxy server. All other servers will go through this server to download any packages. If the package does not exist on the apt proxy server, it will be downloaded from a public server. Now when <code>tmux</code> is installed on 15 different servers, it is only downloaded from a public server once.</p>

<h3 id="acng">apt-cacher-ng</h3>

<p>The best application for apt proxying is <a href="http://www.unix-ag.uni-kl.de/~bloch/acng/">apt-cacher-ng</a>.</p>

<p>To set up <code>acng</code> by using Puppet, either use the <code>puppet module</code> tool or download the module directly from github:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet module <span style="color: #c20cb9; font-weight: bold;">install</span> jtopjian<span style="color: #000000; font-weight: bold;">/</span>acng</div></td></tr></tbody></table></div>

<p>or</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git clone</span> https:<span style="color: #000000; font-weight: bold;">//</span>github.com<span style="color: #000000; font-weight: bold;">/</span>jtopjian<span style="color: #000000; font-weight: bold;">/</span>puppet-acng acng</div></td></tr></tbody></table></div>

<p>Next, apply the following class to the &#8220;apt server&#8221; (the &#8220;apt server&#8221; is the server that will provide all <code>apt</code> services throughout this article):</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'acng::server'</span>: <span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>When <code>acng</code> is installed, it will automatically use the contents of your <code>sources.list</code> file as the upstream <code>apt</code> repository. This will work for 90% of use-cases.</p>

<p>On every other server that will use the apt server as a proxy, apply the following manifest:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'acng::client'</span>:<br />
&nbsp; server <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'apt.example.com'</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<h2 id="mirroring">Mirroring apt Repositories</h2>

<p><code>acng</code> is useful if your servers use the same subset of packages. If you find that your servers are downloading a wide variety of packages &#8212; maybe even most of the available packages in a repository &#8212; it would make more sense to just mirror the entire repository instead of letting <code>acng</code> inevitably do it.</p>

<h3 id="apt-mirror">apt-mirror</h3>

<p><code>apt-mirror</code> is an application that provides an easy way to mirror one or more repositories. To configure with Puppet, first install the <code>apt_mirror</code> module:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet module <span style="color: #c20cb9; font-weight: bold;">install</span> jtopjian<span style="color: #000000; font-weight: bold;">/</span>apt_mirror</div></td></tr></tbody></table></div>

<p>or</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git clone</span> https:<span style="color: #000000; font-weight: bold;">//</span>github.com<span style="color: #000000; font-weight: bold;">/</span>jtopjian<span style="color: #000000; font-weight: bold;">/</span>puppet-apt_mirror apt_mirror</div></td></tr></tbody></table></div>

<p>Next, apply the base manifest:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'apt_mirror'</span>: <span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>The <code>apt_mirror</code> module comes with a defined type to add individual mirrors. For example, to mirror <code>main</code> and <code>contrib</code> repositories for Ubuntu Precise, use the following:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">apt_mirror::mirror <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'ubuntu precise'</span>: &nbsp; <br />
&nbsp; mirror &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'archive.ubuntu.com'</span>,<br />
&nbsp; os &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'ubuntu'</span>,<br />
&nbsp; release &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'precise'</span>, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; components <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'main'</span>, <span style="color:#996600;">'contrib'</span><span style="color:#006600; font-weight:bold;">&#93;</span>, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p><code>apt_mirror</code> installs a cron entry that will run once a day. It creates new mirrors that do not yet exist or updates existing mirrors with any changes.</p>

<p>In order to fully utilize the mirror, you will need to make it accessible to all of your servers. I recommend following <a href="http://unixrob.blogspot.ca/2012/05/create-apt-mirror-with-ubuntu-1204-lts.html">this article</a> for instructions on how to do this manually. I&#8217;ll also describe how to do this with Puppet later in this article.</p>

<h2 id="custom-repo">Creating Your Own apt Repository</h2>

<p>There are several reasons to create your own <code>apt</code> repository:</p>

<ul>
<li>To distribute your own packages</li>
<li>To limit the packages available to servers</li>
<li>To add packages not available in the standard repositories</li>
</ul>

<h3 id="reprepro">reprepro</h3>

<p>Creating an <code>apt</code> repository is not the most straightforward task. The <a href="http://wiki.debian.org/HowToSetupADebianRepository">following page</a> contains a very long list of tools to assist. The most popular tool seems to be <code>reprepro</code> which is what I have chosen to use.</p>

<p>For a great overview on how to use <code>reprepro</code>, please see <a href="http://blog.jonliv.es/2011/04/26/creating-your-own-signed-apt-repository-and-debian-packages/">this</a> article.</p>

<p>To configure <code>reprepro</code> with Puppet, first install the module:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet module <span style="color: #c20cb9; font-weight: bold;">install</span> jtopjian<span style="color: #000000; font-weight: bold;">/</span>reprepro</div></td></tr></tbody></table></div>

<p>or</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">git clone</span> https:<span style="color: #000000; font-weight: bold;">//</span>github.com<span style="color: #000000; font-weight: bold;">/</span>jtopjian<span style="color: #000000; font-weight: bold;">/</span>puppet-reprepro reprepro</div></td></tr></tbody></table></div>

<p>The following manifest will install <code>reprepro</code> and set up the <code>reprepro</code> user:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#ff6633; font-weight:bold;">$basedir</span> = <span style="color:#996600;">'/var/lib/apt/repo'</span><br />
<br />
<span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'reprepro'</span>:<br />
&nbsp; basedir <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff6633; font-weight:bold;">$basedir</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>One <code>reprepro</code> is installed, GPG will need configured. Modern <code>apt</code> versions use GPG to sign both packages and files that maintain the <code>apt</code> repository. In my opinion, it is difficult to use Puppet to maintain GPG keys, so I recommend doing this part outside of Puppet.</p>

<p>First, change to the <code>reprepro</code> user:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #c20cb9; font-weight: bold;">su</span> - reprepro</div></td></tr></tbody></table></div>

<p>Next, generate a GPG key:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>gpg <span style="color: #660033;">--gen-key</span></div></td></tr></tbody></table></div>

<p>Follow and answer the prompts. I recommend not using a passphrase. Although it&#8217;s insecure to do such a thing, GPG is just being used to sign arbitrary <code>apt</code> repository files &#8212; you can always delete the repository and re-create it if you feel your key has become compromised.</p>

<p>Once the key has been created, export it and store it in the <code>reprepro</code> Puppet module:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>gpg <span style="color: #660033;">--export</span> <span style="color: #660033;">--armor</span> foo<span style="color: #000000; font-weight: bold;">@</span>bar.com <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>puppet<span style="color: #000000; font-weight: bold;">/</span>modules<span style="color: #000000; font-weight: bold;">/</span>reprepro<span style="color: #000000; font-weight: bold;">/</span>files<span style="color: #000000; font-weight: bold;">/</span>localpkgs.gpg</div></td></tr></tbody></table></div>

<p>Now to continue with Puppet. The <code>reprepro</code> Puppet module has the ability to crate and maintain several <code>apt</code> repositories. Each repository contains one or more distributions. For most cases, and for the purpose of this document, only one of each will be created.</p>

<p>First, the repository:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">reprepro::repository <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'localpkgs'</span>:<br />
&nbsp; <span style="color:#9966CC; font-weight:bold;">ensure</span> &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> present,<br />
&nbsp; basedir <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff6633; font-weight:bold;">$basedir</span>,<br />
&nbsp; options <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'basedir .'</span><span style="color:#006600; font-weight:bold;">&#93;</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>And next the distribution:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">reprepro::distribution <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'precise'</span>:<br />
&nbsp; basedir &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff6633; font-weight:bold;">$basedir</span>,<br />
&nbsp; repository &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'localpkgs'</span>,<br />
&nbsp; origin &nbsp; &nbsp; &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Foobar'</span>,<br />
&nbsp; label &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Foobar'</span>,<br />
&nbsp; suite &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'stable'</span>,<br />
&nbsp; architectures <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'amd64 i386'</span>,<br />
&nbsp; components &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'main contrib non-free'</span>,<br />
&nbsp; description &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Package repository for local site maintenance'</span>,<br />
&nbsp; sign_with &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'F4D5DAA8'</span>,<br />
&nbsp; not_automatic <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'No'</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>(The <code>sign_with</code> value can be obtained by doing <code>gpg --list-keys</code>)</p>

<p>Once these manifests have been applied to the apt server, you can begin adding packages to your custom repository. The <code>reprepro</code> Puppet module installs a cron entry that will monitor the <code>$basedir/$repository/tmp/$distribution</code> directory for any <code>*.deb</code> files. If it finds any, it will add the files to the repository and clean the <code>tmp</code> dir out.</p>

<h3 id="repo-avail">Making Your Repository Available</h3>

<p>Now that you have an <code>apt</code> repository with packages, you will want all of your servers to be able to download those packages. You&#8217;ll need to make your repository publicly accessible in order to do this. The easiest way is by installing a web server like Apache:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet module <span style="color: #c20cb9; font-weight: bold;">install</span> puppetlabs<span style="color: #000000; font-weight: bold;">/</span>apache</div></td></tr></tbody></table></div>

<p>Next, create a virtual host that uses your repository as its <code>DocumentRoot</code>:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'apache'</span>: <span style="color:#006600; font-weight:bold;">&#125;</span><br />
<br />
apache::vhost <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'localpkgs'</span>:<br />
&nbsp; port &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'80'</span>,<br />
&nbsp; docroot &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'/var/lib/apt/repo/localpkgs'</span>,<br />
&nbsp; servername <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'apt.example.com'</span>,<br />
&nbsp; <span style="color:#CC0066; font-weight:bold;">require</span> &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#6666ff; font-weight:bold;">Reprepro::Distribution</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'precise'</span><span style="color:#006600; font-weight:bold;">&#93;</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>Also make sure your servers can access your GPG key that you generated:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">file <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'/var/lib/apt/repo/localpkgs/localpkgs.gpg'</span>:<br />
&nbsp; <span style="color:#9966CC; font-weight:bold;">ensure</span> &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> present,<br />
&nbsp; owner &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'www-data'</span>,<br />
&nbsp; group &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'reprepro'</span>,<br />
&nbsp; mode &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'0644'</span>,<br />
&nbsp; source &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'puppet:///modules/reprepro/localpkgs.gpg'</span>,<br />
&nbsp; <span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#6666ff; font-weight:bold;">Apache::Vhost</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'localpkgs'</span><span style="color:#006600; font-weight:bold;">&#93;</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>Finally, tell your servers about your repository. You can use the Puppet <code>apt</code> module to do this:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span>puppet module <span style="color: #c20cb9; font-weight: bold;">install</span> puppetlabs<span style="color: #000000; font-weight: bold;">/</span>apt</div></td></tr></tbody></table></div>

<p>And use the following manifest on each server:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">apt::source <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'localpkgs'</span>:<br />
&nbsp; location &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'http://apt.example.com'</span>,<br />
&nbsp; release &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'precise'</span>,<br />
&nbsp; repos &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'main contrib non-free'</span>,<br />
&nbsp; key &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'F4D5DAA8'</span>,<br />
&nbsp; key_source &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'http://apt.example.com/localpkgs.gpg'</span>,<br />
&nbsp; include_src <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">false</span>,<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>Now you should be able to perform an <code>apt-cache search</code> on each server for packages in your repository.</p>

<h2 id="use-case">Use-case: Puppet 2.7.x</h2>

<p>One creative way of using both <code>apt-mirror</code> and <code>reprepro</code> is to ensure your servers are running the latest version of Puppet, but keep them at the <code>2.7.x</code> versions.</p>

<p>Puppetlabs maintains their own <code>apt</code> repository at http://apt.puppetlabs.com. It&#8217;s a great repository and it&#8217;s always up to date. However, it makes no distinction between the 2.7 and 3.0 branches of Puppet. This means that unless you specifically specify a 2.7.x version of Puppet, you will get a 3.0 version.</p>

<p>Personally, I&#8217;m not ready to work with 3.0, but at the same time, I want my servers to have the latest 2.7 version.</p>

<p>The first step in easily doing this is to mirror the Puppetlabs <code>apt</code> repository:</p>

<div class="codecolorer-container ruby default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="ruby codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">apt_mirror::mirror <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#996600;">'puppetlabs'</span>: &nbsp; <br />
&nbsp; mirror &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'apt.puppetlabs.com'</span>,<br />
&nbsp; os &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">''</span>, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; release &nbsp; &nbsp;<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'precise'</span>, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; components <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'main'</span><span style="color:#006600; font-weight:bold;">&#93;</span>, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
<span style="color:#006600; font-weight:bold;">&#125;</span></div></td></tr></tbody></table></div>

<p>Once your <code>apt</code> server has mirrored the repository, navigate to the mirror&#8217;s location (most likely <code>/var/spool/apt-mirror/mirror/apt.puppetlabs.com</code>):</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>var<span style="color: #000000; font-weight: bold;">/</span>spool<span style="color: #000000; font-weight: bold;">/</span>apt-mirror<span style="color: #000000; font-weight: bold;">/</span>mirror<span style="color: #000000; font-weight: bold;">/</span>apt.puppetlabs.com</div></td></tr></tbody></table></div>

<p>Inside this directory, navigate further to <code>pool/precise/main</code>:</p>

<div class="codecolorer-container bash default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666;">$ </span><span style="color: #7a0874; font-weight: bold;">cd</span> pool<span style="color: #000000; font-weight: bold;">/</span>precise<span style="color: #000000; font-weight: bold;">/</span>main</div></td></tr></tbody></table></div>

<p>This directory will contain several directories with a title of a single letter. These directories will contain subdirectories of various Puppet-related packages. For example:</p>

<ul>
<li>f/facter</li>
<li>h/hiera</li>
<li>p/puppet</li>
<li>p/puppetdb</li>
</ul>

<p>Sort through these directories and find the latest 2.7.x versions of each. Copy them to your <code>/var/lib/apt/repo/$repository/tmp/$distribution</code> directory. <code>cron</code> will pick them up and add them to your personal repository.</p>

<p>Now all servers will have access to the latest 2.7.x version of Puppet without having to specify a specific version. Whenever a new 2.7.x release comes out, <code>apt-mirror</code> will retrieve it within 24 hours. Just copy the new version to <code>/var/lib/apt/repo/$repository/tmp/$distribution</code> and it will then be added to your personal repository.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This article described several different ways of configuring <code>apt</code> to help control how groups of servers access packages. Implementing these methods yields benefits of faster downloads as well as limiting or widening access to packages when needed. By using Puppet, you can easily configure a central server to host these roles or configure groups of servers to access the designated apt server.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/administration/apt-infrastructure-with-puppet.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>CentOS 6 Cobbler Server</title>
		<link>http://terrarum.net/administration/centos-6-cobbler-server.html</link>
		<comments>http://terrarum.net/administration/centos-6-cobbler-server.html#comments</comments>
		<pubDate>Thu, 14 Jul 2011 05:56:48 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[administration]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[centos6]]></category>
		<category><![CDATA[cobbler]]></category>
		<category><![CDATA[redhat]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=367</guid>
		<description><![CDATA[Introduction This article will be a step-by-step guide of how to set up a Cobbler server on CentOS 6. Once the server is complete, you will be able to have CentOS 6 automatically installed onto client computers when they are PXE booted on a private network. Table of Contents Server and Environment Attributes Installation Network [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>This article will be a step-by-step guide of how to set up a Cobbler server on CentOS 6. Once the server is complete, you will be able to have CentOS 6 automatically installed onto client computers when they are PXE booted on a private network.</p>

<p><span id="more-367"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#server_environment">Server and Environment Attributes</a></li>
<li><a href="#installation">Installation</a>

<ul>
<li><a href="#network">Network</a></li>
<li><a href="#package_selection">Package Selection</a></li>
</ul></li>
<li><a href="#post_install">Post-Install Steps</a>

<ul>
<li><a href="#firewall">Disable the Firewall</a></li>
<li><a href="#selinux">Disable SELinux</a></li>
<li><a href="#updates">Perform a Software Update</a></li>
<li><a href="#vmware_tools">Install VMWare tools</a></li>
<li><a href="#epel">Install EPEL</a></li>
<li><a href="#nat">Enable NAT</a></li>
<li><a href="#cobbler_install">Install and Configure Cobbler</a></li>
<li><a href="#cobbler_distro">Add a Distro to Cobbler</a></li>
<li><a href="#kickstart">Generate a Base Kickstart File</a></li>
<li><a href="#cobbler_profile">Create a Cobbler Profile</a></li>
<li><a href="#cobbler_system">Create a Cobbler System</a></li>
</ul></li>
<li><a href="#polipo">Extra: Polipo</a>

<ul>
<li><a href="#polipo_install">Install and Configure Polipo</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</p>

<h2 id="server_environment">Server and Environment Attributes</h2>

<p>I used a VMWare virtual machine for the server. The VMWare environment has two networks: a public and private. The Public network had full access to the Internet while the private was a standard host-only network.</p>

<p>The VMWare virtual machine had two NICs &#8212; one for the public and one for the private.</p>

<p>The purpose of the two networks is to allow clients to initially boot up in the private network where Cobbler will handle the installation. Since Cobbler utilizes DHCP and TFTP, making these services listen on a Private network will ensure that they don&#8217;t interfere with any other DHCP or TFTP server on the Public network. Once the installation has finished, the client can be moved to the Public network.</p>

<h2 id="installation">Installation</h2>

<p>Use any CentOS 6 installation medium you wish.</p>

<h3 id="network">Network</h3>

<p>For the Network Configuration, I renamed <code>System eth0</code> and <code>System eth1</code> to <code>Public</code> and <code>Private</code> respectively. I configured each NIC with static information and made sure to check the <code>Connect Automatically</code> checkbox for each &#8212; otherwise <code>NetworkManager</code> will not bring the NICs up.</p>

<h3 id="package_selection">Package Selection</h3>

<p>I chose <code>Minimal Desktop</code>.</p>

<h2 id="post_install">Post-Install Steps</h2>

<h3 id="firewall">Disable the Firewall</h3>

<p>It&#8217;s possible to have the firewall running &#8212; you just need to ensure that the proper ports are opened. For simplicity, and since this server is in a private network, I chose to have the firewall turned off.</p>

<p>You can disable the firewall by doing:</p>

<pre class="brush:bash">
$ system-config-firewall-tui
</pre>

<p>and unselecting &#8220;Enabled&#8221;</p>

<h3 id="selinux">Disable SELinux</h3>

<p>I tried to have Cobbler run properly with SELinux, but in the end, I could not get the two to work together. If anyone has success with this, please let me know.</p>

<p>You can disabled SELinux by editing the <code>/etc/sysconfig/selinux</code> file and changing:</p>

<pre class="brush:bash">
SELINUX=enforcing
</pre>

<p>to</p>

<pre class="brush:bash">
SELINUX=disabled
</pre>

<p>and then rebooting.</p>

<h3 id="updates">Perform a Software Update</h3>

<p>Before proceeding further, make sure everything is up to date:</p>

<pre class="brush:bash">
$ yum update
</pre>

<h3 id="vmware_tools">Install VMWare tools</h3>

<p>This step is only required if you are using VMWare.</p>

<p>Create the file <code>/etc/yum.repos.d/vmware.repo</code> with the following contents:</p>

<pre class="brush:bash">
[vmware-tools]
name=VMware Tools for Red Hat Enterprise Linux $releasever – $basearch
baseurl=http://packages.vmware.com/tools/esx/4.1/rhel6/x86_64
enabled=1
gpgcheck=0
</pre>

<p>Change the <code>x86_64</code> to <code>i686</code> if you need to.</p>

<p>Next, run:</p>

<pre class="brush:bash">
$ yum install vmware-tools
</pre>

<h3 id="epel">Install EPEL</h3>

<p>The <a href="http://fedoraproject.org/wiki/EPEL">EPEL</a> RPM repository contains extra packages, such as Cobbler, for RHEL/CentOS.</p>

<p>Run the following:</p>

<pre class="brush:bash">
$ rpm -Uhv http://download.fedora.redhat.com/pub/epel/6/i386/epel-release-6-5.noarch.rpm
</pre>

<h3 id="nat">Enable NAT</h3>

<p>The following steps will turn the server into a network gateway which will allow clients on the Private side to reach the Internet through the Public side. This allows for network-based installs.</p>

<p>Add the following IPTables rules:</p>

<pre class="brush:bash">
$ /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
$ /sbin/iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
$ /sbin/iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
</pre>

<p>Next, ensure these rules are saved:</p>

<pre class="brush:bash">
$ /etc/init.d/iptables save
$ /etc/init.d/iptables restart
</pre>

<p>Finally, edit <code>/etc/sysctl.conf</code>:</p>

<pre class="brush:bash">
net.ipv4.ip_forward = 1
</pre>

<p>If you do not want to reboot for this to take effect, run:</p>

<pre class="brush:bash">
$ echo 1 > /proc/sys/net/ipv4/ip_forward
</pre>

<h3 id="cobbler_install">Install and Configure Cobbler</h3>

<p>Finally it&#8217;s time to install Cobbler.</p>

<pre class="brush:bash">
$ yum install cobbler cobbler-web pykickstart
</pre>

<p>Ensure some required services will start at boot:</p>

<pre class="brush:bash">
$ chkconfig httpd on
$ chkconfig xinetd on
$ chkconfig cobblerd on
$ service httpd start
$ service xinetd start
$ service cobblerd start
</pre>

<p>For this setup, I will use <code>dnsmasq</code> for DNS and DHCP. The following changes need made in <code>/etc/cobbler/modules.conf</code></p>

<pre class="brush:plain">
[dns]
module = manage_dnsmasq

[dhcp]
module = manage_dnsmasq
</pre>

<p>Edit <code>/etc/cobbler/dnsmasq.template</code> and add the following:</p>

<pre class="brush:plain">
server=192.168.1.1
no-dhcp-interface=eth0
</pre>

<p>Make sure <code>server</code> is set to your upstream DNS resolver. The <code>no-dhcp-interface</code> will ensure the DHCP server does not run on the Public network.</p>

<p>Also change <code>dhcp-range</code> to an appropriate IP range for your Private network.</p>

<p>Finally, restart Cobbler and have it tell you if anything else is needed to be configured:</p>

<pre class="brush:bash">
$ /etc/init.d/cobblerd restart
$ cobbler check
</pre>

<p>When everything looks good, run</p>

<pre class="brush:bash">
$ cobbler sync
</pre>

<h3 id="cobbler_distro">Add a Distro to Cobbler</h3>

<p>For this configuration, I will use a CentOS 6 netinst ISO.</p>

<p>First, mount the ISO:</p>

<pre class="brush:bash">
$ mount /dev/cdrom /mnt
</pre>

<p>Next, import it into Cobbler:</p>

<pre class="brush:bash">
$ cobbler import cobbler import --name=CentOS6 --path=/mnt
</pre>

<p>This will return an error. This is OK since it is not a full distribution ISO.</p>

<p>Finish the import:</p>

<pre class="brush:bash">
$ cobbler distro add --arch=x86_64 --breed=redhat --name=CentOS6 --initrd=/var/www/cobbler/ks_mirror/CentOS6/isolinux/initrd.img --kernel=/var/www/cobbler/ks_mirror/CentOS6/isolinux/vmlinuz
</pre>

<h3 id="kickstart">Generate a Base Kickstart File</h3>

<p>In order to perform an automated install, a <a href="http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Installation_Guide/ch-kickstart2.html">Kickstart</a> file is needed. You can create an initial file by using the <code>system-config-kickstart</code> utility:</p>

<pre class="brush:bash">
$ yum install system-config-kickstart
</pre>

<p>This is a graphical utility so you will need to be logged into X.</p>

<p>You can also use my <a href="http://terrarum.net/centos6.ks">base file</a>. This has been configured to use Cobbler&#8217;s network configuration snippets. The encrypted password is just &#8220;password&#8221;.</p>

<h3 id="cobbler_profile">Create a Cobbler Profile</h3>

<p>Profiles connect Distributions with Kickstart files. Here we can make a standard CentOS6 profile:</p>

<pre class="brush:bash">
$ cobbler profile add --name=CentOS6-Base --distro=CentOS6 --kickstart=/var/lib/cobbler/kickstarts/centos6.ks
</pre>

<h3 id="cobbler_system">Create a Cobbler System</h3>

<p>Systems are individual machines complete with IP addresses and MAC addresses. I prefer to use the <a href="https://fedorahosted.org/cobbler/wiki/CobblerWebInterface">Cobbler Web Interface</a> to add systems.</p>

<p>If you are using VMWare, create a new virtual machine and either make note of the generated MAC address or generate your own. If you are using a physical server, make note of the MAC. You can then configure your system in Cobbler to use that MAC. This will ensure that any system-specific options (such as IP address) are only configured for that system.</p>

<p>Once the system is added, be sure to sync Cobbler either through the web interface or on the command line:</p>

<pre class="brush:bash">
$ cobbler sync
</pre>

<p>Now, PXE boot your client on the Private network. DHCP and TFTP should take over and an automated install should begin.</p>

<h2 id="polipo">Extra: Polipo</h2>

<p>Although it is possible to have Cobbler mirror various repositories, I prefer to only download what is needed. If you configure Kickstart to use a caching proxy server, you can use cached RPMs to install on multiple servers. This way, once the RPMs are cached, future installs will only take a few minutes &#8212; no RPMs will need to be downloaded from the Internet.</p>

<p>I am currently using <a href="http://www.pps.jussieu.fr/~jch/software/polipo/">Polipo</a> to handle this since my own <a href="http://terrarum.net/administration/caching-rpms-with-automirror.html">automirror</a> will not work with CentOS 6.</p>

<p><em>Note:</em> As of RHEL6, a new <code>--proxy</code> option is available to Kickstart files to enable the use of a proxy. Before this, if you wanted to use a proxy, you had to use a &#8220;tunneling&#8221; method as described in automirror.</p>

<h3 id="polipo_install">Install and Configure Polipo</h3>

<p>To install Polipo, just do:</p>

<pre class="brush:bash">
$ yum install polipo
</pre>

<p>To configure, edit the <code>/etc/polipo/config</code> file:</p>

<pre class="brush:plain">
proxyAddress = "::0"
disableIndexing = false
disableServersList = false
</pre>

<p>Next, create the file <code>/etc/polipo/uncachable</code> with the contents:</p>

<pre class="brush:plain">
\.xml$
</pre>

<p>This <em>should</em> ensure that XML files are not cached. This way up-to-date RPM repository data is obtained during installs.</p>

<p>Once that&#8217;s all done, edit the Kickstart file to utilize the proxy. This can be seen in the sample Kickstart file linked above.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This article walked through the process of setting up a Cobbler server that will be able to provide automatic installations of CentOS 6 clients.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/administration/centos-6-cobbler-server.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Perlish – List Utilities</title>
		<link>http://terrarum.net/development/perlish-list-utilities.html</link>
		<comments>http://terrarum.net/development/perlish-list-utilities.html#comments</comments>
		<pubDate>Wed, 27 Apr 2011 15:52:22 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perlish]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=351</guid>
		<description><![CDATA[Introduction There are two CPAN modules that provide some nice utilities for working with and manipulating lists: List::Util and List::MoreUtils. This article quickly covers a few of these utilities that I have found useful. Table of Contents List::Util List::Util::first List::Util::max List::Util::shuffle List::MoreUtils List::MoreUtils::any List::MoreUtils::all List::MoreUtils::uniq List::Util List::Util provides a handful of utilities. Some of these [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>There are two CPAN modules that provide some nice utilities for working with and manipulating lists: <code>List::Util</code> and <code>List::MoreUtils</code>. This article quickly covers a few of these utilities that I have found useful.</p>

<p><span id="more-351"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#listutil">List::Util</a>

<ul>
<li><a href="#list_util_first">List::Util::first</a></li>
<li><a href="#list_util_max">List::Util::max</a></li>
<li><a href="#list_util_shuffle">List::Util::shuffle</a></li>
</ul></li>
<li><a href="#list_moreutils">List::MoreUtils</a>

<ul>
<li><a href="#list_moreutils_any">List::MoreUtils::any</a></li>
<li><a href="#list_moreutils_all">List::MoreUtils::all</a></li>
<li><a href="#list_moreutils_uniq">List::MoreUtils::uniq</a></li>
</ul></li>
</ul>
</p>

<h2 id="listutil">List::Util</h2>

<p><a href="http://search.cpan.org/~gbarr/Scalar-List-Utils-1.23/lib/List/Util.pm">List::Util</a> provides a handful of utilities. Some of these are:</p>

<h3 id="list_util_first">List::Util::first</h3>

<p><code>List::Util::first</code> will return the first element in a list that matches a given criteria:</p>

<pre class="brush:perl">
my @list = qw(1 2 3 4 5 6);
my $first = first { $_ > 4 } @list;  # returns 5
</pre>

<h3 id="list_util_max">List::Util::max</h3>

<p><code>List::Util::max</code> returns the maximum number from a list:</p>

<pre class="brush:perl">
my @list = qw(1 2 3 4 5 6);
my $ = max @list;  # returns 6
</pre>

<h3 id="list_util_shuffle">List::Util::shuffle</h3>

<p><code>List::Util::shuffle</code> randomizes a list:</p>

<pre class="brush:perl">
my @list = shuffle qw(1 2 3 4 5 6);
say $list[0] # prints random number between 1 - 6
</pre>

<h2 id="list_moreutils">List::MoreUtils</h2>

<p><a href="http://search.cpan.org/~adamk/List-MoreUtils-0.30/lib/List/MoreUtils.pm">List::MoreUtils</a>, as the name implies, provides some more list utilities.</p>

<h3 id="list_moreutils_any">List::MoreUtils::any</h3>

<p><code>List::MoreUtils::any</code> returns <code>true</code> if any element in the list fits the given criteria:</p>

<pre class="brush:perl">
say "true" if (any { $_ > 5 } qw(1 2 3 4 5 6));
</pre>

<h3 id="list_moreutils_all">List::MoreUtils::all</h3>

<p><code>List::MoreUtils::all</code> returns <code>true</code> if all elements in the list fit the given criteria:</p>

<pre class="brush:perl">
say "true" if (all {$_ > 0) qw(1 2 3 4 5 6));
</pre>

<h3 id="list_moreutils_uniq">List::MoreUtils::uniq</h3>

<p><code>List::MoreUtils::uniq</code> can provide two functions:</p>

<ul>
<li>In list context, it will return a list of only unique elements</li>
<li>In scalar context, it will return the number of unique elements</li>
</ul>

<pre class="brush:perl">
my @list = qw(1 1 2 3 4 4 4 5 6 6);
my @uniqs = uniq @list;
print Dumper @uniqs; # returns 1 2 3 4 5 6
my $unique_count; = uniq @list;
say $unique_count; # prints 6
</pre>

<h2>Conclusion</h2>

<p>As you can see, these two modules provide some nice convenience functions. There are many more to learn, so check out the CPAN page for each.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/development/perlish-list-utilities.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perlish – DBIx::Simple</title>
		<link>http://terrarum.net/development/perlish-dbixsimple.html</link>
		<comments>http://terrarum.net/development/perlish-dbixsimple.html#comments</comments>
		<pubDate>Tue, 19 Apr 2011 04:01:04 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[dbi]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perlish]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=341</guid>
		<description><![CDATA[Introduction Over the years, I&#8217;ve spent a lot of time with Perl DBI. It&#8217;s a great interface for working with databases, but sometimes I find it a little too heavy. A few months ago I found DBIx::Simple and now all is right with the world. Table of Contents DBIx::Simple Return a Single Value Return Multiple [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>Over the years, I&#8217;ve spent a lot of time with Perl <a href="http://dbi.perl.org/">DBI</a>. It&#8217;s a great interface for working with databases, but sometimes I find it a little too heavy. A few months ago I found <a href="http://search.cpan.org/~juerd/DBIx-Simple-1.35/lib/DBIx/Simple.pm">DBIx::Simple</a> and now all is right with the world.</p>

<p><span id="more-341"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#dbixsimple">DBIx::Simple</a>

<ul>
<li><a href="#singlevalue">Return a Single Value</a></li>
<li><a href="#multivalue">Return Multiple Rows from One Column</a></li>
<li><a href="#map">Create a Hash Indexed by a Column</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</p>

<h2 id="dbixsimple">DBIx::Simple</h2>

<p>To start using <code>DBIx::Simple</code>, create a <code>DBI</code> object in a similar manner to normal DBI.</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use DBIx::Simple;

my $dbh = DBIx::Simple->connect( 'DBI:mysql:database=foo', 'user', 'password' ) or die DBIx::Simple->error;
</pre>

<p>Now comes the fun stuff. With DBI, I use a familiar four-step routine:</p>

<ul>
<li>Prepare</li>
<li>Execute</li>
<li>Fetch</li>
<li>Finish</li>
</ul>

<p>While clean and methodical, these four steps can become a pain to use over and over again.</p>

<p>With <code>DBIx::Simple</code>, it can be as easy as:</p>

<pre class="brush:perl">
    for my $row ($db->query('select username, email from users')->hashes) {
        print "Username: $row->{username}, Email: $row->{email}\n";
    }
</pre>

<p>Here, everything is rolled into one step.</p>

<p><em>note</em>: You don&#8217;t have to combine the Query and Fetch steps &#8212; they can be split. I personally like to for the conciseness.</p>

<p>Query results can be returned in a number of different ways. I&#8217;ll explain a few ways, but all can be found in <code>DBIx::Simple</code>&#8216;s <a href="http://search.cpan.org/~juerd/DBIx-Simple-1.35/lib/DBIx/Simple.pm">perldoc</a> or <a href="http://search.cpan.org/~juerd/DBIx-Simple-1.35/lib/DBIx/Simple/Examples.pod">examples</a> doc.</p>

<h3 id="singlevalue">Return a Single Value</h3>

<p>If you know that your result will only have a single value, you can do:</p>

<pre class="brush:perl">
my $sum = $dbh->query('select 2 + 2')->list;
</pre>

<h3 id="multivalue">Return Multiple Rows from One Column</h3>

<p>Building on the previous example:</p>

<pre class="brush:perl">
my @sums = $dbh->query('select 2 + 2 union select 3 + 3')->flat;
</pre>

<h3 id="map">Create a Hash Indexed by a Column</h3>

<p>This example creates a hash of users with a Key based on the first column and Value based on the second:</p>

<pre class="brush:perl">
my %users = $dbh->query('select id, username from users')->map;
</pre>

<h2 id="conclusion">Conclusion</h2>

<p><code>DBIx::Simple</code> is one of my favorite Perl modules. If you frequently use DBI, I would encourage you to take a look at it.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/development/perlish-dbixsimple.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Caching RPMs with automirror</title>
		<link>http://terrarum.net/administration/caching-rpms-with-automirror.html</link>
		<comments>http://terrarum.net/administration/caching-rpms-with-automirror.html#comments</comments>
		<pubDate>Fri, 15 Apr 2011 02:10:23 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[rpm]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=334</guid>
		<description><![CDATA[Introduction In a previous article, I wrote about how to use pkg-cacher to cache requested RPM files. Since then, the website for pkg-cacher has become unavailable and coincidentally I wrote my own tool that provides the same functionality called automirror. This article describes how to use it. Table of Contents Update &#8211; July 2011 Caching [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>In a <a href="http://terrarum.net/administration/caching-rpms-with-pkg-cacher.html">previous article</a>, I wrote about how to use <code>pkg-cacher</code> to cache requested RPM files. Since then, the website for <code>pkg-cacher</code> has become unavailable and coincidentally I wrote my own tool that provides the same functionality called <code>automirror</code>. This article describes how to use it.</p>

<p><span id="more-334"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#update">Update &#8211; July 2011</a></li>
<li><a href="#caching">Caching Packages</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#usage">Using automirror</a>

<ul>
<li><a href="#usage_normal">Normal Mode</a></li>
<li><a href="#usage_tunnel">Tunnel Mode</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</p>

<h2 id="update">Update &#8211; July 2011</h2>

<p><code>automirror</code> currently does not work with CentOS 6 (and probably RHEL6). It looks like there are some incompatibilities between the <code>HTTP::Proxy</code> Perl module and <code>libcurl</code> (which <code>yum</code> uses).</p>

<p>I have opened a bug report for <code>HTTP::Proxy</code> and hopefully it will be resolved. In the meantime, I would suggest just using a small caching proxy such as <a href="http://www.pps.jussieu.fr/~jch/software/polipo/">Polipo</a>.</p>

<h2 id="caching">Caching Packages</h2>

<p>One reason caching can be useful is: if one has 10 servers that require a package update, it makes more sense to download that package once and then distribute it locally to the other servers than to have each server request the remote package. This saves on outside bandwidth as well as makes retrieving the package extremely faster due to the local connection.</p>

<p><code>automirror</code>&#8216;s caching system is simple: if a server requests a file with a file extension that you have told <code>automirror</code> to watch out for (for example, an <code>.rpm</code> file), it first checks to see if there is a locally available copy. If not, it retrieves the file from the remote source. After retrieving it, it saves a copy. On the next request for the same <code>.rpm</code> file, it will send back the locally saved copy instead of downloading the remote copy again.</p>

<h2 id="installation">Installation</h2>

<p><code>automirror</code> can be downloaded from its <a href="https://bitbucket.org/jtopjian/automirror/overview">BitBucket repository</a>. You can either use mercurial to clone the repository or download a run-of-the mill <code>.zip</code>, <code>.gz</code>, or <code>.bz2</code> archive.</p>

<p>Inside the archive is the actual <code>automirror</code> script and a sample init script called <code>rc.automirror</code>. This init script has been tested on CentOS.</p>

<p>Install the <code>automirror</code> script by just copying it to any directory:</p>

<pre class="brush:bash">
# cp automirror /usr/local/bin
</pre>

<p>To install the init script, first edit it and change any settings or variables necessary. You should only have to edit the options in the <code>OPTIONS</code> section.</p>

<p>By default, the <code>EXTENSIONS</code> option is set to <code>.rpm</code> and <code>.img</code> files to support automated RedHat / CentOS installs.</p>

<p>You can install the init script by doing:</p>

<pre class="brush:bash">
# cp rc.automirror /etc/init.d/automirror
# chkconfig --add automirror
</pre>

<p>You can now start the service by running</p>

<pre class="brush:bash">
# /etc/init.d/automirror start
</pre>

<p>or</p>

<pre class="brush:bash">
# service automirror start
</pre>

<h2 id="usage">Using automirror</h2>

<p>For reference, the various options to <code>automirror</code> can be read by doing:</p>

<pre class="brush:bash">
# perldoc /usr/local/bin/automirror
</pre>

<p>or</p>

<pre class="brush:bash">
# /usr/local/bin/automirror --help
</pre>

<p>But the init script should be all you need to control it.</p>

<p>There are two main ways to use <code>automirror</code>: normal mode and tunneling mode.</p>

<h3 id="usage_normal">Normal Mode</h3>

<p>Normal mode is configured by setting <code>TUNNEL</code> TO <code>0</code> in the init script. In normal mode, simply configure any application (or global environment variable such as <code>http_proxy</code>) to point to the host and port where <code>automirror</code> is listening. For example if <code>automirror</code> was listening on <code>192.168.255.1:8080</code>, to configure <code>yum</code> to use <code>automirror</code> as a proxy, edit <code>/etc/yum.conf</code> and add the following:</p>

<pre class="brush:plain">
proxy=http://192.168.255.1:8080
</pre>

<p>Now when performing any action with <code>yum</code>, it will utilize the proxy and caching service.</p>

<h3 id="usage_tunnel">Tunnel Mode</h3>

<p>Tunnel mode can be turned on by setting <code>TUNNEL</code> TO <code>1</code> in the init script.</p>

<p>There are some applications and services, such as Kickstart or an automated run of Anaconda, that do not support a proxy service. You can use <code>automirror</code> in tunnel mode to get around this. For example, if there is an <code>.rpm</code> file located at</p>

<ul>
<li><code>http://some.centos.mirror.org/path/to/package-1.2-3.rpm</code></li>
</ul>

<p>you can still download the package via <code>automirror</code> by specifying:</p>

<ul>
<li><code>http://192.168.255.1:8080/some.centos.mirror.org/path/to/package-1.2-3.rpm</code></li>
</ul>

<p>In tunnelling mode, <code>automirror</code> will pop the host off of the url and use the next segment as the new host to connect to.</p>

<p>In Kickstart scripts, you can perform an automated install that uses <code>automirror</code> by specifying:</p>

<pre class="brush:plain">
url --url=http://192.168.255.1:8080/some.centos.mirror.org/pub/centos/5/os/i386
</pre>

<h2 id="conclusion">Conclusion</h2>

<p><code>automirror</code> is a very flexible proxy script that can perform a variety of caching. This article covered how to install and use <code>automirror</code> to cache <code>.rpm</code> packages.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/administration/caching-rpms-with-automirror.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Perlish – Dates and Time</title>
		<link>http://terrarum.net/development/perlish-dates-and-time.html</link>
		<comments>http://terrarum.net/development/perlish-dates-and-time.html#comments</comments>
		<pubDate>Thu, 31 Mar 2011 23:23:03 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perlish]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=326</guid>
		<description><![CDATA[Introduction Anyone who frequently writes utility scripts will eventually run into having to deal with Dates and Time. The type of tasks, though, can vary: sometimes you have to format a date, convert a date, add or subtract time to a date, or find a date. This article covers various ways of having to deal [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>Anyone who frequently writes utility scripts will eventually run into having to deal with Dates and Time. The type of tasks, though, can vary: sometimes you have to format a date, convert a date, add or subtract time to a date, or find a date. This article covers various ways of having to deal with Dates and Time in Perl.</p>

<p><span id="more-326"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#core">Core Date Tools</a></li>
<li><a href="#datemanip">Date::Manip</a></li>
<li><a href="#datetime">DateTime</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</p>

<h2 id="core">Core Date Tools</h2>

<p>To start off with, here are some basic ways to deal with dates and time in Perl without the need for external modules. Here&#8217;s a basic script:</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;

# print formatted time
say scalar localtime;
</pre>

<p>Running the above script will simply print:</p>

<pre class="brush:plain">
$ perl time.pl
Thu Mar 31 16:38:03 2011
</pre>

<p>How about dissecting the current date/time:</p>

<pre class="brush:perl">
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $dst) = localtime();
</pre>

<p>It&#8217;s important to note that <code>$mon</code> (for month) is zero-based and <code>$year</code> needs <code>1900</code> added to it. So if you want to print <code>3/31/2011</code>, you need to do:</p>

<pre class="brush:perl">
$mon += 1;
$year += 1900;
say "$mon/$mday/$year";
</pre>

<p>Dealing with <a href="http://www.epochconverter.com/">epoch</a>-based time is a frequent occurrence on *nix-based systems:</p>

<pre class="brush:perl">
# get epoch time
my $time = time;

# get yesterday
$time -= 86400;

# get different parts of specified time
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $dst) = localtime($time);
</pre>

<p>The above code gets the current time in epoch-based seconds, subtracts 1 day from it (60 seconds * 60 minutes * 24 hours) and then retrieves the different parts using the <code>localtime</code> function.</p>

<p>You can easily format a date however you want with the <code>POSIX</code> module and the <code>strftime</code> function:</p>

<pre class="brush:perl">
use POSIX qw/ strftime /;

say "Yesterday was ", strftime("%A, %B %dth, %Y", localtime($time));
</pre>

<p>This prints:</p>

<pre class="brush:plain">
$ perl time.pl
Yesterday was Wednesday, March 30th, 2011
</pre>

<h2 id="datemanip">Date::Manip</h2>

<p>Sometimes you need to do more advanced date calculations. I like <a href="http://search.cpan.org/~sbeck/Date-Manip-6.22/lib/Date/Manip.pod">Date::Manip</a> for these tasks.</p>

<p>For example, here&#8217;s how to parse a date:</p>

<pre class="brush:perl">
use Date::Manip::Date;
my $date = new Date::Manip::Date;
$date->parse('2011-03-31 12:47:33');
say $date->printf("%F");
</pre>

<p>The above prints:</p>

<pre class="brush:plain">
$ perl time.pl
Thursday, March 31, 2011
</pre>

<p><code>Date::Manip</code> supports a huge array of formats, the above is just a simple example.</p>

<p>If you wanted to find how many days are between two dates, <code>Date::Manip</code> can do that:</p>

<pre class="brush:perl">
my $d1 = new Date::Manip::Date;
my $d2 = new Date::Manip::Date;

$d1->parse('2001-01-01');
$d2->parse('epoch ' . time); # Use keyword 'epoch' for epoch-based time

$delta = $d1->calc($d2, 'approx');
printf("[%s] minus [%s] is [%s] days\n", $d1->printf("%F"), $d2->printf("%F"), $delta->printf("%dyd"));
</pre>

<p>This prints:</p>

<pre class="brush:plain">
$ perl time.pl
[Monday, January 01, 2001] minus [Thursday, March 31, 2011] is [3743.29875] days
</pre>

<h2 id="datetime">DateTime</h2>

<p>As Perl has a huge collection of Date and Time modules, <a href="http://datetime.perl.org">DateTime</a> was created to standardize on a set of functions and way to use them. One thing I do like about <code>Date::Manip</code> is that I only have to install the one module to have access to a wide array of Date and Time utilities. <code>DateTime</code> is a little different, and I can understand the need for that, too.</p>

<p>In order to run the following examples, three separate modules needed installed:</p>

<ul>
<li><code>DateTime</code></li>
<li><code>DateTime::Format::Strptime</code></li>
<li><code>DateTime::Format::HTTP</code></li>
</ul>

<p>This example parses and reformats a simple date:</p>

<pre class="brush:perl">
use DateTime;
use DateTime::Format::Strptime;

my $parser = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d' );
my $dt1 = $parser->parse_datetime('2001-01-02');
say $dt1->mdy('/');
</pre>

<p>This prints:</p>

<pre class="brush:plain">
$ perl time.pl
01/02/2001
</pre>

<p>If you don&#8217;t want to supply your own date format to be parsed, <code>DateParse::Format::HTTP</code> should be able to parse it for you:</p>

<pre class="brush:perl">
use DateTime;
use DateTime::Format::HTTP;
my $dt2 = DateTime::Format::HTTP->parse_datetime('2001-01-02');
say $dt2->mdy('/');
</pre>

<p>This prints the same result.</p>

<p>This example gets the current time as it is in Helsinki and then adds 17 days to it:</p>

<pre class="brush:perl">
my $dt3 = DateTime->now(time_zone => 'Europe/Helsinki');
say $dt3->datetime;

$dt3->add( days => 17 );
say $dt3->datetime;
</pre>

<p>The output is:</p>

<pre class="brush:plain">
$ perl time.pl
2011-04-01T01:38:03
2011-04-18T01:38:03
</pre>

<p>This final example subtracts 5 months from the current epoch time and then prints the resulting year:</p>

<pre class="brush:perl">
my $dt4 = DateTime->from_epoch( epoch => time );
$dt4->subtract( months => 5 );
say $dt4->year;
</pre>

<p>The output is:</p>

<pre class="brush:plain">
$ perl time.pl
2010
</pre>

<h2 id="conclusion">Conclusion</h2>

<p>This article covered only a few of the different types of Date and Time manipulations a systems administrator is likely to run into. As you run into different tasks, keep these modules in mind &#8212; they&#8217;re able to do a lot more than what is described above.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/development/perlish-dates-and-time.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perlish – Text::Template</title>
		<link>http://terrarum.net/development/perlish-texttemplate.html</link>
		<comments>http://terrarum.net/development/perlish-texttemplate.html#comments</comments>
		<pubDate>Wed, 30 Mar 2011 21:45:44 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perlish]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=319</guid>
		<description><![CDATA[Introduction Most scripts or applications involve some type of output and reporting. If the script or application starts growing, the output can become more complex. Mixing the code that formats and displays your output in the same area as the code that performs the logic can become messy. Templating is a popular solution to this [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>Most scripts or applications involve some type of output and reporting. If the script or application starts growing, the output can become more complex. Mixing the code that formats and displays your output in the same area as the code that performs the logic can become messy. Templating is a popular solution to this problem. In this article I will quickly cover <code>Text::Template</code>.</p>

<p><span id="more-319"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#texttemplate">Text::Template</a>

<ul>
<li><a href="#reportcode">The Report Code</a></li>
<li><a href="#template">The Template</a></li>
<li><a href="#output">The Full Output</a></li>
</ul></li>
<li><a href="#conlusion">Conclusion</a></li>
</ul>
</p>

<h2 id="texttemplate">Text::Template</h2>

<p><a href="http://search.cpan.org/~mjd/Text-Template-1.45/lib/Text/Template.pm">Text::Template</a> is a simple to use templating system for Perl. It&#8217;s not restricted by output format, so you can use it for text, HTML, XML, or really any type of readable output.</p>

<p>Here&#8217;s a quick example that shows how to use <code>Text::Template</code> with a simple report.</p>

<h3 id="reportcode">The Report Code</h3>

<p>This short script emulates a report of suspended users. For the sake of simplicity, I am just referencing the suspended users by a manually created hash. The script then calls the <code>Text::Template</code> function <code>fill_in_file</code>, which plugs in all data to the template (seen later) and then prints the results.</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use Text::Template qw/ fill_in_file /;

my $users = { 
    moe => 'Wed Mar 30 12:23:26 2011',
    larry => 'Tue Mar 29 09:15:55 2011',
    curly => 'Mon Mar 28 20:45:18 2011',
};

my $data = { 
    title => 'Suspended Users Report',
    users => \$users,
};

say fill_in_file('report.tpl', HASH => $data);
</pre>

<h3 id="template">The Template</h3>

<p>Now here&#8217;s the template that will be used, located in a dedicated file called <code>report.tpl</code>. Perl will interpret anything enclosed in <code>{ }</code>&#8216;s &#8212; this includes variables, functions, and full blocks of code:</p>

<pre class="brush:perl">
{$title}
=======================================
Generated on {localtime}
---------------------------------------

{
    for my $user (keys %{ $users }) {
        $OUT .= sprintf("%-10s %28s\n", $user, $users->{$user});
    }   
}
</pre>

<p>The use of the <code>$OUT</code> variable is a special feature of <code>Text::Template</code>. In short, it will collect the output of a loop and print the final results to the rendered template.</p>

<h3 id="output">The Full Output</h3>

<p>When the script is run, here is the output:</p>

<pre class="brush:plain">
Suspended Users Report
=======================================
Generated on Wed Mar 30 15:35:09 2011
---------------------------------------

larry          Tue Mar 29 09:15:55 2011
curly          Mon Mar 28 20:45:18 2011
moe            Wed Mar 30 12:23:26 2011
</pre>

<h2 id="conlusion">Conclusion</h2>

<p>For a simple example such as the one above, including the output and formatting code would not have been that bad. However, as scripts become larger and more complicated, placing the formatting code into a dedicated template file can bring ease of comprehension by more structure. Although Perl has several templating modules available, I&#8217;ve found <code>Text::Template</code> to be very flexible and simple to understand.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/development/perlish-texttemplate.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perlish – Logging</title>
		<link>http://terrarum.net/development/perlish-logging.html</link>
		<comments>http://terrarum.net/development/perlish-logging.html#comments</comments>
		<pubDate>Mon, 28 Mar 2011 17:23:41 +0000</pubDate>
		<dc:creator>Joe Topjian</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perlish]]></category>

		<guid isPermaLink="false">http://terrarum.net/?p=305</guid>
		<description><![CDATA[Introduction Logging is an essential tool and feature for System Administrators. With Perl, logging can be as simple as using print, die, or warn, or more advanced such as logging to syslog, a private log file, or even a database. This article quickly covers the more advanced logging. Table of Contents Sys::Syslog Log::Log4perl Simple Use [...]]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>

<p>Logging is an essential tool and feature for System Administrators. With Perl, logging can be as simple as using <code>print</code>, <code>die</code>, or <code>warn</code>, or more advanced such as logging to <code>syslog</code>, a private log file, or even a database. This article quickly covers the more advanced logging.</p>

<p><span id="more-305"></span></p>

<h2>Table of Contents</h2>

<p><ul>
<li><a href="#sys_syslog">Sys::Syslog</a></li>
<li><a href="#log4perl">Log::Log4perl</a>

<ul>
<li><a href="#log4perl_simple">Simple Use</a></li>
<li><a href="#log4perl_logfile">Logging to a Logfile</a></li>
<li><a href="#log4perl_syslog">Logging to Syslog</a></li>
<li><a href="#conclusion">Log::Log4perl Resources</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</p>

<h2 id="sys_syslog">Sys::Syslog</h2>

<p><a href="http://perldoc.perl.org/Sys/Syslog.html">Sys::Syslog</a> is a core Perl module. It provides a standard and simple interface to logging to your (*nix-base) server&#8217;s syslog.</p>

<p>Here&#8217;s a simple example:</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use Sys::Syslog;

my $ident = $0; 
openlog($ident, 'pid', 'user');
syslog('info', 'Testing syslog logging');
closelog();
</pre>

<p>After running this script, you can find the following in <code>/var/log/messages</code> (or other appropriate log file):</p>

<pre class="brush:plain">
Mar 25 13:36:28 server syslog.pl[20075]: Testing syslog logging
</pre>

<p>The documentation for <code>Sys::Syslog</code> is very well written and I highly encourage reading it &#8212; especially &#8220;The Rules of Sys::Syslog&#8221;.</p>

<h2 id="log4perl">Log::Log4perl</h2>

<p><a href="http://search.cpan.org/~mschilli/Log-Log4perl-1.32/lib/Log/Log4perl.pm">Log::Log4perl</a> is an extremely robust logging module for Perl. Its use can range from simple logging features to full-fledged application framework logging (including to remote databases). I&#8217;ve never been on a project where <code>Log::Log4perl</code> was needed, but wanted to include it in this article as it seems to be the de-facto logging module for Perl.</p>

<h3 id="log4perl_simple">Simple Use</h3>

<p>This example shows how to print logging information to the screen for everything from the <code>INFO</code> level and up:</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use Log::Log4perl qw/:easy/;

Log::Log4perl->easy_init($INFO);
my $logger = get_logger();
$logger->info("Testing Log4perl logging");
</pre>

<p>When this script is run, the following is printed on the screen:</p>

<pre class="brush:plain">
2011/03/25 13:45:52 Testing Log4perl logging
</pre>

<h3 id="log4perl_logfile">Logging to a Logfile</h3>

<p>This next script writes all logs to a logfile rather than on the screen. As you can see, there&#8217;s a bit more involved than the previous example &#8212; namely the need to begin to configure <code>Log::Log4perl</code>:</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use Log::Log4perl;

my $log_conf = q/  
    log4perl.category = INFO, Logfile
     
    log4perl.appender.Logfile = Log::Log4perl::Appender::File 
    log4perl.appender.Logfile.filename = log4perl.log
    log4perl.appender.Logfile.mode = write 
    log4perl.appender.Logfile.layout = Log::Log4perl::Layout::SimpleLayout     
/;

Log::Log4perl::init( \$log_conf );
my $logger = Log::Log4perl::get_logger();

$logger->error("Testing Log4perl logging");
</pre>

<p>Once this script is run, you should have a log file called <code>log4perl.log</code> in the same directory. The contents will read:</p>

<pre class="brush:plain">
ERROR - Testing Log4perl logging
</pre>

<h3 id="log4perl_syslog">Logging to Syslog</h3>

<p><code>Log::Log4perl</code> is also able to log to syslog, although it requires an extra module: <code>Log::Dispatch::Syslog</code>. Unless you are doing other logging with <code>Log::Log4perl</code>, I would recommend using the standard <code>Sys::Syslog</code> module:</p>

<pre class="brush:perl">
#!/usr/bin/env perl
use strict;
use warnings;
use Log::Log4perl;

my $log_conf = q/  
    log4perl.category = INFO, SYSLOG
     
    log4perl.appender.SYSLOG = Log::Dispatch::Syslog
    log4perl.appender.SYSLOG.min_level = warn 
    log4perl.appender.SYSLOG.ident = log4perl.pl
    log4perl.appender.SYSLOG.facility = user
    log4perl.appender.SYSLOG.layout = Log::Log4perl::Layout::SimpleLayout
/;

Log::Log4perl::init( \$log_conf );
my $logger = Log::Log4perl::get_logger();

$logger->warn("Testing Log4perl logging");
</pre>

<p>Once this script is run, you can find the following message in <code>/var/log/messages</code>:</p>

<pre class="brush:plain">
Mar 25 14:00:02 server log4perl.pl: WARN - Testing Log4perl logging
</pre>

<h3 id="conclusion">Log::Log4perl Resources</h3>

<p>I found the following links to be great resources for <code>Log::Log4perl</code>:</p>

<ul>
<li><a href="http://lena.franken.de/perl_hier/log4perl.html">Log4perl</a></li>
<li><a href="http://www.netlinxinc.com/netlinx-blog/52-perl/126-eight-loglog4perl-recipes.html">8 Useful Log::Log4perl Recipes </a></li>
<li><a href="http://www.perl.com/pub/2002/09/11/log4perl.html">Retire your debugger, log smartly with Log::Log4perl!</a></li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>This article covered two Perl modules that deal with advanced Perl logging, <code>Sys::Syslog</code> and <code>Log::Log4perl</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://terrarum.net/development/perlish-logging.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
