<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.turnkeylinux.org/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0" xml:base="http://www.turnkeylinux.org/blog/admin">
  <channel>
    <title>Blog</title>
    <link>http://www.turnkeylinux.org/blog/admin</link>
    <description />
    <language>en</language>
          <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.turnkeylinux.org/turnkeylinux-blog" /><feedburner:info uri="turnkeylinux-blog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>turnkeylinux-blog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
    <title>SSH session slow to start? It's the DNS stupid!</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/DGINygqvHUE/slow-ssh</link>
    <description>&lt;p&gt;Ever tried logging into a machine with ssh and found you have to wait much longer than reasonable for the session to start? This happened to me a few times and was especially annoying with machines on my local network (or a VM attached to a virtual network) that should be letting me in immediately.&lt;/p&gt;
&lt;p&gt;I eventually got mad enough to strace the SSH daemon and debug what was going on and it turns out it's a DNS thing. Basically the session is slow to start because the SSH server is trying to lookup the hostname of the SSH client and for whatever reason it's timing out (e.g., it can't reach a nameserver, because you happen to be offline)&lt;/p&gt;
&lt;p&gt;There are a couple of very simple ways to fix that:&lt;/p&gt;
&lt;ul class="simple"&gt;
    &lt;li&gt;add &amp;quot;UseDNS no&amp;quot; to /etc/ssh/sshd_config&lt;/li&gt;
    &lt;li&gt;add the client's net address to the server's /etc/hosts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Was that helpful? Has this ever happened to you? &lt;a href="#comment-form"&gt;Post a comment&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/slow-ssh" dc:identifier="http://www.turnkeylinux.org/blog/slow-ssh" dc:title="SSH session slow to start? It&amp;#039;s the DNS stupid!" trackback:ping="http://www.turnkeylinux.org/trackback/1007" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/DGINygqvHUE" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/slow-ssh#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/admin">admin</category>
 <category domain="http://www.turnkeylinux.org/blog/ssh">ssh</category>
 <pubDate>Mon, 08 Mar 2010 11:10:42 +0000</pubDate>
 <dc:creator>Liraz Siri</dc:creator>
 <guid isPermaLink="false">1007 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/slow-ssh</feedburner:origLink></item>
  <item>
    <title>Tweeting for news-a-holics</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/tgZG_3TZQG0/tweetflow</link>
    <description>&lt;p&gt;Hi, my name is Alon and I'm a &lt;em&gt;news-a-holic&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Lets face it, some of us have addictions, be it alcoholism, nicotine, gambling, over-eating, television, or even just mowing the lawn. Mine is being up to date with the latest news.&lt;/p&gt;
&lt;p&gt;Some addictions are worse than others in being self-destructive, and as you already know, identifying and acknowledging you have a problem is the first step. I am also a &lt;em&gt;work-a-holic&lt;/em&gt;, so when I realized that having to know the latest news was impacting my workflow, I needed to find a solution.&lt;/p&gt;
&lt;p&gt;Luckily, with a little self discipline I was able make a rule and stick with it: &lt;em&gt;&amp;quot;Access to RSS reader is off limits from work laptop&amp;quot;&lt;/em&gt;. The problem was how to still consume news, with the added bonus of easily sharing interesting articles on &lt;a href="http://www.twitter.com/alonswartz"&gt;Twitter&lt;/a&gt; that I come across (I am reading the news anyway, so why not share it).&lt;/p&gt;
&lt;p&gt;So, this is what I came up with.&lt;/p&gt;
&lt;p&gt;I have been a long fan of RSS, and have a long list of feeds set up in Google Reader, which I now only access from my mobile. When I come across something that is really interesting, I hit &lt;em&gt;&amp;quot;share&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;center&gt;&lt;img alt="" src="http://www.turnkeylinux.org/files/images/alon_reader.jpg" /&gt;&lt;/center&gt;
&lt;p&gt;Everything I share in Google Reader appears on my &lt;a href="http://www.google.com/reader/shared/alonswartz1"&gt;shared items page&lt;/a&gt;, which has an RSS feed of its own.&lt;/p&gt;
&lt;p&gt;Updating Twitter with an RSS feed is simple thanks to services like &lt;a href="http://twitterfeed.com"&gt;twitterfeed&lt;/a&gt;. I have also configured twitterfeed to use my &lt;a href="http://alonswartz.bit.ly"&gt;bit.ly account&lt;/a&gt; (URL shortener) so I can get click-through stats.&lt;/p&gt;
&lt;p&gt;Initially I planned on tweaking news items I share according to the stats, but in practice I don't. I don't even checkup on the stats, so I guess its just nice-to-have.&lt;/p&gt;
&lt;p&gt;So there you have it:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Sites that interest me are fed into Google Reader.&lt;/li&gt;
    &lt;li&gt;Articles that I share appear in my RSS&amp;nbsp;feed.&lt;/li&gt;
    &lt;li&gt;Twitterfeed picks up the RSS feed, shortens the links with bit.ly, and sends them to Twitter.&lt;/li&gt;
&lt;/ul&gt;
&lt;center&gt;&lt;img alt="" src="http://www.turnkeylinux.org/files/images/tweetflow.jpg" /&gt;&lt;/center&gt;
&lt;p&gt;&lt;strong&gt;Do you have an addiction? How do you tweet? &lt;a href="#comment-form"&gt;Leave a comment&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/tweetflow" dc:identifier="http://www.turnkeylinux.org/blog/tweetflow" dc:title="Tweeting for news-a-holics" trackback:ping="http://www.turnkeylinux.org/trackback/1062" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/tgZG_3TZQG0" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/tweetflow#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/blogging">blogging</category>
 <pubDate>Wed, 03 Mar 2010 10:30:17 +0000</pubDate>
 <dc:creator>Alon Swartz</dc:creator>
 <guid isPermaLink="false">1062 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/tweetflow</feedburner:origLink></item>
  <item>
    <title>Smack yourself if you don't use generic shell script hooks</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/flP86upM410/generic-shell-hooks</link>
    <description>&lt;p&gt;Smack yourself in the forehead if you don't use the following snippet (or an equivalent) in all custom shell scripts that could benefit from a hooking mechanism:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
run_scripts()
{
    for script in $1/*; do

        # skip non-executable snippets
        [ -x &amp;quot;$script&amp;quot; ] || continue

        # execute $script in the context of the current shell
        . $script
    done
}

run_scripts path/to/hooks.d
&lt;/pre&gt;
&lt;p&gt;I've found this pattern useful in nearly every sufficiently complex shell script I maintain, and I even use it to help manage my bashrc and xsession configurations:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ ls .bashrc.d/
paths git editor pager autologout qemu tmpdirs scratch

$ ls .xsession.d/
10-lang 10-tmpdir 90-bell 90-kbdrate 91-xscreensaver 99-fvwm-conf
&lt;/pre&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ol class="arabic"&gt;
    &lt;li&gt;
    &lt;p class="first"&gt;Modular: add or remove code without having to edit a big monolithic file.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
    &lt;p class="first"&gt;Order of execution is determined by the filename. Changing the order is as simple as changing a number prefix.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
    &lt;p class="first"&gt;Disable execution by removing execution bit:&lt;/p&gt;
    &lt;pre class="literal-block"&gt;
chmod -x .bashrc.d/git
&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These days breaking down configurations into separate modular files like this is common in the Linux world, so by now I expect many experienced users are wondering why I'm channeling Captain Obvious. Just keep in mind that many Linux newbies haven't yet learned all our best practices and some lessons are worth reteaching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why not share your own tricks? &lt;/strong&gt;&lt;a href="#comment-form"&gt;&lt;strong&gt;Post a comment&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/generic-shell-hooks" dc:identifier="http://www.turnkeylinux.org/blog/generic-shell-hooks" dc:title="Smack yourself if you don&amp;#039;t use generic shell script hooks" trackback:ping="http://www.turnkeylinux.org/trackback/1008" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/flP86upM410" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/generic-shell-hooks#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/development">development</category>
 <category domain="http://www.turnkeylinux.org/blog/shell">shell</category>
 <pubDate>Mon, 01 Mar 2010 07:01:10 +0000</pubDate>
 <dc:creator>Liraz Siri</dc:creator>
 <guid isPermaLink="false">1008 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/generic-shell-hooks</feedburner:origLink></item>
  <item>
    <title>Background task processing and deferred execution in Django</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/LXOVlubhh5I/django-celery-rabbitmq</link>
    <description>&lt;p&gt;Or, Celery + RabbitMQ = Django awesomeness!&lt;/p&gt;
&lt;p&gt;As you know, &lt;a href="http://www.djangoproject.org"&gt;Django&lt;/a&gt; is synchronous, or blocking. This means each request will not be returned until all processing (e.g., of a view) is complete. It's the expected behavior and usually required in web applications, but there are times when you need tasks to run in the background (immediately, deferred, or periodically) without blocking.&lt;/p&gt;
&lt;p&gt;Some common use cases:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Give the impression of a really snappy web application by finishing a request as soon as possible, even though a task is running in the background, then update the page incrementally using AJAX.&lt;/li&gt;
    &lt;li&gt;Executing tasks asynchronously and using retries to make sure they are completed successfully.&lt;/li&gt;
    &lt;li&gt;Scheduling periodic tasks.&lt;/li&gt;
    &lt;li&gt;Parallel execution (to some degree).&lt;/li&gt;
&lt;/ul&gt;
&amp;lt;!--break--&gt;
&lt;p&gt;There have been multiple requests to add asynchronous support to Django, namely via the python threading module, and even the multiprocessing module released in Python2.6, but I doubt it will happen any time soon, actually I doubt it will ever happen.&lt;/p&gt;
&lt;p&gt;This is a common problem for many, and after scouring over many forum posts the following proposed solution keeps popping up, which reminds of me of the saying &lt;em&gt;&amp;quot;when all you have is a hammer, everything looks like a nail&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Create a table in the database to store tasks.&lt;/li&gt;
    &lt;li&gt;Setup a cron job to trigger processing of said tasks.&lt;/li&gt;
    &lt;li&gt;Bonus: Create an API for task management and monitoring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well, you can do it like that, but it usually leads to ugly, coupled code, which can become very complex over time, not very flexible, doesn't scale well, and generally a bad idea.&lt;/p&gt;
&lt;p&gt;In my opinion, it ultimately comes down to &lt;a href="http://en.wikipedia.org/wiki/Separation_of_concerns"&gt;seperation of concerns&lt;/a&gt;. I recently fell in love with the message queuing world (&lt;a href="http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol"&gt;AMQP&lt;/a&gt;), in particular &lt;a href="http://www.rabbitmq.com"&gt;RabbitMQ&lt;/a&gt;, which can be used as an integral part of a really elegant solution for this issue, especially when coupled with &lt;a href="http://github.com/ask/celery"&gt;Celery&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Define a task.&lt;/li&gt;
    &lt;li&gt;Send it to a processing queue.&lt;/li&gt;
    &lt;li&gt;Let other code handle the processing.&lt;br /&gt;
    &amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What is Celery&lt;/h2&gt;
&lt;p&gt;Celery is a task queue system based on distributed message passing.&amp;nbsp; Originally developed for Django, it can now be used in any Python project.&lt;/p&gt;
&lt;p&gt;It's focused on real-time operation, but supports scheduling as well. The execution units, called tasks, are executed concurrently on a single (or multiple) worker server. Tasks can execute asynchronously (in the background) or synchronously (wait until ready).&lt;/p&gt;
&lt;p&gt;Celery provides a powerful and flexible interface to defining, executing, managing and monitoring tasks. If you have a use-case, chances are you can do it with Celery.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Installation and configuration&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Install Celery&lt;/h3&gt;
&lt;p&gt;One of Celery's dependencies is the multiprocessing module released in Python2.6. If you have an earlier version, such as Python2.5, you're in luck as the module has been backported.&lt;/p&gt;
&lt;p&gt;When installing the backported module, it will need to be compiled, so lets install the required support.&lt;/p&gt;
&lt;pre&gt;
apt-get install gcc python-dev
&lt;/pre&gt;
&lt;p&gt;Now we are ready to install celery, lets install a few more dependencies and let easy_install take care of the rest.&lt;/p&gt;
&lt;pre&gt;
apt-get install python-setuptools python-simplejson
easy_install celery
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Install RabbitMQ&lt;/h3&gt;
&lt;p&gt;Celery's recommended message broker is RabbitMQ.&lt;/p&gt;
&lt;p&gt;RabbitMQ is a complete and highly reliable enterprise messaging system based on the emerging AMQP standard. It is based on a proven platform, offers exceptionally high reliability, availability and scalability.&lt;/p&gt;
&lt;p&gt;In the below example, I will download and install the latest release (at time of writing), but you should check their &lt;a href="http://www.rabbitmq.com/server.html"&gt;download page&lt;/a&gt; for newer versions and/or support for your platform.&lt;/p&gt;
&lt;p&gt;Note: Installation will fail if there are missing dependencies. Because of this, we use the --fix-broken workaround.&lt;/p&gt;
&lt;pre&gt;
wget &lt;a href="http://www.rabbitmq.com/releases/rabbitmq-server/v1.7.2/rabbitmq-server_1.7.2-1_all.deb" title="http://www.rabbitmq.com/releases/rabbitmq-server/v1.7.2/rabbitmq-server_1.7.2-1_all.deb"&gt;http://www.rabbitmq.com/releases/rabbitmq-server/v1.7.2/rabbitmq-server_...&lt;/a&gt;
dpkg -i rabbit-server_1.7.2-1_all.deb
apt-get --fix-broken install
&lt;/pre&gt;
&lt;p&gt;The default installation includes a &lt;em&gt;guest&lt;/em&gt; user with the password of &lt;em&gt;guest&lt;/em&gt;. Don't be fooled by the wording of the account, guest has full permissions on the default virtual host called &lt;em&gt;/&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We will use the default configuration below, but you are encouraged to &lt;a href="http://www.rabbitmq.com/admin-guide.html#access-control"&gt;tweak your setup&lt;/a&gt;.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Configure Django project to use Celery/RabbitMQ&lt;/h3&gt;
&lt;p&gt;Add the following to settings.py&lt;/p&gt;
&lt;pre&gt;
BROKER_HOST = &amp;quot;127.0.0.1&amp;quot;
BROKER_PORT = 5672
BROKER_VHOST = &amp;quot;/&amp;quot;
BROKER_USER = &amp;quot;guest&amp;quot;
BROKER_PASSWORD = &amp;quot;guest&amp;quot;

INSTALLED_APPS = (
    ...
    'celery',
)
&lt;/pre&gt;
&lt;p&gt;Synchronize the database&lt;/p&gt;
&lt;pre&gt;
python manage.py syncdb
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Sample code&lt;/h2&gt;
&lt;p&gt;Now that everything is installed and configured, here is some sample code to get you started. But, I recommend taking a look at the &lt;a href="http://ask.github.com/celery/"&gt;Celery documentation&lt;/a&gt; to get acquainted with its power and flexibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fooapp/tasks.py&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
from celery.task import Task
from celery.registry import tasks

class MyTask(Task):
    def run(self, some_arg, **kwargs):
        logger = self.get_logger(**kwargs)
        ...
        logger.info(&amp;quot;Did something: %s&amp;quot; % some_arg)

tasks.register(MyTask)
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;fooapp/views.py&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
from fooapp.tasks import MyTask

def foo(request):
    MyTask.delay(some_arg=&amp;quot;foo&amp;quot;)
    ...
&lt;/pre&gt;
&lt;p&gt;Now start the daemon and test your code.&lt;/p&gt;
&lt;pre&gt;
python manage.py celeryd -l INFO
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;
For convenience, there is a shortcut decorator @task which makes simple tasks that much cleaner.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;A note on state:&lt;/u&gt; Since Celery is a distributed system, you can't know in which process, or even on what machine the task will run. So you shouldn't pass Django model objects as arguments to tasks, its almost always better to re-fetch the object from the database instead, as there are possible race conditions involved.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have you ever needed to use background/deferred execution in Django? &lt;/strong&gt;&lt;a href="#comment-form"&gt;&lt;strong&gt;Post a comment&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/django-celery-rabbitmq" dc:identifier="http://www.turnkeylinux.org/blog/django-celery-rabbitmq" dc:title="Background task processing and deferred execution in Django" trackback:ping="http://www.turnkeylinux.org/trackback/1049" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/LXOVlubhh5I" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/django-celery-rabbitmq#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/development">development</category>
 <category domain="http://www.turnkeylinux.org/blog/django">django</category>
 <pubDate>Wed, 24 Feb 2010 06:15:09 +0000</pubDate>
 <dc:creator>Alon Swartz</dc:creator>
 <guid isPermaLink="false">1049 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/django-celery-rabbitmq</feedburner:origLink></item>
  <item>
    <title>Good automation vs bad automation</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/ePQnZ9Tr2Fg/good-vs-bad-automation</link>
    <description>&lt;p&gt;I recently eliminated a bit of code that was supposed to handle upgrading our build infrastructure from using one distribution (e.g., Ubuntu 8.04 LTS) to another (e.g., Ubuntu 10.04 LTS). That got me thinking about how to decide (and then explain) when it's a good idea to automate and when it isn't.&lt;/p&gt;
&amp;lt;!--break--&gt;
&lt;p&gt;Since writing and maintaining any piece of software comes with a cost, you always have to weigh the costs against the benefits.&lt;/p&gt;
&lt;p&gt;I boiled it down to the following:&lt;/p&gt;
&lt;ul class="simple"&gt;
    &lt;li&gt;&lt;strong&gt;Good automation&lt;/strong&gt;: the best kind of automation can reliably handle tasks that it is slow, error prone and labor intensive to perform manually. That kind of automation is usually a good idea because once it works well enough you can basically forget about it and enjoy the benefits from an accelerated development cycle (AKA tightened developer feedback loop), reduced amount of friction from debugging human mistakes. It basically frees up your mind and precious labor for other tasks.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Bad automation&lt;/strong&gt;: the worst kind of automation handles in an unreliable, error-prone way tasks that are infrequently performed and simple to handle manually.  Not only do you have to pay for the cost of developing and maintaining this kind of bad automation, you also get an overall decrease in productivity for your efforts because the automation mechanism will require more maintenance and attention then the tasks it supposedly handles.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A litmus test for good vs bad automation is the ability to test. For example, if you try to automate something that happens infrequently there is a good chance that the assumptions embedded in your automation will not hold over time.&lt;/p&gt;
&lt;p&gt;So case in point, since there is no way to test that a bit of automation will work for a future release transition, it's safe to assume it probably won't.&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/good-vs-bad-automation" dc:identifier="http://www.turnkeylinux.org/blog/good-vs-bad-automation" dc:title="Good automation vs bad automation" trackback:ping="http://www.turnkeylinux.org/trackback/1010" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/ePQnZ9Tr2Fg" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/good-vs-bad-automation#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/development">development</category>
 <pubDate>Mon, 22 Feb 2010 12:30:52 +0000</pubDate>
 <dc:creator>Liraz Siri</dc:creator>
 <guid isPermaLink="false">1010 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/good-vs-bad-automation</feedburner:origLink></item>
  <item>
    <title>Django Signals: Be lazy, let stuff happen magically</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/NcLPRePU4Kg/django-signals</link>
    <description>&lt;p&gt;When I first learned about &lt;a href="http://docs.djangoproject.com/en/dev/topics/signals/"&gt;Django signals&lt;/a&gt;, it gave me the same warm fuzzy feeling I got when I started using RSS. Define what I'm interested in, sit back, relax, and let the information come to me.&lt;/p&gt;
&lt;p&gt;You can do the same in Django, but instead of getting news type notifications, you define what &lt;em&gt;stuff&lt;/em&gt; should happen when other &lt;em&gt;stuff&lt;/em&gt; happens, and best of all, the so-called &lt;em&gt;stuff&lt;/em&gt; is global throughout your project, not just within applications (supporting decoupled apps).&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Before a blog comment is published, check it for spam.&lt;/li&gt;
    &lt;li&gt;After a user logs in successfully, update his twitter status.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What you can do with signals are plentiful, and up to your imagination, so lets get into it.&lt;/p&gt;
&amp;lt;!--break--&gt;
&lt;h2&gt;What are Django signals?&lt;/h2&gt;
&lt;p&gt;In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place. They're especially useful when many pieces of code may be interested in the same events.&lt;/p&gt;
&lt;p&gt;This might remind you of &lt;a href="http://en.wikipedia.org/wiki/Event-driven_programming"&gt;Event driven programming&lt;/a&gt;, and rightfully so, the core concepts are very similar.&lt;/p&gt;
&lt;p&gt;Django provides a set of built-in signals that let user defined code get notified by Django itself of certain actions, for example:&lt;/p&gt;
&lt;pre&gt;&lt;strong&gt;# Sent before or after a model's save() method is called.&lt;/strong&gt;
django.db.models.signals.pre_save | post_save

&lt;strong&gt;# Sent before or after a model's delete() method is called.&lt;/strong&gt;
django.db.models.signals.pre_delete | post_delete

&lt;strong&gt;# Sent when a ManyToManyField on a model is changed.&lt;/strong&gt;
django.db.models.signals.m2m_changed

&lt;strong&gt;# Sent when Django starts or finishes an HTTP request.&lt;/strong&gt;
django.core.signals.request_started | request_finished
&lt;/pre&gt;
&lt;p&gt;Built-in signals are really useful, but what I really like is the ability to define custom signals, and due to the way the &lt;em&gt;&amp;quot;signal dispatcher&amp;quot;&lt;/em&gt; works, it allows decoupled applications to be notified when actions occur elsewhere in the framework.&lt;/p&gt;
&lt;p&gt;In other words, one of your apps can send a signal when something happens, and a different app can listen for the signal and do something when it's received.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Using Django signals&lt;/h2&gt;
&lt;h3&gt;Defining and sending a signal&lt;/h3&gt;
&lt;p&gt;All signals are &lt;em&gt;django.dispatch.Signal&lt;/em&gt; instances. The &lt;em&gt;providing_args&lt;/em&gt; is a list of the names of arguments the signal will provide to listeners.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Application: foo&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;u&gt;&lt;strong&gt;signals.py&lt;/strong&gt;&lt;/u&gt;

from django.dispatch import Signal
user_login = Signal(providing_args=[&amp;quot;request&amp;quot;, &amp;quot;user&amp;quot;])

&lt;u&gt;&lt;strong&gt;views.py&lt;/strong&gt;&lt;/u&gt;

from foo import signals

def login(request):
&amp;nbsp;&amp;nbsp;&amp;nbsp; ...
&amp;nbsp;&amp;nbsp;&amp;nbsp; if request.user.is_authenticated():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; signals.user_login.send(sender=None, request=request, user=request.user)
&amp;nbsp;&amp;nbsp;&amp;nbsp; ...
&lt;/pre&gt;
&lt;p&gt;In the above example, a signal will be sent once the user logs in successfully.&lt;br /&gt;
&lt;br /&gt;
Note: The above is just for exemplary purposes, it should be very rare to create your own authentication system as opposed to leveraging django.contrib.auth or one of the great apps out there.&lt;br /&gt;
&lt;br /&gt;
Just as a side note, &lt;em&gt;sender&lt;/em&gt; is usually defined as &lt;em&gt;self&lt;/em&gt; when sending the signal from a class, such as a model class. This gives the intercepting handler instant access to the related class instance.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Listening to signals&lt;/h3&gt;
&lt;p&gt;To receive a signal, you need to register a receiver function that gets called when the signal is sent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Application: bar&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;u&gt;&lt;strong&gt;tasks.py&lt;/strong&gt;&lt;/u&gt;

from foo.signals import user_login

def user_login_handler(sender, **kwargs):
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;&amp;quot;&amp;quot;signal intercept for user_login&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp; user = kwargs['user']
&amp;nbsp;&amp;nbsp;&amp;nbsp; ...

user_login.connect(user_login_handler)
&lt;/pre&gt;
&lt;p&gt;Now, when a user logs in successfully and the signal is sent, it will be intercepted and the handler can then do what ever is required. Note that multiple receivers can be registered for a single signal.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Where should the code live?&lt;/h2&gt;
&lt;p&gt;You can put signal handling and registration code anywhere you like.&lt;br /&gt;
&lt;br /&gt;
However, you'll need to make sure that the module it's in gets imported early on so that the signal handling gets registered before any signals need to be sent. This makes your apps models.py a good place to put registration of signal handlers.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Integrating with django.contrib.auth&lt;/h2&gt;
&lt;p&gt;Seeing as I opened the door to this, let me digress a little.&lt;br /&gt;
&lt;br /&gt;
The login and logout methods of &lt;em&gt;django.contrib.auth&lt;/em&gt; do not send signals, and is discussed in this &lt;a href="http://code.djangoproject.com/ticket/5612"&gt;ticket&lt;/a&gt;. The main reason is that signals are synchronous (discussed in the next section) and would slow down the logout/logout process.&lt;/p&gt;
&lt;p&gt;If you do need authentication related signals, there are a few ways of accomplishing it, each with their own pros and cons, such as:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Patch django.contrib.auth&lt;/li&gt;
    &lt;li&gt;Create a custom auth backend&lt;/li&gt;
    &lt;li&gt;Create a wrapping view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating a wrapping view provides the most flexibility, while writing less code and leveraging great code that already exists.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Application: foo&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;u&gt;&lt;strong&gt;urls.py&lt;/strong&gt;&lt;/u&gt;

from django.conf.urls.defaults import *
from foo.views import login

urlpatterns = patterns('',
   url(r'^login/$', login,
       {'template_name': 'fooapp/login.html'}, name='auth_login'),
   )

&lt;u&gt;&lt;strong&gt;views.py&lt;/strong&gt;&lt;/u&gt;

from django.contrib.auth.views import login as auth_login
from foo import signals

def login(request, **kwargs):
    &amp;quot;&amp;quot;&amp;quot;workaround wrapper for auth.login that sends user_login signal&amp;quot;&amp;quot;&amp;quot;
    response = auth_login(request, **kwargs)
    if request.user.is_authenticated():
        signals.user_login.send(sender=None, request=request, user=request.user)

    return response
&lt;/pre&gt;
&lt;p&gt;The above will wrap the &lt;em&gt;auth.login&lt;/em&gt; method, and send a signal when a user successfully logs in. We are also passing a custom login template, but of course you don't need to.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Signals are synchronous&lt;/h2&gt;
&lt;p&gt;As I mentioned above, signals are synchronous, or blocking (just like everything else in Django). This means that the request will not be returned until all signal handlers are done.&lt;/p&gt;
&lt;p&gt;There have been multiple requests to add asynchronous support to Django, namely via the Python threading module, but I doubt it will happen anytime soon, if at all. In my opinion it comes down to &lt;a href="http://en.wikipedia.org/wiki/Separation_of_concerns"&gt;separation of concerns&lt;/a&gt;, and there is a whole other world called message queuing, namely &lt;a href="http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol"&gt;AMQP&lt;/a&gt; which is designed for these sort of things.&lt;/p&gt;
&lt;p&gt;In an upcoming post I will discuss implementing AMQP (Advanced Message Queuing Protocol) with RabbitMQ and Celery.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ever needed to use signaling in your Django webapp? &lt;a href="#comment-form"&gt;Post a comment&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/django-signals" dc:identifier="http://www.turnkeylinux.org/blog/django-signals" dc:title="Django Signals: Be lazy, let stuff happen magically" trackback:ping="http://www.turnkeylinux.org/trackback/1021" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/NcLPRePU4Kg" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/django-signals#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/development">development</category>
 <category domain="http://www.turnkeylinux.org/blog/django">django</category>
 <pubDate>Wed, 17 Feb 2010 10:10:46 +0000</pubDate>
 <dc:creator>Alon Swartz</dc:creator>
 <guid isPermaLink="false">1021 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/django-signals</feedburner:origLink></item>
  <item>
    <title>We don't need no stinking SSL</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/xakV0vNBmok/we-dont-need-no-stinking-ssl</link>
    <description>&lt;p&gt;&lt;em&gt;Why we disabled SSL and use an SSH tunnel for web site administration&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Content managements systems like the one we're using for the web site (Drupal) need to provide a privileged administration interface which you usually want to access securely. Due to the insecure nature of the Internet, it's reasonable to assume your traffic may be intercepted at some point.  So how do you prevent that?&lt;/p&gt;
&lt;p&gt;Up until recently, we used SSL. You could access the web site from both:&lt;/p&gt;
&lt;ul class="simple"&gt;
    &lt;li&gt;&lt;a href="http://www.turnkeylinux.org/" title="http://www.turnkeylinux.org/"&gt;http://www.turnkeylinux.org/&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.turnkeylinux.org/" title="https://www.turnkeylinux.org/"&gt;https://www.turnkeylinux.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, as the site grew in complexity this created a range of subtle but annoying paper-cut type problems.&lt;/p&gt;
&amp;lt;!--break--&gt;
&lt;p&gt;For example, I configured the site to use a content filter which translates local urls (e.g., /lamp) into absolute urls (e.g.,  &lt;a href="http://www.turnkeylinux.org/lamp" title="http://www.turnkeylinux.org/lamp"&gt;http://www.turnkeylinux.org/lamp&lt;/a&gt; ). This is necessary for links and images in blog posts to work correctly in RSS feeds and email updates (e.g., such as those provided by FeedBurner). We didn't want to do that by hand because we were using FCKeditor and IMCE which create local links by default and it wasn't any fun to have to translate every single link into an absolute URL by hand.&lt;/p&gt;
&lt;p&gt;The problem is that Drupal cache saves pages AFTER the content filter, so guess what happens to links in pages you access via SSL? The situation could be a mix-up of http and https links which would be potentially confusing to both human visitors and search engines.&lt;/p&gt;
&lt;p&gt;More importantly while in theory SSL should have protected our administration credentials in practice it didn't.&lt;/p&gt;
&lt;p&gt;Drupal, along with many other web applications doesn't support secure SSL cookies. That means even if you login via SSL your cookie is still transmitted over the clear when you access the site, say accidentally, via HTTP. In a perfect world that might never happen but in practice it's quite a frequent mistake.&lt;/p&gt;
&lt;p&gt;An attacker that can intercept your traffic can then intercept the cookie containing your session key and use that to access the site with your privileges.&lt;/p&gt;
&lt;p&gt;So using SSL in this way doesn't really add that much security.&lt;/p&gt;
&lt;p&gt;OTOH, session keys are temporary where passwords have much longer lifetimes so you still don't want to transmit your password in the clear.&lt;/p&gt;
&lt;h2&gt;Alternative: access the site through an SSH tunnel&lt;/h2&gt;
&lt;p&gt;So perhaps somewhat unintuitively, after considering our options we decided to turn off SSL and access the web site through an SSH tunnel which serves as a poor man's VPN.&lt;/p&gt;
&lt;p&gt;That sounds a bit complicated but it really isn't.&lt;/p&gt;
&lt;p&gt;From my ~/.ssh/config:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
host tkl
DynamicForward 1081
&lt;/pre&gt;
&lt;p&gt;Then when I ssh to tkl, which is the VPS which hosts the web site, this creates a virtual socks proxy bound to 127.0.0.1:1081. Constructing the tunnel in this way can be a bit of a hassle but you can set that up to happen automatically (future post).&lt;/p&gt;
&lt;p&gt;We then configure SwitchProxy FireFox extension to make it easy to switch to browsing within the tunnel.&lt;/p&gt;
&lt;p&gt;Configuration tips:&lt;/p&gt;
&lt;ul class="simple"&gt;
    &lt;li&gt;disable the SwitchProxy toolbar (Views-&amp;gt;Toolbars)&lt;/li&gt;
    &lt;li&gt;added my staging server test.turnkeylinux.org to &amp;quot;No proxy for...&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then to switch into the tunnel: Tools-&amp;gt;SwitchProxy-&amp;gt;tkl&lt;/p&gt;
&lt;p&gt;A nice bonus is that in our case this setup seems to be noticeably snappier than using SSL.&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/we-dont-need-no-stinking-ssl" dc:identifier="http://www.turnkeylinux.org/blog/we-dont-need-no-stinking-ssl" dc:title="We don&amp;#039;t need no stinking SSL" trackback:ping="http://www.turnkeylinux.org/trackback/1004" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/xakV0vNBmok" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/we-dont-need-no-stinking-ssl#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/admin">admin</category>
 <category domain="http://www.turnkeylinux.org/blog/security">security</category>
 <category domain="http://www.turnkeylinux.org/blog/ssh">ssh</category>
 <pubDate>Mon, 15 Feb 2010 06:34:57 +0000</pubDate>
 <dc:creator>Liraz Siri</dc:creator>
 <guid isPermaLink="false">1004 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/we-dont-need-no-stinking-ssl</feedburner:origLink></item>
  <item>
    <title>Converting a virtual disk image: VDI or VMDK to an ISO you can distribute</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/DnAsMnt1x20/convert-vm-iso</link>
    <description>&lt;p&gt;Why would anyone in their right mind want to convert a VM into an ISO?&lt;/p&gt;
&lt;p&gt;Good question, the answer for Conor Fox (who was the inspiration for this post - thanks Conor!) was to distribute his customized TurnKey PostgreSQL image so others could use it.&lt;/p&gt;
&lt;p&gt;Distributing an ISO as opposed to a VM image allows it to be installed on any virtualization platform, as well as on bare metal, with the added bonus of running live.&lt;/p&gt;
&lt;p&gt;I suppose that's a good enough reason, so lets get to it.&lt;/p&gt;
&amp;lt;!--break--&gt;
&lt;br/&gt;
&lt;h2&gt;Convert VM disk to raw image and mount it&lt;/h2&gt;
&lt;p&gt;First we need to get qemu-img, a tool bundled with &lt;a href="http://www.qemu.org"&gt;qemu&lt;/a&gt; (KVM's virtualization backend) to convert the VM disk to a raw image, and &lt;a href="http://www.turnkeylinux.org/docs/tklpatch"&gt;TKLPatch&lt;/a&gt;, the TurnKey customization mechanism to package the ISO.&lt;/p&gt;
&lt;p&gt;If you are not using a TurnKey installation, see the &lt;a href="http://www.turnkeylinux.org/docs/tklpatch/installation"&gt;TKLPatch installation notes&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;
apt-get install qemu
apt-get install tklpatch&lt;/pre&gt;
&lt;p&gt;I'll show how to convert a VMWare VMDK image into raw disk format. If you are using a different virtualization platform such as Virtualbox, see &lt;a href="http://www.turnkeylinux.org/blog/convert-vdi-vmdk"&gt;this post&lt;/a&gt; on converting a VDI to a raw image.&lt;/p&gt;
&lt;pre&gt;
qemu-img convert -f vmdk turnkey-core.vmdk -O raw turnkey-core.raw
&lt;/pre&gt;
&lt;p&gt;Next, mount the raw disk as a loopback device.&lt;/p&gt;
&lt;pre&gt;
mkdir turnkey-core.mount
mount -o loop turnkey-core.raw turnkey-core.mount&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA 1&lt;/strong&gt;: If your VM has partitions, it's a little tricker. You'll need to setup the loop device, partition mappings and finally mount the rootfs partition. You will need kpartx to setup the mappings.&lt;/p&gt;
&lt;pre&gt;
loopdev=$(losetup -s -f turnkey-core.raw)

apt-get install kpartx
kpartx -a $loopdev

&lt;em&gt;# p1 refers to the first partition (rootfs)&lt;/em&gt;
mkdir turnkey-core.mount
mount /dev/mapper/$(basename $loopdev)p1 turnkey-core.mount&lt;/pre&gt;
&lt;h2&gt;&lt;br /&gt;
Extract root filesystem and tweak for ISO configuration&lt;/h2&gt;
&lt;p&gt;Now, make a copy of the root filesystem and unmount the loopback.&lt;/p&gt;
&lt;pre&gt;
mkdir turnkey-core.rootfs
rsync -a -t -r -S -I turnkey-core.mount/ turnkey-core.rootfs

umount -d turnkey-core.mount

# If your VM had partitions (GOTCHA 1):
kpartx -d $loopdev
losetup -d $loopdev&lt;/pre&gt;
&lt;p&gt;Because the VM is an installed system as opposed to the ISO, the file system table needs to be updated.&lt;/p&gt;
&lt;pre&gt;
cat&amp;gt;turnkey-core.rootfs/etc/fstab&amp;lt;&amp;lt;EOF
aufs / aufs rw 0 0
tmpfs /tmp tmpfs nosuid,nodev 0 0
EOF&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA 2:&lt;/strong&gt; If your VM uses a kernel optimized for virtualization (like the one included in the TurnKey VM builds), you need to replace it with a generic kernel, and also remove vmware-tools if installed.&lt;/p&gt;
&lt;pre&gt;
tklpatch-chroot turnkey-core.rootfs

&lt;em&gt;# inside the chroot&lt;/em&gt;
apt-get update
apt-get install linux-image-generic
dpkg --purge $(dpkg-query --showformat='${Package}\n' -W 'vmware-tools*')
dpkg --purge $(dpkg-query --showformat='${Package}\n' -W '*-virtual')

exit&lt;/pre&gt;
&lt;h2&gt;&lt;br /&gt;
Generate the ISO&lt;/h2&gt;
&lt;p&gt;Finally, prepare the cdroot and generate the ISO.&lt;/p&gt;
&lt;pre&gt;
tklpatch-prepare-cdroot turnkey-core.rootfs/
tklpatch-geniso turnkey-core.cdroot/&lt;/pre&gt;
&lt;p&gt;Thats it!&lt;/p&gt;
&lt;p&gt;Bonus: By default the ISO will boot automatically. If you want to include the &lt;a href="http://www.turnkeylinux.org/screenshots/boot"&gt;TurnKey bootsplash and bootmenu&lt;/a&gt;, extract the cdroot from a TurnKey ISO and tell tklpatch-prepare-cdroot to use it as a template.&lt;/p&gt;
&lt;pre&gt;
tklpatch-extractiso turnkey-core.iso
tklpatch-prepare-cdroot turnkey-core.rootfs/ turnkey-core.cdroot/
tklpatch-geniso turnkey-core.cdroot/&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ever needed to package a VM as a distributable ISO? &lt;a href="#comment-form"&gt;Post a comment&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;&lt;/br/&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/convert-vm-iso" dc:identifier="http://www.turnkeylinux.org/blog/convert-vm-iso" dc:title="Converting a virtual disk image: VDI or VMDK to an ISO you can distribute" trackback:ping="http://www.turnkeylinux.org/trackback/1019" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/DnAsMnt1x20" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/convert-vm-iso#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/convert">convert</category>
 <category domain="http://www.turnkeylinux.org/blog/virtualbox">virtualbox</category>
 <category domain="http://www.turnkeylinux.org/blog/vmware">vmware</category>
 <pubDate>Thu, 11 Feb 2010 07:21:03 +0000</pubDate>
 <dc:creator>Alon Swartz</dc:creator>
 <guid isPermaLink="false">1019 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/convert-vm-iso</feedburner:origLink></item>
  <item>
    <title>Converting a virtual disk image: VDI to VMDK to a raw loopback file you can mount</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/MPrY7JIcMNw/convert-vdi-vmdk</link>
    <description>&lt;p&gt;By default, VirtualBox creates virtual disk images in a special format called VDI, which is unique to VirtualBox. Disk images are stored in $HOME/.VirtualBox/HardDisks.&lt;/p&gt;
&lt;p&gt;You'll need to convert VDI into another format if you want to run a VirtualBox VM on another virtualization platform, such as VMWare or KVM.&lt;/p&gt;
&amp;lt;!--break--&gt;
&lt;p&gt;The VMDK virtual disk format is a good choice because even though it originated with VMWare it is supported by other virtualization platforms including VirtualBox and KVM.&lt;/p&gt;
&lt;p&gt;VirtualBox enables the conversion using the low-level &amp;quot;VBoxManage clonehd&amp;quot; command:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
VBoxManage list hdds # prints a list of disk image UUIDs
VBoxManage clonehd &amp;lt;UUID&amp;gt; -o converted.vmdk format VMDK
cd ~/.VirtualBox/HardDisks/
ls -la converted.vmdk
&lt;/pre&gt;
&lt;p&gt;Once you have converted to VMDK you can use qemu-img, a tool bundled with qemu (KVM's virtualization backend) to further convert VMDK to other formats.&lt;/p&gt;
&lt;p&gt;A particularly useful format to convert to is 'raw' which you can then mount as a loopback device:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
apt-get install qemu
qemu-img convert -f vmdk converted.vmdk -O raw converted.raw
mount -o loop converted.raw /mnt&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ever had to convert between disk image formats? &lt;a href="#comment-form"&gt;Post a comment&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/convert-vdi-vmdk" dc:identifier="http://www.turnkeylinux.org/blog/convert-vdi-vmdk" dc:title="Converting a virtual disk image: VDI to VMDK to a raw loopback file you can mount" trackback:ping="http://www.turnkeylinux.org/trackback/1009" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/MPrY7JIcMNw" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/convert-vdi-vmdk#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/convert">convert</category>
 <category domain="http://www.turnkeylinux.org/blog/virtualbox">virtualbox</category>
 <category domain="http://www.turnkeylinux.org/blog/vmware">vmware</category>
 <pubDate>Tue, 09 Feb 2010 18:00:00 +0000</pubDate>
 <dc:creator>Liraz Siri</dc:creator>
 <guid isPermaLink="false">1009 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/convert-vdi-vmdk</feedburner:origLink></item>
  <item>
    <title>Friends don't let friends program in shell script</title>
    <link>http://feeds.turnkeylinux.org/~r/turnkeylinux-blog/~3/bkCtihLMjz8/friends-dont-let-friends-program-shell-script</link>
    <description>&lt;p&gt;Lately I've been going over a hellish patch-work of old shell scripts we wrote to automate some internal processes and I realized something: friends shouldn't let friends program in shell script.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Shell script is ugly.&lt;/li&gt;
    &lt;li&gt;Shell script is unreliable.&lt;/li&gt;
    &lt;li&gt;Shell script doesn't have exceptions.&lt;/li&gt;
    &lt;li&gt;Shell script is hard to debug.&lt;/li&gt;
    &lt;li&gt;Shell script is hard to test.&lt;/li&gt;
    &lt;li&gt;Shell script isn't object oriented.&lt;/li&gt;
    &lt;li&gt;Shell script is full of idiosyncratic cruft.&lt;/li&gt;
    &lt;li&gt;Shell script doesn't have any decent data structures.&lt;/li&gt;
    &lt;li&gt;Shell script doesn't scale.&lt;/li&gt;
    &lt;li&gt;Shell script encourages code duplication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem with shell script is that it's so deceptively easy to get started. Once you've started adding each bit of extra complexity seems easier than trashing the whole thing and starting from scratch in a decent language (e.g., Python) with a decent design.&lt;/p&gt;
&lt;p&gt;Speaking from personal experience, nearly every time I have used shell script in recent times for something that isn't completely trivial, I have come to regret it. Ugh.&lt;/p&gt;
&lt;p&gt;Sometimes you don't have a choice. For example, one time I had to use shell script to create a transparent command line hijacking function for a utility I was working on, and just look at the abomination I am ashamed to have been forced to write:&lt;/p&gt;
&lt;pre&gt;
hijack_command() {
    CALLBACK=$1
    COMMAND=$2
    NEW_COMMAND=$3

    ORIG_COMMAND=$(which $COMMAND 2&amp;gt;/dev/null)

    eval &amp;quot;

        function $COMMAND() {
            args=()
            for arg; do
                args[\${#args[*]}]=\&amp;quot;'\$arg'\&amp;quot;
            done
            if $CALLBACK; then
                eval $NEW_COMMAND \${args[*]}
            else
                if [ \&amp;quot;$ORIG_COMMAND\&amp;quot; ]; then
                    eval $ORIG_COMMAND \${args[*]}
                fi
            fi
        }&amp;quot;
}
&lt;/pre&gt;
&lt;p&gt;When a language forces you to do nested metaprogramming something as trivial as this, run and don't look back!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever regretted using shell script? &lt;a href="#comment-form"&gt;Share your thoughts!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;!--
&lt;rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"&gt;
&lt;rdf:Description rdf:about="http://www.turnkeylinux.org/blog/friends-dont-let-friends-program-shell-script" dc:identifier="http://www.turnkeylinux.org/blog/friends-dont-let-friends-program-shell-script" dc:title="Friends don&amp;#039;t let friends program in shell script" trackback:ping="http://www.turnkeylinux.org/trackback/985" /&gt;
&lt;/rdf:RDF&gt;
--&gt;&lt;img src="http://feeds.feedburner.com/~r/turnkeylinux-blog/~4/bkCtihLMjz8" height="1" width="1"/&gt;</description>
     <comments>http://www.turnkeylinux.org/blog/friends-dont-let-friends-program-shell-script#comments</comments>
 <category domain="http://www.turnkeylinux.org/blog/development">development</category>
 <category domain="http://www.turnkeylinux.org/blog/shell">shell</category>
 <pubDate>Tue, 02 Feb 2010 05:38:54 +0000</pubDate>
 <dc:creator>Liraz Siri</dc:creator>
 <guid isPermaLink="false">985 at http://www.turnkeylinux.org</guid>
  <feedburner:origLink>http://www.turnkeylinux.org/blog/friends-dont-let-friends-program-shell-script</feedburner:origLink></item>
  </channel>
</rss>
