<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>dan&#039;s linux blog &#187; Code</title>
	<atom:link href="http://www.dark.ca/category/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.dark.ca</link>
	<description>direct from the mysterious land of the sysadmin</description>
	<lastBuildDate>Tue, 10 Aug 2010 10:16:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>CPAN RPMs in RHEL / CentOS : generation, conflict, and solutions</title>
		<link>http://www.dark.ca/2010/04/08/cpan-rpms-in-rhel-centos/</link>
		<comments>http://www.dark.ca/2010/04/08/cpan-rpms-in-rhel-centos/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 15:25:24 +0000</pubDate>
		<dc:creator>dan</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[How-to]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[common tools]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[rpm]]></category>

		<guid isPermaLink="false">http://www.dark.ca/?p=185</guid>
		<description><![CDATA[Hello all !  Today we&#8217;re going to take a look at a somewhat obscure problem that &#8211; once encountered &#8211; can cause nothing but headaches for a system administrator.  The problem relates to conflicts in CPAN RPM packages, and what can be done to work around the issue.  If you&#8217;ve made it this far, i&#8217;m [...]]]></description>
			<content:encoded><![CDATA[<p>Hello all !  Today we&#8217;re going to take a look at a somewhat obscure problem that &#8211; once encountered &#8211; can cause nothing but headaches for a system administrator.  The problem relates to conflicts in CPAN RPM packages, and what can be done to work around the issue.  If you&#8217;ve made it this far, i&#8217;m going to assume a couple of things : you&#8217;re comfortable with RPMs and repositories, have worked with a .spec file before, and you know what Perl modules are.  Good ?  Ok, let&#8217;s go.</p>
<p><em>Edit : About a week after i posted this article, the pastebin i uploaded the examples to disappeared.  Maybe it will come back &#8211; i don&#8217;t know &#8211; but if not, sorry for the broken links&#8230;</em></p>
<p><a href="http://www.cpan.org/">CPAN</a> is an enormous collection of Perl modules.  If you&#8217;ve ever written a Perl script, there&#8217;s a good chance you&#8217;ve used a module that &#8211; at one point or another &#8211; came from this archive.  One of the really neat features of CPAN is the <a href="http://search.cpan.org/~jhi/perl-5.8.0/lib/CPAN.pm#Interactive_Mode">interactive manner</a> in which modules can be downloaded and installed from the archive using Perl right from the command line (frankly, if you&#8217;re reading this post, there&#8217;s a good chance you&#8217;ve used this feature, too).  This is a fairly common way to install new modules and add functionality to your system, especially if you&#8217;re coding for local use (i.e. on your personal box).</p>
<p>It&#8217;s useful, but it&#8217;s not perfect, and one of the key areas where it starts to fail is scalability : if you&#8217;ve got a bunch of machines, and you need to SSH into each one to interactively install a CPAN module or two, it&#8217;s going to be a hassle.  Likewise, CPAN doesn&#8217;t often find its way into the hearts and minds of enterprise Red Hat or CentOS environments, where the official policy is often to install software via RPM only (for support, administration, and sanity reasons, this is often the case).</p>
<p>Luckily, some of the most commonly used CPAN modules exist as RPMs in the default repositories.  Some, but not all (and not even « many ») &#8211; for this, there are other repositories available.  Some examples :</p>
<ul>
<li><a href="http://fedoraproject.org/wiki/EPEL">EPEL</a></li>
<li><a href="http://dag.wieers.com/rpm/">Dag</a></li>
<li><a href="https://rpmrepo.org/RPMforge/">RPMForge</a></li>
<li><a href="http://rpm.mag-sol.com/">Magnum Solutions</a></li>
</ul>
<p>That last one &#8211; Magnum &#8211; is particularly interesting given the subject of our post today.  From their info page :</p>
<blockquote><p>At Magnum we have a firm rule that all CPAN modules on our machines are installed from RPMs. The Fedora and Centos projects build RPMs for many CPAN modules, but there are always ones missing and the ones that are available often lag behind the most up to date versions.  For that reason, we build a lot of RPMs of CPAN modules. And we don&#8217;t want to keep that work to ourselves, so on these pages we make them available for anyone to download.</p></blockquote>
<p>Their RPMs are generated automagically using a great tool called « cpanspec », which does exactly what you think it does : given a CPAN tarball, it will generate a .spec file suitable for building an installable RPM.  It is available in the standard repositories, and can be installed easily via YUM as normal, so go ahead and do that now.  Ok, example time : say you needed HTML::Laundry, but after a quick peek through your repositories, it becomes readily apparent that an RPM is not available.  Thanks to cpanspec, all is not lost :</p>
<pre>[build@host-119 ~]$ wget http://search.cpan.org/CPAN/authors/id/S/ST/STEVECOOK/HTML-Laundry-0.0103.tar.gz
[build@host-119 ~]$ cpanspec --packager "build &lt;build@domain.ext&gt;" HTML-Laundry-0.0103.tar.gz</pre>
<p>We just downloaded the tarball right from the CPAN website, and ran cpanspec against it.  The « &#8211;packager » argument simple defines the person who&#8217;s generating the .spec, and doesn&#8217;t necessarily have to be anything accurate.  Go ahead and try it for yourself.  Now take a look at the resulting .spec file (or on the a pastebin <a href="http://pastebin.ca/1858756">here</a>).  As you can see, it fills in all the fields, including the critical (and often tricky-to-determine) « BuildRequires » and « Requires » items.  Frankly, it&#8217;s solid gold, and it has made the lives of CentOS / RHEL admins all over the world much easier.</p>
<p>That said, it&#8217;s not perfect, and there are times when you might run into problems.  Actually, you may run into two problems in particular.  The first is conflicts over ownership, which arises when multiple RPMs claim to be responsible for the same file (or files, or directories, or features, or whatever).  The second is more nefarious : an RPM that writes files to the system without declaring ownership for them &#8211; a condition often referred to as « clobbering ».  The former is irritating, but at least it&#8217;s not destructive, unlike the latter, which can cause all manner of headaches.  To illustrate these two problems, let&#8217;s take a look at another example (this one being decidedly more real-world than that of Laundry above) : <a href="http://search.cpan.org/dist/CGI.pm/lib/CGI.pm">CGI.pm</a>.</p>
<p>The <a href="http://pastebin.ca/1858885">.spec file</a> that is generated from this tarball is functional and correct, and we can build an installable RPM out of it, so at first all appears well.  Again, go ahead and try for yourself &#8211; i&#8217;ll wait.  You may wish to capture the build output for review &#8211; otherwise, check the <a href="http://pastebin.ca/1858890">pastebin</a>.  I&#8217;d like to draw your attention to the « Installing » lines.  By trimming the « Installing /var/tmp/perl-CGI.pm.3.49-1-root-root » element from each of those lines, we can see the actual paths and files that this RPM will install to.  Examples :</p>
<pre>/usr/lib/perl5/vendor_perl/5.8.8/CGI.pm
/usr/lib/perl5/vendor_perl/5.8.8/CGI/Cookie.pm
/usr/lib/perl5/vendor_perl/5.8.8/CGI/Util.pm
/usr/share/man/man3/CGI.3pm
/usr/share/man/man3/CGI::Pretty.3pm
/usr/share/man/man3/CGI::Cookie.3pm</pre>
<p>At first glance this looks perfectly acceptable.  But look what happens when we try to install the resulting RPM (clipped for brevity) :</p>
<pre>[root@host-119 build]# rpm -iv /usr/src/redhat/RPMS/noarch/perl-CGI.pm-3.49-1.noarch.rpm
Preparing packages for installation...
file /usr/share/man/man3/CGI.3pm.gz from install of perl-CGI.pm-3.49-1.noarch conflicts with file from package perl-5.8.8-27.el5.x86_64
file /usr/share/man/man3/CGI::Cookie.3pm.gz from install of perl-CGI.pm-3.49-1.noarch conflicts with file from package perl-5.8.8-27.el5.x86_64
file /usr/share/man/man3/CGI::Pretty.3pm.gz from install of perl-CGI.pm-3.49-1.noarch conflicts with file from package perl-5.8.8-27.el5.x86_64</pre>
<p>As it turns out, the Perl package that comes with RHEL / CentOS already contains CGI.pm.  This is normal, since it&#8217;s so popular, and is included as a convenience.  Thus, RPM &#8211; in an attempt to preserve the coherence of the package management system &#8211; refuses to install overtop of the existing owned files.  This is a fine illustration of the first of the two problems previously noted : conflicts over ownership.  As i mentioned above, it&#8217;s aggravating, but it&#8217;s not a bug &#8211; it&#8217;s a feature, and it&#8217;s doing exactly what it&#8217;s designed to do.  Irritating, but not ultimately dire.</p>
<p>If you look carefully, though, it&#8217;s also an illustration of the second problem.  Note the list of files that are conflicting.  Look back to the list of files that the package contains &#8211; notice anything missing from the conflicts list ?  That&#8217;s right &#8211; the actual module files (*.pm) are not showing conflicts, which means they&#8217;d get overwritten without complaint by RPM.  You might be thinking « who cares ? that&#8217;s what i want » right now, but trust me, it&#8217;s not what you want.  Imagine this CGI package, with this version of CGI.pm gets installed, and then later you upgrade the Perl package &#8211; your CGI.pm files will get overwritten by the Perl package, because as far as RPM is concerned, Perl owns those files.  All of a sudden, things break because you had scripts that relied on your particular version, but since you just upgraded Perl, you think (quite naturally) that the problem could be anywhere &#8211; where do you even start looking ?</p>
<p>Imagine the headache if there are multiple administrators, multiple servers, multiple data centres, and multiple clients paying multiple dollars.  No fun at all.</p>
<p>So how can we upgrade CGI.pm, using an RPM, without running into these problems ?  As is often the case, the answer is deceptively simple, but not immediately obvious.  Ultimately what we want to accomplish is twofold :</p>
<ul>
<li>Avoid the man conflicts.</li>
<li>Ensure that the existing owned module files are not clobbered by our new package.</li>
</ul>
<p>Concerning the man pages &#8211; and i&#8217;m going to be perfectly blunt here &#8211; the solution is to simply not install them, since, of course, they&#8217;re already there.  As for avoiding a clobbering condition, this requires a little bit of investigation into how Perl modules and libraries are stored on an RHEL / CentOS machine.  Consider the following output :</p>
<pre>[root@host-119 ~]# ls -d /usr/lib64/perl5/*
/usr/lib64/perl5/5.8.8  /usr/lib64/perl5/site_perl  /usr/lib64/perl5/vendor_perl</pre>
<p>What&#8217;s it all mean ?  Well, the « 5.8.8 » directory is the default directory as defined by the Perl architecture, and is system and platform-agnostic, which is to say that it&#8217;s (supposed to be) the same on every system.  The « vendor_perl » directory contains everything that specific to RHEL / CentOS (the « vendor » of the distribution).  As you may recall from the rpmbuild output above, this is where the RPM wants to install the modules (thus creating the clobbering condition).</p>
<p>There&#8217;s a third directory there, promisingly named « site_perl » ; as the name implies, this is where site-specific files are stored, which is to say items that are neither part of the default Perl architecture, nor part of the RHEL / CentOS distribution.  As you&#8217;ve no doubt guessed by now, site_perl is where we&#8217;re going to put our new modules.</p>
<p>Luckily for us, the only thing that needs to be changed is the .spec file &#8211; and we even get a headstart, since cpanspec does most of the heavy lifting for us.  Examining the <a href="http://pastebin.ca/1858885">.spec file</a> once more, we see the following lines of note (again, cut for brevity) :</p>
<pre>%build
%{__perl} Makefile.PL INSTALLDIRS=vendor
%files
%{perl_vendorlib}/*</pre>
<p>These indicate that the target installation directory is that of the vendor, which is normally the case, and thus the default setting.  Since we want to install to the site directory, we make the following changes :</p>
<pre>%build
%{__perl} Makefile.PL INSTALLDIRS=site
%files
%{perl_sitelib}/*</pre>
<p>That solves our clobbering problem quite nicely, but what about the man files ?  As i mentioned above, the idea is to simply avoid installing them altogether, but since they&#8217;re generated automatically during the build process, how can we exclude them ?  What i&#8217;m about to present is a bit of a hack, but it&#8217;s absolutely effective, and ultimately quite clean : we delete them after they&#8217;ve been generated, and then don&#8217;t declare them in the file list.  Some items are already being potentially deleted by default, so let&#8217;s go ahead and add our own line into the mix :</p>
<pre>find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2&gt;/dev/null \;
# destroy manified man, man.
find $RPM_BUILD_ROOT -type f -name '*.3pm' -exec rm -f {} \;</pre>
<p>This will look for all of the « manified » man files and just remove from the build tree.  All that&#8217;s left now is to remove them from the file list.  This is as simple as deleting (or commenting out) their sole declaration :</p>
<pre>#%{_mandir}/man3/*</pre>
<p>Another option is to simply install use the « &#8211;excludedocs » argument when installing the RPM.  I opted to remove the docs altogether in order to ensure that the package can be installed without errors by anyone else without needed to know about the argument requirement ahead of time (and to facilitate automated rollouts).</p>
<p>What you&#8217;ll end up with is a .spec file that <a href="http://pastebin.ca/1858951">looks like this</a>.  Go ahead and build your RPM &#8211; it&#8217;ll install without conflicts and without danger.  This is a technique that can be used for other CPAN packages as well, so go ahead and install everything you&#8217;ve always wanted.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dark.ca/2010/04/08/cpan-rpms-in-rhel-centos/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>(complex) partitioning in kickstart</title>
		<link>http://www.dark.ca/2009/08/03/complex-partitioning-in-kickstart/</link>
		<comments>http://www.dark.ca/2009/08/03/complex-partitioning-in-kickstart/#comments</comments>
		<pubDate>Mon, 03 Aug 2009 18:05:57 +0000</pubDate>
		<dc:creator>dan</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[How-to]]></category>
		<category><![CDATA[fedora]]></category>
		<category><![CDATA[filesystem]]></category>
		<category><![CDATA[kickstart]]></category>
		<category><![CDATA[shell script]]></category>
		<category><![CDATA[uncommon tools]]></category>

		<guid isPermaLink="false">http://www.dark.ca/?p=119</guid>
		<description><![CDATA[Bonjour my geeky friends ! :)  As you are likely aware, it is now summer-time here in the northern hemisphere, and thus, i&#8217;ve been spending as much time away from the computer as possible.  That said, it&#8217;s been a long time, i shouldn&#8217;t have left you, without a strong beat to step to. Now, if [...]]]></description>
			<content:encoded><![CDATA[<p>Bonjour my geeky friends ! :)  As you are likely aware, it is now summer-time here in the northern hemisphere, and thus, i&#8217;ve been spending as much time <em>away</em> from the computer as possible.  That said, it&#8217;s been a long time, i shouldn&#8217;t have left you, without a strong beat to step to.</p>
<p>Now, if you&#8217;re not familiar with kickstarting, it&#8217;s basically just a way to automate the installation of an operating environment on a machine &#8211; think hands-free installation.  Anaconda is the OS installation tool used in Fedora, RedHat, and some other Linux OS&#8217;s, and it can be used in a kickstart capacity.  For those of you looking for an intro, i heavily suggest reading over the <a href="http://fedoraproject.org/wiki/Anaconda/Kickstart">excellent documentation</a> at the Fedora project website.  The kickstart configuration process could very easily be a couple of blog entries on its own (which i&#8217;ll no doubt get around to in the future), but for now i want to touch on one particular aspect of it : <em>complex partition schemes</em>.</p>
<h3>how it is</h3>
<p>The current method for declaring partitions is relatively powerful, in that all manner of basic partitions, LVM components, and even RAID devices can be specified &#8211; but where it fails is in the creating of the actual partitions on the disk itself.  The options that can be supplied to the partition keywords can make this clunky at best (and impossible at worst).</p>
<p>A basic example of a partitioning scheme that requires nothing outside of the available functions :</p>
<pre>DEVICE                 MOUNTPOINT               SIZE
/dev/sda               (total)                  500,000 MB
/dev/sda1              /boot/                       128 MB
/dev/sda2              /                         20,000 MB
/dev/sda3              /var/log/                 20,000 MB
/dev/sda5              /home/                   400,000 MB
/dev/sda6              /opt/                     51,680 MB
/dev/sda7              swap                       8,192 MB</pre>
<p>Great, no problem we can easily define that in the kickstart thusly :</p>
<pre>part  /boot     --asprimary  --size=128
part  /         --asprimary  --size=20000
part  /var/log  --asprimary  --size=20000
part  /home                  --size=400000
part  /opt                   --size=51680
part  swap                   --size=8192</pre>
<p>But what happens if we want to use this same kickstart on another machine (or, indeed, <em>many</em> other machines) that <em>don&#8217;t</em> have the same disk size ?  One of the options that can be used with the « part » keyword is « &#8211;grow », which tells Anaconda to create as large a partition as possible.  This can be used along with « &#8211;maxsize= », which does exactly what you think it does.</p>
<p>Continuing with the example, we can modify the « /home » partition to be of a variable size, which should do us nicely on disks which may be smaller or larger than our original 500GB unit.</p>
<pre>part  /home  --size=1024  --grow</pre>
<p>Here we&#8217;ve stated that we&#8217;d like the partition to be <em>at least</em> a gig, but that it should otherwise be as large as possible given the constraints of both the other partitions, as well as the total space available on the device.  But what if you <em>also</em> want « /opt » to be variable in size ?  One way would be to grow both of them :</p>
<pre>part  /home  --size=1024  --grow
part  /opt   --size=1024  --grow</pre>
<p>Now, what do you think that will do ?  If you guessed « grow both of them to <em>half</em> the total available size each », you&#8217;d be correct.  Maybe this is what you wanted &#8211; but then again, maybe it wasn&#8217;t.  Of course, we could always specify a maximum ceiling on how far /opt will grow :</p>
<pre>part  /opt  --size=1024  --maxsize=200000  --grow</pre>
<p>That works, but only at the <em>potential expense</em> of /home.  Consider what would happen if this was run against a 250GB disk ; the other (static) partitions would eat up some 48GB, /opt would grow to the maximum specified size of 200GB, and /home would be left with the remaining 2GB of available space.</p>
<p>If we were to add more partitions into the mix, the whole thing would become an imprecise mess rather quickly.  Furthermore, we haven&#8217;t even begun to look at scenarios where there may (or may not) more than one disk, nor any fun tricks like automatically setting the swap size to be same as the actual amount of RAM (for example).  For these sorts of things we need a different approach.</p>
<h3>the magic of pre, the power of parted</h3>
<p>The kickstart configuration contains a section called « <a href="http://fedoraproject.org/wiki/Anaconda/Kickstart#Chapter_4._Pre-installation_Script">%pre</a> », which should be familiar to anybody who&#8217;s dealt with RPM packaging.  Basically, the pre section contains text which will be parsed by the shell during the installation process &#8211; in other words, you can write a shell script here.  Fairly be thee warned, however, as the shell spawned by Anaconda is « <a href="http://en.wikipedia.org/wiki/BusyBox">BusyBox</a> », <em>not</em> « <a href="http://en.wikipedia.org/wiki/Bash">bash</a> », and it lacks some of the functionality that you might expect.  We can use the %pre section to our advantage in many ways &#8211; including partitioning.  Instead of using the built-in functions to set up the partitions, we can do it ourselves (in a manner of speaking) using « <a href="http://en.wikipedia.org/wiki/GNU_Parted">parted</a> ».</p>
<p>Parted is, as you might expect, a tool for editing partition data.  Generally speaking it&#8217;s an interactive tool, but one of the nifty features is the « scripted mode », wherein partitioning commands can be passed to Parted on the command-line and executed immediately without further intervention.  This is <em>very</em> handy in any sort of automated scenario, including during a kickstart.</p>
<p>We can use Parted to lay the groundwork for the basic example above, wherein /home is dynamically sized.  Initially this will appear inefficient, since we won&#8217;t be doing anything that can&#8217;t be accomplished by using the existing Kickstart functionality, but it provides an excellent base from which to do more interesting things.  What follows (until otherwise noted) are text blocks that can be inserted directly into the %pre section of the kickstart config :</p>
<pre># clear the MBR and partition table
dd if=/dev/zero of=/dev/sda bs=512 count=1
parted -s /dev/sda mklabel msdos</pre>
<p>This ensures that the disk is clean, so that we don&#8217;t run into any existing partition data that might cause trouble.  The « dd » command overwrites the first bit of the disk, so that any basic partition information is destroyed, then Parted is used to create a new disk label.</p>
<pre>TOTAL=`parted -s /dev/sda unit mb print free | grep Free | awk '{print $3}' | cut -d "M" -f1`</pre>
<p>That little line gives us the total size of the disk, and assigns to a variable named « TOTAL ».  There are other ways to obtain this value, but in keeping with the spirit of using Parted to solve our problems, this works.  In this instance, « <a href="http://en.wikipedia.org/wiki/AWK">awk</a> » and « <a href="http://www.gnu.org/software/coreutils/manual/html_node/cut-invocation.html">cut</a> » are used to extract the string we&#8217;re interested in.  Continuing on&#8230;</p>
<pre># calculate start points
let SWAP_START=$TOTAL-8192
let OPT_START=$SWAP_START-51680</pre>
<p>Here we determine the<em> starting position</em> for the swap and /opt partitions.  Since we know the total size, we can subtract 8GB from it, and that gives us where the swap partition <em>starts</em>.  Likewise, we can calculate the starting position of /opt based on the start point of swap (and so forth, were there other partitions to calculate).</p>
<pre># partitions IN ORDER
parted -s /dev/sda mkpart primary ext3 0 128
parted -s /dev/sda mkpart primary ext3 128 20128
parted -s /dev/sda mkpart primary ext3 20128 40256
parted -s /dev/sda mkpart extended 40256 $TOTAL
parted -s /dev/sda mkpart logical ext3 40256 $OPT_START
parted -s /dev/sda mkpart logical ext3 $OPT_START $SWAP_START
parted -s /dev/sda mkpart logical $SWAP_START $TOTAL</pre>
<p>The variables we populated above are used here in order to create the partitions on the disk.  The syntax is very simple :</p>
<ul>
<li>« parted -s »  : run Parted in scripted (non-interactive) mode.</li>
<li>« /dev/sda » : the device (later, we&#8217;ll see how to determine this dynamically).</li>
<li>« mkpart » : the action to take (make partition).</li>
<li>« primary | extended | logical » : the <a href="http://en.wikipedia.org/wiki/Disk_partitioning#PC_BIOS_partition_types">type of partition</a>.</li>
<li>« ext3 » : the type of filesystem (there are a number of possible options, but ext3 is pretty standard).
<ul>
<li>Notice that the « extended » and « swap » definitions do not contain a filesystem type &#8211; it is not necessary.</li>
</ul>
</li>
<li>« start# end# » : the start and end points, expressed in MB.</li>
</ul>
<p>Finally, we must still declare the partitions in the usual way.  Take note that this does <em>not</em> occur in the %pre section &#8211; this goes in the normal portion of the configuration for defining partitions :</p>
<pre>
<pre>part  /boot     --onpart=/dev/sda1
part  /         --onpart=/dev/sda2
part  /var/log  --onpart=/dev/sda3
part  /home     --onpart=/dev/sda5
part  /opt      --onpart=/dev/sda6
part  swap      --onpart=/dev/sda7</pre>
</pre>
<p>As i mentioned when we began this section, yes, this is (so far) a remarkably inefficient way to set this particular basic configuration up.  But, again to re-iterate, this exercise is about putting the groundwork in place for much more interesting applications of the technique.</p>
<h3>mo&#8217; drives, mo&#8217; better</h3>
<p>Perhaps some of your machines have more than one drive, and some don&#8217;t.  These sorts of things can be determined, and then reacted upon dynamically using the described technique.  Back to the %pre section :</p>
<pre># Determine number of drives (one or two in this case)
set $(list-harddrives)
let numd=$#/2
d1=$1
d2=$3</pre>
<p>In this case, we&#8217;re using a built-in function called « list-harddrives » to help us determine which drive or drives are present, and then assign their device identifiers to variables.  In other words, if you have an « sda » and an « sdb », those identifiers will be assigned to « $d1 » and « $d2 », and if you just have an sda, then $d2 will be empty.</p>
<p>This gives us some interesting new options ; for example, if we wanted to put /home on to the second drive, we could write up some simple logic to make that happen :</p>
<pre># if $d2 has a value, it's that of the second device.
if [ ! -z $d2 ]
then
  HOMEDEVICE=$d2
else
  HOMEDEVICE=$d1
fi

# snip...
part  /home  --size=1024  --ondisk=/dev/$HOMEDEVICE  --grow</pre>
<p>That, of course, assumes that the other partitions are defined, and that /home is the only entity which should be grown dynamically &#8211; but you get the idea.  There&#8217;s nothing stopping us from writing a normal shell script that could determine the number of drives, their total size, and where the partition start points should be based on that information.  In fact, let&#8217;s examine this idea a little further.</p>
<h3>the size, she is dynamic !</h3>
<p>Instead of trying to wrangle the partition sizes together with the default options, we can get as complex (or as simple) as we like with a few if statements, and some basic maths.  Thinking about our layout then, we can express something like the following quite easily :</p>
<ul>
<li>If there is one drive that is at least 500 GB in size, then /opt should be 200 GB, and /home should consume the rest.</li>
<li>If there is one drive is less than 500 GB, but more than 250 GB, then /opt and /home should each take half.</li>
<li>If there is one drive that is less than 250 GB, then /home should take two-thirds, and /opt gets the rest.</li>
</ul>
<pre># $TOTAL from above...
if [ $TOTAL -ge 512000 ]
then
  let OPT_START=$SWAP_START-204800
elif [ $TOTAL -lt 512000 ] &amp;&amp; [ $TOTAL -ge 256000 ]
then
  # get the dynamic space total, which is between where /var/log ends, and swap begins
  let DYN_TOTAL=$SWAP_START-40256
  let OPT_START=$DYN_TOTAL/2
elif [ $TOTAL -lt 256000 ]
then
  let DYN_TOTAL=$SWAP_START-40256
  let OPT_START=$DYN_TOTAL/3
  let OPT_START=$OPT_START+$OPT_START
fi</pre>
<p>Now, instead of having to create three different kickstart files, each describing a different scenario, we&#8217;ve covered it with one &#8211; nice !</p>
<h3>other possibilities</h3>
<p>At the end of the day, the possilibities are nearly endless, with the only restriction being that whatever you&#8217;d like to do has to be do-able in BusyBox &#8211; which, at this level, provides <em>a lot</em> great functionality.</p>
<p>Stay tuned for more entries related to kickstarting, PXE-based installations, and so forth, all to come here on dan&#8217;s linux blog.  Cheers !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dark.ca/2009/08/03/complex-partitioning-in-kickstart/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>how to be properly lazy, with perl !</title>
		<link>http://www.dark.ca/2009/06/12/how-to-be-perl-lazy/</link>
		<comments>http://www.dark.ca/2009/06/12/how-to-be-perl-lazy/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 10:58:27 +0000</pubDate>
		<dc:creator>dan</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Commentary]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[regex]]></category>

		<guid isPermaLink="false">http://www.dark.ca/?p=51</guid>
		<description><![CDATA[One of the wonderful things about Perl is that it enables the busy System Administrator to be lazy &#8211; and that&#8217;s a good thing ! Of course, i don&#8217;t mean lazy as in unmotivated, or possesed of a poor work ethic, i mean it in the sense that Perl lets us do as little work [...]]]></description>
			<content:encoded><![CDATA[<p>One of the wonderful things about Perl is that it enables the busy System Administrator to be lazy &#8211; and that&#8217;s a good thing !  Of course, i don&#8217;t mean lazy as in unmotivated, or possesed of a poor work ethic, i mean it in the sense that Perl lets us do as little work as possible in a wide variety of situations.  Let&#8217;s examine this idea, shall we ?</p>
<p>In the computer world, one often finds themselves doing the same sorts of things over and over again, such as adding a new user to the network, or verifying that the backups executed properly.  Usually, these are relatively simple processes which are less about problem solving, and more about following the same set of steps over and over until the desired goal is attained.  It is in these situations that the (properly) lazy admin identifies a way to automate as much as possible these processes, so that he or she can get back to more brain-intensive work (this has the net effect of improving overall efficiency and value &#8211; see how laziness pays off in the end ? :) )</p>
<p>There are, of course, as many scripting and programming languages as there are grains of sand on a beach, but despite the many competitors and alternatives out there, Perl remains the language of choice for many Linux admins around the world.  This is in no small part due to Perl&#8217;s ability to manipulate data in a rapid, logical, and easily deployable manner &#8211; the most obvious example of this being the vaunted « Perl One-Liner ».</p>
<h3>example !</h3>
<p>There comes a time in every admin&#8217;s life when they must take a bunch of text files, and systematically swap some of the text within with new data &#8211; commonly known as searching and replacing.  You could certainly do this by hand using an editor or by using a relatively straightforward C program if you were so inclined.  But there is another way &#8211; a better, smarter, <em>lazier</em> way : the Perl search &amp; replace one-liner !  Let&#8217;s take a look at the code, then break down each component.</p>
<pre>$ perl -p -i -e 's/oldandbusted/newhotness/' *.txt</pre>
<p>That&#8217;s it, you&#8217;re done &#8211; take a lap and hit the showers.  So, what exactly just happened there ?  We employed a classic and very common usage method in command-line Perl which can easily be remembered as « pie » :</p>
<ul>
<li>« -p » : In a nutshell, this tells Perl to loop through each line of input, then perform the desired action (in this case, the search &amp; replace) against each of those lines.</li>
<li>« -i » : This instructs Perl to actually edit the input files directly (or « in place »), instead of just displaying the changes on the screen.</li>
<li>« -e » : This describes exactly one line of code &#8211; in this case, the search and replace regular expression&#8230;</li>
<li>« &#8216;s/old/new/&#8217; » : This is the regular expression (or « regex ») which Perl will use to perform the search &amp; replace.  (What&#8217;s a regex ?  <a href="http://en.wikipedia.org/wiki/Regular_expression" target="_blank">Wikipedia has the answers you seek</a> !)</li>
<li>« *.txt » : The target filename &#8211; in this case, a simple glob.  (What&#8217;s a glob ?  <a href="http://en.wikipedia.org/wiki/Glob_(programming)" target="_blank">Wikipedia has the answer</a> !)</li>
</ul>
<p>The key to this whole operation was the fourth bullet point &#8211; the regex.  Don&#8217;t worry if your regex-fu is not yet strong &#8211; this is just an example, and it could have been anything &#8211; the point is that Perl can be used to rapidly execute regular expressions on data in simple, easy to execute ways, such as the search &amp; replace one-liner above.  This sort of thing comes in handy on a <em>daily basis</em>, and thus, the perl one-liner is a powerful tool in the System Administrator&#8217;s toolbox.</p>
<p>For more one-liners, use the Google : <a href="http://www.google.fr/search?q=perl+one-liners" target="_blank">http://www.google.fr/search?q=perl+one-liners</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.dark.ca/2009/06/12/how-to-be-perl-lazy/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
