<?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>btm.geek &#187; btm</title>
	<atom:link href="http://blog.loftninjas.org/author/admin/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.loftninjas.org</link>
	<description></description>
	<lastBuildDate>Mon, 23 Jan 2012 23:12:39 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Stubbing class constants with rspec and Ruby</title>
		<link>http://blog.loftninjas.org/2012/01/23/stubbing-class-constants-with-rspec/</link>
		<comments>http://blog.loftninjas.org/2012/01/23/stubbing-class-constants-with-rspec/#comments</comments>
		<pubDate>Mon, 23 Jan 2012 23:12:39 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=748</guid>
		<description><![CDATA[I had some Ruby code that utilized File::SEPARATOR and File::PATH_SEPARATOR to run on both unix and windows, so I wanted to stub these values to test for both platforms. There are couple examples out there, building on each other. This example adds a feature that saves and recalls the former value and this example builds [...]]]></description>
			<content:encoded><![CDATA[<p>I had some Ruby code that utilized File::SEPARATOR and File::PATH_SEPARATOR to run on both unix and windows, so I wanted to stub these values to test for both platforms. There are couple examples out there, building on each other. This <a href="http://digitaldumptruck.jotabout.com/?p=551">example</a> adds a feature that saves and recalls the former value and this <a href="http://missingbit.blogspot.com/2011/07/stubbing-constants-in-rspec_20.html">example</a> builds on that to support class constants. Both expect Activerecord, so there&#8217;s a little working around that added here. I&#8217;m ripping this directly from my spec_helper.rb before I throw it away because it feels over-engineered and complicated.</p>
<pre class="brush: ruby; title: ; notranslate">
def with_warnings(flag)
  old_verbose, $VERBOSE = $VERBOSE, flag
  yield
ensure
  $VERBOSE = old_verbose
end

# http://missingbit.blogspot.com/2011/07/stubbing-constants-in-rspec_20.html
def parse_constant(constant)
  source, _, constant_name = constant.to_s.rpartition('::')

  [constantize(source), constant_name]
end

def with_constants(constants, &amp;block)
  saved_constants = {}
  constants.each do |constant, val|
    source_object, const_name = parse_constant(constant)

    saved_constants[constant] = source_object.const_get(const_name)
    with_warnings(nil) {source_object.const_set(const_name, val) }
  end

  begin
    block.call
  ensure
    constants.each do |constant, val|
      source_object, const_name = parse_constant(constant)

      with_warnings(nil) { source_object.const_set(const_name, saved_constants[constant]) }
    end
  end
end
####################

# File activesupport/lib/active_support/inflector/methods.rb, line 209
def constantize(camel_cased_word)
  names = camel_cased_word.split('::')
  names.shift if names.empty? || names.first.empty?

  constant = Object
  names.each do |name|
    constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
  end
  constant
end
</pre>
<p>Then you can perform:</p>
<pre class="brush: ruby; title: ; notranslate">
  it &quot;does something when running on Windows&quot; do
    with_constants &quot;::File::PATH_SEPARATOR&quot; =&gt; &quot;;&quot; do
      # code
    end
  end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2012/01/23/stubbing-class-constants-with-rspec/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Downloading All The Github Repositories</title>
		<link>http://blog.loftninjas.org/2012/01/12/downloading-all-the-github-repositories/</link>
		<comments>http://blog.loftninjas.org/2012/01/12/downloading-all-the-github-repositories/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 16:00:31 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=745</guid>
		<description><![CDATA[I had a need to grab all of the Github repositories for Cookbooks, which is a Github user maintained by the Chef community for collecting many cookbooks in one place for development. All of these cookbooks should be on the Opscode Community site, which is where you should go if you&#8217;re browsing for cookbooks to [...]]]></description>
			<content:encoded><![CDATA[<p>I had a need to grab all of the <a href="https://github.com/cookbooks">Github repositories for Cookbooks</a>, which is a Github user maintained by the Chef community for collecting many cookbooks in one place for development. All of these cookbooks should be on the <a href="http://community.opscode.com/">Opscode Community</a> site, which is where you should go if you&#8217;re browsing for cookbooks to use yourself. But I needed to grep through a large number of cookbooks to develop statistics on Chef Cookbook usage patterns, so I needed <a href="http://knowyourmeme.com/memes/x-all-the-y">All The Things</a>.</p>
<pre class="brush: plain; title: ; notranslate">
#!/usr/bin/env ruby
# 2012-01-11 Bryan McLellan &lt;btm@loftninjas.org&gt;
# Fetch the list of repositories from a Github user and 'git clone' them all

require 'rubygems'
require 'json'
require 'net/http'

url = &quot;http://github.com/api/v2/json/repos/show/cookbooks&quot;
dir = &quot;cookbooks&quot;

if File.basename(Dir.getwd) != dir
 if File.exists?(dir)
   puts &quot;Target directory of '#{dir}' already exists.&quot;
   exit 1
 end

 Dir.mkdir(dir)
 Dir.chdir(dir)
end

resp = Net::HTTP.get_response(URI.parse(url))
data = resp.body

result = JSON.parse(data)

result['repositories'].each { |repo|
 puts &quot;Fetching #{repo['url']}&quot;
 system &quot;git clone #{repo['url']}&quot;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2012/01/12/downloading-all-the-github-repositories/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Generating entropy in the cloud</title>
		<link>http://blog.loftninjas.org/2011/07/29/generating-entropy-in-the-cloud/</link>
		<comments>http://blog.loftninjas.org/2011/07/29/generating-entropy-in-the-cloud/#comments</comments>
		<pubDate>Fri, 29 Jul 2011 15:01:47 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=736</guid>
		<description><![CDATA[Virtual machines don&#8217;t produce a lot of entropy on their own. Typically the need for additional entropy triggers talk of hardware based entropy generators or network based entropy distribution protocols. Sometimes you just need a little bit of entropy for a moment.

$ sbuild-update --keygen
Generating archive key.

Not enough random bytes available.  Please do some other [...]]]></description>
			<content:encoded><![CDATA[<p>Virtual machines don&#8217;t produce a lot of entropy on their own. Typically the need for additional entropy triggers <a href="http://blog.dt.org/index.php/2009/08/entropy-in-cloud-computing-applications/">talk of hardware based entropy generators</a> or network based entropy distribution protocols. Sometimes you just need a little bit of entropy for a moment.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ sbuild-update --keygen
Generating archive key.

Not enough random bytes available.  Please do some other work to give
the OS a chance to collect more entropy! (Need 279 more bytes)
</pre>
<p>Disk tends to be one of the only remaining sources of entropy on virtual systems. I usually do something like this:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ while true ; do cat /proc/sys/kernel/random/entropy_avail  ; \
    sudo find / &gt; /tmp/find.log ; sync ; done
</pre>
<p>This numbers printed should go up and down as your application consumes the entropy. Hit CTRL+C when you&#8217;ve got enough. This is probably a bad source of entropy, but the world is inherently dangerous.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/07/29/generating-entropy-in-the-cloud/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Disabling Firefox shortcuts on OS X</title>
		<link>http://blog.loftninjas.org/2011/07/15/disabling-firefox-shortcuts-on-os-x/</link>
		<comments>http://blog.loftninjas.org/2011/07/15/disabling-firefox-shortcuts-on-os-x/#comments</comments>
		<pubDate>Fri, 15 Jul 2011 15:10:56 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=731</guid>
		<description><![CDATA[I joined a startup, and they gave me a MacBook Pro. It was bound to happen eventually; all the cool kids use MBPs and startups are cool, right?
The great period of adaption began, as I learned I couldn&#8217;t have simple technology like sloppy focus. One of the greatest inconveniences is the keyboard. I have a [...]]]></description>
			<content:encoded><![CDATA[<p>I joined a startup, and they gave me a MacBook Pro. It was bound to happen eventually; <a href="http://onelessmac.com/">all the cool kids use MBPs</a> and startups are cool, right?</p>
<p>The great period of adaption began, as I learned I couldn&#8217;t have simple technology like <a href="http://en.wikipedia.org/wiki/Focus_%28computing%29#Sloppy_focus">sloppy focus</a>. One of the greatest inconveniences is the keyboard. I have a hard time using the keyboard on the laptop because special keys are in different places than I&#8217;m used to. Even with a <a href="http://pckeyboards.stores.yahoo.net/onthestick.html">Unicomp Spacesaver M</a> (for those of us attached to the <a href="http://en.wikipedia.org/wiki/Model_M_keyboard">Model M</a>), some change is apparent, like Apple using &#8220;delete&#8221; when they mean &#8220;backspace&#8221; (The <a href="http://www.flickr.com/photos/btmspox/5939671807/">Unicomp uses &#8220;delete ->&#8221;</a> when they mean &#8220;delete&#8221;).</p>
<p>Most frustrating of this set of issues is that in Firefox the home and end keys go to the top and bottom of the page, whereas you have to use cmd+left and cmd+right to go to the beginning and end of a line in a textbox. <em>However</em> sometimes these keys represent page forward and page back, and sometimes they don&#8217;t (usually in a flash app, I believe). The solution is to install the <a href="http://forums.mozillazine.org/viewtopic.php?t=72994">keyconfig extension</a>. After you restart firefox, you will find it in the Tools menu where you can disable &#8220;GoBackKb&#8221; and &#8220;GoForwardKb&#8221;. Then these keys work as expected in a text box and you no longer find yourself going back a page unintentionally, possibly losing a textbox full of input along the way.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/07/15/disabling-firefox-shortcuts-on-os-x/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Recreating the Opscode Chef validation key</title>
		<link>http://blog.loftninjas.org/2011/07/14/recreating-the-opscode-chef-validation-key/</link>
		<comments>http://blog.loftninjas.org/2011/07/14/recreating-the-opscode-chef-validation-key/#comments</comments>
		<pubDate>Thu, 14 Jul 2011 12:15:53 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=724</guid>
		<description><![CDATA[Chef uses a special key pair to create new clients called the &#8220;validation client.&#8221; If you lose this file, or perhaps you end up with an empty CouchDB database and no longer have this client in the database, you could get a 401 Unauthorized error when trying to use it.

$ sudo chef-client
[Thu, 14 Jul 2011 [...]]]></description>
			<content:encoded><![CDATA[<p>Chef uses a special key pair to create new clients called the &#8220;validation client.&#8221; If you lose this file, or perhaps you end up with an empty CouchDB database and no longer have this client in the database, you could get a 401 Unauthorized error when trying to use it.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ sudo chef-client
[Thu, 14 Jul 2011 11:44:44 +0000] INFO: *** Chef 0.10.2 ***
[Thu, 14 Jul 2011 11:44:44 +0000] INFO: Client key /etc/chef/client.pem is not present - registering
[Thu, 14 Jul 2011 11:44:44 +0000] INFO: HTTP Request Returned 401 Unauthorized: Failed to authenticate. Ensure that your client key is valid.
[Thu, 14 Jul 2011 11:44:44 +0000] FATAL: Stacktrace dumped to /var/cache/chef/chef-stacktrace.out
[Thu, 14 Jul 2011 11:44:44 +0000] FATAL: Net::HTTPServerException: 401 &quot;Unauthorized&quot;
</pre>
<p>Removing your validation key on the server, which is typically stored on the filesystem at /etc/chef/validation.pem and restarting the chef-server will create a new key pair both on disk and in the database.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ ls -l /etc/chef/validation.pem
-rw-r--r-- 1 root root 1676 2011-07-14 11:44 /etc/chef/validation.pem
$ sudo rm /etc/chef/validation.pem
$ sudo /etc/init.d/chef-server restart
 * Restarting chef-server
 ~ Killing pid 10783 with INT
 ~ In 12051
   ...done.
$ ls -l /etc/chef/validation.pem
-rw------- 1 chef chef 1679 2011-07-14 11:55 /etc/chef/validation.pem
</pre>
<p>The same process works with the webui key pair, which knife uses as the default &#8220;admin&#8221; key to create initial knife clients.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ ls -l /etc/chef/webui.pem
-rw------- 1 chef chef 1675 2011-07-14 11:31 /etc/chef/webui.pem
$ sudo rm /etc/chef/webui.pem
$ sudo /etc/init.d/chef-server restart
 * Restarting chef-server
 ~ Killing pid 12051 with INT
 ~ In 12091
   ...done.
$ ls -l /etc/chef/webui.pem
-rw------- 1 chef chef 1675 2011-07-14 11:57 /etc/chef/webui.pem
$ sudo /etc/init.d/chef-server-webui restart
 * Restarting chef-server-webui
 ~ Killing pid 10945 with INT
 ~ In 12129
   ...done.
</pre>
<p>If you&#8217;ve also lost your key for your knife client, you will need to create another one. Use a new client name unless you&#8217;re sure that the server does not still contain a registration for the previous client. After creating the new client, you can delete the old one from the server using &#8216;knife client delete MY_OLD_CLIENT&#8217; by replacing MY_OLD_CLIENT with the name of the former client.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ sudo knife configure --initial
Overwrite /home/ubuntu/.chef/knife.rb? (Y/N) y
Please enter the chef server URL: [http://ip-10-204-150-209.ec2.internal:4000]
Please enter a clientname for the new client: [ubuntu] new_ubuntu
Please enter the existing admin clientname: [chef-webui]
Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem]
Please enter the validation clientname: [chef-validator]
Please enter the location of the validation key: [/etc/chef/validation.pem]
Please enter the path to a chef repository (or leave blank):
Creating initial API user...
Created client[new_ubuntu]
Configuration file written to /home/ubuntu/.chef/knife.rb
</pre>
<p>Provided with the new validation.pem, your node should be able to register now, as long as there is not still a client by the same name. If there is, you will need to delete that client first. Note that on Opscode Hosted Chef, you currently will need to delete the node as well, because the default permissions only allow the client that created the node to modify it.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ knife client list
  chef-validator
  chef-webui
  ip-10-204-150-209.ec2.internal
  new_ubuntu
  ubuntu
$ knife client delete ip-10-204-150-209.ec2.internal
Do you really want to delete ip-10-204-150-209.ec2.internal? (Y/N) y
Deleted client[ip-10-204-150-209.ec2.internal]
$ sudo chef-client
[Thu, 14 Jul 2011 12:04:24 +0000] INFO: *** Chef 0.10.2 ***
[Thu, 14 Jul 2011 12:04:26 +0000] INFO: Client key /etc/chef/client.pem is not present - registering
[Thu, 14 Jul 2011 12:04:27 +0000] INFO: Run List is []
[Thu, 14 Jul 2011 12:04:27 +0000] INFO: Run List expands to []
[Thu, 14 Jul 2011 12:04:27 +0000] INFO: Starting Chef Run for ip-10-204-150-209.ec2.internal
[Thu, 14 Jul 2011 12:04:27 +0000] INFO: Loading cookbooks []
[Thu, 14 Jul 2011 12:04:27 +0000] WARN: Node ip-10-204-150-209.ec2.internal has an empty run list.
[Thu, 14 Jul 2011 12:04:28 +0000] INFO: Chef Run complete in 0.623124 seconds
[Thu, 14 Jul 2011 12:04:28 +0000] INFO: Running report handlers
[Thu, 14 Jul 2011 12:04:28 +0000] INFO: Report handlers complete
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/07/14/recreating-the-opscode-chef-validation-key/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>require-rubygems.overrides and gem2deb 0.2.2</title>
		<link>http://blog.loftninjas.org/2011/05/05/require-rubygems-overrides-and-gem2deb-0-2-2/</link>
		<comments>http://blog.loftninjas.org/2011/05/05/require-rubygems-overrides-and-gem2deb-0-2-2/#comments</comments>
		<pubDate>Fri, 06 May 2011 00:43:01 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=720</guid>
		<description><![CDATA[For those working on moving debian ruby library packaging to gem2deb, you can exempt specific hits from the slick built in &#8216;require rubygems&#8217; test by adding the path to debian/require-rubygems.overrides.
For instance, to exempt this:

debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/version'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/dependency'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/spec_fetcher'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/platform'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/format'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/dependency_installer'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/uninstaller'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/specification'
debian/chef/usr/lib/ruby/vendor_ruby/chef/providers.rb: require 'chef/provider/package/rubygems'
Found some [...]]]></description>
			<content:encoded><![CDATA[<p>For those working on moving debian ruby library packaging to gem2deb, you can exempt specific hits from the slick built in &#8216;require rubygems&#8217; test by adding the path to debian/require-rubygems.overrides.</p>
<p>For instance, to exempt this:</p>
<pre class="brush: plain; gutter: false; title: ; toolbar: false; notranslate">
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/version'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/dependency'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/spec_fetcher'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/platform'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/format'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/dependency_installer'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/uninstaller'
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb: require 'rubygems/specification'
debian/chef/usr/lib/ruby/vendor_ruby/chef/providers.rb: require 'chef/provider/package/rubygems'
Found some 'require rubygems' without overrides (see above).
ERROR: Test &quot;require-rubygems&quot; failed. Exiting.
dh_auto_install: dh_ruby --install /«BUILDDIR»/chef-0.10.0/debian/chef returned exit code 1
make: *** [binary] Error 1
dpkg-buildpackage: error: fakeroot debian/rules binary gave error exit status 2
</pre>
<p>debian/require-rubygems.overrides should contain:</p>
<pre class="brush: plain; gutter: false; title: ; toolbar: false; notranslate">
debian/chef/usr/lib/ruby/vendor_ruby/chef/provider/package/rubygems.rb
debian/chef/usr/lib/ruby/vendor_ruby/chef/providers.rb
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/05/05/require-rubygems-overrides-and-gem2deb-0-2-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>locale errors on debian</title>
		<link>http://blog.loftninjas.org/2011/04/14/locale-errors-on-debian/</link>
		<comments>http://blog.loftninjas.org/2011/04/14/locale-errors-on-debian/#comments</comments>
		<pubDate>Thu, 14 Apr 2011 21:51:00 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=717</guid>
		<description><![CDATA[I received the following error while working on a Debian sid box:

$ schroot -l
terminate called after throwing an instance of 'std::runtime_error'
  what():  locale::facet::_S_create_c_locale name not valid
Aborted

With debconf + locales already installed, I ran &#8216;export &#124; grep LANG&#8217; to discover that my locale was set to &#8216;en_US.UTF-8&#8242;. Then I ran &#8216;dpkg-reconfigure locale&#8217; and checked [...]]]></description>
			<content:encoded><![CDATA[<p>I received the following error while working on a Debian sid box:</p>
<pre class="brush: plain; title: ; notranslate">
$ schroot -l
terminate called after throwing an instance of 'std::runtime_error'
  what():  locale::facet::_S_create_c_locale name not valid
Aborted
</pre>
<p>With <a href="http://people.debian.org/~schultmc/locales.html">debconf + locales</a> already installed, I ran &#8216;export | grep LANG&#8217; to discover that my locale was set to &#8216;en_US.UTF-8&#8242;. Then I ran &#8216;dpkg-reconfigure locale&#8217; and checked that locale and set it to the default.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/04/14/locale-errors-on-debian/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating a Debian sid emi for Eucalyptus</title>
		<link>http://blog.loftninjas.org/2011/04/13/creating-a-debian-sid-emi-for-eucalyptus/</link>
		<comments>http://blog.loftninjas.org/2011/04/13/creating-a-debian-sid-emi-for-eucalyptus/#comments</comments>
		<pubDate>Wed, 13 Apr 2011 23:43:52 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=709</guid>
		<description><![CDATA[For the most part, this is the same as any post about creating an image for Eucalyptus, but I had a hard time figuring out exactly how to put it all together. You need an up to date Debian sid system nearby to take the kernel and ramdisk from. I found having a sid VM [...]]]></description>
			<content:encoded><![CDATA[<p>For the most part, this is the same as any post about creating an image for Eucalyptus, but I had a hard time figuring out exactly how to put it all together. You need an up to date Debian sid system nearby to take the kernel and ramdisk from. I found having a sid VM easier than discovering the commands to build a sid initrd on my Ubuntu workstation.</p>
<pre class="brush: plain; title: ; notranslate">
# First, the prerequisites. You need debootstrap and the eucalyptools tools installed.
sudo apt-get install debootstrap euca2ools

# Export your eucalyptus variables to use the tools.
source ~/.euca/eucarc

# Create an empty disk image. You can adjust the count to change the root disk size. 1000 is about a GB.
dd if=/dev/zero of=image count=1000 bs=1M

# Put a filesystem on the new disk image
mkfs.ext3 -F image

# Mount the filesystem
mkdir chroot
sudo mount -o loop image chroot

# Install debian sid to the chroot. Notice that the ssh server and curl are included here
sudo debootstrap --include=openssh-server,curl,vim --arch amd64 sid chroot/ http://ftp.debian.org

# chroot into the image
sudo chroot chroot

# Setup basic networking and disk configurations
echo -e 'auto lo\niface lo inet loopback\nauto eth0\niface eth0 inet dhcp' &gt;&gt; /etc/network/interfaces
echo -e '/dev/sda1 / ext3 defaults 0 1\n/dev/sda2 swap swap defaults 0 0' &gt; /etc/fstab

# Set a default root password if you want
# passwd

# Set up the image to automatically install ssh keys
mkdir /root/.ssh
cat &lt;&lt;EOS &gt; /etc/rc.local
echo &gt;&gt; /root/.ssh/authorized_keys
curl -m 10 -s http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key | grep 'ssh-rsa' &gt;&gt; /root/.ssh/authorized_keys
echo &quot;AUTHORIZED_KEYS:&quot;
echo &quot;************************&quot;
cat /root/.ssh/authorized_keys
echo &quot;************************&quot;
exit 0
EOS

# Leave the image
exit

# Unmount the image
sudo umount chroot

# After you've copied the latest /boot/vmlinuz* and /boot/initrd* from your sid system, upload the kernel + ramdisk
euca-bundle-image --image vmlinuz-2.6.38-2-amd64 --kernel true
euca-upload-bundle --bucket sid --manifest vmlinuz-2.6.38-2-amd64.manifest.xml
euca-register sid/vmlinuz-2.6.38-2-amd64.manifest.xml
euca-bundle-image --image initrd.img-2.6.38-2-amd64 --ramdisk true
euca-upload-bundle --bucket sid --manifest initrd.img-2.6.38-2-amd64.manifest.xml
euca-register sid/initrd.img-2.6.38-2-amd64.manifest.xml

# Prepare the image for upload, use the values given by euca-register above here
euca-bundle-image -i image --kernel eki-XXXXXXXX --ramdisk eri-XXXXXXXX

# Rename to manifest to something descriptive and upload it
mv image.manifest.xml `date +%Y%m%d`.sid.manifest.xml
euca-upload-bundle -b sid -m `date +%Y%m%d`.sid.manifest.xml

# Register the image to get an EMI
euca-register sid/`date +%Y%m%d`.sid.manifest.xml
</pre>
<p>You should be able to use euca-run-instance on the emi that is returned by the last command. Remember to pass an ssh key (that eucalyptus knows about) using -k. If there are any issues, use euca-get-console-output to monitor the instance startup and tail the eucalyptus/nc.log file on the node controller for any errors. Building the initrd this way is a little hackish, because it is actually generated for your sid system, not for the one running in eucalyptus. Chicken, or the egg?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/04/13/creating-a-debian-sid-emi-for-eucalyptus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LVM errors with sbuild</title>
		<link>http://blog.loftninjas.org/2011/03/10/lvm-errors-with-sbuild/</link>
		<comments>http://blog.loftninjas.org/2011/03/10/lvm-errors-with-sbuild/#comments</comments>
		<pubDate>Thu, 10 Mar 2011 19:51:17 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=705</guid>
		<description><![CDATA[Here is a strange one that I fixed but I&#8217;m not sure why. Roughly using the SBuildLVM Howto, and the Chef sbuild cookbook, I have an sbuild server. It was working alright for me, but another user was seeing this:
schroot -c lucid
E: 05lvm: File descriptor 3 (socket:[460392]) leaked on lvcreate invocation.
E: lucid-40c0e109-2d5d-4103-bf92-a44288595dcc: Chroot setup failed: [...]]]></description>
			<content:encoded><![CDATA[<p>Here is a strange one that I fixed but I&#8217;m not sure why. Roughly using the <a href="https://help.ubuntu.com/community/SbuildLVMHowto">SBuildLVM Howto</a>, and the <a href="http://community.opscode.com/cookbooks/sbuild">Chef sbuild cookbook</a>, I have an sbuild server. It was working alright for me, but another user was seeing this:</p>
<p><code>schroot -c lucid<br />
E: 05lvm: File descriptor 3 (socket:[460392]) leaked on lvcreate invocation.<br />
E: lucid-40c0e109-2d5d-4103-bf92-a44288595dcc: Chroot setup failed: stage=setup-start</code></p>
<p>When he ran with verbose mode, this line was particularly interesting:</p>
<p><code>E: 05lvm:   </code></p>
<p>When I su&#8217;d to his user, it worked fine for me without verbose but failed similarly with the verbose flag. </p>
<p>In the course of debugging, I started trying to redirect output and I found that these changes to /etc/schroot/setup.d/05lvm fixed the problem. Unfortunately I&#8217;m running behind on work so I can&#8217;t track down the root cause right now.</p>
<pre class="brush: plain; title: ; notranslate">
--- 05lvm.orig	2011-03-10 19:28:17.000000000 +0000
+++ 05lvm	2011-03-10 19:37:54.000000000 +0000
@@ -36,10 +36,10 @@

 	if [ &quot;$AUTH_VERBOSITY&quot; = &quot;verbose&quot; ]; then
 	    lvcreate $VERBOSE --snapshot --name &quot;$CHROOT_LVM_SNAPSHOT_NAME&quot; \
-		&quot;$CHROOT_DEVICE&quot; $CHROOT_LVM_SNAPSHOT_OPTIONS
+		&quot;$CHROOT_DEVICE&quot; $CHROOT_LVM_SNAPSHOT_OPTIONS 2&gt;&amp;1
 	else
 	    lvcreate $VERBOSE --snapshot --name &quot;$CHROOT_LVM_SNAPSHOT_NAME&quot; \
-		&quot;$CHROOT_DEVICE&quot; $CHROOT_LVM_SNAPSHOT_OPTIONS &gt; /dev/null
+		&quot;$CHROOT_DEVICE&quot; $CHROOT_LVM_SNAPSHOT_OPTIONS 2&gt;&amp;1 &gt; /dev/null
 	fi

     elif [ $1 = &quot;setup-stop&quot; ]; then
@@ -57,9 +57,9 @@
 		--pid=$PID || true

 	    if [ &quot;$AUTH_VERBOSITY&quot; = &quot;verbose&quot; ]; then
-		lvremove $VERBOSE -f &quot;$CHROOT_LVM_SNAPSHOT_DEVICE&quot; || true
+		lvremove $VERBOSE -f &quot;$CHROOT_LVM_SNAPSHOT_DEVICE&quot; 2&gt;&amp;1 || true
 	    else
-		lvremove $VERBOSE -f &quot;$CHROOT_LVM_SNAPSHOT_DEVICE&quot; &gt; /dev/null || true
+		lvremove $VERBOSE -f &quot;$CHROOT_LVM_SNAPSHOT_DEVICE&quot; 2&gt;&amp;1 &gt; /dev/null || true
 	    fi
 	else
 	    # The block device no longer exists, or was never created,
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/03/10/lvm-errors-with-sbuild/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>munin-cgi-graph with fcgid on ubuntu lucid</title>
		<link>http://blog.loftninjas.org/2011/02/24/munin-cgi-graph-with-fcgid-on-ubuntu-lucid/</link>
		<comments>http://blog.loftninjas.org/2011/02/24/munin-cgi-graph-with-fcgid-on-ubuntu-lucid/#comments</comments>
		<pubDate>Fri, 25 Feb 2011 00:02:49 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=695</guid>
		<description><![CDATA[Two and a half years have passed since I wrote about running Munin with fastcgi triggered graphs on Debian etch. Unfortunately, not a lot has changed since then. A revolution in trending would have been nice. When I started here munin was triggering graph generation using CGI and was painfully slow to use. I switched [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.loftninjas.org/wp-content/uploads/2011/02/munin_stats-day.png"><img src="http://blog.loftninjas.org/wp-content/uploads/2011/02/munin_stats-day-300x187.png" alt="" title="munin_stats-day" width="300" height="187" class="alignleft size-medium wp-image-703" /></a>Two and a half years have passed since I wrote about running <a href="http://blog.loftninjas.org/2008/09/16/munin-cgi-graph-with-fastcgi-on-debian-etch/">Munin with fastcgi triggered graphs on Debian etch</a>. Unfortunately, not a lot has changed since then. A revolution in trending would have been nice. When I started here munin was triggering graph generation using CGI and was painfully slow to use. I switched over to cron triggered graph generation and was happy. After a data center migration, drawing the munin graphs for that cluster from cron was taking about 130 seconds. As a precaution I wanted to get this down a bit. </p>
<p>Someone asked me why munin-graph would have caused data loss because munin-update collects the data and I couldn&#8217;t remember. I had problems with both munin-update and munin-update taking over five minutes in certain circumstances back then. The latter was primarily from the slow response time of the SNMP queries I was doing against MSSQL servers. That was back during Munin 1.2 as well and a few things have changed since then, most relevant is that you no longer have to patch Munin for fastcgi support.</p>
<p>This time around I used <a href="http://httpd.apache.org/mod_fcgid/">fcgid</a> instead of fastcgi. There are less licensing hurdles for fcgid, which was written to be compatible with fastcgi. Provided you already have munin running, install the prerequsites first.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
sudo apt-get install libcgi-fast-perl libdate-manip-perl libapache2-mod-fcgid
</pre>
<p>The packaging should restart Apache as required to load the new module we just installed, but we need to configure our Munin site a bit to link our CGI script to fcgid. Add this to or update the VirtualHost block for your Apache configuration and reload Apache.</p>
<pre class="brush: plain; title: ; notranslate">
  ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/

  &lt;Directory /usr/lib/cgi-bin/&gt;
    AllowOverride None
    Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
  &lt;/Directory&gt;

  &lt;Location /cgi-bin/munin-fastcgi-graph&gt;
    SetHandler  fastcgi-script
  &lt;/Location&gt;
</pre>
<p>Add the following lines to your munin.conf. This causes the munin-graph that is run from cron to not generate any graphs (noops) and munin-html will update the img src links to use the CGI script to generate the graphs rather than linking directly to files. You&#8217;ll need to wait for the cron job to run once or run munin-html yourself to trigger this.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
graph_strategy cgi
cgiurl_graph /cgi-bin/munin-fastcgi-graph
</pre>
<p>Triggering munin-html manually:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
sudo -s
sudo -u munin /usr/share/munin/munin-html --debug
</pre>
<p>Remember that Apache needs to be able to write the graphs out. You will get no graphs and HTTP 500 errors in your Apache logs if the munin-cgi-graph script cannot write the graphs out. My Munin data directory, /var/www/munin/ is owned by &#8216;munin&#8217; while Apache runs as &#8216;www-data&#8217;. The following commands fix this, but Apache is going to change the user ownership to &#8216;www-data&#8217; when it saves a file by default, so if you try to switch back to munin-graph via cron, you&#8217;ll need to fix permissions again.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
sudo chgrp -R www-data /var/www/munin
sudo chmod -R g+w /var/www/munin
sudo chgrp www-data /var/log/munin /var/log/munin/munin-graph.log
sudo chmod g+w /var/log/munin /var/log/munin/munin-graph.log
</pre>
<p>After the switch to fcgid generated munin graphs, generating all the graphs for a single node would take minutes and was quite painful. I gave the node more CPU resources, but it still took two minutes to draw a page of graphs. I ended up switching back to cron based graph generation. The additional CPU resources cut about forty seconds off the munin-graph time from cron, which is progress. Having the graphs immediately available when you need them is worth the cost of the CPU resources you could otherwise share that you would save from demand based graph generation via CGI. For the time being I intend to keep giving Munin more CPU until I find settle on a better way to do trending.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/02/24/munin-cgi-graph-with-fcgid-on-ubuntu-lucid/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>The power of Chef and Ruby</title>
		<link>http://blog.loftninjas.org/2011/02/16/the-power-of-chef-and-ruby/</link>
		<comments>http://blog.loftninjas.org/2011/02/16/the-power-of-chef-and-ruby/#comments</comments>
		<pubDate>Wed, 16 Feb 2011 20:57:07 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=682</guid>
		<description><![CDATA[The argument that Chef is difficult to learn because recipes are written in Ruby is a fallacy. 

package &#34;vim&#34;

cookbook_file &#34;/home/btm/.vimrc&#34; do
  source &#34;dot-vimrc&#34;
  owner &#34;btm&#34;
  group &#34;btm&#34;
  mode &#34;0644&#34;
end

With the exception of the do/end block, that doesn&#8217;t look like a programming language at all and is way easier to grok than [...]]]></description>
			<content:encoded><![CDATA[<p>The argument that Chef is difficult to learn because recipes are written in Ruby is a fallacy. </p>
<pre class="brush: ruby; title: ; notranslate">
package &quot;vim&quot;

cookbook_file &quot;/home/btm/.vimrc&quot; do
  source &quot;dot-vimrc&quot;
  owner &quot;btm&quot;
  group &quot;btm&quot;
  mode &quot;0644&quot;
end
</pre>
<p>With the exception of the do/end block, that doesn&#8217;t look like a programming language at all and is way easier to grok than some configuration file syntaxes I&#8217;ve used. Any tool&#8217;s configuration file syntax has a learning curve and refusing to learn a new one means you&#8217;re going to be stuck in the past using old tools. Someone may not want to try out nginx today because they already know how to configure Apache, and I understand that up to a point. The tool you know is sometimes easier to use in the less than ideal conditions because you already understand it. I can&#8217;t spend all of my time learning new tools anymore than the next person, but frankly if you are unwilling to learn something new, you are in the wrong industry. We are moving fast over here.</p>
<p>Even if you don&#8217;t know any Ruby, over time you start reusing other people&#8217;s code shortcuts because it is easier to write understandable and flexible code.</p>
<pre class="brush: ruby; title: ; notranslate">
# Install useful troubleshooting tools that get regular use
%w{htop dstat strace sysstat gdb tmux tshark}.each do |tool_package|
  package tool_package
end

# Install the correct apache package depending on distribution
package &quot;apache2&quot; do
  case node[:platform]
  when &quot;centos&quot;,&quot;redhat&quot;,&quot;fedora&quot;,&quot;suse&quot;
    package_name &quot;httpd&quot;
  when &quot;debian&quot;,&quot;ubuntu&quot;
    package_name &quot;apache2&quot;
  end
  action :install
end
</pre>
<p>Because Chef recipes are written in Ruby and they are compiled on the client rather than the server you can leverage Ruby in very powerful ways. When we want to create databases and grant privileges for a web application, we can use a number of <a href="http://wiki.opscode.com/display/chef/Resources">Chef resources</a>, primarily execute, to use existing tools such as mysqladmin. We can also leverage Ruby to access Ruby libraries. Ruby code in a Chef recipe is executed during convergence, but Ruby code in a <a href="http://wiki.opscode.com/display/chef/Resources#Resources-RubyBlock">ruby_block</a> resource is executed along with other resources during compilation and can be notified like any other resource. You can get a better idea of when these steps happen from the <a href="http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run">Anatomy of a Chef Run</a> page on the wiki. Here is some code I used recently that is quite a bit simpler to read and shorter than using resources to perform all of the steps.</p>
<pre class="brush: ruby; title: ; notranslate">
    ruby_block &quot;Create database + execute grants&quot; do
      block do
        require 'rubygems'
        Gem.clear_paths
        require 'mysql'

        m = Mysql.new(mysql_host, &quot;root&quot;, mysql_root_password)
        if !m.list_dbs.include?(node[:jira][:database_name])
          # Create the database
          Chef::Log.info &quot;Creating mysql database #{node[:jira][:database_name]}&quot;
          m.query(&quot;CREATE DATABASE #{node[:jira][:database_name]} CHARACTER SET utf8&quot;)

          # Grant and flush permissions
          Chef::Log.info &quot;Granting access to #{node[:jira][:database_name]} for #{node[:jira][:database_user]}&quot;
          m.query(&quot;GRANT ALL ON #{node[:jira][:database_name]}.* TO '#{node[:jira][:database_user]}'@'localhost' IDENTIFIED BY '#{node[:jira][:database_password]}'&quot;)
          m.reload
        end
      end
    end
</pre>
<p>Because Chef makes it easy to model data, you don&#8217;t need to write the above code. You can just use what I wrote and change your variable names. If you use it for more than one web_application, you could make it a cookbook definition or <a href="http://wiki.opscode.com/display/chef/Lightweight+Resources+and+Providers+%28LWRP%29">LWRP</a> that you could extend as you need more features.</p>
<pre class="brush: ruby; title: ; notranslate">
initialize_mysql_db &quot;jiradb&quot; do
  database_name node[:jira][:database_name]
  database_user node[:jira][:database_user]
  database_password node[:jira][:database_password]
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/02/16/the-power-of-chef-and-ruby/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Monitoring Unicorn connections with munin</title>
		<link>http://blog.loftninjas.org/2011/02/11/monitoring-unicorn-connections-with-munin/</link>
		<comments>http://blog.loftninjas.org/2011/02/11/monitoring-unicorn-connections-with-munin/#comments</comments>
		<pubDate>Sat, 12 Feb 2011 02:00:37 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=673</guid>
		<description><![CDATA[Unicorn doesn&#8217;t have any monitoring hooks. Typically folks either put nginx in front and monitor response time, do some backlog magic and track errors or make guesses based on other available information. I&#8217;ve been using a modified version of the unicorn_status munin plugin for a while. It tracks CPU time for a thread and considers [...]]]></description>
			<content:encoded><![CDATA[<p>Unicorn doesn&#8217;t have any monitoring hooks. Typically folks either put nginx in front and monitor response time, do some <a href="http://rubyforge.org/pipermail/mongrel-unicorn/2010-February/000405.html">backlog magic</a> and track errors or make guesses based on other available information. I&#8217;ve been using a modified version of the <a href="http://exchange.munin-monitoring.org/plugins/unicorn_status/version/1">unicorn_status munin plugin</a> for a while. It tracks CPU time for a thread and considers that thread idle if it hasn&#8217;t changed after sleeping for a second. This doesn&#8217;t pan out under load. Still, here it is.</p>
<pre class="brush: ruby; title: ; notranslate">
#!/usr/bin/env ruby
#
# unicorn_status - A munin plugin for Linux to monitor unicorn processes
#
#  Copyright (C) 2010 Shinji Furuya - shinji.furuya@gmail.com
#  Copyright (C) 2010 Opscode, Inc. - Bryan McLellan &lt;btm@loftninjas.org&gt;
#    - Specify pid file via environment variable
#    - Do not assume process names
#  Licensed under the MIT license:
#  http://www.opensource.org/licenses/mit-license.php
#

module Munin
  class UnicornStatus

    def initialize
      @pid_file = ENV['UNICORN_PID']
    end

    def master_pid
      File.read(@pid_file).to_i
    end

    def worker_pids
      result = []
      ps_output = `ps w --ppid #{master_pid}`
      ps_output.each_line do |line|
        chunks = line.strip.split(/\s+/, 5)
        pid = chunks[0]
        result &lt;&lt; pid.to_i if pid =~ /\A\d+\z/
      end
      result
    end

    def worker_count
      worker_pids.size
    end

    def idle_worker_count
      result = 0
      before_cpu = {}
      worker_pids.each do |pid|
        before_cpu[pid] = cpu_time(pid)
      end
      sleep 1
      after_cpu = {}
      worker_pids.each do |pid|
        after_cpu[pid] = cpu_time(pid)
      end
      worker_pids.each do |pid|
        result += 1 if after_cpu[pid] - before_cpu[pid] == 0
      end
      result
    end

    def cpu_time(pid)
      usr, sys = `cat /proc/#{pid}/stat | awk '{print $14,$15 }'`.strip.split(/\s+/).collect { |i| i.to_i }
      usr + sys
    end
  end
end

case ARGV[0]
when &quot;autoconf&quot;
  puts &quot;yes&quot;
when &quot;config&quot;
  puts &quot;graph_title Unicorn - Status&quot;
  puts &quot;graph_args -l 0&quot;
  puts &quot;graph_vlabel number of workers&quot;
  puts &quot;graph_category Unicorn&quot;
  puts &quot;total_worker.label total_workers&quot;
  puts &quot;idle_worker.label idle_workers&quot;
else
  m = Munin::UnicornStatus.new
  puts &quot;total_worker.value #{m.worker_count}&quot;
  puts &quot;idle_worker.value #{m.idle_worker_count}&quot;
end
</pre>
<p>And the configuration file:</p>
<pre class="brush: plain; title: ; notranslate">
$ sudo cat /etc/munin/plugin-conf.d/unicorn
      [unicorn_*]
      user root
      env.UNICORN_PID /etc/sv/opscode-chef/supervise/pid
</pre>
<p>I wrote another plugin today that uses <a href="http://raindrops.bogomips.org/">raindrops</a> to collect information about the active and queued connections. It is interesting how greatly active connections fluctuates. Thus, active connections don&#8217;t produce a stable munin graph, but having the queue depth recorded is pretty useful for tracking down latency issues.</p>
<pre class="brush: ruby; title: ; notranslate">
#!/usr/bin/env ruby
#  Copyright: 2011 Opscode, Inc.
#  Author: Bryan McLellan &lt;btm@loftninjas.org&gt;
#
#   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

require 'rubygems'
require 'raindrops'

def collect(port)
  # raindrops requires an array of strings, even if it denies this
  addr = [ &quot;0.0.0.0:#{port}&quot; ]
  stats = Raindrops::Linux.tcp_listener_stats(addr)

  puts &quot;active.value #{stats[addr[0]].active}&quot;
  puts &quot;queued.value #{stats[addr[0]].queued}&quot;
end

if ARGV[0] == &quot;config&quot;
  puts &quot;graph_title Unicorn - connections&quot;
  puts &quot;graph_args -l 0&quot;
  puts &quot;graph_printf %6.0lf&quot;
  puts &quot;graph_vlabel connections&quot;
  puts &quot;graph_category Unicorn&quot;
  puts &quot;active.label active&quot;
  puts &quot;queued.label queued&quot;
  exit 0
end

if $0 =~ /.*_(\d+)/
  # the munin wildcard format of plugin_value
  port = $1
elsif ARGV.size &gt; 0
  port = ARGV[0]
else
  usage = &quot;Usage: #$0 port or #{$0}_port&quot;
  abort usage
end

collect(port)
</pre>
<p>Usage is the same as any wildcard munin plugin. </p>
<ol>
<li>Install the raindrops gem</li>
<li>Drop the above code in &#8220;/usr/share/munin/plugins/unicorn_connections_&#8221;</li>
<li>Create a link from &#8220;/etc/munin/plugins/unicorn_connections_UNICORNPORT&#8221; to the above script</li>
<li>killall -HUP munin-node</li>
</ol>
<p>Graphs should start showing up in five or ten minutes. You can always test like so:</p>
<pre class="brush: plain; title: ; notranslate">
$ nc localhost 4949
# munin node at unicorn.example.org
fetch unicorn_connections_6880
active.value 5
queued.value 0
.
quit
</pre>
<p>Of course, I use the Chef and the munin cookbook&#8217;s <a href="https://github.com/opscode/cookbooks/blob/master/munin/definitions/munin_plugin.rb">munin_plugin definition</a>, so my application&#8217;s cookbook has this additional code:</p>
<pre class="brush: plain; title: ; notranslate">
# required for unicorn_connections_ munin plugin
gem_package &quot;raindrops&quot;

munin_plugin &quot;unicorn_connections_&quot; do
  plugin &quot;unicorn_connections_6880&quot;
  create_file true
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/02/11/monitoring-unicorn-connections-with-munin/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Init replacements change fundamental assumptions</title>
		<link>http://blog.loftninjas.org/2011/02/10/init-replacements-change-fundamental-assumptions/</link>
		<comments>http://blog.loftninjas.org/2011/02/10/init-replacements-change-fundamental-assumptions/#comments</comments>
		<pubDate>Thu, 10 Feb 2011 21:15:18 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=644</guid>
		<description><![CDATA[The trend with init replacements
When you write a number of service resource providers for a configuration management system, you get some intimate experience with the quirks of init systems. A slew of new ones are working their way into stable releases lately which seem primarily motivated by decreasing system startup time by allowing services to [...]]]></description>
			<content:encoded><![CDATA[<p><strong>The trend with init replacements</strong></p>
<p>When you write a number of <a href="http://wiki.opscode.com/display/chef/Resources#Resources-Service">service resource</a> providers for a configuration management system, you get some intimate experience with the quirks of init systems. A slew of new ones are working their way into stable releases lately which seem primarily motivated by decreasing system startup time by allowing services to be started in parallel. For instance, Ubuntu has been <a href="http://netsplit.com/2006/08/26/upstart-in-universe/">moving to upstart</a>, the latest release of <a href="http://wiki.debian.org/LSBInitScripts/DependencyBasedBoot">Debian uses insserv</a>, and <a href="http://launchd.macosforge.org/">OS X uses launchd</a>. There is overlap in design, and certainly parallel service execution isn&#8217;t the only significant improvement. Since init is a basic building block of our systems, small changes can cause large ripples. In the end we will have some great new functionality, but we&#8217;re in a rough patch of transition right now and need to ensure the functionality we rely upon doesn&#8217;t get passed over.</p>
<p><strong>Disabling services with Upstart</strong></p>
<p>If you want a service to not start on system startup, but still want to be able to start it, you have to comment out a line in the configuration file. Programmatically editing configuration files, from a script or a configuration management system is difficult to do cleanly. In general you want to avoid minor changes to configuration files because then you have to reconcile the differences when you upgrade the package. There are <a href="https://bugs.launchpad.net/upstart/+bug/94065">plans to add support for an override file</a> wherein you can specify that the service is manual, but clearly Ubuntu server users are taking a backseat to desktop users inside Canonical where Upstart is developed.</p>
<p><strong>Restarting services with Upstart</strong></p>
<p>Which is interesting, as Ubuntu server related packages are being migrated to use Upstart. We start to run into additional quirks, such as when you restart a service that isn&#8217;t running, Upstart does not start it. We plan to work around this behavior <a href="http://tickets.opscode.com/browse/CHEF-1424">in Chef</a> but others have clearly <a href="http://twitter.com/#!/crucially/status/35753942273110016">taken notice</a>.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ status mysql
mysql start/running, process 548
$ sudo restart mysql
mysql start/running, process 649
$ sudo stop mysql
mysql stop/waiting
$ sudo restart mysql
restart: Unknown instance:
</pre>
<p><strong>Insserv changes how you specify runlevels</strong></p>
<p>On Debian lenny you could specify service runlevels and priorities as such:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ sudo update-rc.d apache2 start 20 3 4 5 . stop 80 0 1 .
 Adding system startup for /etc/init.d/apache2 ...
   /etc/rc0.d/K80apache2 -&amp;gt; ../init.d/apache2
   /etc/rc1.d/K80apache2 -&amp;gt; ../init.d/apache2
   /etc/rc3.d/S20apache2 -&amp;gt; ../init.d/apache2
   /etc/rc4.d/S20apache2 -&amp;gt; ../init.d/apache2
   /etc/rc5.d/S20apache2 -&amp;gt; ../init.d/apache2
</pre>
<p>However on squeeze, update-rc.d is wrapped by insserv, which ignores your request and acts on the LSB headers.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ sudo update-rc.d apache2 start 20 3 4 5 . stop 80 0 1 2 6 .
update-rc.d: using dependency based boot sequencing
update-rc.d: warning: apache2 start runlevel arguments (3 4 5) do not match LSB Default-Start values (2 3 4 5)
update-rc.d: warning: apache2 stop runlevel arguments (0 1 2 6) do not match LSB Default-Stop values (0 1 6)
$ find /etc/rc* -name '*apache*'
/etc/rc0.d/K01apache2
/etc/rc1.d/K01apache2
/etc/rc2.d/S18apache2
/etc/rc3.d/S18apache2
/etc/rc4.d/S18apache2
/etc/rc5.d/S18apache2
/etc/rc6.d/K01apache2
</pre>
<p>Insserv does have an option to override the LSB headers, but the update-rc.d wrapper doesn&#8217;t use it and you have to be very careful as it fails silently if you use it wrong.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ sudo insserv -r apache2
$ sudo insserv apache2,start=3,4,5,stop=0,1,2,6
$ find /etc/rc* -name '*apache*'
/etc/rc0.d/K01apache2
/etc/rc1.d/K01apache2
/etc/rc2.d/K01apache2
/etc/rc2.d/S18apache2
/etc/rc3.d/S18apache2
/etc/rc4.d/S18apache2
/etc/rc5.d/S18apache2
/etc/rc6.d/K01apache2
</pre>
<p>Additional behavior to work around <a href="http://tickets.opscode.com/browse/CHEF-2034">in Chef</a>.</p>
<p><strong>Moving forward</strong></p>
<p>Distributions continue to change the way we interact with init with every release. This is clearly a reasons to use a configuration management tool. You know that you want mysql to never start automatically<a href="http://www.krisbuytaert.be/blog/ensure-running"> because your cluster resource manager controls it</a>, but how you achieve that has been changing lately with regularity. You can let your configuration management tool abstract that from you. Still, we need to stay involved in the discussions in the open source communities whose software we use and be proactive citizens.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/02/10/init-replacements-change-fundamental-assumptions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Knife Reporting: apt + updates</title>
		<link>http://blog.loftninjas.org/2011/01/12/knife-reporting-apt-updates/</link>
		<comments>http://blog.loftninjas.org/2011/01/12/knife-reporting-apt-updates/#comments</comments>
		<pubDate>Thu, 13 Jan 2011 00:12:00 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>
		<category><![CDATA[knife]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=639</guid>
		<description><![CDATA[Nathan and I were discussing yesterday the lack of a good way to visualize all of the updates waiting to be installed across a server cluster. I wrote a another knife script to do this, and Seth Falcon helped me clean it up.

# Knife exec script to search for and describe systems needing updates
# 2011-01-11 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://twitter.com/tmonk42">Nathan</a> and I were discussing yesterday the lack of a good way to visualize all of the updates waiting to be installed across a server cluster. I wrote a another knife script to do this, and <a href="http://twitter.com/sfalcon">Seth Falcon</a> helped me clean it up.</p>
<pre class="brush: ruby; title: ; notranslate">
# Knife exec script to search for and describe systems needing updates
# 2011-01-11 - Bryan McLellan &lt;btm@loftninjas.org&gt;

gem &quot;net-ssh&quot;, &quot;&gt;= 2.0.23&quot;
require 'net/ssh/multi'

class AptSsh &lt; Chef::Knife::Ssh
  # Override configure_session so we can specify where to get the query
  def configure_session
    @longest = 0 # Set in Chef::Knife::Ssh.run
    q = Chef::Search::Query.new
    @action_nodes = q.search(:node, ARGV[2])[0]
    fqdns = @action_nodes.map { |item| item.fqdn }
    if fqdns.empty?
      Chef::Log.fatal(&quot;No nodes returned from search!&quot;)
      exit 10
    end
    session_from_list(fqdns)
  end

  def capture_command(command, subsession=nil)
    host_data = Hash.new { |h, k| h[k] = &quot;&quot; }
    subsession ||= session
    command = fixup_sudo(command)
    subsession.open_channel do |ch|
      ch.request_pty
      ch.exec command do |ch, success|
        raise ArgumentError, &quot;Cannot execute #{command}&quot; unless success
        ch.on_data do |ichannel, data|
          host_data[ichannel[:host]] &lt;&lt; data
          if data =~ /^knife sudo password: /
            ichannel.send_data(&quot;#{get_password}\n&quot;)
          end
        end
      end
    end
    session.loop
    return host_data
  end
end

abort(&quot;usage: knife exec apt.knife QUERY&quot;) unless ARGV[2]
ssh = AptSsh.new
ssh.configure_session

# install apt-show-versions if it isn't installed
install_show_versions = &lt;&lt;EOH
if [ ! -e /usr/bin/apt-show-versions ] ; then
  echo INSTALLING APT-SHOW-VERSIONS ; sudo apt-get install apt-show-versions -y
fi
EOH
ssh.ssh_command(install_show_versions)

apt_data = ssh.capture_command('apt-show-versions -u -b')

apt_data.each do |host, data|
  puts &quot;#{host} - #{data.count(&quot;\n&quot;)} updates, #{data.scan(&quot;-security&quot;).length} of which are security updates&quot;
  data.each_line do |line|
    puts &quot;  #{line}&quot;
  end
end

# Prevents knife from trying to execute any command line arguments as addtional script files, see CHEF-1973
exit 0
</pre>
<p>Given a search query, this provides an output of:</p>
<pre class="brush: plain; title: ; notranslate">
$ knife exec apt.knife role:dev
webui-dev.example.org - 6 updates, 6 of which are security updates
  libc-bin/lucid-security
  libc-dev-bin/lucid-security
  libc6/lucid-security
  libc6-dev/lucid-security
  libc6-i686/lucid-security
  libc6-xen/lucid-security
monitoring-dev.example.orgs - 6 updates, 6 of which are security updates
  libc-bin/lucid-security
  libc-dev-bin/lucid-security
  libc6/lucid-security
  libc6-dev/lucid-security
  libc6-i686/lucid-security
  libc6-xen/lucid-security
rabbitmq-dev.example.org - 6 updates, 6 of which are security updates
  libc-bin/lucid-security
  libc-dev-bin/lucid-security
  libc6/lucid-security
  libc6-dev/lucid-security
  libc6-i686/lucid-security
  libc6-xen/lucid-security
couchdb-dev.example.org - 7 updates, 7 of which are security updates
  libc-bin/lucid-security
  libc-dev-bin/lucid-security
  libc6/lucid-security
  libc6-dev/lucid-security
  xulrunner-1.9.2/lucid-security
  xulrunner-1.9.2-dev/lucid-security
  xulrunner-dev/lucid-security
</pre>
<p>Lets say that you didn&#8217;t want to upgrade the couch box, you could modify the search query to not include that box and run again to confirm. Then reuse that search string to trigger the update.</p>
<pre class="brush: plain; title: ; notranslate">
$ knife exec apt.knife &quot;role:dev NOT hostname:couchdb-dev&quot;
$ knife ssh &quot;role:dev NOT hostname:couchdb-dev&quot; &quot;sudo apt-get upgrade -y&quot;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/01/12/knife-reporting-apt-updates/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Reporting using Chef&#8217;s Knife</title>
		<link>http://blog.loftninjas.org/2011/01/06/reporting-using-chefs-knife/</link>
		<comments>http://blog.loftninjas.org/2011/01/06/reporting-using-chefs-knife/#comments</comments>
		<pubDate>Fri, 07 Jan 2011 04:41:24 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>
		<category><![CDATA[knife]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=609</guid>
		<description><![CDATA[We have a table in our corporate Confluence wiki that looks something like this. It was a product of a few quick notes to allow the team to build out VMs in parallel, distributed across a number of virtual hosts, and not rely on luck for proper resource utilization. The number fields are the amount [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.loftninjas.org/wp-content/uploads/2011/01/Cropped_VM_Table.jpg"><img class="alignleft size-medium wp-image-624" style="padding: 10px;" title="Cropped_VM_Table" src="http://blog.loftninjas.org/wp-content/uploads/2011/01/Cropped_VM_Table-300x111.jpg" alt="" width="300" height="111" /></a>We have a table in our corporate Confluence wiki that looks something like this. It was a product of a few quick notes to allow the team to build out VMs in parallel, distributed across a number of virtual hosts, and not rely on luck for proper resource utilization. The number fields are the amount of gigabytes of RAM allocated to the guests. As long as the total didn&#8217;t exceed a magic number for the entire host, we could keep building and the team remained unblocked. It got the job done, but it is no way to keep track of guests and resources. First, wiki&#8217;s have a tendency to get out of date and rot. It takes a fair amount of work to know what needs to be updated and keep it that way on a daily basis. Also, tables in Confluence are not all that great. They are far from Excel. The total row contains no formula to autosum the column, and you find yourself regularly switching between editor modes depending on how you are entering data, such as by hand or using cut and paste.</p>
<p>So, what if your &#8220;back of the napkin&#8221; calculations could be sourced from real data? This is usually unrealistic because you don&#8217;t know what data you need until you need it, so it hasn&#8217;t been captured. But we do capture a lot of data about nodes in Chef, so it is sitting there waiting for you to have that bright idea. In this case, I wanted to reconcile the memory usage on the VM hosts. I could ssh to each host, and collect this information from libvirt by hand, and put it in a spreadsheet somewhere or add it up myself for Confluence. But what happens when a teammate builds another server tomorrow? Will they update the documentation? Is that a step we want to keep doing by hand, as we build and destroy VMs on a regular basis? Is it a step we should be doing by hand, these days?</p>
<pre class="brush: ruby; title: ; notranslate">
Chef::Log.level= :fatal
printf &quot;%-10s %-12s %-8s %s\n&quot;, &quot;host&quot;, &quot;guest&quot;, &quot;MB RAM&quot;, &quot;Run List&quot;
search(:node, 'role:virt').each do |host|
  total_mem = 0
  host[:virtualization][:domains].each do |domain,attribs|
    begin
      guest = nodes.show(domain)
    rescue
      guest = search(:node, &quot;hostname:#{domain}&quot;)[0]
    end
    run_list = guest.run_list if guest
    printf &quot;%-10s %-12s %-8s %s\n&quot;, host.name, domain, attribs[:memory] / 1024, run_list
    total_mem += attribs[:memory]
  end
  printf &quot;%-10s %-12s %-8s %s\n&quot;, host.name, &quot;TOTAL&quot;, total_mem / 1024, &quot;&quot;
end
</pre>
<p>This example is a knife exec script. If you saved this to a file named <code>virt_ram.knife</code>, then you could run it with <code>knife exec virt_ram.knife</code>. While Chef has full blown APIs you can interface with, that can raise the cost of a small project higher than its worth. With knife exec, small proof of concept projects done on the side of your desk are approachable with ease.</p>
<p>Let us take a moment to step through the code.</p>
<p>1 &#8212; Set the Chef log level to fatal to surpress warnings generated my line 7 when we look a non-existent node.<br />
2 &#8212; Print out a header describing the columns of data we are going to generate<br />
3 &#8212; Search chef for all of the nodes with the role &#8220;virt&#8221; and loop through them, naming the node object &#8216;host&#8217;<br />
5 &#8212; Each virtual host object contains a hash of domains in host[:virtualization][:domains]. Step through these assigning the key to &#8216;domain&#8217; and the value (another hash) to &#8216;attribs&#8217;<br />
6-10 &#8211;  Look to see if we have a node in Chef whose name matches the domain name in libvirt. If not, rescue and trap that failure and try to search for a node with that hostname. Your node names in chef don&#8217;t have to be your hostnames or fqdns. At Opscode we use short unique identifiers such as EC2 instance IDs, portions of randomly generated GUIDs, and asset tracking numbers.<br />
11 &#8212; If we did find a matching node, get its run_list. This really explains what a host does at Opscode, as we tend two only have two or three meta roles applied to a node. Usually one represents the environment it is in, such as &#8220;prod&#8221; or &#8220;dev&#8221; and the other is its role like &#8220;webserver&#8221; or &#8220;couchdb&#8221;<br />
12 &#8212; Print out the information we known about this guest<br />
13 &#8212; Then add the memory used by that guest to the running total for the host.<br />
15 &#8212; Finally, print out the total memory we&#8217;ve calculated for that host.<br />
16 &#8212; Go back around and do it all again for the next host.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
$ knife exec virt_ram.knife
host guest        MB RAM   Run List
vm1  rv-735a342e  2048     role[prod], role[web]
vm1  rv-8ef1f3d1  4096     role[prod], role[database]
vm1  rv-eb574386  512      role[prod], role[dns]
vm1  TOTAL        6656
vm2  rv-91ba412e  2048     role[prod], role[web]
vm2  rv-8e342d11  4096     role[prod], role[database]
vm2  rv-e3829f86  512      role[prod], role[dns]
vm2  TOTAL        6656
vm3  cobbler1     1024
vm3  rv-e3829f86  512      role[prod], role[dns]
vm3  TOTAL        1536
</pre>
<p>This data is made up, but I&#8217;ve shown on vm3 something that I found in my own infrastructure; there were guests left over from testing that weren&#8217;t named properly and never made it into the chef server. I wouldn&#8217;t know they were there if I hadn&#8217;t done an audit of the servers this way. This exemplifies the Chef philosophy that it should help you do what you want, not model what it thinks you should be doing. This isn&#8217;t a carefully engineered reporting feature built around a common practice of virtualization management. This is a script I hacked on with <a href="http://twitter.com/kallistec">Dan</a>&#8217;s endless helpful guidance while I was waiting for an rsync to finish. I know others have written similar scripts to reconcile EC2 instances by comparing Chef and EC2 via Fog.</p>
<p>I love it. Do you have some spare time? What do you need? Chef will get you there.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2011/01/06/reporting-using-chefs-knife/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Knife one-liners</title>
		<link>http://blog.loftninjas.org/2010/12/29/knife-one-liners/</link>
		<comments>http://blog.loftninjas.org/2010/12/29/knife-one-liners/#comments</comments>
		<pubDate>Wed, 29 Dec 2010 22:25:05 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>
		<category><![CDATA[knife]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=598</guid>
		<description><![CDATA[Knife&#8217;s exec sub-command makes it easier to interact with a Chef server from the command line. Let&#8217;s assume I&#8217;ve created a data bag named cluster as follows:

{
  &#34;id&#34;: &#34;www1&#34;,
  &#34;cats&#34;: &#34;lol&#34;,
  &#34;hostname&#34;: &#34;www1.example.org&#34;
}
{
  &#34;id&#34;: &#34;www2&#34;,
  &#34;cats&#34;: &#34;lol&#34;,
  &#34;hostname&#34;: &#34;www2.example.org&#34;
}
{
  &#34;id&#34;: &#34;www3&#34;,
  &#34;cats&#34;: &#34;plz&#34;,
  &#34;hostname&#34;: &#34;www3.example.org&#34;
}

If I [...]]]></description>
			<content:encoded><![CDATA[<p>Knife&#8217;s exec sub-command makes it easier to interact with a Chef server from the command line. Let&#8217;s assume I&#8217;ve created a data bag named cluster as follows:</p>
<pre class="brush: plain; title: ; notranslate">
{
  &quot;id&quot;: &quot;www1&quot;,
  &quot;cats&quot;: &quot;lol&quot;,
  &quot;hostname&quot;: &quot;www1.example.org&quot;
}
{
  &quot;id&quot;: &quot;www2&quot;,
  &quot;cats&quot;: &quot;lol&quot;,
  &quot;hostname&quot;: &quot;www2.example.org&quot;
}
{
  &quot;id&quot;: &quot;www3&quot;,
  &quot;cats&quot;: &quot;plz&quot;,
  &quot;hostname&quot;: &quot;www3.example.org&quot;
}
</pre>
<p>If I wanted to get a list of hostnames for each data bag item where the value of &#8216;cats&#8217; is &#8216;lol&#8217;, I would run:</p>
<pre class="brush: plain; title: ; notranslate">
$ knife exec -E &quot;search(:cluster, 'cats:lol').each {|host| puts host[:hostname] }&quot;
www2.example.org
www1.example.org
</pre>
<p>Granted, I could get this data from the search sub-command as well:</p>
<pre class="brush: plain; title: ; notranslate">
$ knife search cluster cats:lol
{
  &quot;start&quot;: 0,
  &quot;total&quot;: 2,
  &quot;rows&quot;: [
    {
      &quot;id&quot;: &quot;www2&quot;,
      &quot;cats&quot;: &quot;lol&quot;,
      &quot;hostname&quot;: &quot;www2.example.org&quot;
    },
    {
      &quot;id&quot;: &quot;www1&quot;,
      &quot;cats&quot;: &quot;lol&quot;,
      &quot;hostname&quot;: &quot;www1.example.org&quot;
    }
  ]
}
</pre>
<p>However, it is hard to manipulate the result of this data. For instance, if I wanted to to check the status of ntp on each of these nodes as a &#8220;one-off command&#8221;, I could run:</p>
<pre class="brush: plain; title: ; notranslate">
$ knife ssh -m \
&quot;`knife exec -E &quot;search(:cluster, 'cats:plz').each {|host| puts host[:hostname] }&quot; | xargs`&quot; \
'/etc/init.d/ntp status'
www1.example.org  * NTP server is running
www2.example.org  * NTP server is running
</pre>
<p>The quoting can get pretty tricky fast. Instead, if you leave off the -E flag to knife exec, you can pass a script file to knife where you can write clearer scripts, which makes it easier to do more.</p>
<pre class="brush: plain; title: ; notranslate">
# Script contents
$ cat /tmp/knife.exec
targets = Array.new
search(:cluster, 'cats:lol').each do |host|
  targets &lt;&lt; host[:hostname]
end
puts targets.join(' ')

# Execute the script
$ knife exec /tmp/knife.exec
www2.example.org www1.example.org
</pre>
<p>What if you needed to reconcile your hardware support contracts with the systems currently deployed? It is no problem to get a list of hardware with chef and knife.</p>
<pre class="brush: plain; title: ; notranslate">
# Script contents
$ cat /tmp/dell.exec
search(:node, 'dmi_system_manufacturer:Dell*').each do |node|
  puts node[:dmi][:system][:serial_number] + &quot;\t&quot; + node[:fqdn]
end

# Execute the script
$ knife exec /tmp/dell.exec
XJS1NF1 www1.example.org
XJS1NF2 www2.example.org
XJS1NF3 www3.example.org
</pre>
<p>These are pretty simple examples, but hopefully you can see how easy it is with Chef to use knife scripts to create reports, collect data, and execute one-off commands.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/12/29/knife-one-liners/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wrangling 32bit debs on a 64bit system</title>
		<link>http://blog.loftninjas.org/2010/12/23/wrangling-32bit-debs-on-a-64bit-system/</link>
		<comments>http://blog.loftninjas.org/2010/12/23/wrangling-32bit-debs-on-a-64bit-system/#comments</comments>
		<pubDate>Fri, 24 Dec 2010 01:21:35 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=590</guid>
		<description><![CDATA[Typically directions for downloading a i386 version of a library for a x86_64 system link to a specific deb package and tell you to download it with wget. A new release of that package often breaks the link, so I wanted to document how to do this using apt. Unfortunately, it looks like apt won&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>Typically directions for downloading a i386 version of a library for a x86_64 system link to a specific deb package and tell you to download it with wget. A new release of that package often breaks the link, so I wanted to document how to do this using apt. Unfortunately, it looks like <a href="http://lists.debian.org/deity/2009/07/msg00064.html">apt won&#8217;t download</a> a single deb if it can&#8217;t resolve dependencies, but aptitude will, so we use them together.</p>
<p>I use a separate sources.list here just to speed up the process, as we need to correct apt when we&#8217;re finished.</p>
<pre class="brush: plain; title: ; wrap-lines: false; notranslate">
# Download 32bit list files from the mirror specified in /tmp/sources.list
apt-get -o=APT::Architecture=&quot;i386&quot; -o=Dir::Etc::sourcelist=&quot;/tmp/sources.list&quot; -o=Dir::Etc::sourceparts=&quot;/dev/null&quot; update
# Download the single library. Set libstdc++5 to whatever library you want
aptitude -o Apt::Architecture=i386 download libstdc++5
# Return apts lists to their preconfigured state
apt-get update
# Optionally, install the package
dpkg --force-architecture -i libstdc++5_1%3a3.3.6-20~lucid1_i386.deb
</pre>
<p>Note that if you install the package, it would overwrite the 64bit version of the library if it is installed. 32bit packages meant for 64bit systems, like the ia32-libs package, install to /lib32 and /usr/lib32 to avoid this. You could also extract the package with &#8216;dpkg -x libstdc++5_1%3a3.3.6-20~lucid1_i386.deb&#8217; and copy the libraries to where you like, then run &#8216;ldconfig&#8217;. The <a href="http://ubuntuforums.org/showthread.php?t=474790">getlibs</a> tool will try to repack debs more appropriately for you, if you like.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/12/23/wrangling-32bit-debs-on-a-64bit-system/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>libvirtError: monitor socket did not show up</title>
		<link>http://blog.loftninjas.org/2010/12/03/libvirterror-monitor-socket-did-not-show-up/</link>
		<comments>http://blog.loftninjas.org/2010/12/03/libvirterror-monitor-socket-did-not-show-up/#comments</comments>
		<pubDate>Sat, 04 Dec 2010 02:29:25 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=587</guid>
		<description><![CDATA[Sometimes errors don&#8217;t float to the top of stacks well. 
Our virtualization stack is pretty automated wherein we have a custom script that uses vmbuilder to create the guest, register it with libvirt, create first boot scripts that will have it register with a chef server, and start the VM. We saw this error today [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes errors don&#8217;t float to the top of stacks well. </p>
<p>Our virtualization stack is pretty automated wherein we have a custom script that uses vmbuilder to create the guest, register it with libvirt, create first boot scripts that will have it register with a chef server, and start the VM. We saw this error today <code>libvirtError: monitor socket did not show up.: Connection refused</code>, and I commented that my memory contained a lot of libvirt/kvm errors, and many resolutions, but the two don&#8217;t always stay connected. I checked the libvirt logs in /var/log/libvirt and even ran libvirt with <code>LIBVIRT_DEBUG=1 libvirtd -v</code>. When I tried running kvm by hand using the syntax in the logs, but with the -net options removed from the command line, kvm just spouted <code>Aborted</code>. After starting at it for a bit, I noticed that instead of <code>-m 1024</code> KVM was trying to run with <code>-m 1073741824</code> (1024^3). This was due to a small conversion bug in our custom script.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/12/03/libvirterror-monitor-socket-did-not-show-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Amazon EC2 Network Subnets</title>
		<link>http://blog.loftninjas.org/2010/12/01/amazon-ec2-network-subnets/</link>
		<comments>http://blog.loftninjas.org/2010/12/01/amazon-ec2-network-subnets/#comments</comments>
		<pubDate>Thu, 02 Dec 2010 01:10:56 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=578</guid>
		<description><![CDATA[For a project that exists both in Amazon Web Services EC2 US-EAST-1b and another cloud, I wanted to block network traffic between the two to ensure they didn&#8217;t affect each other. I started by doing an whois looking via ARIN for all of the IP addresses we are currently assigned in EC2, and I ultimately [...]]]></description>
			<content:encoded><![CDATA[<p>For a project that exists both in Amazon Web Services EC2 US-EAST-1b and another cloud, I wanted to block network traffic between the two to ensure they didn&#8217;t affect each other. I started by doing an whois looking via ARIN for all of the IP addresses we are currently assigned in EC2, and I ultimately got the same list that I found registered to the <a href="http://whois.arin.net/rest/org/AMAZO-4/nets">AMAZO-4</a> contact with ARIN, with the exception of AMAZON-AES, which I presume is for Amazon Enterprise Solutions. I couldn&#8217;t tell you offhand if the same IP blocks are used in other AWS zones.</p>
<table border="0" cellspacing="8">
<tbody>
<tr>
<th>Network</th>
<th>CIDR</th>
<th>Netmask</th>
<th>ARIN Name</th>
</tr>
<tr>
<td>72.44.32.0</td>
<td>/19</td>
<td>255.255.224.0</td>
<td>AMAZON-EC2-2</td>
</tr>
<tr>
<td>67.202.0.0</td>
<td>/18</td>
<td>255.255.192.0</td>
<td>AMAZON-EC2-3</td>
</tr>
<tr>
<td>75.101.128.0</td>
<td>/17</td>
<td>255.255.128.0</td>
<td>AMAZON-EC2-4</td>
</tr>
<tr>
<td>174.129.0.0</td>
<td>/16</td>
<td>255.255.0.0</td>
<td>AMAZON-EC2-5</td>
</tr>
<tr>
<td>204.236.128.0</td>
<td>/17</td>
<td>255.255.128.0</td>
<td>AMAZON-EC2-6</td>
</tr>
<tr>
<td>184.72.0.0</td>
<td>/15</td>
<td>255.254.0.0</td>
<td>AMAZON-EC2-7</td>
</tr>
<tr>
<td>50.16.0.0</td>
<td>/14</td>
<td>255.252.0.0</td>
<td>AMAZON-EC2-8</td>
</tr>
</tbody>
</table>
<p>Here are the IOS commands:</p>
<pre class="brush: plain; title: ; notranslate">
name 72.44.32.0 EC2-2 description AMAZON-EC2-2
name 67.202.0.0 EC2-3 description AMAZON-EC2-3
name 75.101.128.0 EC2-4 description AMAZON-EC2-4
name 174.129.0.0 EC2-5 description AMAZON-EC2-5
name 204.236.128.0 EC2-6 description AMAZON-EC2-6
name 184.72.0.0 EC2-7 description AMAZON-EC2-7
name 50.16.0.0 EC2-8 description AMAZON-EC2-8
object-group network ec2-us-east
   network-object 174.129.0.0 255.255.0.0
   network-object 184.72.0.0 255.254.0.0
   network-object 204.236.128.0 255.255.128.0
   network-object 50.16.0.0 255.252.0.0
   network-object 67.202.0.0 255.255.192.0
   network-object 72.44.32.0 255.255.224.0
   network-object 75.101.128.0 255.255.128.0
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/12/01/amazon-ec2-network-subnets/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Script hacks: waiting for the internet</title>
		<link>http://blog.loftninjas.org/2010/11/30/script-hacks-waiting-for-the-internet/</link>
		<comments>http://blog.loftninjas.org/2010/11/30/script-hacks-waiting-for-the-internet/#comments</comments>
		<pubDate>Wed, 01 Dec 2010 03:52:32 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=571</guid>
		<description><![CDATA[Now and then the VMs (kvm, libvirt + vmbuilder) I was cranking out would start up too fast, and the &#8220;first boot&#8221; script would run before the host got an IP address and had internet access. Since the first thing I was doing was downloading the Rubygems source using wget (to install chef), and since [...]]]></description>
			<content:encoded><![CDATA[<p>Now and then the VMs (kvm, libvirt + vmbuilder) I was cranking out would start up too fast, and the &#8220;first boot&#8221; script would run before the host got an IP address and had internet access. Since the first thing I was doing was downloading the Rubygems source using wget (to install chef), and since wget lacks a <a href="https://bugzilla.redhat.com/show_bug.cgi?id=202956#c6">retry for dns failure</a>, I hacked up this script to wait for the internet a bit.</p>
<pre class="brush: plain; title: ; notranslate">
#!/bin/bash

# Wait for internet to come up (DHCP)
MAXWAIT=60
WAITTIME=0
host production.cf.rubygems.org &gt; /dev/null

while [ $? == 1 ] &amp;&amp; [ $WAITTIME -le $MAXWAIT ] ; do
  WAITTIME=$(($WAITTIME + 10))
  sleep 10
  echo -n .
  host production.cf.rubygems.org &gt; /dev/null
done
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/11/30/script-hacks-waiting-for-the-internet/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>DNS-SD, a printer, and a little luck</title>
		<link>http://blog.loftninjas.org/2010/11/29/dns-sd-a-printer-and-a-little-luck/</link>
		<comments>http://blog.loftninjas.org/2010/11/29/dns-sd-a-printer-and-a-little-luck/#comments</comments>
		<pubDate>Tue, 30 Nov 2010 05:21:08 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=565</guid>
		<description><![CDATA[DNS SD, also known as Apple&#8217;s Bonjour, utilizes DNS as a configuration database for automatic service discovery. For the most part, it appears its used by devices more than people. The multicast implementation, or mDNS, is what makes printers automatically show up in OS X when you put them on your network. I recently moved [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS SD</a>, also known as Apple&#8217;s Bonjour, utilizes DNS as a configuration database for automatic service discovery. For the most part, it appears its used by devices more than people. The multicast implementation, or mDNS, is what makes printers automatically show up in OS X when you put them on your network. I recently moved such a printer from a flat network, to one where the wired and wireless workstations were on separate subnets. In an attempt to make the printer easy to find, I implemented DNS SD over unicast so OS X laptops in the office could detect the <a href="http://developer.apple.com/networking/bonjour/bonjourprinting.pdf">printer with Bonjour</a>.</p>
<p>First, I set the Domain Name to &#8220;office.opscode.com&#8221; using DHCP, so I would have a nice sandbox to mess around with DNS without breaking anything. Then I created a few DNS records:</p>
<pre class="brush: plain; title: ; wrap-lines: false; notranslate">
OfficejetPro8500.office.opscode.com A 172.28.0.5
lb._dns-sd._udp.office.opscode.com PTR office.opscode.com.
b._dns-sd._udp.office.opscode.com PTR office.opscode.com.
_printer._tcp.office.opscode.com PTR _OfficejetPro8500._pdl-datastream._tcp.office.opscode.com.
_pdl-datastream._tcp.office.opscode.com PTR _OfficejetPro8500._pdl-datastream._tcp.office.opscode.com.
_OfficejetPro8500._pdl-datastream._tcp.office.opscode.com SRV 0 0 9100 OfficejetPro8500.office.opscode.com.
_OfficejetPro8500._pdl-datastream._tcp.office.opscode.com TXT &quot;txtvers=1&quot; &quot;note=Office Entry&quot; &quot;usb_MFG=HP&quot; &quot;usb_MDL=Officejet Pro 8500 A909g&quot; &quot;ty=HP Officejet Pro 8500&quot;
</pre>
<ol>
<li>Specifies the internal IP address of the resource. We use this later in the SRV record.</li>
<li>What domain the client should browse if they haven&#8217;t specified one.</li>
<li>What domain a client in this domain should browse.</li>
<li>Define a LPR/LPD printer. LPR is the &#8220;Flagship&#8221; protocol and &#8220;must&#8221; be defined (Port 515)</li>
<li>Define a PDL printer, sometimes called raw (Port 9100)</li>
<li>Specify the printer service. The last four fields there are priority, weight, port and host, per <a href="http://www.ietf.org/rfc/rfc2782.txt">RFC 2782</a>.</li>
<li>Provide additional configuration information related to the printer</li>
</ol>
<p>There isn&#8217;t a lot of clear information regarding how you should specify multiple key/value pairs in the TXT field. <a href="http://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a> specifies, <em>&lt;character-string&gt; is a single length octet followed by that number of characters.  &lt;character-string&gt; is treated as binary information, and can be up to 256 characters in length (including the length octet)</em>. For Microsoft DNS, check out <a href="http://www.grouplogic.com/Knowledge/PDFUpload/Info/WanBonjour_1.pdf">this article</a>. I was using DynInc&#8217;s Dynect, and was able to put all the key/value pairs in double quotes in the single input field. Also, if you are too, use the &#8220;Expert Editor&#8221; which is a menu option under the &#8220;Simple Editor,&#8221; it is a little easier to specify the multi-part hostnames this way. It <a href="http://lists.apple.com/archives/macos-x-server/2007/Mar/msg00781.html">sounds like in bind</a> you put one key/value pair in double quotes per line, with the series wrapped in parenthesis.</p>
<p>Dynect wouldn&#8217;t let me specify the SRV record without a preceding underscore, which is a shame, because this is what OS X detects as the device name which also lower-cased it, making it a little difficult to read. You should be able to spaces in these names, but I wasn&#8217;t about to try escaping that. The key/value pairs in the TXT resource record are documented in the <a href="http://developer.apple.com/networking/bonjour/bonjourprinting.pdf">Apple Bonjour Printing specification</a>.</p>
<ul>
<li>txtvers / Define what version of this format we are using</li>
<li>note / User-readable information about the device, OS X displays this as Location</li>
<li>usb_MFG / the Manufacturer name that the USB driver would specify. I made educated guesses at these.</li>
<li>usb_MDL / the Model that the USB device would specify. Combined with the last field this will choose the driver for the user.</li>
<li>ty / a User-readable name for the device. I had hoped this would be used in the Printer Name field in the GUI, but it wasn&#8217;t.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/11/29/dns-sd-a-printer-and-a-little-luck/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>virt-manager keymaps on OS X</title>
		<link>http://blog.loftninjas.org/2010/11/17/virt-manager-keymaps-on-os-x/</link>
		<comments>http://blog.loftninjas.org/2010/11/17/virt-manager-keymaps-on-os-x/#comments</comments>
		<pubDate>Thu, 18 Nov 2010 03:12:26 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=556</guid>
		<description><![CDATA[I&#8217;m not crazy about the lack of a definitive package manager for OS X. I tried for about a day to work with Open Source on OS X, then I built an Ubuntu VM. I&#8217;ve been using ssh with X forwarding when I need a graphical interface; OS X has reasonable good built in support [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m not crazy about the lack of a definitive package manager for OS X. I tried for about a day to work with Open Source on OS X, then I built an Ubuntu VM. I&#8217;ve been using ssh with X forwarding when I need a graphical interface; OS X has reasonable good built in support for X11. However, others have found that the <a href="http://www.arnebrodowski.de/blog/keymap-problems-with-virt-manager.html">keymap</a> and <a href="http://shortbus.org/bloggin/2009/06/01/apples-x11-keymap-and-virt-manager/">meta keys</a> are broken. While I got a kick out of &#8220;After some time I discovered that the number 8 is interpreted as Return,&#8221; I did need to log in to a guest to do some debugging.</p>
<p>The <a href="http://shortbus.org/bloggin/2009/06/01/apples-x11-keymap-and-virt-manager/">accepted solution</a> to making Ctrl+Alt release keyboard focus correctly in the vncviewer spawned by virt-manager is to create a .Xmodmap file in your home directory with this content:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
clear Mod1
keycode 66 = Alt_L
keycode 69 = Alt_R
add Mod1 = Alt_L
add Mod1 = Alt_R
</pre>
<p>I killed the X server by focusing on it and choosing quit, and it seemed to be read the .Xmodmap file okay without my needing to restart the entire system.</p>
<p>The workaround for the <a href="http://www.arnebrodowski.de/blog/keymap-problems-with-virt-manager.html">broken keymap</a> pointed me in the right direction, but I wasn&#8217;t happy with the solution. A little digging around the <a href="http://libvirt.org/formatdomain.html">libvirt domain xml</a> reference pointed out that you can add a keymap as an attribute to the vnc element in the domain xml definition. Use &#8216;virsh edit&#8217; to edit the domain XML and modify the vnc line to add this attribute so it looks like so:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
&lt;graphics type='vnc' port='5900' autoport='yes' listen='127.0.0.1' keymap='en-us'/&gt;
</pre>
<p>I destroyed the guest and restarted it and the keyboard worked now without any &#8220;8 is now enter&#8221; trickery. I&#8217;m pretty sure you can <a href="http://www.mail-archive.com/libvir-list@redhat.com/msg13340.html">choose any keymap</a> from <code>/usr/share/qemu/keymaps</code>. If you use vmbuilder you will want to add this to <code>/etc/vmbuilder/libvirt/libvirtxml.tmpl</code> as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/11/17/virt-manager-keymaps-on-os-x/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Motorola Backflip charging</title>
		<link>http://blog.loftninjas.org/2010/10/23/motorola-backflip-charging/</link>
		<comments>http://blog.loftninjas.org/2010/10/23/motorola-backflip-charging/#comments</comments>
		<pubDate>Sat, 23 Oct 2010 21:22:43 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=552</guid>
		<description><![CDATA[Nightmare.
Chargers:
AC1) Motorola DC4050US0301 5.1V DC 850MA
AC2) AT&#038;T 03577 5.0V 1000ma
DC1) AT&#038;T USB VPC03578
DC2) AT&#038;T USB + MiniUSB MV302927
Cables:
M1) Motorola SKN6378A
M2) &#8220;Motorola&#8221; SKN6238A
M3) Monoprice generic microusb
Dead Phone, AC1, M1 OR M2 OR M3
Green light on Phone, OS starts, displays charging battery
Dead Phone, AC2, M1 OR M2 OR M3
Blue light on AC2, Green light on Phone, OS [...]]]></description>
			<content:encoded><![CDATA[<p>Nightmare.</p>
<p>Chargers:</p>
<p>AC1) Motorola DC4050US0301 5.1V DC 850MA<br />
AC2) AT&#038;T 03577 5.0V 1000ma<br />
DC1) AT&#038;T USB VPC03578<br />
DC2) AT&#038;T USB + MiniUSB MV302927</p>
<p>Cables:</p>
<p>M1) Motorola SKN6378A<br />
M2) &#8220;Motorola&#8221; SKN6238A<br />
M3) Monoprice generic microusb</p>
<p>Dead Phone, AC1, M1 OR M2 OR M3<br />
Green light on Phone, OS starts, displays charging battery</p>
<p>Dead Phone, AC2, M1 OR M2 OR M3<br />
Blue light on AC2, Green light on Phone, OS starts<br />
Green light / OS cycle every 15 seconds</p>
<p>Dead Phone, DC1, M1 OR M2 OR M3<br />
White light on DC1, Green light on Phone, OS starts<br />
Green light / OS cycle every 15 seconds</p>
<p>Dead Phone, DC2, M1 OR M2 OR M3<br />
White light on DC2, Green light alternates on/off on Phone</p>
<p>Phone on, AC1, M1 OR M2 OR M3<br />
Green light on, charge symbol in battery on display</p>
<p>Phone on, AC2, M1<br />
Blue light on AC2 for five seconds</p>
<p>Phone on, AC2, M2 OR M3<br />
Blue light on AC2<br />
Green light on, no charge symbol in battery on display</p>
<p>I have an AT&#038;T AC charger at work that I believe works as well as the stock Motorola. The AT&#038;T AC charger here at home, listed above, is a &#8220;five star&#8221; model that consumes 0W when not charging, I assume that is what the blue light turning off indicates. Hopefully the combinations that keep the green light on the phone on are charging, just very slowly, and are still somewhat useful. More to come.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/10/23/motorola-backflip-charging/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Munin Aggregation with Multigraph</title>
		<link>http://blog.loftninjas.org/2010/10/22/munin-aggregation-with-multigraph/</link>
		<comments>http://blog.loftninjas.org/2010/10/22/munin-aggregation-with-multigraph/#comments</comments>
		<pubDate>Sat, 23 Oct 2010 00:33:50 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=535</guid>
		<description><![CDATA[Six months ago I made note to the pattern for referring to stacked graph data sources in munin:

load.double.stack one=localhost.localdomain:load.load two=localhost.localdomain:load.load
This syntax evaluates to:
graph.value.stack line=host.domain:plugin.value
I&#8217;ve been using multigraph more since then, which is a boon to performance, but it complicates stacked graphs a little. This hurts because it remains very difficult to tell why your graphs [...]]]></description>
			<content:encoded><![CDATA[<p>Six months ago I made note to the pattern for referring to <a href="http://blog.loftninjas.org/2010/04/08/an-evening-with-munin-graph-aggregation/">stacked graph data sources in munin</a>:</p>
<blockquote>
<pre>load.double.stack one=localhost.localdomain:load.load two=localhost.localdomain:load.load</pre>
<p>This syntax evaluates to:<br />
graph.value.stack line=host.domain:plugin.value</p></blockquote>
<p>I&#8217;ve been using multigraph more since then, which is a boon to performance, but it complicates stacked graphs a little. This hurts because it remains very difficult to tell why your graphs are not drawing when you incorrectly reference a data source. To debug, as the munin user (use &#8217;su -l munin&#8217;, &#8217;sudo -s -u munin&#8217; or &#8216;chpst -u munin&#8217;) run:<br />
<code>/usr/share/munin/munin-graph --service 'load.double.stack' --debug</code><br />
Be sure to replace &#8220;load.double.stack&#8221; with the name of the graph you&#8217;re trying to draw.</p>
<p>The <a href="http://munin-monitoring.org/wiki/aggregate_examples">munin wiki example for stacked graphs</a> explains data source names as:</p>
<pre class="brush: plain; title: ; notranslate">
snmp_ups_current.inputtotal.sum \
---------------- ---------- ---
        |             |      |
        |             |      `-- The sum mechanism
        |             `--------- One of this virtual plugin's values
        `----------------------- The name of the virtual plugin

ups-5a:snmp_ups_ups-5a_current.inputcurrent \
ups-5b:snmp_ups_ups-5b_current.inputcurrent
------ ----------------------- ------------
   |               |                 |
   |               |                 `------ The &quot;inputcurrent&quot; value from the real plugin
   |               `------------------------ The real plugin's name (symlink)
   `---------------------------------------- The host name from which to seek information
</pre>
<p>However, with multigraph the name of the plugins symlink isn&#8217;t necessarily the name of the graph. The trick I found was to connect the the munin node and call the multigraph plugin, looking for the &#8216;multigraph&#8217; line.</p>
<pre class="brush: plain; title: ; notranslate">
$ nc localhost 4949
# munin node at server.example.org
cap multigraph # tell munin-node that you are multigraph aware
cap multigraph
fetch diskstats # fetch the diskstats multigraph plugin
multigraph diskstats_latency
sdb_avgwait.value 0
multigraph diskstats_latency.sdb
avgwait.value 0
.
</pre>
<p>I&#8217;ve removed a significant portion of the returned data here. Pay attention to the fact that this plugin returned a &#8220;diskstats_latency&#8221; graph that contains data for all of the disks, as well as individual graphs for each disk, here &#8220;diskstats_latency.sdb&#8221; In this example your stacked graph configuration would be:</p>
<pre class="brush: plain; title: ; notranslate">
disk.double.stack \
  one=localhost.localdomain:diskstats_latency.sdb.avgwait \
  two=localhost.localdomain:diskstats_latency.sdb.avgwait
  -1- ----------2---------- -----------3--------- ---4---
</pre>
<p>(1) The alias and label for this host or data point<br />
(2) The configured node name of the host<br />
(3) The original graphs name, either the plugin or multigraph name<br />
(4) The value from the plugin/graph</p>
<p>Notice that while the period is used to separate the value from the rest of the field, there may be periods in the rest of the field. Also keep in mind that in the past I have seen dashes in configured graph names become underscores at the end of the day.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/10/22/munin-aggregation-with-multigraph/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Community Cooking</title>
		<link>http://blog.loftninjas.org/2010/10/20/community-cooking/</link>
		<comments>http://blog.loftninjas.org/2010/10/20/community-cooking/#comments</comments>
		<pubDate>Wed, 20 Oct 2010 22:09:11 +0000</pubDate>
		<dc:creator>btm</dc:creator>
				<category><![CDATA[chef]]></category>

		<guid isPermaLink="false">http://blog.loftninjas.org/?p=520</guid>
		<description><![CDATA[It&#8217;s been a year since the Opscode Cookbook site was launched and a recent project got me thinking about what parts of my hopes that I wrote about then have taken effect so far. I recently heard that a major Chef user has switched to Ubuntu from another Linux distribution because that is what most [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been a year since the <a href="http://www.opscode.com/blog/2009/10/29/the-new-opscode-cookbook-site-/">Opscode Cookbook site was launched</a> and a recent project got me thinking about what parts of <a href="http://blog.loftninjas.org/2009/10/30/opscode-cookbooks-community-announced/">my hopes that I wrote about</a> then have taken effect so far. I recently heard that a major Chef user has switched to Ubuntu from another Linux distribution because that is what most of the cookbooks that Opscode maintains are written for and tested on. Choice of distribution is typically something that is very dear to administrators and somewhere in the world there is a flame war on this topic every second. Consequently, this is huge and I&#8217;ve been thinking about it for a while. </p>
<p>It is one thing for a company to choose a distribution based on a software package that is significant to them; in the past I have had to battle against stakeholders that wanted to choose a particular distribution solely on the availability of support. Chef runs on a lot of platforms, but of course some get much more attention than others because that is where the community is. But here we&#8217;re seeing a company choose their distribution not because of Chef&#8217;s support for it, but for the community support for Cookbooks that run on it. This is clear evidence that what I wrote about is starting to happen.</p>
<p>I&#8217;ve been working on a Chef + Cobbler writeup in my spare time. I went out the other day and bought a consumer desktop to use as a libvirt/kvm host for this project. It often tends to be least painful to use cloud resources, but sometimes that which is taken care of for you is too unknown, too much of a &#8220;black box,&#8221; and you need the deep visibility into your infrastructure that building it yourself provides. There is indication that some have gotten Cobbler <a href="http://www.ubuntugeek.com/ubuntu-linux-provisioning-automation-with-cobbler.html">running on</a> and <a href="http://terrarum.net/administration/deploying-ubuntu-with-cobbler.html">deploying Ubuntu</a>, but it doesn&#8217;t appear to have taken hold. There&#8217;s a <a href="https://wiki.ubuntu.com/CobblerSpec">launchpad spec</a> claiming there is a package, but I couldn&#8217;t find it. <a href="https://blueprints.launchpad.net/ubuntu/+spec/server-lucid-cobbler">Another spec</a> makes it clear that files for debian packaging from upstream are not finished. It is here that I first ran into problems. I couldn&#8217;t get the init.d scripts provided in the debian/ directory of the upstream repository to work. They clearly needed some help, and after spending some time on them it became clear that they&#8217;re untested templates created by debhelper.</p>
<p>My goal wasn&#8217;t to fix these init scripts, I just wanted to get the cobbler server running. Then I remembered that we had a great existing runit cookbook that I was familiar with. The API for the cookbook site has progressed since release. Unfortunately the <a href="http://help.opscode.com/faqs/cookbooksite/cookbook-site-rest-api">documentation for the cookbook API</a> is a little behind, but the <a href="http://help.opscode.com/faqs/cookbooksite/downloading-a-cookbook-from-the-opscode-cookbook-site">new functionality</a> has been built into knife, the command line tool that interacts with the Chef server or Opscode platform, as well as multiple cloud providers. From within my personal <a href="http://wiki.opscode.com/display/chef/Chef+Repository">chef-repo</a>, I ran:</p>
<p><code>knife cookbook site vendor runit</code></p>
<p>This downloaded the runit cookbook from the Cookbooks site into a branch in my chef-repo git repository, then merged it into my current branch, allowing me to track changes between my local copy and the upstream copy using git. Then I added a couple templates and a call to the runit_service definition to my cookbook:</p>
<p>templates/default/sv-cobbler-run.erb:</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin
exec 2&gt;&amp;1
exec /usr/bin/env cobblerd -F
</pre>
<p>templates/default/sv-cobbler-log-run.erb:</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
exec svlogd -tt ./main
</pre>
<p>recipes/default.rb:</p>
<pre class="brush: ruby; title: ; notranslate">
# SNIP
runit_service &quot;cobbler&quot;
</pre>
<p>And then cobblerd was running under runit. There&#8217;s beauty in being able to take something somewhat complex like runit, and make it easy. So easy, that I used it rather than fixing up an init script.</p>
<p>Then I found that cobbler wanted to called through apache as proxy. No problem though, I vendored the apache2 cookbook as well. I spent a few minutes determining that I needed a couple of Apache modules, as the Cobbler instructions are pretty centric to Redhat and I got the impression that they make assumptions about what that gives you. Then I used the apache2 cookbook to proxy cobbler by adding this to the top of my recipe:</p>
<p>recipes/default.rb:</p>
<pre class="brush: ruby; title: ; notranslate">
include_recipe &quot;apache2&quot;
include_recipe &quot;apache2::mod_proxy_http&quot;
include_recipe &quot;apache2::mod_python&quot;
</pre>
<p>I had some permission problems with mod_proxy, again likely a difference between Redhat and Ubuntu, but it wasn&#8217;t out of my way to ship the apache config provided by upstream using Chef with a small modification:</p>
<pre class="brush: plain; title: ; notranslate">
&lt;Proxy http://localhost:25151&gt;
    AddDefaultCharset off
    Order deny,allow
    Allow from all
&lt;/Proxy&gt;
</pre>
<p>I&#8217;ll write about the Cobbler cookbook more later. You can, of course, <a href="http://cookbooks.opscode.com/cookbooks/cobbler">follow it on the Cookbook site</a> in the interim. I want to emphasize how I used a single command to grab existing code and leverage it to make it easier for me to do what I was trying to do: get Cobbler running. The Cookbook site combined with the <a href="http://blog.opscode.com/blog/2010/10/20/chef-reaches-200-contributors/">awesome Chef community</a> made this possible. If you haven&#8217;t used the &#8220;cookbook site&#8221; subcommands in knife yet, take a moment to try them out.</p>
<pre class="brush: plain; title: ; notranslate">
$ knife cookbook site --help
Available cookbook site subcommands: (for details, knife SUB-COMMAND --help)

** COOKBOOK SITE COMMANDS **
knife cookbook site vendor COOKBOOK [VERSION] (options)
knife cookbook site show COOKBOOK [VERSION] (options)
knife cookbook site share COOKBOOK CATEGORY (options)
knife cookbook site search QUERY (options)
knife cookbook site list (options)
knife cookbook site download COOKBOOK [VERSION] (options)
knife cookbook site unshare COOKBOOK
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.loftninjas.org/2010/10/20/community-cooking/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

