<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>spaceblog</title><link>https://spacepants.org/blog/</link><description>Recent content in spaceblog on spacepants.org</description><generator>Hugo -- gohugo.io</generator><language>en-AU</language><copyright>All content Copyright &#169; 2002-2024 Jamie Wilkinson.
Entries in this blog are licensed under the Creative Commons Attribution-Sharealike v2 License.</copyright><lastBuildDate>Wed, 24 Nov 2021 16:52:00 +1100</lastBuildDate><atom:link href="https://spacepants.org/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Monitoring Cron Jobs</title><link>https://spacepants.org/blog/cron-monitoring/</link><pubDate>Wed, 24 Feb 2010 22:27:00 +1000</pubDate><guid>https://spacepants.org/blog/cron-monitoring/</guid><description>
&lt;p>(This article comes from one I helped edit and publish inside work, so I can&amp;rsquo;t take any credit for the ideas expressed within, though I do vehemently and violently subscribe to the sentiment! Thanks to Alan Sundell for originally educating me.)&lt;/p>
&lt;p>When you set (or don&amp;rsquo;t set at all) &lt;code>MAILTO&lt;/code> in a crontab fragment, typically it&amp;rsquo;s because you want to be notified if your job fails &amp;ndash; failure in this case if and only if the job only prints to stdout/stderr if there is an exceptional condition&amp;hellip; However not all jobs print only on exceptional conditions, many use stderr for &lt;em>logging&lt;/em>, and email is just not a great solution to this problem, especially at scale.&lt;/p>
&lt;h3 id="problems-with-alerting-by-email-from-cron">Problems with alerting by email from &lt;code>cron&lt;/code>.&lt;/h3>
&lt;p>Why is it a bad idea to rely on cron mail?&lt;/p>
&lt;ul>
&lt;li>We all get so much mail from cron that few are in the habit of reading it anymore.&lt;/li>
&lt;li>If your server is broken, the mail submission agent may be broken too.&lt;/li>
&lt;li>You may handle your cron mail, but you may be on holiday when it arrives.&lt;/li>
&lt;li>&lt;code>crond&lt;/code> can crash.&lt;/li>
&lt;li>You will get mutiple emails for the same failure.&lt;/li>
&lt;li>Your cron mail will get delivered to every one of your mailboxes, eating up storage.&lt;/li>
&lt;li>You cannot suppress cron mail notifications.&lt;/li>
&lt;li>Your cron mail has no concept of dependencies.&lt;/li>
&lt;li>You will get notified of temporary failures when you only care about persistent ones.&lt;/li>
&lt;/ul>
&lt;p>If a cronjob running successfully is critical to operation, then it seems that what you really need is some kind of monitoring system that addresses all of these things, and can send alerts to some oncall rotation that determines who is responsible for handling alerts.&lt;/p>
&lt;h3 id="a-potential-solution">A potential solution&lt;/h3>
&lt;p>Here&amp;rsquo;s an idea that might help with that.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Direct the output of your job to some log file for debugging, in the event of persistent failure. Note the truncate:&lt;/p>
&lt;pre>&lt;code>MAILTO=&amp;quot;&amp;quot;
*/1 * * * * root cronjob &amp;gt; /var/log/cronjob.log 2&amp;gt;&amp;amp;1
&lt;/code>&lt;/pre>
&lt;p>(If you decide to append, not overwrite the log each execution, then make sure you logrotate that file.)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>At the end of &lt;code>cronjob&lt;/code>, update a status file, like so:&lt;/p>
&lt;pre>&lt;code>scriptname=$(basename $0)
date +%s &amp;gt; /var/tmp/cronjob-last-success-timestamp
&lt;/code>&lt;/pre>
&lt;p>Ensure that your job exits on error before reaching the last line!&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Collect the content of that file regularly with your monitoring system; scrape it with the nagios host agent, pump it into collectd, whatever you hip open source cats are using these days.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Configure your monitoring system to send a notification on the timestamp having not been updated in some time period.&lt;/p>
&lt;pre>&lt;code>if cronjob-last-success-timestamp
&amp;lt;
(time() - 30m)
then alert
&lt;/code>&lt;/pre>
&lt;/li>
&lt;li>
&lt;p>Profit!&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Now you only generate an alert if the cron job hasn&amp;rsquo;t succeeded in the last 30 minutes (a threshold you can adjust to match your monitoring scrape intervals and service SLAs), and with a sufficiently mature monitoring system you can now express dependencies, suppress the notification, and send it to an oncall rotation, and so on!&lt;/p>
&lt;p>Most significantly, we have converted a system that always reported failure, into a system that is based around checking for success &amp;ndash; a failsafe.&lt;/p></description></item><item><title>I, for one, welcome our new enhancement proposals</title><link>https://spacepants.org/blog/welcome-deps/</link><pubDate>Mon, 04 Feb 2008 13:08:00 +1000</pubDate><guid>https://spacepants.org/blog/welcome-deps/</guid><description>
&lt;p>I just read with excitement &lt;a href="http://lists.debian.org/debian-project/2008/01/msg00045.html">the announcement of Debian Enhancement Proposals&lt;/a>, something that I too have been contemplating in recent months (but due to my ghastly lack of commitment to the Debian community doubted my ability to drive it).&lt;/p>
&lt;p>I work in company driven by engineering documents and designs, I like RFCs, and I like what Python has done with its PEPs. Debian&amp;rsquo;s adoption of this useful tool can only improve the community and the distribution.&lt;/p>
&lt;p>&lt;a href="http://dep.debian.net/">Yay!&lt;/a>&lt;/p></description></item><item><title>nsscache open source launch</title><link>https://spacepants.org/blog/nsscache-launch/</link><pubDate>Mon, 05 Nov 2007 21:54:00 +1000</pubDate><guid>https://spacepants.org/blog/nsscache-launch/</guid><description>
&lt;p>Today we open sourced the project I&amp;rsquo;ve been working on for the last 9
months, &lt;a href="https://github.com/google/nsscache">nsscache&lt;/a>.&lt;/p>
&lt;p>It&amp;rsquo;s a glorified version of:&lt;/p>
&lt;pre>&lt;code>ldapsearch | awk &amp;gt; /etc/passwd
&lt;/code>&lt;/pre>
&lt;p>in that we in theory support more than just LDAP as a data source, and
offer two types of database storage (nss_db using Berkeley DB, and
plain text files).&lt;/p>
&lt;p>If you&amp;rsquo;re having issues with your nss_ldap setup, then try it out :)&lt;/p></description></item><item><title>linux.conf.au 2007 programme choices</title><link>https://spacepants.org/blog/lca2007-programme-choices/</link><pubDate>Fri, 12 Jan 2007 01:39:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-programme-choices/</guid><description>
&lt;p>As the clock ticks approach single digit figures, the organising team
are ramping up. Everything&amp;rsquo;s coming along smoothly in time for a
kick-arse start on Monday.&lt;/p>
&lt;p>One thing that makes me sad is that I&amp;rsquo;ll likely not be able to watch a
lot of the talks &amp;ndash; but if I do get a chance, I&amp;rsquo;d see these:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="http://lca2007.linux.org.au/talk/109">clustering tdb&lt;/a> by Andrew
Tridgell. When I first saw this proposal came in, I knew it was a
good one. Someone should make an LDAP server that doesn&amp;rsquo;t suck,
and use tdb to solve the multimaster replication problem in the
storage layer. Oh wait &amp;ndash; that&amp;rsquo;s what he&amp;rsquo;s doing already.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="http://lca2007.linux.org.au/talk/84">Puppet&lt;/a> by Luke Kanies.
Luke&amp;rsquo;s been working on this awesome next-generation systems
configuration management tool for a few years now. He approached
me, back in the day, to be a beta-tester &amp;ndash; he and I were both
hitting scaling problems with &lt;code>cfengine&lt;/code>. I hope every sysadmin
makes it to this talk!&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>There&amp;rsquo;s a few others to note: Theodore T&amp;rsquo;so always has an interesting
talk; this year he&amp;rsquo;s giving two cool subjects a run. The tutorial on
heartbeat 2 by Alan Robertson is sure to be full of good loadbalancing
fu.&lt;/p>
&lt;p>There&amp;rsquo;s lots of exciting things going on in the programme, so I hope
to see you all next week!&lt;/p></description></item><item><title>we're #1!</title><link>https://spacepants.org/blog/lca2007-best-linux-conference/</link><pubDate>Tue, 14 Nov 2006 19:31:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-best-linux-conference/</guid><description>
&lt;p>Who&amp;rsquo;s the &lt;a href="http://www.google.com/search?q=best+linux+conference">best linux
conference&lt;/a> in
the world? &lt;a href="http://lca2007.linux.org.au">WE ARE, BABY&lt;/a>!&lt;/p>
&lt;p>Turns out we&amp;rsquo;re only &lt;a href="http://www.google.com/search?q=linux+conference">second
hit&lt;/a> for &amp;ldquo;linux
conference&amp;rdquo;, but we&amp;rsquo;re still
&lt;a href="http://pleasesendustolinuxconfau.info/">best&lt;/a>!&lt;/p></description></item><item><title>linux.conf.au boned, fixed again</title><link>https://spacepants.org/blog/lca2007-boned-rego-but-fixed-now/</link><pubDate>Tue, 14 Nov 2006 18:54:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-boned-rego-but-fixed-now/</guid><description>
&lt;p>Yeah, I &lt;a href="http://justblamepia.com">broke the registration process&lt;/a> when I rolled out some bugfixes
last night, but I&amp;rsquo;ve fixed them now! Continue in your merry scramble
for tickets to the ROCKIN&amp;rsquo;EST FREE AND OPEN SORES GIG IN THE SOUTH.&lt;/p>
&lt;p>(In unrelated news, can you think of a good reason why &lt;a href="http://pleasedontsendthemtolinuxconfau.info/">they shouldn&amp;rsquo;t
go to lca2007&lt;/a>?&lt;/p></description></item><item><title>lca2007 registrations now open!</title><link>https://spacepants.org/blog/lca2007-registrations-open/</link><pubDate>Wed, 01 Nov 2006 20:34:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-registrations-open/</guid><description>
&lt;p>&lt;a href="http://lca2007.linux.org.au/Registration">Registrations&lt;/a> to LCA 2007
are now open! Get in quick!&lt;/p></description></item><item><title>linux.conf.au 2007 programme first draft</title><link>https://spacepants.org/blog/lca2007-programme-first-draft/</link><pubDate>Wed, 18 Oct 2006 07:41:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-programme-first-draft/</guid><description>
&lt;p>Last night the Seven met, as they do, around a dark table, deep below
the city in a room built by the Templar Knights; surrounded, as
normal, by ancient iconography of power and knowledge. Their goal: a
draft programme for &lt;a href="http://lca2007.linux.org.au">LCA 2007&lt;/a>.&lt;/p>
&lt;p>We ranked the streams in order of popularity; nothing too scientific,
based purely on presenter name and subject matter, what would likely
draw the biggest crowds?&lt;/p>
&lt;p>Then with that ordering, we&amp;rsquo;d go through the streams, picking off the
top of all lists, and putting them in the biggest room. Repeat for
the next one in the next talk slot, offsetting them so that no two
venues would carry the same stream at the same time.&lt;/p>
&lt;p>&lt;img src="http://static.flickr.com/103/272182553_2338c6c2f5_o.jpg" alt="halfway through the first programme draft">&lt;/p>
&lt;p>By the end of it, we had a nice patchwork quilt.&lt;/p>
&lt;p>&lt;img src="http://static.flickr.com/86/272182569_5fef4b1833_o.jpg" alt="the finished draft">&lt;/p>
&lt;p>I don&amp;rsquo;t think we&amp;rsquo;re done with it, &lt;a href="http://justblamepia.com">yet&lt;/a>, but it&amp;rsquo;s a good start!&lt;/p></description></item><item><title>linux.conf.au 2007 proposal count through the roof!</title><link>https://spacepants.org/blog/lca2007-cfp-updates-2/</link><pubDate>Sun, 17 Sep 2006 09:59:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-cfp-updates-2/</guid><description>
&lt;p>&lt;img src="https://spacepants.org/images/lca2007.png" alt="lca 2007 logo">&lt;/p>
&lt;p>The response to the &lt;a href="http://lca2007.linux.org.au/cfp">CFP&lt;/a> has been
massive! Right now our reviewers are starting to read the torrent of
submissions&amp;hellip;&lt;/p>
&lt;ul>
&lt;li>212 proposals for presentations/seminars&lt;/li>
&lt;li>31 proposals for tutorials&lt;/li>
&lt;li>17 proposals for miniconfs&lt;/li>
&lt;/ul>
&lt;p>Holy shit!&lt;/p>
&lt;p>Reading through some of the titles and abstracts, theres a &lt;em>lot&lt;/em> of
really awesome stuff that people have been working on, I&amp;rsquo;m really
excited about what the programme is going to look like!&lt;/p>
&lt;p>Unfortunately, we&amp;rsquo;re going to have to reject a lot of these :( On the
upside, it does mean that the stuff that does make it through is going
to be so mindblowingly awesome that you can&amp;rsquo;t afford not to be at
linux.conf.au 2007!&lt;/p>
&lt;p>Rock!&lt;/p></description></item><item><title>woo, new command</title><link>https://spacepants.org/blog/woo-new-command/</link><pubDate>Thu, 14 Sep 2006 10:51:00 +1000</pubDate><guid>https://spacepants.org/blog/woo-new-command/</guid><description>
&lt;p>Shoulder-surfing &lt;a href="http://inodes.org/blog">johnf&lt;/a> the other night at the &lt;a href="http://lca2007.linux.org.au">seven&lt;/a> meeting, I saw him use&lt;/p>
&lt;pre>&lt;code>cd -
&lt;/code>&lt;/pre>
&lt;p>to return to a previous directory&amp;hellip;&lt;/p>
&lt;p>I&amp;rsquo;d been using &lt;code>pushd&lt;/code> and &lt;code>popd&lt;/code> when I remembered; tried out &lt;code>cd -&lt;/code> just now and bam! productivity increased 300%! Now I won&amp;rsquo;t be opening new terminals just to keep a shell in past directories&amp;hellip;&lt;/p></description></item><item><title>wrapping CGI applications in WSGI</title><link>https://spacepants.org/blog/wrapping-cgi-apps-in-wsgi/</link><pubDate>Tue, 12 Sep 2006 12:30:00 +1000</pubDate><guid>https://spacepants.org/blog/wrapping-cgi-apps-in-wsgi/</guid><description>
&lt;p>We&amp;rsquo;ve got a large &amp;ldquo;legacy&amp;rdquo; body of code that is used by our staff to
track most of our business, it&amp;rsquo;s a whole lot of Python CGI that uses
some custom HTML and DB frameworky code; it&amp;rsquo;s pretty ugly and having
become a convert to the cult of &lt;a href="http://pylonshq.com">Pylons&lt;/a>,
&lt;a href="http://wsgi.org">WSGI&lt;/a>, and &lt;a href="http://sqlalchemy.org">SQLAlchemy&lt;/a>, I
really want to replace it.&lt;/p>
&lt;p>Of course, anyone knows that one of the &lt;a href="http://joelonsoftware.com/articles/fog0000000069.html">Things You Should Never
Do&lt;/a> is rewrite
from scratch. Even in the same language.&lt;/p>
&lt;p>It would be much easier to integrate the old app into a new Pylons
app, have them running side-by-side, and slowly deprecate the old one
as new interfaces are written. (This is still not a perfect idea, as
demonstrated by the 4 year old TCL code that the current app was meant to replace still
running in production ;-) As bugs in the old code are found, we can
either &lt;a href="http://angryflower.com/pathof.gif">beat our heads against brick
walls&lt;/a> or replace just that
functionality with a sane data model, similar looking templates, and
shiny new controller smarts, and no-one would be the wiser, except of
course that for some reason the developers are no longer constantly
grumpy and the webapp is running smoother and faster than before, and
crashing less often&amp;hellip;&lt;/p>
&lt;p>It occurred to me yesterday the best way to get a legacy CGI app to run
along with Pylons is to convert it to a WSGI application, and just
mash it in at the bottom of the application stack, where Pylons would
normally go when it 404s.&lt;/p>
&lt;p>Here&amp;rsquo;s the result of some free time and caffeinated excitement this
morning:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">imp&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">sys&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">StringIO&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">application&lt;/span>(environ, start_response):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># trap the exit handler, we don&amp;#39;t want scripts exiting randomly&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># we might want to do something with the return code later&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> retcode &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">None&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">exit_handler&lt;/span>(rc):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> retcode &lt;span style="color:#666">=&lt;/span> rc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sys&lt;span style="color:#666">.&lt;/span>exit &lt;span style="color:#666">=&lt;/span> exit_handler
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># trap the output buffer&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> outbuf &lt;span style="color:#666">=&lt;/span> StringIO&lt;span style="color:#666">.&lt;/span>StringIO()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sys&lt;span style="color:#666">.&lt;/span>stdout &lt;span style="color:#666">=&lt;/span> outbuf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># catch stderr output in the parent&amp;#39;s error stream&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sys&lt;span style="color:#666">.&lt;/span>stderr &lt;span style="color:#666">=&lt;/span> environ[&lt;span style="color:#4070a0">&amp;#39;wsgi.errors&amp;#39;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># import the script&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> script &lt;span style="color:#666">=&lt;/span> environ[&lt;span style="color:#4070a0">&amp;#39;PATH_TRANSLATED&amp;#39;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">open&lt;/span>(script, &lt;span style="color:#4070a0">&amp;#34;rb&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> imp&lt;span style="color:#666">.&lt;/span>load_module(&lt;span style="color:#4070a0">&amp;#39;__main__&amp;#39;&lt;/span>, f, script, (&lt;span style="color:#4070a0">&amp;#34;py&amp;#34;&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;rb&amp;#34;&lt;/span>, imp&lt;span style="color:#666">.&lt;/span>PY_SOURCE))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f&lt;span style="color:#666">.&lt;/span>close()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># outbuf has a typical CGI response, headers separated by a double&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># newline, then content&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (header, content) &lt;span style="color:#666">=&lt;/span> outbuf&lt;span style="color:#666">.&lt;/span>getvalue()&lt;span style="color:#666">.&lt;/span>split(&lt;span style="color:#4070a0">&amp;#39;&lt;/span>&lt;span style="color:#4070a0;font-weight:bold">\n\n&lt;/span>&lt;span style="color:#4070a0">&amp;#39;&lt;/span>, &lt;span style="color:#40a070">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> headers &lt;span style="color:#666">=&lt;/span> [&lt;span style="color:#007020">tuple&lt;/span>(x&lt;span style="color:#666">.&lt;/span>split(&lt;span style="color:#4070a0">&amp;#39;: &amp;#39;&lt;/span>, &lt;span style="color:#40a070">1&lt;/span>)) &lt;span style="color:#007020;font-weight:bold">for&lt;/span> x &lt;span style="color:#007020;font-weight:bold">in&lt;/span> header&lt;span style="color:#666">.&lt;/span>split(&lt;span style="color:#4070a0">&amp;#39;&lt;/span>&lt;span style="color:#4070a0;font-weight:bold">\n&lt;/span>&lt;span style="color:#4070a0">&amp;#39;&lt;/span>)]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># return it wsgi style&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> start_response(&lt;span style="color:#4070a0">&amp;#39;200 OK&amp;#39;&lt;/span>, headers)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> [content]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Our CGI apps print out on &lt;code>stdout&lt;/code>, as you&amp;rsquo;d expect, so we need to
trap that, here done with a &lt;code>StringIO&lt;/code> monkeypatched on the top of
&lt;code>sys.stdout&lt;/code>. We also need to hack &lt;code>sys.exit&lt;/code> out of the way, so that
the CGIs don&amp;rsquo;t quit before we&amp;rsquo;ve completed the WSGI protocol. (I
think this might cause some bugs in the execution though, because now
it&amp;rsquo;s not terminating execution of the module, but I haven&amp;rsquo;t found an
example yet to bother worrying about it.)&lt;/p>
&lt;p>I import the script, rather than using &lt;code>os.system&lt;/code>, because it
feels right. I use &lt;code>imp.load_module&lt;/code> rather than &lt;code>import&lt;/code> because we
don&amp;rsquo;t know what the script is until runtime :)&lt;/p>
&lt;p>The real trick comes from a tip I found
&lt;a href="http://www.python.net/crew/davem/cgifaq/faqw.cgi?req=show&amp;amp;file=faq01.008.htp">here&lt;/a>
, whilst looking for how to run the imported module as &lt;code>__main__&lt;/code>.
Just &lt;code>imp.load_module&lt;/code> and tell it that it&amp;rsquo;s &lt;code>__main__&lt;/code>! Simple!&lt;/p>
&lt;p>(The hardest part about this whole excercise was now fiddling with
&lt;code>sys.path&lt;/code> and the CWD to make sure the imported script was running
with the right environment that the CGIs used to expect, this is all
done in the CGI runner &lt;code>dispatch.cgi&lt;/code> which I won&amp;rsquo;t copy here because
it&amp;rsquo;s pretty trivial and well documented in the &lt;a href="http://www.python.org/dev/peps/pep-0333/">WSGI spec&lt;/a>.)&lt;/p></description></item><item><title>more than a feeling</title><link>https://spacepants.org/blog/more-than-a-feeling/</link><pubDate>Sun, 10 Sep 2006 14:43:00 +1000</pubDate><guid>https://spacepants.org/blog/more-than-a-feeling/</guid><description>
&lt;blockquote>
&lt;p>I woke up this morning, and the sun was gone
Turned on some music to start my day..&lt;/p>
&lt;/blockquote>
&lt;p>For a while, I&amp;rsquo;ve wanted to be woken up by anything other than my
clock radio, so last night I peeked at
&lt;a href="http://banshee-project.org/">banshee&lt;/a> to see if it had a remote
control&amp;hellip; turns out it does!&lt;/p>
&lt;p>Hacked up this script, &lt;code>shins&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">DISPLAY&lt;/span>&lt;span style="color:#666">=&lt;/span>:0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">XAUTHORITY&lt;/span>&lt;span style="color:#666">=&lt;/span>/home/jaq/.Xauthority
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">export&lt;/span> DISPLAY XAUTHORITY
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>banshee --enqueue /media/usbdisk/music0/Albums/The&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>Shins/Chutes&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>Too&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>Narrow/01&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>-&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>Kissing&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>the&lt;span style="color:#4070a0;font-weight:bold">\ &lt;/span>Lipless.ogg
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>banshee --play
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>and set it to run at 9am:&lt;/p>
&lt;pre>&lt;code>dawn% at 9am
warning: commands will be executed using /bin/sh
at&amp;gt; sh shins
at&amp;gt; &amp;lt;EOT&amp;gt;
job 6 at Sun Sep 10 09:00:00 2006
&lt;/code>&lt;/pre>
&lt;p>and this morning I was woken to the soft sounds of &lt;a href="http://www.last.fm/music/The+Shins">The
Shins&lt;/a>, just as planned. Great
start to the day!&lt;/p></description></item><item><title>pylons gotchas</title><link>https://spacepants.org/blog/pylons-gotchas/</link><pubDate>Sun, 03 Sep 2006 12:42:00 +1000</pubDate><guid>https://spacepants.org/blog/pylons-gotchas/</guid><description>
&lt;p>&lt;a href="http://benno.id.au">Benno&lt;/a> was over, hacking on the &lt;a href="http://lca2007.linux.org.au">LCA 2007 website&lt;/a> with me yesterday, and
we hit two gotchas, both I knew about but when I explained them to
him they sounded silly.&lt;/p>
&lt;h3 id="c-considered-harmful">&lt;code>c&lt;/code> considered harmful.&lt;/h3>
&lt;p>&lt;code>c&lt;/code> is a request-local global object that you can attach objects to,
which is useful as a way of passing data from the controller to the
template code &amp;ndash; when you&amp;rsquo;re calling a parameterised template you
might not know at call time what the args the template wants are, but
you can pass them all in on &lt;code>c&lt;/code>. If you&amp;rsquo;re using some pattern like a
mixin CRUD class for generalising common data operations, then the
code that actually calls the template doesn&amp;rsquo;t know what the object is,
but the template it&amp;rsquo;s calling does.&lt;/p>
&lt;p>&lt;code>c&lt;/code> has the magical property that it has overloaded &lt;code>__getattr__&lt;/code> to
return an empty string if the attribute is not found. This is a mixed
blessing; your templates can access an attribute that hasn&amp;rsquo;t been
attached and it&amp;rsquo;ll mostly cope with it. (Problems happen when you try
to access attributes of nonexistent attributes, and you get the
confusing message &amp;lsquo;str has no attribute X&amp;rsquo;.)&lt;/p>
&lt;p>However, this means you hide bugs; you&amp;rsquo;ve forgotten to attach the
object you want to &lt;code>c&lt;/code> and then your code runs fine; it&amp;rsquo;s the users
who find the problem after deployment, not during development. Having
a &lt;code>__getattr__&lt;/code> that throws exceptions means you find out about these
problems a lot sooner.&lt;/p>
&lt;p>I think both of these points show that &lt;code>c&lt;/code> in general is a bad idea;
you should make use of explicit args so that your template interface
is clearly defined &amp;ndash; I haven&amp;rsquo;t yet found a nice way of doing it that
is as easy as or better than using &lt;code>c&lt;/code> though.&lt;/p>
&lt;h3 id="myghty-expressions-that-evaluate-to-false-return-empty-strings">Myghty expressions that evaluate to False return empty strings.&lt;/h3>
&lt;p>We had a simple construct like so:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-html" data-lang="html">&lt;span style="display:flex;">&lt;span> Count: &lt;span style="">&amp;lt;&lt;/span>% len(c.review_collection) %&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>which has the interesting property of evaluating to &amp;rsquo;&amp;rsquo; when
&lt;code>c.review_collection&lt;/code> is empty; &lt;code>len()&lt;/code> returns 0 which is False.&lt;/p>
&lt;p>This is pretty retarded; I suspect there&amp;rsquo;s a shortcut along the lines
of:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span> content &lt;span style="color:#666">=&lt;/span> evaluate_fragment(&lt;span style="color:#4070a0">&amp;#34;len(c.review_collection)&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> content:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> write(content)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>when these inline blocks are rendered; the &lt;code>if&lt;/code> block clearly will
fail to trigger when the inline block evaluates to &lt;code>0&lt;/code>, &lt;code>False&lt;/code>, &lt;code>[]&lt;/code>, or
&lt;code>{}&lt;/code>. I can&amp;rsquo;t think of a case where this is a good thing.&lt;/p>
&lt;p>The workaround is to wrap the &lt;code>len()&lt;/code> call in &lt;code>str()&lt;/code>, so that the
fragment doesn&amp;rsquo;t evaluate to false.&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-html" data-lang="html">&lt;span style="display:flex;">&lt;span> &lt;span style="">&amp;lt;&lt;/span>% str(len(c.review_collection)) %&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>More gotchas as they come to hand.&lt;/p></description></item><item><title>LCA 2007 proposals so far</title><link>https://spacepants.org/blog/lca-proposals-so-far/</link><pubDate>Thu, 31 Aug 2006 13:40:00 +1000</pubDate><guid>https://spacepants.org/blog/lca-proposals-so-far/</guid><description>
&lt;p>I&amp;rsquo;m having a browse of the submissions to LCA that we&amp;rsquo;ve got so far, and there&amp;rsquo;s some cool stuff in there!&lt;/p>
&lt;p>There&amp;rsquo;s still a little over 2 weeks for you to get your proposals in, so don&amp;rsquo;t hold back! I&amp;rsquo;m sure you &lt;em>all&lt;/em> have something very exciting you want to talk about at the conference!&lt;/p>
&lt;p>The more submissions we get, the rockin&amp;rsquo;er the conference will be!&lt;/p></description></item><item><title>Dad, I dug another hole...</title><link>https://spacepants.org/blog/unittest-urllib2-mock-object/</link><pubDate>Tue, 15 Aug 2006 13:53:00 +1000</pubDate><guid>https://spacepants.org/blog/unittest-urllib2-mock-object/</guid><description>
&lt;p>I wrote another mock object, this time replacing &lt;code>urlopen&lt;/code> from urllib2.&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">urllib2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">StringIO&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">unittest&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">Dummy_urllib2&lt;/span>(&lt;span style="color:#007020">object&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">install&lt;/span>(cls):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> urllib2&lt;span style="color:#666">.&lt;/span>urlopen &lt;span style="color:#666">=&lt;/span> Dummy_urllib2&lt;span style="color:#666">.&lt;/span>urlopen
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> install &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">classmethod&lt;/span>(install)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">urlopen&lt;/span>(self, url, data&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#007020;font-weight:bold">None&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>url &lt;span style="color:#666">=&lt;/span> url
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>data &lt;span style="color:#666">=&lt;/span> data
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response &lt;span style="color:#666">=&lt;/span> StringIO&lt;span style="color:#666">.&lt;/span>StringIO(&lt;span style="color:#4070a0">&amp;#34;foo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">geturl&lt;/span>():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> url
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response&lt;span style="color:#666">.&lt;/span>geturl &lt;span style="color:#666">=&lt;/span> geturl
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">info&lt;/span>():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> {}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response&lt;span style="color:#666">.&lt;/span>info &lt;span style="color:#666">=&lt;/span> info
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> response
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> urlopen &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">classmethod&lt;/span>(urlopen)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">TestDummy_urllib2&lt;/span>(unittest&lt;span style="color:#666">.&lt;/span>TestCase):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">test_install&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Dummy_urllib2&lt;span style="color:#666">.&lt;/span>install()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;http://notfound.example.org&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">try&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> r &lt;span style="color:#666">=&lt;/span> urllib2&lt;span style="color:#666">.&lt;/span>urlopen(url)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">except&lt;/span> urllib2&lt;span style="color:#666">.&lt;/span>URLError, e:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>fail(&lt;span style="color:#4070a0">&amp;#34;URLError raised, Dummy_urllib2 not installed or failed: &lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">%&lt;/span> e)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(url, Dummy_urllib2&lt;span style="color:#666">.&lt;/span>url)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(url, r&lt;span style="color:#666">.&lt;/span>geturl())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(&lt;span style="color:#007020;font-weight:bold">None&lt;/span>, Dummy_urllib2&lt;span style="color:#666">.&lt;/span>data)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(&lt;span style="color:#4070a0">&amp;#34;foo&amp;#34;&lt;/span>, r&lt;span style="color:#666">.&lt;/span>read())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> __name__ &lt;span style="color:#666">==&lt;/span> &lt;span style="color:#4070a0">&amp;#39;__main__&amp;#39;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> unittest&lt;span style="color:#666">.&lt;/span>main()&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This time it comes with &lt;em>it&amp;rsquo;s own&lt;/em> test suite. How meta!&lt;/p></description></item><item><title>linux.conf.au 2007 CFP updates</title><link>https://spacepants.org/blog/lca2007-cfp-updates/</link><pubDate>Mon, 14 Aug 2006 17:22:00 +1000</pubDate><guid>https://spacepants.org/blog/lca2007-cfp-updates/</guid><description>
&lt;p>I&amp;rsquo;m exhausted; a weekend of hacking and deployment has left me a bit frazzled.&lt;/p>
&lt;p>But it&amp;rsquo;s OK! &lt;a href="http://lca2007.linux.org.au">linux.conf.au 2007&lt;/a>&amp;rsquo;s website has had a facelift, thanks to Andy Fitzsimmons for the CSS tweaks, and our CFP submission process is a lot better.&lt;/p>
&lt;p>If you haven&amp;rsquo;t yet put in a proposal for a talk, a miniconf, a tutorial, then now&amp;rsquo;s the time! &lt;a href="http://lca2007.linux.org.au/cfp">The CFP is still open&lt;/a>!&lt;/p></description></item><item><title>a sudoku solver in Erlang</title><link>https://spacepants.org/blog/erlang-sudoku-solver/</link><pubDate>Mon, 31 Jul 2006 12:36:00 +1000</pubDate><guid>https://spacepants.org/blog/erlang-sudoku-solver/</guid><description>
&lt;p>Matt will be pleased to hear that despite my claims that I&amp;rsquo;d have a
productive weekend doing important things for &lt;a href="http://lca2007.linux.org.au">LCA
2007&lt;/a> organisation, I spent the weekend
programming.&lt;/p>
&lt;p>&lt;a href="http://algorithm.com.au">Some bastard&lt;/a> gave a great talk on Erlang at
&lt;a href="http://slug.org.au">SLUG&lt;/a> last Friday, and I really wanted to cut my
teeth on it. So, hungover, I spent most of Saturday and a little bit
of Sunday morning playing with the &lt;a href="http://erlang.se/doc/doc-5.4/doc/getting_started/part_frame.html">getting started
guide&lt;/a>
and writing a Sudoku solver.&lt;/p>
&lt;p>If you&amp;rsquo;re curious, you can pull it down from my
&lt;a href="http://bazaar-vcs.org">bzr&lt;/a> repository:&lt;/p>
&lt;p>&lt;a href="http://repo.spacepants.org/sudoku/sudoku.dev">http://repo.spacepants.org/sudoku/sudoku.dev&lt;/a>&lt;/p></description></item><item><title>mock LDAP server object</title><link>https://spacepants.org/blog/mock-ldap-server-object/</link><pubDate>Wed, 26 Jul 2006 19:04:00 +1000</pubDate><guid>https://spacepants.org/blog/mock-ldap-server-object/</guid><description>
&lt;p>Today&amp;rsquo;s big achievement was an LDAP mock object, similar to the SMTP mock object found in &lt;code>paste.fixture&lt;/code>. I was refactoring the sign in and sign out code of an in-house application that uses LDAP as an authentication store, and I needed to test that the logic of the controllers was correct. So, referring back to &lt;a href="http://pythonpaste.org">Paste&lt;/a>&amp;rsquo;s lovely &lt;code>fixture&lt;/code> module, I came up with the following:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">ldap&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">Dummy_ldap&lt;/span>(&lt;span style="color:#007020">object&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> __init__(self, server):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> &lt;span style="color:#4070a0">&amp;#34;dummy ldap init&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>server &lt;span style="color:#666">=&lt;/span> server
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">install&lt;/span>(cls):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ldap&lt;span style="color:#666">.&lt;/span>initialize &lt;span style="color:#666">=&lt;/span> cls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> install &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">classmethod&lt;/span>(install)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">simple_bind_s&lt;/span>(self, dn, passwd):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#007020;font-weight:bold">True&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">search&lt;/span>(self, base, scope, search_filter):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>base &lt;span style="color:#666">=&lt;/span> base
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>filter &lt;span style="color:#666">=&lt;/span> search_filter
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>results &lt;span style="color:#666">=&lt;/span> [(ldap&lt;span style="color:#666">.&lt;/span>RES_SEARCH_ENTRY, [(&lt;span style="color:#4070a0">&amp;#39;dn&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;cn=test,&lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">%&lt;/span> self&lt;span style="color:#666">.&lt;/span>base),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#4070a0">&amp;#39;mail&amp;#39;&lt;/span>, [&lt;span style="color:#4070a0">&amp;#39;test&amp;#39;&lt;/span>])]),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (ldap&lt;span style="color:#666">.&lt;/span>RES_SEARCH_RESULT, []),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>counter &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">result&lt;/span>(self, rid, number):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> r &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>results[self&lt;span style="color:#666">.&lt;/span>counter]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>counter &lt;span style="color:#666">+=&lt;/span> &lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> r
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>A bit hackish, yes, but I&amp;rsquo;m not trying to reimplement LDAP here, I just want to trap the calls I use. Obviously a little bit of work is needed to, say, disallow the bind, or throw an exception, but these are trivial extensions.&lt;/p>
&lt;p>Just as in &lt;code>Dummy_smtplib&lt;/code>, you call the classmethod &lt;code>install&lt;/code> to set it up (i.e. monkeypatch &lt;code>ldap.initialize&lt;/code>) and you get to trap how it behaves.&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">TestAccountController&lt;/span>(ControllerTest):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">test_signin_signout&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Dummy_ldap&lt;span style="color:#666">.&lt;/span>install()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># ... do stuff&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Simple!&lt;/p></description></item><item><title>paste.fixture's Dummy_smtplib</title><link>https://spacepants.org/blog/paste-fixture-dummy-smtplib/</link><pubDate>Thu, 20 Jul 2006 21:21:00 +1000</pubDate><guid>https://spacepants.org/blog/paste-fixture-dummy-smtplib/</guid><description>
&lt;p>I&amp;rsquo;m working on two webapps that need to send email to users, and in
both, the user is expected to click on a URL to confirm their
registration. A common enough idiom for website accounts.&lt;/p>
&lt;p>As a disciple of the cult of test-driven development, I want to be
able to make a test that generates that email, inspects the contents
for the URL, visits that generated URL, and then checks that the
registration is completed.&lt;/p>
&lt;p>Michael K, who &lt;a href="https://spacepants.org/blog/semiautomatic-test-generation">previously
suggested&lt;/a>
other abuses of Python&amp;rsquo;s dynamic nature, reminded me a few months ago
that you could monkeypatch an imported library with your own, and it&amp;rsquo;d
be preserved throughout the &amp;ldquo;address space&amp;rdquo; of the running Python
program.&lt;/p>
&lt;p>I&amp;rsquo;d not really played with it, and had been putting off writing a test
for email because I didn&amp;rsquo;t really understand what I wanted. The other
night at DebSIG, I asked (complained?) again about it, and he said
&amp;ldquo;Hey, &lt;code>paste.fixture&lt;/code> already does it!&amp;rdquo; I&amp;rsquo;d said I knew, but it had
no documentation, and no-one on the paste users list had responded to
my request for examples. He gave me a curt &amp;ldquo;Read the fucking source&amp;rdquo;
response (in a much nicer way of course) and I thought, he&amp;rsquo;s right!
Back in the day I used to read library source code in order to work
out poorly documented APIs, why now do I rely on clear documentation
so much? I should just dig in and write some example code to test it
out, and JFDI.&lt;/p>
&lt;p>Enough of the backstory.&lt;/p>
&lt;p>Here&amp;rsquo;s a quick guide to setting up an email sender test, using
&lt;code>paste.fixture&lt;/code> around your &lt;a href="http://pylonshq.com">Pylons&lt;/a> application.&lt;/p>
&lt;p>Firstly, in your controller, you have something that sends email, like so:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">smtplib&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">from&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">app.lib.base&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> BaseController, m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">FooController&lt;/span>(BaseController):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">index&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Do something and send email while you&amp;#39;re at it&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s &lt;span style="color:#666">=&lt;/span> smtplib&lt;span style="color:#666">.&lt;/span>SMTP(&lt;span style="color:#4070a0">&amp;#34;localhost&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s&lt;span style="color:#666">.&lt;/span>sendmail(&lt;span style="color:#4070a0">&amp;#34;from@example.org&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;to@example.com&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">Hey, this is a message
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">http://localhost/foo/activate/37
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> s&lt;span style="color:#666">.&lt;/span>quit()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> m&lt;span style="color:#666">.&lt;/span>subexec(&lt;span style="color:#4070a0">&amp;#34;foo/index.myt&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">activate&lt;/span>(self, &lt;span style="color:#007020">id&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> m&lt;span style="color:#666">.&lt;/span>write(&lt;span style="color:#4070a0">&amp;#34;awesome, activating &lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">%&lt;/span> &lt;span style="color:#007020">id&lt;/span>)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Pretty basic, if we visit &lt;code>/foo/&lt;/code> then we send an email, and then if we visit &lt;code>/foo/activate/N&lt;/code> we inform the visitor of the activation.&lt;/p>
&lt;p>The test is pretty simple too:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">re&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">unittest&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">from&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">paste.fixture&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> Dummy_smtplib
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">TestFooController&lt;/span>(unittest&lt;span style="color:#666">.&lt;/span>TestCase):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">test_foo_activation&lt;/span>(self):
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You just call the classmethod &lt;code>install&lt;/code> on &lt;code>Dummy_smtplib&lt;/code> to set it up, which does some magic behind the scenes (really it just replaces &lt;code>smtplib.SMTP&lt;/code> with itself)&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span> Dummy_smtplib&lt;span style="color:#666">.&lt;/span>install()&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>then run through the process you want to test&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># get the start page&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>app&lt;span style="color:#666">.&lt;/span>get(&lt;span style="color:#4070a0">&amp;#39;/foo&amp;#39;&lt;/span>)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and now we check that the message was sent, and its contents&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>failIfEqual(&lt;span style="color:#007020;font-weight:bold">None&lt;/span>, Dummy_smtplib&lt;span style="color:#666">.&lt;/span>existing, &lt;span style="color:#4070a0">&amp;#34;no message sent&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">match&lt;/span> &lt;span style="color:#666">=&lt;/span> re&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#007020;font-weight:bold">match&lt;/span>(&lt;span style="color:#4070a0">r&lt;/span>&lt;span style="color:#4070a0">&amp;#39;^.*/foo/activate/([^ ]+)&amp;#39;&lt;/span>, Dummy_smtplib&lt;span style="color:#666">.&lt;/span>existing&lt;span style="color:#666">.&lt;/span>message)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>failIfEqual(&lt;span style="color:#007020;font-weight:bold">None&lt;/span>, &lt;span style="color:#007020;font-weight:bold">match&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># visit the URL&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>app&lt;span style="color:#666">.&lt;/span>get(&lt;span style="color:#4070a0">&amp;#39;/foo/activate/&lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0">&amp;#39;&lt;/span> &lt;span style="color:#666">%&lt;/span> &lt;span style="color:#007020;font-weight:bold">match&lt;/span>&lt;span style="color:#666">.&lt;/span>group(&lt;span style="color:#40a070">1&lt;/span>))&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and finally, test the result of the activation.&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span> res&lt;span style="color:#666">.&lt;/span>mustcontain(&lt;span style="color:#4070a0">&amp;#39;awesome&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res&lt;span style="color:#666">.&lt;/span>mustcontain(&lt;span style="color:#007020;font-weight:bold">match&lt;/span>&lt;span style="color:#666">.&lt;/span>group(&lt;span style="color:#40a070">1&lt;/span>))&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>You&amp;rsquo;ve also got to clean up, reset the dummy SMTP library for next time (you&amp;rsquo;ll get an exception thrown if you don&amp;rsquo;t, to remind you).&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span> Dummy_smtplib&lt;span style="color:#666">.&lt;/span>existing&lt;span style="color:#666">.&lt;/span>reset()&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>If you do any database stuff, then the times to check the status of the data model are just before the actuvation URL is visited, and again afterwards. I keep the model empty for each test, so I can pull out all the records and make sure that there&amp;rsquo;s only one afterwards, and that it has the right attributes before and after.&lt;/p>
&lt;p>Pretty easy stuff.&lt;/p></description></item><item><title>lca2007 CFP open!</title><link>https://spacepants.org/blog/seven-cfp-open/</link><pubDate>Tue, 18 Jul 2006 19:49:00 +1000</pubDate><guid>https://spacepants.org/blog/seven-cfp-open/</guid><description><p><img src="http://lca2007.linux.org.au/cfp.png" class="icon"/></p>
&lt;p>I was on a jet from Austin to London when it happened, but busy the
hours beforehand in a flat in Austin nursing a hangover putting the
final touches on &lt;a href="http://lca2007.linux.org.au">the website&lt;/a>. So,
though I missed the chance to announce at the time, I&amp;rsquo;ll take this
opportunity to blog about it now!&lt;/p>
&lt;p>The &lt;a href="http://lca2007.linux.org.au/cfp">LCA 2007 CFP&lt;/a> is open, so &lt;a href="http://lca2007.linux.org.au/cfp/submit">submit your
proposal&lt;/a> for a talk,
miniconf, etc now! (Or in reality, start writing your proposal now,
so that you can submit it before the CFP closes ;-)&lt;/p>
&lt;p>We&amp;rsquo;re looking forward to your submissions!&lt;/p></description></item><item><title>pylons, paste, and wsgi</title><link>https://spacepants.org/blog/pylons-paste-stack/</link><pubDate>Mon, 17 Jul 2006 14:20:00 +1000</pubDate><guid>https://spacepants.org/blog/pylons-paste-stack/</guid><description>
&lt;p>I scribbled this down on our whiteboard last Friday, trying to explain
how &lt;a href="http://pylonshq.com">Pylons&lt;/a> and &lt;a href="http://pythonpaste.org">Paste&lt;/a>
fit together. Prevously &lt;a href="http://perkypants.org">jdub&lt;/a> and Lindsay had
asked me similar questions. Until Friday, I wasn&amp;rsquo;t even sure myself.&lt;/p>
&lt;p>&lt;img src="https://spacepants.org/images/pylons-paste-stack.png" alt="pylons and paste stack diagram">&lt;/p>
&lt;p>The first thing to note is that Paste is not a framework or single
library, it&amp;rsquo;s a collection of components that by themselves don&amp;rsquo;t do a
lot, but with their powers combined form a set of useful and sometimes
essential tools for building a web application in Python.&lt;/p>
&lt;p>Paste implements an interface known as &lt;a href="http://wsgi.org">WSGI&lt;/a>, aka
the Web Server Gateway Interface. It&amp;rsquo;s defined in &lt;a href="http://www.python.org/dev/peps/pep-0333/">PEP
333&lt;/a>. Basically WSGI
describes a Chain of Command design pattern; each piece of a WSGI
application takes a request, and either acts on that request or passes
it along the chain. The interface described by WSGI means you can
plug WSGI apps (or as Pylons calls them, /middleware/) together in any
order as you like.&lt;/p>
&lt;p>Why is this useful? Well, it means you can take an off-the-shelf
authentication handler to cope with 403 and 401 responses and take
care of logins. One would only need to say &amp;ldquo;this is how you
authenticate someone&amp;rdquo; and &amp;ldquo;this is how you ask the user for their
password.&amp;rdquo; Other things are possible; Pylons ships with an ultra-sexy
500 handler that puts you in a DHTML debugger, complete with traceback
and Python interpreter. (Of course such a tool is a giant security
hole so it is easily turned off in production environments.)&lt;/p>
&lt;p>So, that&amp;rsquo;s Paste. There&amp;rsquo;s a few special cases in there, though:
PasteScript and PasteDeploy. They&amp;rsquo;re special in that they tend to be
at the bottom of the stack &amp;ndash; they&amp;rsquo;re specifically for launching WSGI
applications, configuration of the application (e.g. authenticatoin
details alluded to above) and connecting to the application
(e.g. direct HTTP, FastCGI, and other connectors). I suspect that my
diagram above doesn&amp;rsquo;t lend itself well to describing how PasteScript
and PasteDeploy really work; it&amp;rsquo;s still a bit of dark magic to me. I
hope someone else would be able to build on this article with their
own that rebuts the errors and clears the grey areas.&lt;/p>
&lt;p>In a Pylons app, you tend not to notice Paste, except when deploying
(because you tend to run the command &lt;code>paster serve&lt;/code> to
launch a development environment). Pylons itself is mostly just
glue. It&amp;rsquo;s a thin veil of a framework over the top of some very
powerful supporting libraries but presents them in a convenient and
well defined way.&lt;/p>
&lt;p>When you create a Pylons app, you get your paste middleware built for
you, and then the entry point for your app is created as a WSGI
application too. So it sits on top of the stack, taking in requests,
and sending out responses. Your app can define its own middleware,
too, so you have a lot of control over what happens between your app
and the browser.&lt;/p>
&lt;p>The main components of a Pylons app are:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>A route mapper, by default &lt;a href="http://routes.groovie.org">Routes&lt;/a>. The route mapper takes in URLs from the request passed into the app, and maps that URL to a controller object and method call. (If you&amp;rsquo;ve used RoR then you probably are familiar with this already.)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>A templating engine, by default &lt;a href="http://myghty.org">Myghty&lt;/a>. The templating engine generates the view presented to the browser.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>A data model. Pylons doesn&amp;rsquo;t prefer any method of data model, it just makes available a &lt;code>model&lt;/code> module within which you can define your own data model. I use &lt;a href="http://sqlalchemy.org">SQLAlchemy&lt;/a> as an ORM because it is very powerful and is nicely suited to working with existing schemas. It works as an MVC between the data model presented to the application and the database schema itself.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Pylons lets you swap out any of these components with your own, if you
desire. I find Routes and Myghty to be powerful and flexible and
friendly enough that there&amp;rsquo;s no reason to want anything else.&lt;/p>
&lt;p>Your controller objects, like any MVC pattern, coordinate between the
model and the view. An action performed on a controller retrieves
some data from the model, possibly altering it, and renders that data
using the template engine.&lt;/p>
&lt;p>There are other parts, other libraries that you&amp;rsquo;ll see in a Pylons
app, that aren&amp;rsquo;t represented here. WebHelpers is a library of
convenience functions used in the template engine, for generating
common HTML and JavaScript. &lt;code>paste.fixture&lt;/code> is a web app test
framework that takes advantage of the common interface of WSGI to
allow one to test their application without requiring a full web
server and socket handling. &lt;a href="http://formencode.org">FormEncode&lt;/a>
handles form validation, useful from within a controller object.
These are but to name a few.&lt;/p>
&lt;p>Unfortunately there is a sore need for overviews like this one in the
Paste and Pylons community; as stated earlier I didn&amp;rsquo;t fully
understand the relationships myself until I came up with this diagram.
Hopefully then, dear reader, you have a better insight into how this
collection of names fit together, and can avoid the steep learning
curve :-)&lt;/p></description></item><item><title>TDD promotes good health</title><link>https://spacepants.org/blog/tdd-promotes-good-health/</link><pubDate>Thu, 01 Jun 2006 16:01:00 +1000</pubDate><guid>https://spacepants.org/blog/tdd-promotes-good-health/</guid><description>
&lt;p>There&amp;rsquo;s an important advantage to Test Driven Development that I don&amp;rsquo;t think was covered on list or by Rob at his talk.&lt;/p>
&lt;p>By having a test suite, you can code after a heavy liquid lunch and be sure that you&amp;rsquo;re not decreasing the quality of existing code. It makes it easier to focus on a specific task and write code to solve that problem. Having something do all the work for you when testing is a massive bonus, because obviulsy the side-effects of a pub lunch are that you are easily distracted and lack the willingness to focus on the task at hand. Test suites lower the barrier of entry to getting work done.&lt;/p>
&lt;p>Who&amp;rsquo;da thought that best practices would also be best for drinking practice?&lt;/p></description></item><item><title>semiautomatic test generation</title><link>https://spacepants.org/blog/semiautomatic-test-generation/</link><pubDate>Mon, 29 May 2006 19:30:00 +1000</pubDate><guid>https://spacepants.org/blog/semiautomatic-test-generation/</guid><description>
&lt;p>Whilst developing two webapps, one at work and one for the &lt;a href="http://lca2007.linux.org.au">los palmas
seven&lt;/a>, I found that when writing the
tests for the data model (hell yes I use test driven development, it&amp;rsquo;s very productive
as it defines short and medium term goals and keeps you in focus) I
was writing the same code over and over, but was very reliant on the
structure of the data model I was testing.&lt;/p>
&lt;p>Having done lots of currying in functional programming back in uni, I
could see there was an obvious pattern, and thus it needed refactoring
to reduce the amount of code I was writing and in doing so reduce the
probability of error.&lt;/p>
&lt;p>So I pulled out a set of functoins from one test, and made it a base
class, and refactored it to allow the child class to specify the data;
but due to the way python&amp;rsquo;s &lt;code>unittest&lt;/code> and the test runner &lt;code>nose&lt;/code>
work, I had to manually write a function in each child class to call
the worker functoin in the parent.&lt;/p>
&lt;p>Obviously requiring the user to type more meant that there was a risk
of error, and it was also tedious (read: very boring) to have to do
this every time. Not knowing enough about python internals, I &lt;a href="http://lists.slug.org.au/archives/coders/2006/05/msg00013.html">posted
to the new slug coders
list&lt;/a>, christening it with
the first development related post, and got back a good suggestion,
but I didn&amp;rsquo;t try it out. Michael K suggested at the slug meeting last
friday that I check out metaclasses if his previous suggestion didn&amp;rsquo;t
work.&lt;/p>
&lt;p>Saturday I jumped into the deep end with metaclasses, and it turned
out they were easier than I thought, but having done so now I
wouldn&amp;rsquo;t recommend them as a hammer to bash in all those screw and
rivet problems. They &lt;em>are&lt;/em> very cool though.&lt;/p>
&lt;p>So I ended up with a base class that had a metaclass that would monkey
patch the child class with the correct method names just as it was
being inspected, and had a small test program written that
demonstrated my idea was sound.&lt;/p>
&lt;p>However, applying this method to the actual tests running under &lt;code>nose&lt;/code>
didn&amp;rsquo;t work. Lots of debugging printfs later I eventually traced this
to a peculiarity in the way &lt;code>nose&lt;/code> decides on what tests to run: to
prevent test methods being run twice, it makes sure that when running
a test, the module that it is defined in is the same as the module
currently being tested, i.e. it makes sure that &lt;code>__module__&lt;/code> matches
in both the callable and the current test case.&lt;/p>
&lt;p>Now, when you define your methods in a parent, the method&amp;rsquo;s module is
that of the parent, so a structure like the following:&lt;/p>
&lt;pre>&lt;code>tests/module/
tests/module/__init__.py
tests/module/test_something.py
&lt;/code>&lt;/pre>
&lt;p>sets &lt;code>__module__&lt;/code> in &lt;code>__init__.py&lt;/code> to &lt;code>tests.module&lt;/code> and in
&lt;code>test_something.py&lt;/code> to &lt;code>tests.module.test_something&lt;/code>. Running a
method from &lt;code>tests.module.test_something&lt;/code> that&amp;rsquo;s defined in
&lt;code>tests.module&lt;/code>, &lt;code>nose&lt;/code> says no.&lt;/p>
&lt;p>Enter &lt;a href="http://benno.id.au/blog/">Benno&lt;/a> and his knowledge of python
internals. An impromptu &lt;a href="http://lca2007.linux.org.au">7&lt;/a> hackfest on
Sunday at
&lt;a href="http://perkypants.org/blog">jdub&lt;/a>&amp;rsquo;s house let me show him what I was
trying to do, where he suggested using python&amp;rsquo;s &lt;code>new&lt;/code> package, and the
&lt;code>im_func&lt;/code> attributes on callables, to build a workaround for &lt;code>nose&lt;/code>&amp;rsquo;s
features, which is much better than what I was thinking of doing to
solve the problem (something about injecting docstrings into the child
class and then &lt;code>eval&lt;/code>ing them in the child&amp;rsquo;s context).&lt;/p>
&lt;p>Some quick hacks later, we had a test program that showed it&amp;rsquo;d work,
and patched up the base test class. Appended for your enjoyment, a
base test class that one can inherit from to automatically generate
common tests for tables when using
&lt;a href="http://sqlalchemy.org">SQLAlchemy&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 34
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 35
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 36
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 37
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 38
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 39
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 40
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 41
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 42
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 43
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 44
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 45
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 46
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 47
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 48
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 49
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 50
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 51
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 52
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 53
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 54
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 55
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 56
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 57
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 58
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 59
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 60
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 61
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 62
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 63
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 64
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 65
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 66
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 67
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 68
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 69
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 70
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 71
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 72
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 73
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 74
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 75
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 76
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 77
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 78
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 79
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 80
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 81
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 82
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 83
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 84
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 85
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 86
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 87
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 88
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 89
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 90
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 91
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 92
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 93
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 94
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 95
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 96
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 97
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 98
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 99
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">100
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">101
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">102
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">103
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">104
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">105
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">106
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">107
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">108
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">109
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">110
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">111
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">112
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">113
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">114
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">115
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">116
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">117
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">118
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">119
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">120
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">121
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">122
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">123
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">124
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">125
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">126
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">127
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">128
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">129
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">130
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">131
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">132
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">133
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">134
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">135
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">136
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">137
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">138
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">139
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">140
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">141
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">142
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">143
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">144
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">145
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">146
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">147
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">148
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">149
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">150
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">151
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">152
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">153
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">154
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">155
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">156
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">157
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">158
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">159
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">160
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">161
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">162
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">163
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">164
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">165
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">166
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">167
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">168
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">169
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">170
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">171
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">172
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">173
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">174
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">175
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">176
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">177
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">178
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">179
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">180
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">181
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">182
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">183
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">184
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">185
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">186
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">187
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">188
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">189
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">190
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">191
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">192
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">193
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">194
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">195
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">196
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">197
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">198
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">199
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">200
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">201
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">202
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">203
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">204
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">205
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">206
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">207
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">208
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">209
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">210
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">211
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">212
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">213
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">214
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">215
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">216
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">217
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">218
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">219
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">220
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">221
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">222
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">223
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">224
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">from&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">unittest&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> TestCase
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">sqlalchemy&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">zookeepr.models&lt;/span> &lt;span style="color:#007020;font-weight:bold">as&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">model&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">TestBase&lt;/span>(TestCase):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">assertRaisesAny&lt;/span>(self, callable_obj, &lt;span style="color:#666">*&lt;/span>args, &lt;span style="color:#666">**&lt;/span>kwargs):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Assert that the ``callable_obj`` raises any exception.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">try&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> callable_obj(&lt;span style="color:#666">*&lt;/span>args, &lt;span style="color:#666">**&lt;/span>kwargs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">except&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">pass&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>fail(&lt;span style="color:#4070a0">&amp;#34;callable &lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0"> failed to raise an exception&amp;#34;&lt;/span> &lt;span style="color:#666">%&lt;/span> callable_obj)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">monkeypatch&lt;/span>(cls, test_name, func_name):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Create a method on a class with a different name.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> This method patches ``cls`` with a method called ``test_name``, which
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> is bound to the actual callable ``func_name``.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> In order to make sure test cases get run in children of the assortment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> of test base classes in this module, we do not name the worker methods
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> with the prefix &amp;#39;test_&amp;#39;. Instead they are named otherwise, and we
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> alias them in the metaclass of the test class.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> However, due to the behaviour of ``nose`` to not run tests that are
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> defined outside of the module of the current test class being run, we
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> need to create these test aliases with the model of the child class,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> rather than simply calling ``setattr``.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> (Curious readers can study ``node.selector``, in particular the
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> ``wantMethod``, ``callableInTests``, and ``anytests`` methods (as of
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> this writing).)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> You can&amp;#39;t set __module__ directly because it&amp;#39;s a r/o attribute, so we
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> call ``new.function`` to create a new function with the same code as
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> the original. The __module__ attribute is set by the new.function
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> method from the globals dict that it is passed, so here we make a
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> shallow copy of the original and override the __name__ attribute to
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> point to the module of the class we&amp;#39;re actually testing.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> By this stage, you may think that this is crack. You&amp;#39;re right.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> But at least I don&amp;#39;t have to repeat the same code over and
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> over in the actual tests ;-)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># get the code&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> code &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">getattr&lt;/span>(cls, func_name)&lt;span style="color:#666">.&lt;/span>im_func&lt;span style="color:#666">.&lt;/span>func_code
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># get the function globals so we can overwrite the module&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> g &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">getattr&lt;/span>(cls, func_name)&lt;span style="color:#666">.&lt;/span>im_func&lt;span style="color:#666">.&lt;/span>func_globals&lt;span style="color:#666">.&lt;/span>copy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> g[&lt;span style="color:#4070a0">&amp;#39;__name__&amp;#39;&lt;/span>] &lt;span style="color:#666">=&lt;/span> cls&lt;span style="color:#666">.&lt;/span>__module__
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># create a new function with:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># the code of the original function,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># our patched globals,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># and the new name of the function&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">setattr&lt;/span>(cls, test_name, new&lt;span style="color:#666">.&lt;/span>function(code, g, test_name))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">TableTestGenerator&lt;/span>(&lt;span style="color:#007020">type&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Monkeypatching metaclass for table schema test classes.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> This metaclass does some funky class method rewriting to generate
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> test methods so that one doesn&amp;#39;t actually need to do any work to get
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> table tests written. How awesome is that for TDD? :-)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> __init__(mcs, name, bases, classdict):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">type&lt;/span>&lt;span style="color:#666">.&lt;/span>__init__(mcs, name, bases, classdict)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#4070a0">&amp;#39;table&amp;#39;&lt;/span> &lt;span style="color:#007020;font-weight:bold">in&lt;/span> classdict:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> monkeypatch(mcs, &lt;span style="color:#4070a0">&amp;#39;test_insert&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;insert&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> k &lt;span style="color:#007020;font-weight:bold">in&lt;/span> [&lt;span style="color:#4070a0">&amp;#39;not_nullable&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;unique&amp;#39;&lt;/span>]:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> k &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;s&amp;#39;&lt;/span> &lt;span style="color:#007020;font-weight:bold">in&lt;/span> classdict:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> monkeypatch(mcs, &lt;span style="color:#4070a0">&amp;#39;test_&amp;#39;&lt;/span> &lt;span style="color:#666">+&lt;/span> k, k)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">TableTest&lt;/span>(TestBase):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Base class for testing the database schema.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Derived classes should set the following attributes:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> ``table`` is a string containing the name of the table being tested,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> scoped relative to the module ``zookeepr.models``.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> ``samples`` is a list of dictionaries of columns and their values to use
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> when inserting a row into the table.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> ``not_nullables`` is a list of column names that must not be undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> in the table.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> ``uniques`` is a list of column names that must uniquely identify
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> the object.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> An example using this base class:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> class TestSomeTable(TestTable):
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> table = &amp;#39;module.SomeTable&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> samples = [dict(name=&amp;#39;testguy&amp;#39;, email_address=&amp;#39;test@example.org&amp;#39;)]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> not_nullables = [&amp;#39;name&amp;#39;]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> uniques = [&amp;#39;name&amp;#39;, &amp;#39;email_address&amp;#39;]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> __metaclass__ &lt;span style="color:#666">=&lt;/span> TableTestGenerator
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">get_table&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Return the table, coping with scoping.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Set the ``table`` class variable to the name of the table variable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> relative to anchor.model.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> module &lt;span style="color:#666">=&lt;/span> model
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># cope with classes in sub-models&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> submodule &lt;span style="color:#007020;font-weight:bold">in&lt;/span> self&lt;span style="color:#666">.&lt;/span>table&lt;span style="color:#666">.&lt;/span>split(&lt;span style="color:#4070a0">&amp;#39;.&amp;#39;&lt;/span>):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> module &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">getattr&lt;/span>(module, submodule)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> module
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">check_empty_table&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Check that the database was left empty after the test&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> sqlalchemy&lt;span style="color:#666">.&lt;/span>select([sqlalchemy&lt;span style="color:#666">.&lt;/span>func&lt;span style="color:#666">.&lt;/span>count(self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>c&lt;span style="color:#666">.&lt;/span>id)])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#666">=&lt;/span> query&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(&lt;span style="color:#40a070">0&lt;/span>, result&lt;span style="color:#666">.&lt;/span>fetchone()[&lt;span style="color:#40a070">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">insert&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Test insertion of sample data
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Insert a row into the table, check that it was
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> inserted into the database, and then delete it.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Set the attributes for this model object in the ``attrs`` class
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> variable.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>failIf(&lt;span style="color:#007020">len&lt;/span>(self&lt;span style="color:#666">.&lt;/span>samples) &lt;span style="color:#666">&amp;lt;&lt;/span> &lt;span style="color:#40a070">1&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;not enough sample data, stranger&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> sample &lt;span style="color:#007020;font-weight:bold">in&lt;/span> self&lt;span style="color:#666">.&lt;/span>samples:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> &lt;span style="color:#4070a0">&amp;#34;testing insert of s &lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">%&lt;/span> sample
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>insert()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> query
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query&lt;span style="color:#666">.&lt;/span>execute(sample)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> key &lt;span style="color:#007020;font-weight:bold">in&lt;/span> sample&lt;span style="color:#666">.&lt;/span>keys():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> col &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020">getattr&lt;/span>(self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>c, key)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> sqlalchemy&lt;span style="color:#666">.&lt;/span>select([col])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> &lt;span style="color:#4070a0">&amp;#34;query&amp;#34;&lt;/span>, query
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#666">=&lt;/span> query&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> result
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> row &lt;span style="color:#666">=&lt;/span> result&lt;span style="color:#666">.&lt;/span>fetchone()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> &lt;span style="color:#4070a0">&amp;#34;row&amp;#34;&lt;/span>, row
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(sample[key], row[&lt;span style="color:#40a070">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>delete()&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># do this again to make sure the test data is all able to go into&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># the db, so that we know it&amp;#39;s good to do uniqueness tests, for example&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> sample &lt;span style="color:#007020;font-weight:bold">in&lt;/span> self&lt;span style="color:#666">.&lt;/span>samples:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>insert()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query&lt;span style="color:#666">.&lt;/span>execute(sample)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># get the count of rows&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> sqlalchemy&lt;span style="color:#666">.&lt;/span>select([sqlalchemy&lt;span style="color:#666">.&lt;/span>func&lt;span style="color:#666">.&lt;/span>count(self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>c&lt;span style="color:#666">.&lt;/span>id)])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result &lt;span style="color:#666">=&lt;/span> query&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># check that it&amp;#39;s the same length as the sample data&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertEqual(&lt;span style="color:#007020">len&lt;/span>(self&lt;span style="color:#666">.&lt;/span>samples), result&lt;span style="color:#666">.&lt;/span>fetchone()[&lt;span style="color:#40a070">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># ok, delete it&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>delete()&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>check_empty_table()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">not_nullable&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Check that certain columns of a table are not nullable.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Specify the ``not_nullables`` class variable with a list of column names
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> that must not be null, and this method will insert into the table rows
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> with each set to null and test for an exception from the database layer.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>failIf(&lt;span style="color:#007020">len&lt;/span>(self&lt;span style="color:#666">.&lt;/span>samples) &lt;span style="color:#666">&amp;lt;&lt;/span> &lt;span style="color:#40a070">1&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;not enough sample data, stranger&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> col &lt;span style="color:#007020;font-weight:bold">in&lt;/span> self&lt;span style="color:#666">.&lt;/span>not_nullables:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> &lt;span style="color:#4070a0">&amp;#34;testing that &lt;/span>&lt;span style="color:#70a0d0">%s&lt;/span>&lt;span style="color:#4070a0"> is not nullable&amp;#34;&lt;/span> &lt;span style="color:#666">%&lt;/span> col
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># construct an attribute dictionary without the &amp;#39;not null&amp;#39; attribute&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coldata &lt;span style="color:#666">=&lt;/span> {}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coldata&lt;span style="color:#666">.&lt;/span>update(self&lt;span style="color:#666">.&lt;/span>samples[&lt;span style="color:#40a070">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> coldata[col] &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">None&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># create the model object&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">print&lt;/span> coldata
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>insert()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertRaisesAny(query&lt;span style="color:#666">.&lt;/span>execute, coldata)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>delete()&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>check_empty_table()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">def&lt;/span> &lt;span style="color:#06287e">unique&lt;/span>(self):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;Check that certain attributes of a model object are unique.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Specify the ``uniques`` class variable with a list of attributes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> that must be unique, and this method will create two copies of the
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> model object with that attribute the same and test for an exception
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> from the database layer.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>failIf(&lt;span style="color:#007020">len&lt;/span>(self&lt;span style="color:#666">.&lt;/span>samples) &lt;span style="color:#666">&amp;lt;&lt;/span> &lt;span style="color:#40a070">2&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;not enough sample data, stranger&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> col &lt;span style="color:#007020;font-weight:bold">in&lt;/span> self&lt;span style="color:#666">.&lt;/span>uniques:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>insert()&lt;span style="color:#666">.&lt;/span>execute(self&lt;span style="color:#666">.&lt;/span>samples[&lt;span style="color:#40a070">0&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> attr &lt;span style="color:#666">=&lt;/span> {}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> attr&lt;span style="color:#666">.&lt;/span>update(self&lt;span style="color:#666">.&lt;/span>samples[&lt;span style="color:#40a070">1&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> attr[col] &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>samples[&lt;span style="color:#40a070">0&lt;/span>][col]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> query &lt;span style="color:#666">=&lt;/span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>insert()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>assertRaisesAny(query&lt;span style="color:#666">.&lt;/span>execute, attr)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>get_table()&lt;span style="color:#666">.&lt;/span>delete()&lt;span style="color:#666">.&lt;/span>execute()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self&lt;span style="color:#666">.&lt;/span>check_empty_table()&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Feel free to use this in your own code, I&amp;rsquo;m placing it in the public
domain.&lt;/p></description></item><item><title>cityrail ticket cubohemioctahedron</title><link>https://spacepants.org/blog/cubohemioctahedron/</link><pubDate>Mon, 15 May 2006 10:55:00 +1000</pubDate><guid>https://spacepants.org/blog/cubohemioctahedron/</guid><description>
&lt;p>A while ago, as many of you would, I saw the &lt;a href="http://www.deadprogrammer.com/?p=1747">Metrocard tricontahedron&lt;/a> linked from the
&lt;a href="http://www.makezine.com/blog">Makezine blog&lt;/a>.&lt;/p>
&lt;p>This weekend, whilst hacking away, I took a break and was distracted
by the large pile of used tickets that have built up on my desk. I
found the above site again, and spend about 15 minutes folding some
tickets until I emerged to the loungeroom to show off the results.&lt;/p>
&lt;p>&lt;img src="http://static.flickr.com/52/145519285_061af93062_m_d.jpg" alt="cityrail ticket
cubohemioctahedron">
(&lt;a href="http://www.flickr.com/photos/spacejaq/145519285/">flickr&lt;/a>)&lt;/p>
&lt;p>So what it this shape actually called? I googled &amp;rsquo;tricontrahedron'
(because I can&amp;rsquo;t read) and then &amp;rsquo;tricontahedron&amp;rsquo;, and
&lt;a href="http://72.14.207.104/search?q=cache:fcQ6np24nKQJ:go.hrw.com/resources/go_mt/a1/c13/APOLY.PDF+conta+hedron&amp;amp;hl=en&amp;amp;gl=au&amp;amp;ct=clnk&amp;amp;cd=2&amp;amp;client=firefox">discovered&lt;/a>
that this name is derived from the prefix &lt;em>tri-&lt;/em>, the modifier
&lt;em>conta-&lt;/em> (which refers to a group of ten), and the suffix &lt;em>-hedron&lt;/em>,
meaning &amp;ldquo;faces&amp;rdquo;, so we have a 30-faced polyhedra. True so far, but
actually googling or searching
&lt;a href="http://mathworld.wolfram.com/Triacontahedron.html">Mathworld&lt;/a> for
&lt;code>tricontahedron&lt;/code> wasn&amp;rsquo;t showing me any graphical evidence that this
was the right name. In fact, the closest by name, the &lt;a href="http://mathworld.wolfram.com/RhombicTriacontahedron.html">rhombic
tricontahedron&lt;/a>,
looks nothing like this shape.&lt;/p>
&lt;p>But finally I stumbled on a set of excellent polyhedra sites:&lt;/p>
&lt;ul>
&lt;li>Anthony Thyssen&amp;rsquo;s &lt;a href="http://www.cit.gu.edu.au/~anthony/graphics/polyhedra/sources.shtml">Studies into Polyhedra&lt;/a>&lt;/li>
&lt;li>V. Bulatov&amp;rsquo;s &lt;a href="http://www.physics.orst.edu/~bulatov/polyhedra/">Polyhedra Collection&lt;/a>&lt;/li>
&lt;li>George Hart&amp;rsquo;s &lt;a href="http://www.georgehart.com/virtual-polyhedra/vp.html">Encyclopaedia of
Polyhedra&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Thanks to &lt;a href="http://freewrl.sourceforge.net">FreeWRL&lt;/a> I managed to
finally find the
&lt;a href="http://www.physics.orst.edu/~bulatov/polyhedra/uniform/u20.html">cubohemioctahedron&lt;/a>,
which although the image on that linked page doesn&amp;rsquo;t look terribly
much like our shape, upon loading the
&lt;a href="http://www.physics.orst.edu/~bulatov/polyhedra/uniform/u20.wrl">VRML&lt;/a>
for the polyhedron I was able to convince myself that this is the one.&lt;/p></description></item><item><title>welcome to 1998</title><link>https://spacepants.org/blog/welcome-to-1998/</link><pubDate>Sun, 14 May 2006 00:47:00 +1000</pubDate><guid>https://spacepants.org/blog/welcome-to-1998/</guid><description>
&lt;p>My PCMCIA wireless card was flaking out today, after I got my IrDA port working; I&amp;rsquo;d reconfigued the port in the BIOS to use IRQ 3. After some time I found that the wireless card was also choosing IRQ 3, thanks to the output from &lt;code>dmesg&lt;/code>. Pushing the IrDA port to IRQ 4 fixed that little conflict. I&amp;rsquo;m still left wondering why in this day and age I&amp;rsquo;m editing IRQ settings on a laptop that&amp;rsquo;s barely a year old.&lt;/p>
&lt;p>I started out trying to get &lt;a href="http://www.flickr.com/photos/spacejaq/">photos&lt;/a> off of my phone, which I&amp;rsquo;ve successfully done in the past but always forget how; today the phone and laptop just refused to connect. The cause of the problem was that the &lt;code>serial8250&lt;/code> driver was grabbing the IO ports and IRQ first, and preventing the &lt;code>nsc-ircc&lt;/code> driver from getting it later in the boot process. I &lt;em>think&lt;/em> the fix for that was to put &lt;code>nsc-ircc&lt;/code> at the top of &lt;code>/etc/modules&lt;/code>; at least, it&amp;rsquo;s working now.&lt;/p>
&lt;p>As I was rolling on my &amp;ldquo;fix things that are pissing me off&amp;rdquo; rampage, I decided to crack at the &amp;ldquo;why the hell isn&amp;rsquo;t DMA working&amp;rdquo; one. The X40 has an ICH4-M chipset, handled by the &lt;code>piix&lt;/code> driver. The driver was loading, but &lt;code>hdparm&lt;/code> was always getting this:&lt;/p>
&lt;pre>&lt;code># hdparm -d 1 /dev/hda
/dev/hda:
setting using_dma to 1 (on)
HDIO_SET_DMA failed: Operation not permitted
using_dma = 0 (off)
&lt;/code>&lt;/pre>
&lt;p>After spending way too long following threads full of erroneous opinion on the ubuntu forums, debian-user mailing list archives, and other useful tomes, I discovered this clue in &lt;code>dmesg&lt;/code>:&lt;/p>
&lt;pre>&lt;code>ICH4: port 0x01f0 already claimed by ide0
&lt;/code>&lt;/pre>
&lt;p>which led me to &lt;a href="http://lists.debian.org/debian-kernel/2006/02/msg00504.html">this post&lt;/a>, cluing me to the fact that the &lt;code>ide-generic&lt;/code> driver was being greedy. Again, a driver conflict, but to fix this one, I had to put &lt;code>piix&lt;/code> at the top of &lt;code>/etc/mkinitramfs/modules&lt;/code> and then rebuild the initrd (running &lt;code>dpkg-reconfigure&lt;/code> on your kernel package will take care of this). So, thanks Christophe, for your bug report that finally helped solve this one, and now holy shit my computer is fast.&lt;/p>
&lt;p>&lt;a href="http://abc.net.au/rage/playlist/archive/2006/20060513.htm">Datarock programmed Rage&lt;/a> tonight, too, which was an awesome soundtrack to fix things to ;-) &lt;a href="http://achmed.livejournal.com/110664.html">They and I have such similar tastes in music&lt;/a>.&lt;/p></description></item><item><title>NET-SNMP Linux disk IO collector, mark III</title><link>https://spacepants.org/blog/net-snmp-linux-diskio-collector-3/</link><pubDate>Fri, 12 May 2006 17:02:00 +1000</pubDate><guid>https://spacepants.org/blog/net-snmp-linux-diskio-collector-3/</guid><description>
&lt;p>Ok, this might be the last one ;-)&lt;/p>
&lt;p>As &lt;a href="https://spacepants.org/blog/net-snmp-linux-diskio-collector">mentioned&lt;/a> &lt;a href="https://spacepants.org/blog/net-snmp-linux-diskio-collector-2">previously&lt;/a>, I&amp;rsquo;ve been writing a script that &lt;code>net-snmp&lt;/code> can pass off the &lt;code>UCD-DISKIO-MIB&lt;/code> requests to, for those versions that don&amp;rsquo;t have it built in, and to save one from rebuilding &lt;code>net-snmp&lt;/code> packages.&lt;/p>
&lt;p>This time around, I&amp;rsquo;ve added support for 2.4 series kernels, which use &lt;code>/proc/partitions&lt;/code> instead of &lt;code>/proc/diskstats&lt;/code> to log the disk IO stats.&lt;/p>
&lt;p>I&amp;rsquo;ve now got this running in production across a handful of machines, and it&amp;rsquo;s pretty neat &amp;ndash; I am able to see the shape of disk traffic during a vacuum on one of our machines, alongside the CPU and memory usage, and load average graphs, for example.&lt;/p>
&lt;p>As before, plop it into &lt;code>/usr/local/sbin/snmp-diskio-collector&lt;/code> and add this line to your &lt;code>snmpd.conf&lt;/code>:&lt;/p>
&lt;pre>&lt;code>pass .1.3.6.1.4.1.2021.13.15 /usr/local/sbin/snmp-diskio-collector
&lt;/code>&lt;/pre>
&lt;p>Here&amp;rsquo;s the script:&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 34
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 35
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 36
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 37
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 38
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 39
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 40
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 41
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 42
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 43
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 44
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 45
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 46
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 47
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 48
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 49
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 50
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 51
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 52
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 53
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 54
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 55
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 56
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 57
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 58
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 59
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 60
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 61
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 62
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 63
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 64
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 65
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 66
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 67
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 68
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 69
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 70
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 71
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 72
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 73
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 74
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 75
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 76
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 77
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 78
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 79
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 80
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 81
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 82
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 83
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 84
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 85
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 86
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 87
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 88
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 89
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 90
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 91
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 92
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 93
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 94
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 95
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 96
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 97
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 98
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 99
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">100
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">101
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">102
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">103
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">104
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">105
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">106
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">107
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">108
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">109
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">110
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">111
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">112
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">113
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">114
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">115
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">116
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">117
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">118
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">119
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">120
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">121
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">122
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">123
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">124
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">125
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">126
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">127
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">128
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">129
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">130
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">131
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">132
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">133
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">134
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">135
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">136
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">137
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">138
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">139
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">140
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">141
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">142
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">143
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">144
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">145
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">146
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># by Jamie Wilkinson &amp;lt;jamie@anchor.net.au&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># this code is in the public domain&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># based on passtest from the net-snmp distribution examples&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># WARNING there is shitloads of IO required to get this information :)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">debug_flag&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#666">()&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#bb60d5">$debug_flag&lt;/span> -eq &lt;span style="color:#40a070">1&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$*&lt;/span> &amp;gt;&amp;gt; /tmp/snmp-diskio-collector-debug.log
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">PLACE&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;.1.3.6.1.4.1.2021.13.15&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">REQ&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$2&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;new run&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;args &lt;/span>&lt;span style="color:#bb60d5">$*&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;-s&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># the &amp;#39;tail&amp;#39; of the oid, everything after $PLACE&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">oidtail&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$REQ&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#34;s/^&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#4070a0">//&amp;#34;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;oidtail=&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># number of devices we can export&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -f /proc/diskstats &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">devcount&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>wc -l /proc/diskstats | sed &lt;span style="color:#4070a0">&amp;#39;s/^ *//&amp;#39;&lt;/span> | cut -f1 -d&lt;span style="color:#4070a0">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">devcount&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>wc -l /proc/partitions | sed &lt;span style="color:#4070a0">&amp;#39;s/^ *//&amp;#39;&lt;/span> | cut -f1 -d&lt;span style="color:#4070a0">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">devcount&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$devcount&lt;/span> - 2&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;devcount=&lt;/span>&lt;span style="color:#bb60d5">$devcount&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oidtail&lt;/span> | cut -f4 -d.&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oidtail&lt;/span> | cut -f5 -d.&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;oidtail=&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">, item=&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">, index=&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;-n&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -z &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">elif&lt;/span> &lt;span style="color:#666">[&lt;/span> -z &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$index&lt;/span> + 1&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> -gt &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$devcount&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$item&lt;/span> + 1&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> -gt &lt;span style="color:#40a070">6&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># break out of the loop&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.&lt;span style="color:#bb60d5">$item&lt;/span>.&lt;span style="color:#bb60d5">$index&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">case&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$REQ&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$REQ&lt;/span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;after -n, item=&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">, index=&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;RET is now &lt;/span>&lt;span style="color:#bb60d5">$RET&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$RET&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;oidtail=&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">, item=&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">, index=&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># awk uses this variable in the environment below&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">export&lt;/span> index
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># see linux kernel Documentation/iostats.txt for format&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -n &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">case&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 1&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOIndex&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIOIndex &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;integer&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$index&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 2&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIODevice&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIODevice &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;string&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -f /proc/diskstats &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $3 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] + 2 { print $4 }&amp;#39;&lt;/span> /proc/partitions
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 3&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIONRead&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIONRead &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -f /proc/diskstats &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $6 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] + 2 { print $7 }&amp;#39;&lt;/span> /proc/partitions
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 4&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIONWritten&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIONWritten &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -f /proc/diskstats &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $10 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] + 2 { print $11 }&amp;#39;&lt;/span> /proc/partitions
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 5&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOReads&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIOReads &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -f /proc/diskstats &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $4 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] + 2 { print $5 }&amp;#39;&lt;/span> /proc/partitions
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 6&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOWrites&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIOWrites &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -f /proc/diskstats &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $8 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] + 2 { print $9 }&amp;#39;&lt;/span> /proc/partitions
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0; &lt;span style="color:#60a0b0;font-style:italic">#echo &amp;#34;string&amp;#34;; echo &amp;#34;debug... $RET $REQ&amp;#34;; exit 0 ;;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>NET-SNMP Linux disk IO collector, mark II</title><link>https://spacepants.org/blog/net-snmp-linux-diskio-collector-2/</link><pubDate>Fri, 12 May 2006 14:28:00 +1000</pubDate><guid>https://spacepants.org/blog/net-snmp-linux-diskio-collector-2/</guid><description>
&lt;p>This is an improvement on a &lt;a href="https://spacepants.org/blog/net-snmp-linux-diskio-collector">previous&lt;/a> post on adding UCD-DISKIO-MIB support to older versions of &lt;code>net-snmp&lt;/code>; this one actually works on 2.6 kernels (and thanks to &lt;a href="http://hardy.dropbear.id.au">Pete&lt;/a> loses about a million forks and pipes).&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 34
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 35
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 36
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 37
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 38
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 39
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 40
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 41
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 42
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 43
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 44
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 45
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 46
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 47
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 48
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 49
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 50
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 51
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 52
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 53
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 54
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 55
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 56
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 57
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 58
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 59
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 60
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 61
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 62
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 63
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 64
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 65
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 66
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 67
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 68
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 69
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 70
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 71
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 72
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 73
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 74
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 75
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 76
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 77
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 78
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 79
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 80
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 81
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 82
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 83
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 84
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 85
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 86
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 87
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 88
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 89
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 90
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 91
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 92
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 93
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 94
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 95
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 96
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 97
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 98
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 99
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">100
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">101
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">102
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">103
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">104
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">105
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">106
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">107
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">108
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">109
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">110
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">111
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">112
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">113
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">114
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">115
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">116
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">117
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">118
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">119
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">120
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">121
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># by Jamie Wilkinson &amp;lt;jamie@anchor.net.au&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># this code is in the public domain&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># based on passtest from the net-snmp distribution examples&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># WARNING there is shitloads of IO required to get this information :)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">debug_flag&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#666">()&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#bb60d5">$debug_flag&lt;/span> -eq &lt;span style="color:#40a070">1&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$*&lt;/span> &amp;gt;&amp;gt; /tmp/snmp-diskio-collector-debug.log
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">PLACE&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;.1.3.6.1.4.1.2021.13.15&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">REQ&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$2&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;new run&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;args &lt;/span>&lt;span style="color:#bb60d5">$*&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;-s&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># the &amp;#39;tail&amp;#39; of the oid, everything after $PLACE&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">oidtail&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$REQ&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#34;s/^&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#4070a0">//&amp;#34;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;oidtail=&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># number of devices we can export&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">devcount&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>wc -l /proc/diskstats | cut -f1 -d&lt;span style="color:#4070a0">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;devcount=&lt;/span>&lt;span style="color:#bb60d5">$devcount&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oidtail&lt;/span> | cut -f4 -d.&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oidtail&lt;/span> | cut -f5 -d.&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;oidtail=&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">, item=&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">, index=&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;-n&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -z &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">elif&lt;/span> &lt;span style="color:#666">[&lt;/span> -z &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$index&lt;/span> + 1&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> -gt &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$devcount&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$item&lt;/span> + 1&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> -gt &lt;span style="color:#40a070">6&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># break out of the loop&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.&lt;span style="color:#bb60d5">$item&lt;/span>.&lt;span style="color:#bb60d5">$index&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">case&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$REQ&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$REQ&lt;/span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;after -n, item=&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">, index=&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;RET is now &lt;/span>&lt;span style="color:#bb60d5">$RET&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$RET&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>debug &lt;span style="color:#4070a0">&amp;#34;oidtail=&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">, item=&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">, index=&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># awk uses this variable in the environment below&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">export&lt;/span> index
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># see linux kernel Documentation/iostats.txt for format&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> -n &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">case&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 1&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOIndex&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIOIndex &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;integer&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$index&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 2&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIODevice&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIODevice &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;string&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $3 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 3&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIONRead&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIONRead &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $6 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 4&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIONWritten&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIONWritten &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $10 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 5&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOReads&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIOReads &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $4 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 6&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOWrites&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> debug &lt;span style="color:#4070a0">&amp;#34;result: diskIOWrites &lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> awk &lt;span style="color:#4070a0">&amp;#39;FNR == ENVIRON[&amp;#34;index&amp;#34;] { print $8 }&amp;#39;&lt;/span> /proc/diskstats
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0; &lt;span style="color:#60a0b0;font-style:italic">#echo &amp;#34;string&amp;#34;; echo &amp;#34;debug... $RET $REQ&amp;#34;; exit 0 ;;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>NET-SNMP Linux disk IO collector</title><link>https://spacepants.org/blog/net-snmp-linux-diskio-collector/</link><pubDate>Wed, 10 May 2006 15:35:00 +1000</pubDate><guid>https://spacepants.org/blog/net-snmp-linux-diskio-collector/</guid><description>
&lt;p>The UCD-DISKIO-MIB isn&amp;rsquo;t supported in versions of &lt;code>net-snmp&lt;/code> on most stable distros, but a few tools have add-on support for trending the data returned by this MIB.&lt;/p>
&lt;p>You can recompile &lt;code>net-snmp&lt;/code>, but I find that both boring and a maintenance headache; I don&amp;rsquo;t manually build anything that the vendor already supplies unless there&amp;rsquo;s a really good reason. Disk IO statistics are not a good enough reason to recompile.&lt;/p>
&lt;p>The other option, the one I took, is to use the &lt;code>pass&lt;/code> option of &lt;code>snmpd.conf&lt;/code> to offload the collection of a MIBOID subtree to an external program. The API is pretty simple but there&amp;rsquo;s a dearth of examples on the interweb, so I present this one hopefully as an example for others to extend their own MIBs and also in the hope that others wanting to analyse disk performance find it useful.&lt;/p>
&lt;p>It&amp;rsquo;s not terribly efficient, each call of the script looks at
&lt;code>/proc/diskstats&lt;/code> twice, there&amp;rsquo;s some tens of forks involved, and if
you&amp;rsquo;re walking the subtree, that means you can be calling this script
6 times for every block device in the system. However, if you want
efficiency, rather than trying to optimise this script, just rebuild
&lt;code>net-snmp&lt;/code>. My goal was to &lt;em>reduce&lt;/em> deployment effort.&lt;/p>
&lt;div class="highlight">&lt;div style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 34
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 35
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 36
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 37
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 38
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 39
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 40
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 41
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 42
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 43
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 44
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 45
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 46
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 47
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 48
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 49
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 50
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 51
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 52
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 53
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 54
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 55
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 56
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 57
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 58
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 59
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 60
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 61
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 62
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 63
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 64
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 65
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 66
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 67
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 68
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 69
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 70
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 71
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 72
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 73
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 74
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 75
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 76
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 77
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 78
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 79
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 80
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 81
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 82
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 83
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 84
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 85
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 86
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 87
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 88
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 89
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 90
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 91
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 92
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 93
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 94
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 95
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 96
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 97
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 98
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 99
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">100
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">101
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">102
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># /usr/local/sbin/snmp-diskio-collector &lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># by Jamie Wilkinson &amp;lt;jamie@anchor.net.au&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># this code is in the public domain&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># based on passtest from the net-snmp distribution examples&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># WARNING there is shitloads of IO required to get this information :)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">PLACE&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;.1.3.6.1.4.1.2021.13.15&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">REQ&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$2&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;-s&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># the &amp;#39;tail&amp;#39; of the oid, everything after $PLACE&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">oidtail&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$REQ&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#34;s/^&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#4070a0">//&amp;#34;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># number of devices we can export&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">devcount&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>wc -l /proc/diskstats | cut -f1 -d&lt;span style="color:#4070a0">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;-n&amp;#34;&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">case&lt;/span> x&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$oidtail&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> x|x.1|x.1.1|x.1.1.1&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># bootstrap a snmpwalk to the first index and item&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.1.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> x.1.1.*.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># item is the element of the diskIOEntry&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oidtail&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#39;s/\.1\.1\.\([^\.]*\)\..*/\1/&amp;#39;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># index is our disk table entry&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">index&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oidtail&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#39;s/\.1\.1\.[^\.]*\.\(.*\)/\1/&amp;#39;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$index&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> -eq &lt;span style="color:#bb60d5">$devcount&lt;/span> &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># wrap to the next item, starting at index 1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$item&lt;/span> + 1&lt;span style="color:#4070a0">`&lt;/span>.1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#666">[&lt;/span> x&lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$item&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> &lt;span style="color:#666">=&lt;/span> x7 &lt;span style="color:#666">]&lt;/span>; &lt;span style="color:#007020;font-weight:bold">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># bail because we aren&amp;#39;t doing more than 6 items in the&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># entry&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.&lt;span style="color:#bb60d5">$item&lt;/span>.&lt;span style="color:#4070a0">`&lt;/span>expr &lt;span style="color:#bb60d5">$index&lt;/span> + 1&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">case&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$REQ&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#bb60d5">RET&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$REQ&lt;/span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$RET&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">oid&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$RET&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#34;s/^&lt;/span>&lt;span style="color:#bb60d5">$PLACE&lt;/span>&lt;span style="color:#4070a0">//&amp;#34;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">item&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$oid&lt;/span> | sed &lt;span style="color:#4070a0">&amp;#34;s/.*\.//&amp;#34;&lt;/span>&lt;span style="color:#4070a0">`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># see linux kernel Documentation/iostats.txt for format&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">case&lt;/span> &lt;span style="color:#4070a0">&amp;#34;&lt;/span>&lt;span style="color:#bb60d5">$RET&lt;/span>&lt;span style="color:#4070a0">&amp;#34;&lt;/span> in
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span> ;; &lt;span style="color:#60a0b0;font-style:italic">#diskIOTable&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span> ;; &lt;span style="color:#60a0b0;font-style:italic">#diskIOEntry&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.1&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0;; &lt;span style="color:#60a0b0;font-style:italic">#diskIOIndex&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.1.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOIndex&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;integer&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">$item&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.2.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIODevice&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;string&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> head -n &lt;span style="color:#bb60d5">$item&lt;/span> /proc/diskstats | tail -n &lt;span style="color:#40a070">1&lt;/span> | awk &lt;span style="color:#4070a0">&amp;#39;{print $3}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.3.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIONRead&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> head -n &lt;span style="color:#bb60d5">$item&lt;/span> /proc/diskstats | tail -n &lt;span style="color:#40a070">1&lt;/span> | awk &lt;span style="color:#4070a0">&amp;#39;{print $6}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.4.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIONWritten&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> head -n &lt;span style="color:#bb60d5">$item&lt;/span> /proc/diskstats | tail -n &lt;span style="color:#40a070">1&lt;/span> | awk &lt;span style="color:#4070a0">&amp;#39;{print $10}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.5.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOReads&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> head -n &lt;span style="color:#bb60d5">$item&lt;/span> /proc/diskstats | tail -n &lt;span style="color:#40a070">1&lt;/span> | awk &lt;span style="color:#4070a0">&amp;#39;{print $4}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#bb60d5">$PLACE&lt;/span>.1.1.6.*&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic"># diskIOWrites&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#4070a0">&amp;#34;counter&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> head -n &lt;span style="color:#bb60d5">$item&lt;/span> /proc/diskstats | tail -n &lt;span style="color:#40a070">1&lt;/span> | awk &lt;span style="color:#4070a0">&amp;#39;{print $8}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">exit&lt;/span> &lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> *&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#007020">exit&lt;/span> 0;;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">esac&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This script gets hooked into &lt;code>snmpd.conf&lt;/code> with a line like the following:&lt;/p>
&lt;pre>&lt;code>pass .1.3.6.1.4.1.2021.13.15 /usr/local/sbin/snmp-diskio-collector
&lt;/code>&lt;/pre>
&lt;p>and then you can query the MIB with either&lt;/p>
&lt;pre>&lt;code>snmpwalk -m ALL -v1 -c public localhost diskIOTable
&lt;/code>&lt;/pre>
&lt;p>or&lt;/p>
&lt;pre>&lt;code>snmptable -m ALL -v1 -c public localhost diskIOTable
&lt;/code>&lt;/pre>
&lt;p>(but of course you wouldn&amp;rsquo;t use SNMPv1 or v2c if you were going over
the internets, right? :-)&lt;/p>
&lt;p>Now you can tie this into &lt;a href="http://www.cacti.net">Cacti&lt;/a> or your
favourite NMS, such as &lt;a href="http://forums.cacti.net/about8777-0-asc-0.html">described here&lt;/a>.&lt;/p></description></item><item><title>cross-language bindings, marshalling, a thought experiment in evile</title><link>https://spacepants.org/blog/cross-language-marshalling/</link><pubDate>Sat, 29 Apr 2006 14:02:00 +1000</pubDate><guid>https://spacepants.org/blog/cross-language-marshalling/</guid><description>
&lt;p>Last night, &lt;a href="http://www.mega-nerd.com/erikd/Blog/">Statler&lt;/a> and
&lt;a href="http://www.hezmatt.org/~mpalmer/blog/general/">Waldorf&lt;/a> talked about
free software that they hated on the &lt;a href="http://james.k-sit.com/articles.php?s_id=58&amp;amp;art_id=252">LA live
broadcast&lt;/a>,
including a lovely piece of work Erik mentioned, called
&lt;a href="http://www.mega-nerd.com/erikd/Blog/WTF/intelib.html">intelib&lt;/a>.&lt;/p>
&lt;p>Putting LISP-like syntax into C++ didn&amp;rsquo;t actually surprise me, maybe
that&amp;rsquo;s more of an insight into my own dark and twisted past than
anything, there&amp;rsquo;s plenty of cross-language stuff around; in fact as
Matt went on a small tangent talking about wrapping APIs for the
purposes of adding functionality to other languages, I realised I
could concoct something evil enough to make them both vomit.&lt;/p>
&lt;p>Conveniently, James Dumay later talked about Mono and .NET, and showed a few
languages that are usable with the language/platform. Again, not
surprising, I did some stuff with marshalling types and function calls
between C and Haskell back at uni, and as long as you keep all the
type information around (as .NET CIL does) it&amp;rsquo;s pretty easy. However,
you don&amp;rsquo;t need .NET to marshall data and function calls between languages:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="http://www.boost.org/libs/python/doc/">Boost.Python&lt;/a> is &amp;ldquo;A C++ library which enables seamless interoperability between C++ and the Python programming language.&amp;rdquo; It lets you call Python methods and objects from C++ code as if they were native to the language.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="http://www.python.org/moin/PyPerl">PyPerl&lt;/a> is &amp;ldquo;a module for integrating Perl in Python.&amp;rdquo; Again, it lets you use a Perl module&amp;rsquo;s API directly within your Python code.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The &lt;a href="http://cpan.uwinnipeg.ca/search?query=inline%3A%3A&amp;amp;mode=dist">Inline::&lt;/a> series of Perl modules allow you to &amp;ldquo;write Perl subroutines in X&amp;rdquo;, for values of X such as assembler, awk, Basic, Befunge, C, Interp, Java, Lua, Octave, Parrot, &lt;a href="http://cpan.uwinnipeg.ca/module/Inline::Perl">Perl&lt;/a> (wtf?), Python, Ruby, SLang, Tcl, and a whole lot of other shit that you&amp;rsquo;ll never want to use. So, you can call functions in language X from within Perl.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>I think you can see where I&amp;rsquo;m going with this. It&amp;rsquo;s possible to write
a piece of code in C++ that links Boost.Python to import PyPerl to use
Inline::Lua to call functions in a binding around a C library. That&amp;rsquo;s
just the tip of the iceberg ;-)&lt;/p></description></item><item><title>backporting OpenLDAP to old distros</title><link>https://spacepants.org/blog/backporting-openldap-to-old-distros/</link><pubDate>Mon, 24 Apr 2006 11:06:00 +1000</pubDate><guid>https://spacepants.org/blog/backporting-openldap-to-old-distros/</guid><description>
&lt;p>It would be really nice if those projects that consider themselves to
be major pieces of infrastructure made it a priority to support a
build on stock vendor releases of distros older than the current release.&lt;/p>
&lt;p>I&amp;rsquo;m trying to build OpenLDAP 2.3.20 on Red Hat 7.3. Yes, that sounds
like a bad idea, and it &lt;em>is&lt;/em> quite painful. This pain is mostly due
to the versions of Berkeley DB that OpenLDAP wants to build against:
Red Hat 7.3 shipped with BDB 3.3.11; &lt;a href="http://www.openldap.org/faq/data/cache/44.html">it wants at least version
4&lt;/a>, bud the
&lt;code>configure&lt;/code> script makes it quite clear that version 4.1 isn&amp;rsquo;t supported.&lt;/p>
&lt;p>The LDBM backend can use older BDBs, i.e. 3.x, but &lt;a href="http://www.openldap.org/faq/data/cache/84.html">OpenLDAP 2.2
deprecates the LDBM
backend&lt;/a> in favour of
&lt;code>back-bdb&lt;/code>. That concerns me for a 2.3 series build of OpenLDAP: is
it still stable, or has it been left to rot? Of course the test suite
doesn&amp;rsquo;t cover the LDBM backend&amp;hellip;&lt;/p>
&lt;p>I would really like to not have to build for this old OS version, but
sadly it exists and it needs to be a &lt;code>syncrepl&lt;/code> slave. In order to
finally put this box to rest it &lt;em>needs&lt;/em> to have OpenLDAP running, so
that we can migrate the services off it with minimal outage. There are so
many bugs running OpenLDAP on this box that I want to try this 2.3
series version on it, in the same configuration as every other machine
on the network, but the more I work on it, the less it feels like I&amp;rsquo;m
making any progress on it at all.&lt;/p>
&lt;p>Fighting version dependencies on old distros just isn&amp;rsquo;t fun. If the
developers had considered this, and &lt;em>at least&lt;/em> said &amp;ldquo;OK, well, RHEL
2.1 is still supported by Red Hat, so let&amp;rsquo;s try to configure and build
on that platform too&amp;rdquo; then this wouldn&amp;rsquo;t nearly be as painful as it
is.&lt;/p>
&lt;p>The &lt;a href="http://annodex.net">Annodex&lt;/a> developers, as well as
Conrads&amp;rsquo; other projects (&lt;a href="http://metadecks.org">sweep, etc&lt;/a>) work
quite hard to make sure they build with the versions of libraries on
peoples year-old desktop machines; sure this means sometimes having
extra code to cope with API differences, but a small amount of effort
on the developers part makes a massive difference when you think about
how much time the users will spend trying to get the software to build.&lt;/p>
&lt;p>It&amp;rsquo;s the little things that make the difference between an OK project and an Awesome project.&lt;/p>
&lt;p>Meanwhile, on the cutting edge of RHEL 3 and 4, and FC 4 and 5,
latest OpenLDAP 2.3 looks to be a really promising piece of work, the
amount of work that&amp;rsquo;s gone into it since 2.2.27 is impressive, and if it
works nearly as well as it looks like it will from the ChangeLog, then
that&amp;rsquo;ll finally put to rest a whole lot of problems we&amp;rsquo;ve had since
deploying LDAP as our authentication database.&lt;/p>
&lt;p>Fingers crossed I can get there without losing all my hair.&lt;/p></description></item></channel></rss>