<?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>Gabe Ortiz &#187; code</title>
	<atom:link href="http://gabeortiz.net/category/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://gabeortiz.net</link>
	<description>storybook american generalist</description>
	<lastBuildDate>Tue, 22 Mar 2011 22:15:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>I just wrote one of the best one-line bash scripts of all time.</title>
		<link>http://gabeortiz.net/2009/i-just-wrote-one-of-the-best-one-line-bash-scripts-of-all-time/</link>
		<comments>http://gabeortiz.net/2009/i-just-wrote-one-of-the-best-one-line-bash-scripts-of-all-time/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 03:16:48 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.signalnine.net/?p=38</guid>
		<description><![CDATA[ALL TIME! curl -s -u Frank__Booth:password -d "track=heineken" http://stream.twitter.com/track.xml &#124; grep "/screen_name" &#124; grep -v Frank__Booth &#124; sed -e 's/[^0-9a-zA-Z]//g' &#124; sed -e 's/screenname//g' &#124; xargs -I '{}' curl --basic --user Frank__Booth:password --data status="@{} Heineken? Fuck that shit! PABST BLUE RIBBON" http://twitter.com/statuses/update.xml]]></description>
			<content:encoded><![CDATA[<p>ALL TIME!</p>
<p><code>curl -s -u Frank__Booth:password -d "track=heineken" http://stream.twitter.com/track.xml | grep "/screen_name" | grep -v Frank__Booth | sed -e 's/[^0-9a-zA-Z]//g' | sed -e 's/screenname//g' | xargs -I '{}' curl --basic --user Frank__Booth:password --data status="@{} Heineken? Fuck that shit! PABST BLUE RIBBON" http://twitter.com/statuses/update.xml</code></p>
]]></content:encoded>
			<wfw:commentRss>http://gabeortiz.net/2009/i-just-wrote-one-of-the-best-one-line-bash-scripts-of-all-time/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitter JSON stream parser</title>
		<link>http://gabeortiz.net/2009/twitter-json-stream-parser/</link>
		<comments>http://gabeortiz.net/2009/twitter-json-stream-parser/#comments</comments>
		<pubDate>Thu, 11 Jun 2009 05:27:01 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.signalnine.net/?p=29</guid>
		<description><![CDATA[So recently I&#8217;ve had occasion to parse the Twitter JSON stream, specifically the spritzer stream for data mining purposes. Turns out this is a pretty difficult problem to solve in most languages. So here&#8217;s my Alexandrian solution to this particular Gordian knot, in Bash, because that&#8217;s just how I roll. curl -s --basic --user username:password ...]]></description>
			<content:encoded><![CDATA[<p>So recently I&#8217;ve had occasion to parse the Twitter JSON stream, specifically the spritzer stream for data mining purposes. Turns out this is a pretty difficult problem to solve in most languages. So here&#8217;s my Alexandrian solution to this particular Gordian knot, in Bash, because that&#8217;s just how I roll.</p>
<p><code>curl -s --basic --user username:password http://stream.twitter.com/spritzer.json | while read line; do echo "${line}" > temp_tweet ;  cat temp_tweet | sed -e 's/=\"/\=\\"/g' | sed -e 's/\">/\\">/g' | ./twitterparse.pl; done</code></p>
<p>This parses the JSON steam and passes each tweet to a perl script which does the actual parsing. </p>
<p>Hey kids: Don&#8217;t do this. It&#8217;s bad. If you must, use a tool like <a href="http://github.com/micha/jsawk">jsawk</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://gabeortiz.net/2009/twitter-json-stream-parser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Debian Diskless Cluster Howto</title>
		<link>http://gabeortiz.net/2009/debian-diskless-cluster-howto/</link>
		<comments>http://gabeortiz.net/2009/debian-diskless-cluster-howto/#comments</comments>
		<pubDate>Tue, 14 Apr 2009 19:29:56 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://blog.signalnine.net/?p=12</guid>
		<description><![CDATA[Inital Setup This guide will walk you through the diskless cluster install and setup process. The cluster has a head node that serves boot images to the compute nodes and the database node. We&#8217;re attempting to present a unified system image across the cluster. For this reason, all nodes are looking at the same root ...]]></description>
			<content:encoded><![CDATA[<h2>Inital Setup</h2>
<p>This guide will walk you through the diskless cluster install and setup process. The cluster has a head node that serves boot images to the compute nodes and the database node. We&#8217;re attempting to present a unified system image across the cluster. For this reason, all nodes are looking at the same root filesystem, served via NFS.</p>
<h3>DHCP</h3>
<p>I started from a bare-bones netinstall of Debian Squeeze (testing) on the head node. This should work about equally well on any Debian-derived distribution.</p>
<p>First, we need to install some packages we&#8217;ll need in a minute.</p>
<pre>
sudo apt-get install dnsmasq syslinux nfs-kernel-server nfs-common debootstrap tftpd-hpa xinetd
</pre>
<p>Now, we need to configure dnsmasq, which will serve as our DHCP server for diskless booting.</p>
<p>You replace your existing /etc/dnsmasq.conf with something like this:</p>
<pre>
dhcp-range=192.168.1.50,192.168.1.150,255.255.255.0,12h
dhcp-boot=pxelinux.0,headnode,192.168.1.1
</pre>
<p>Replace 192.168.1.x with your preferred IP subnet and &#8220;headnode&#8221; with the hostname of your head node.</p>
<h3>tftp</h3>
<p>Our tftp server needs to be configured to launch on command from xinetd. The binary is already installed from our previous apt-get command.</p>
<p>create a file, /etc/xinetd/tftp-hpa that looks like this:</p>
<pre>
service tftp
{
        disable         = no
        id              = chargen-dgram
        socket_type     = dgram
        protocol        = udp
        user            = root
        wait            = yes
        server          = /usr/sbin/in.tftpd
        server_args     = -s /var/lib/tftpboot/
}
</pre>
<h3>PXE</h3>
<p>Now we need to tell the PXE server what to serve our clients.</p>
<p>Let&#8217;s set up our pxelinux configuration directory.</p>
<pre>
sudo cp -r /usr/lib/syslinux/pxelinux.0 /var/lib/tftpboot/
sudo mkdir /var/lib/tftpboot/pxelinux.cfg
</pre>
<p>We&#8217;ll need a kernel and a initial ramdisk to give to your diskless clients. Assuming you&#8217;re going to be running the same kernel on the head node as your diskless clients (recommended), you can just copy the kernel from /boot.</p>
<pre>
sudo cp /boot/vmlinuz-`uname -r` /var/lib/tftpboot/
</pre>
<p>You&#8217;re going to need to create a NFS-root-enabled ramdisk. This accomplished with the tool mkinitramfs. You should have a configuration directory, /etc/initramfs-tools/. Make a copy of it:</p>
<pre>
sudo cp -r /etc/initramfs-tools /etc/initramfs-pxe
</pre>
<p>Note: On Debian Squeeze, the installed <tt>/etc/initramfs-tools</tt> did not work for unknown reasons, it seems to be missing module configurations. I ended up copying a <tt>/etc/initramfs-tools</tt> from an Ubuntu 8.04 install. It worked fine.</p>
<p>Edit /etc/initramfs-pxe/initramfs.conf. Change <tt>BOOT=local</tt> to <tt>BOOT=nfs</tt>.<br />
Now we can create the ramdisk.</p>
<pre>
sudo mkinitramfs -d /etc/initramfs-pxe -o /var/lib/tftpboot/initrd.img-`uname -r` `uname -r`
</pre>
<p>We should be ready to create a default boot configuration now. We&#8217;ll need to create <tt>/var/lib/tftpboot/pxelinux.cfg/default</tt></p>
<pre>
LABEL linux
KERNEL vmlinuz-2.6.29
APPEND root=/dev/nfs initrd=initrd.img-2.6.29 nfsroot=192.168.1.1:/home/nfsroot ip=dhcp rw
</pre>
<p>Change 2.6.29 to match your kernel, obviously.</p>
<p>If you want to pass different parameters to different machines, you can create individual configuration files in <tt>/var/lib/tftpboot/pxelinux.cfg/</tt> based on their MAC addresses. For example, if I create a file, <tt>/var/lib/tftpboot/pxelinux.cfg/01-00-21-97-7a-24-0f</tt>, then my node with a MAC of <tt>00:21:97:7a:24:0f</tt> will load that instead of the default. I like to create softlinks in the configuration directory corresponding to the hostnames of my nodes because if you can remember MAC addresses of individual machines then you&#8217;re a better man than I am.</p>
<h3>NFS</h3>
<p>NFS time! Create a directory to store your NFS root you&#8217;ll be serving clients.</p>
<pre>
sudo mkdir /home/nfsroot
</pre>
<p>Edit <tt>/etc/exports</tt>. It should look something like this:</p>
<pre>
/home/nfsroot 192.168.1.0/255.255.255.0(rw,no_subtree_check,async,no_root_squash)
</pre>
<p>Now we just have to bootstrap a basic Debian install into <tt>/home/nfsroot</tt>. Luckily for us, there&#8217;s a nifty little tool called <tt>debootstrap</tt> that does just that. For a 64-bit Debian Squeeze environment, I do this:</p>
<pre>
debootstrap --arch amd64 squeeze /home/nfsroot/
</pre>
<p>A few minutes later, it&#8217;s installed. Now you need to make some modifications to that system you just installed.<br />
Edit <tt>/home/nfsroot/etc/fstab</tt> to look something like this:</p>
<pre>
# <file system> <mount point> <type>
<options> <dump>
<pass>
proc            /proc         proc   defaults       0      0
/dev/nfs        /             nfs    defaults       0      0
none            /tmp            tmpfs   defaults 0 0
none            /var/run        tmpfs   defaults 0 0
none            /var/lock       tmpfs   defaults 0 0
none            /var/tmp        tmpfs   defaults 0 0
none            /media          tmpfs   defaults 0 0
none		/var/log	tmpfs	defaults 0 0
</pre>
<p><tt>/home/nfsroot/etc/network/interfaces</tt> should be:</p>
<pre>
auto lo
iface lo inet loopback
iface eth0 inet dhcp
</pre>
<p>Note that <tt>auto eth0</tt> isn&#8217;t there anymore. That&#8217;s because your primary ethernet interface is already up. If you try to initialize it again, it might drop your existing connection and it&#8217;ll dump you out of the boot process.</p>
<h3>Testing</h3>
<p>At this point you&#8217;re ready to test. Make sure to restart xinetd, dnsmasq and nfs-kernel-server to make sure your new settings take effect. Then, check your node&#8217;s BIOS to verify that network boot is enabled and give it a shot.</p>
<p>Congratulations! You now have a diskless cluster. Next we&#8217;ll make some special modifications to the configuration of the nodes to make them play nicely together and make maintenance easier.</p>
<h3>Networking</h3>
<p>Each node will receive an IP address from dnsmasq on the head node. We can either just note which IP each node gets, as it should give each node a unique IP by default and these are persistent as long as the node&#8217;s MAC address remains the same, or you can force each node to a specified IP with a configuration similar to this in <tt>/etc/dnsmasq.conf</tt> on the head node:</p>
<pre>
dhcp-host=id:00:21:97:7d:ad:bf,192.168.1.10
dhcp-host=id:00:21:97:7a:24:0f,192.168.1.11
dhcp-host=id:00:21:97:7d:b3:26,192.168.1.12
</pre>
<p>Either way, you&#8217;ll need <tt>/etc/hosts</tt> on your head node to reflect the IP addresses of your nodes. Mine looks like this:</p>
<pre>
127.0.0.1	localhost
10.13.99.1	scoop head
192.168.1.10	dizzy db
192.168.1.11	tumbler
192.168.1.12	scrambler
</pre>
<p>You&#8217;ll want to copy that hosts file over to <tt>/home/nfsroot/etc/hosts</tt> as well.</p>
<h3>Init Tricks</h3>
<p>Sometimes you want the nodes to behave just a little bit differently from each other. I wanted my nodes to have different hostnames, fancy that. So, I wrote this bash script to figure out what their hostname should be:</p>
<pre>
#!/bin/bash
#finds node's hostname based on matching ip in /etc/hosts

grep `ifconfig  | grep 'inet addr:'| grep -v '127.0.0.1' | /usr/bin/cut -d: -f2 \
| /usr/bin/awk '{ print $1}'` /etc/hosts | /usr/bin/awk '{print $2}'
</pre>
<p>Save the script in <tt>/home/nfsroot/bin/whereami</tt>. You&#8217;ll need awk in order for it to work. Boot up a node and just apt-get it from the node itself before running the script. Package installation is best accomplished from a booted diskless node, just try not to install multiple packages from multiple nodes simultaneously. You might corrupt your apt database.</p>
<p>Now that we have that taken care of, we can modify <tt>/etc/init.d/hostname.sh</tt> to set our hostname on boot based on the IP we&#8217;ve received. This is as simple as changing this:</p>
<pre>
	[ -f /etc/hostname ] &#038;&#038; HOSTNAME="$(cat /etc/hostname)"
</pre>
<p>To this:</p>
<pre>
	[ -f /etc/hostname ] &#038;&#038; HOSTNAME="$(/bin/whereami)"
</pre>
<p>This also allows us to modify other init scripts so they&#8217;ll only run on particular nodes. For example, I wanted MySQL to start only on the database node, dizzy. So I added this to the top of <tt>/etc/init.d/mysql</tt>:</p>
<pre>
hostname=$(hostname)
if [ $hostname != "dizzy" ];
then exit 0
fi
</pre>
<h3>Logging</h3>
<p>Since we&#8217;re not saving local log files on the diskless nodes, it makes sense to centralize our logging on the head node. We&#8217;ll need a better logging daemon to accomplish this.</p>
<p>On both the head node and a diskless node (only do this on one of your nodes, changes populate to the others, remember?)</p>
<pre>
sudo apt-get install syslog-ng
</pre>
<p>Edit <tt>/etc/syslog-ng/syslog-ng.conf</tt> on the <b>head node</b>.</p>
<pre>
## add this to the options section
create_dirs(yes);
long_hostnames(off);
keep_hostname(yes);

## add this to the source section
source s_udp {
	udp ( ip(192.168.1.1) ); # replace with your system's IP address
};

## add this to the destination section
destination df_udp {
        file ("/var/log/$HOST/$FACILITY");
};

## add this to the log section
log {
        source(s_udp);
        destination (df_udp);
};
</pre>
<p>Now edit <tt>/etc/syslog-ng/syslog-ng.conf</tt> on one of the <b>diskless nodes</b>.</p>
<pre>
## add this to the destination section
destination remote_udp { udp("192.168.1.1"); }; # replace with your log server's IP address

## add this to the log section
log { source(src); destination(remote_udp); };
</pre>
<p>Restart syslog-ng on both head and diskless nodes.</p>
<h3>That&#8217;s It</h3>
<p>I hope this was helpful. Feel free to ask questions or leave comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://gabeortiz.net/2009/debian-diskless-cluster-howto/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Toodledo Bash Script</title>
		<link>http://gabeortiz.net/2009/toodledo-bash-script/</link>
		<comments>http://gabeortiz.net/2009/toodledo-bash-script/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 07:54:34 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://blog.signalnine.net/?p=3</guid>
		<description><![CDATA[So, I&#8217;ve been playing with Toodledo recently. It&#8217;s a pretty impressive web-based todo system with pretty much all the functionality you could ask for. However, I don&#8217;t really like the Ruby command line client, so I wrote my own in Bash. It requires that you have a local MTA in order to function properly. I ...]]></description>
			<content:encoded><![CDATA[<p>So, I&#8217;ve been playing with <a href="http://toodledo.com">Toodledo</a> recently. It&#8217;s a pretty impressive web-based todo system with pretty much all the functionality you could ask for. However, I don&#8217;t really like the Ruby command line client, so I wrote my own in Bash. It requires that you have a local MTA in order to function properly. I recommend <a href="http://www.linux.com/feature/132006">ssmpt</a> if you don&#8217;t need a full-featured MTA.<br />
Downloadable here: <a href="http://signalnine.net/toodledo">http://signalnine.net/toodledo</a></p>
<p>Licence: <a href="http://www.gnu.org/licenses/gpl-2.0.html">GPL v2</a>.<br />
Install instructions:</p>
<ol>
<li>Download the script, put it in a directory that&#8217;s in your path. (i.e., ~/bin/ or /usr/bin/)</li>
<li>Edit the script, replace from variable with your email and to with your toodledo secret email address which you can find <a href="http://www.toodledo.com/import.php">here</a>.</li>
<li>Make the toodledo script executable.</li>
<li>Follow the Toodledo email format, which the script reminds you of on execution.</li>
</ol>
<p>That&#8217;s it. Have fun.</p>
]]></content:encoded>
			<wfw:commentRss>http://gabeortiz.net/2009/toodledo-bash-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

