<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>
    jd:/dev/blog  </title>
    <link href="http://julien.danjou.info/blog/index.xml" rel="self" />
  
    <link href="http://julien.danjou.info/blog/"/>
  
    
  <updated>2012-05-18T11:20:25Z</updated>

  <id>http://julien.danjou.info/blog/index.xml</id>

    <entry>
    <title type="html">OpenStack Swift eventual consistency analysis &amp; bottlenecks</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/openstack-swift-consistency-analysis"/>
    <updated>2012-04-23T12:06:00Z</updated>
    <published>2012-04-23T12:06:00Z</published>
    <id>/blog/2012/openstack-swift-consistency-analysis</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="swift"
                label="Swift" />
        <category   scheme="/blog/tags"
                term="python"
                label="Python" />
    
    <content type="html">
            &lt;p&gt;&lt;a href=&#34;https://launchpad.net/swift&#34;&gt;Swift&lt;/a&gt; is the software behind the &lt;a href=&#34;http://openstack.org/projects/storage/&#34;&gt;OpenStack
Object Storage&lt;/a&gt; service.&lt;/p&gt;
&lt;p&gt;This service provides a simple storage service for applications using
&lt;a href=&#34;http://docs.openstack.org/api/openstack-object-storage/1.0/content/&#34;&gt;RESTful
interfaces&lt;/a&gt;,
providing maximum data availability and storage capacity.&lt;/p&gt;
&lt;p&gt;I explain here how some parts of the storage and replication in Swift works,
and show some of its current limitations.&lt;/p&gt;
&lt;p&gt;If you don&#39;t know Swift and want to read a more &#34;shallow&#34; overview first,
you can read John Dickinson&#39;s &lt;a href=&#34;http://programmerthoughts.com/openstack/swift-tech-overview/&#34;&gt;Swift Tech
Overview&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;How Swift storage works&lt;/h1&gt;
&lt;p&gt;If we refer to the &lt;a href=&#34;http://en.wikipedia.org/wiki/CAP_theorem&#34;&gt;CAP theorem&lt;/a&gt;,
Swift chose &lt;strong&gt;availability&lt;/strong&gt; and &lt;strong&gt;partition tolerance&lt;/strong&gt; and dropped
&lt;strong&gt;consistency&lt;/strong&gt;. That means that you&#39;ll always get your data, they will be
dispersed on many places, but you could get an old version of them (or no
data at all) in some odd cases (like some server overload or failure). This
compromise is made to allow maximum availability and scalability of the
storage platform.&lt;/p&gt;
&lt;p&gt;But there are mechanisms built into Swift to minimize the potential data
inconsistency window: they are responsible for data replication and
consistency.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://swift.openstack.org/&#34;&gt;official Swift documentation&lt;/a&gt; explains the
internal storage in a certain way, but I&#39;m going to write my own explanation
here about this.&lt;/p&gt;
&lt;h2&gt;Consistent hashing&lt;/h2&gt;
&lt;p&gt;Swift uses the principle of &lt;a href=&#34;http://en.wikipedia.org/wiki/Consistent_hashing&#34;&gt;consistent
hashing&lt;/a&gt;. It builds what it
calls a &lt;em&gt;ring&lt;/em&gt;. A ring represents the space of all possible computed hash
values divided in equivalent parts. Each part of this space is called a
&lt;em&gt;partition&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The following schema (stolen from the &lt;a href=&#34;http://wiki.basho.com/&#34;&gt;Riak&lt;/a&gt;
project) shows the principle nicely:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Consistent hashing ring&#34; src=&#34;/media/images/blog/2012/riak-ring.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;In a simple world, if you wanted to store some objects and distribute them
on 4 nodes, you would split your hash space in 4. You would have 4
partitions, and computing &lt;em&gt;hash(object) modulo 4&lt;/em&gt; would tell you where to
store your object: on node 0, 1, 2 or 3.&lt;/p&gt;
&lt;p&gt;But since you want to be able to extend your storage cluster to more nodes
without breaking the whole hash mapping and moving everything around, you
need to build a lot more partitions. Let&#39;s say we&#39;re going to build
2&lt;sup&gt;10&lt;/sup&gt; partitions. Since we have 4 nodes, each node will
have &lt;code&gt;2&lt;sup&gt;10&lt;/sup&gt; ÷ 4 = 256&lt;/code&gt; partitions. If we ever want to
add a 5&lt;sup&gt;th&lt;/sup&gt; node, it&#39;s easy: we just have to re-balance the
partitions and move 1⁄4 of the partitions from each node to this
5&lt;sup&gt;th&lt;/sup&gt; node. That means all our nodes will end up
with &lt;code&gt;2&lt;sup&gt;10&lt;/sup&gt; ÷ 5 ≈ 204&lt;/code&gt; partitions. We can also define a
&lt;em&gt;weight&lt;/em&gt; for each node, in order for some nodes to get more partitions than
others.&lt;/p&gt;
&lt;p&gt;With 2&lt;sup&gt;10&lt;/sup&gt; partitions, we can have up to 2&lt;sup&gt;10&lt;/sup&gt; nodes in our
cluster. Yeepee.&lt;/p&gt;
&lt;p&gt;For reference, Gregory Holt, one of the Swift authors, also wrote &lt;a href=&#34;http://www.tlohg.com/p/building-consistent-hashing-ring.html&#34;&gt;an
explanation post about the
ring&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Concretely, when building one Swift ring, you&#39;ll have to say how much
partitions you want, and this is what this value is really about.&lt;/p&gt;
&lt;h2&gt;Data duplication&lt;/h2&gt;
&lt;p&gt;Now, to assure availability and partitioning (as seen in the &lt;em&gt;CAP theorem&lt;/em&gt;)
we also want to store replicas of our objects. By default, Swift stores 3
copies of every objects, but that&#39;s configurable.&lt;/p&gt;
&lt;p&gt;In that case, we need to store each partition defined above not only on 1
node, but on 2 others. So Swift adds another concept: zones. A zone is an
isolated space that does not depends on other zone, so in case of an outage
on a zone, the other zones are still available. Concretely, a zone is likely
to be a disk, a server, or a whole cabinet, depending on the size of your
cluster. It&#39;s up to you to chose anyway.&lt;/p&gt;
&lt;p&gt;Consequently, each partitions has not to be mapped to 1 host only anymore,
but to N hosts. Each node will therefore store this number of partitions:&lt;/p&gt;
&lt;pre&gt;
number of partition stored on one node = number of replicas × total number of partitions ÷ number of node
&lt;/pre&gt;

&lt;p&gt;Examples:&lt;/p&gt;
&lt;blockquote&gt;
We split the ring in 2&lt;sup&gt;10&lt;/sup&gt; = 1024 partitions. We have 3 nodes. We want 3 replicas of data.&lt;br&gt;
→ Each node will store a copy of the full partition space: &lt;code&gt;3 × 2&lt;sup&gt;10&lt;/sup&gt; ÷ 3 = 2&lt;sup&gt;10&lt;/sup&gt; = 1024 partitions&lt;/code&gt;.
&lt;/blockquote&gt;

&lt;blockquote&gt;
We split the ring in 2&lt;sup&gt;11&lt;/sup&gt; = 2048 partitions. We have 5 nodes. We want 3 replicas of data.&lt;br&gt;
→ Each node will store &lt;code&gt;2&lt;sup&gt;11&lt;/sup&gt; × 3 ÷ 5 ≈ 1129 partitions&lt;/code&gt;.
&lt;/blockquote&gt;

&lt;blockquote&gt;
We split the ring in 2&lt;sup&gt;11&lt;/sup&gt; = 2048 partitions. We have 6 nodes. We want 3 replicas of data.&lt;br&gt;
→ Each node will store &lt;code&gt;2&lt;sup&gt;11&lt;/sup&gt; × 3 ÷ 6 = 1024 partitions&lt;/code&gt;.
&lt;/blockquote&gt;

&lt;h2&gt;Three rings to rule them all&lt;/h2&gt;
&lt;p&gt;In Swift, there is 3 categories of thing to store: &lt;em&gt;account&lt;/em&gt;, &lt;em&gt;container&lt;/em&gt;
and &lt;em&gt;objects&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;An &lt;strong&gt;account&lt;/strong&gt; is what you&#39;d expect it to be, a user account. An account
contains &lt;strong&gt;containers&lt;/strong&gt; (the equivalent of Amazon S3&#39;s buckets). Each
container can contains user-defined key and values (just like a hash table
or a dictionary): values are what Swift call &lt;strong&gt;objects&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Swift wants you to build 3 different and independent rings to store its 3
kind of things (&lt;em&gt;accounts&lt;/em&gt;, &lt;em&gt;containers&lt;/em&gt; and &lt;em&gt;objects&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Internally, the two first categories are stored as
&lt;a href=&#34;http://www.sqlite.org/&#34;&gt;SQLite&lt;/a&gt; databases, whereas the last one is stored
using regular files.&lt;/p&gt;
&lt;p&gt;Note that this 3 rings can be stored and managed on 3 completely different
set of servers.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Swift storage schema&#34; src=&#34;/media/images/blog/2012/openstack-swift-storage.png&#34; /&gt;&lt;/p&gt;
&lt;h1&gt;Data replication&lt;/h1&gt;
&lt;p&gt;Now that we have our storage theory in place (accounts, containers and
objects distributed into partitions, themselves stored into multiple zones),
let&#39;s go the replication practice.&lt;/p&gt;
&lt;p&gt;When you put something in one of the 3 rings (being an account, a container
or an object) it is uploaded into all the zones responsible for the ring
partition the object belongs to. This upload into the different zones is the
responsibility of the &lt;em&gt;swift-proxy&lt;/em&gt; daemon.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Swift proxy schema&#34; src=&#34;/media/images/blog/2012/openstack-swift-replication.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;But if one of the zone is failing, you can&#39;t upload all your copies in all
zones at the upload time. So you need a mechanism to be sure the failing
zone will catch up to a correct state at some point.&lt;/p&gt;
&lt;p&gt;That&#39;s the role of the &lt;em&gt;swift-{container,account,object}-replicator&lt;/em&gt;
processes. This processes are &lt;strong&gt;running on each node part of a zone&lt;/strong&gt; and
replicates their contents to nodes of the other zones.&lt;/p&gt;
&lt;p&gt;When they run, they walk through all the contents from all the partitions on
the whole file system and for each partition, issue a special &lt;em&gt;REPLICATE&lt;/em&gt;
HTTP request to all the other zones responsible for that same partition. The
other zone responds with information about the local state of the partition.
That allows the replicator process to decide if the remote zone has an
up-to-date version of the partition.&lt;/p&gt;
&lt;p&gt;In case of account and containers, it doesn&#39;t check at the partition level,
but check each account/container contained inside each partition.&lt;/p&gt;
&lt;p&gt;If something is not up-to-date, it will be pushed using &lt;em&gt;rsync&lt;/em&gt; by the
replicator process. This is why you&#39;ll read that the replication updates are
&lt;em&gt;&#34;push based&#34;&lt;/em&gt; in Swift documentation.&lt;/p&gt;
&lt;pre class=&#34;prettyprint&#34;&gt;
# Pseudo code describing replication process for accounts
# The principle is exactly the same for containers
for account in accounts:
    # Determine the partition used to store this account
    partition = hash(account) % number_of_partitions
    # The number of zone is the number of replicas configured
    for zone in partition.get_zones_storing_this_partition():
        # Send a HTTP REPLICATE command to the remote swift-account-server process
        version_of_account = zone.send_HTTP_REPLICATE_for(account):
        if version_of_account &lt; account.version()
            account.sync_to(zone)
&lt;/pre&gt;

&lt;p&gt;This replication process is &lt;em&gt;O(number of account × number of replicas)&lt;/em&gt;. The
more your number of account will increase and the more you will want
replicas for your data, the more the replication time for your accounts will
grow. The same rule applies for containers.&lt;/p&gt;
&lt;pre class=&#34;prettyprint&#34;&gt;
# Pseudo code describing replication process for objects
for partition in partitions_storing_objects:
    # The number of zone is the number of replicas configured
    for zone in partition.get_zones_storing_this_partition():
        # Send a HTTP REPLICATE command to the remote swift-object-server process
        verion_of_partition = zone.send_HTTP_REPLICATE_for(partition):
        if version_of_partition &lt; partition.version()
            # Use rsync to synchronize the whole partition
            # and all its objects
            partition.rsync_to(zone)
&lt;/pre&gt;

&lt;p&gt;This replication process is
&lt;em&gt;O(number of objects partitions × number of replicas)&lt;/em&gt;. The more your
number of objects partitions will increase, and the more you will want
replicas for your data, the more the replication time for your objects will
grow.&lt;/p&gt;
&lt;p&gt;I think this is something important to know when deciding how to build your
Swift architecture. Choose the right number the number of replicas,
partitions and nodes.&lt;/p&gt;
&lt;h1&gt;Replication process bottlenecks&lt;/h1&gt;
&lt;p&gt;&lt;span class=&#34;pull-right&#34;&gt;
&lt;img alt=&#34;Copycat&#34; src=&#34;/media/images/blog/2012/copy-cat.jpg&#34; /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;File accesses&lt;/h2&gt;
&lt;p&gt;The problem, as you might have guessed, is that to replicate, &lt;strong&gt;it walks
through every damn things&lt;/strong&gt;, things being accounts, containers, or object&#39;s
partition hash files. This means it need to open and read (part of) a every
file your node stores to check that data need or not to be replicated!&lt;/p&gt;
&lt;p&gt;For accounts &amp;amp; containers replication, this is done every 30 seconds by
default, but it will likely take more than 30 seconds as soon as you hit
around 12 000 containers on a node (see measurements below). Therefore
you&#39;ll end up checking consistency of accounts &amp;amp; containers on each all node
&lt;strong&gt;all the time&lt;/strong&gt;, using obviously a lot of CPU time.&lt;/p&gt;
&lt;p&gt;For reference, &lt;a href=&#34;http://alexyang.sinaapp.com/?p=115&#34;&gt;Alex Yang also did an
analysis&lt;/a&gt; of that same problem.&lt;/p&gt;
&lt;h2&gt;TCP connections&lt;/h2&gt;
&lt;p&gt;Worst, the HTTP connections used to send the &lt;em&gt;REPLICATE&lt;/em&gt; commands are not
pooled: a new TCP connection is established each time something has to be
checked against the same thing stored on a remote zone.&lt;/p&gt;
&lt;p&gt;This is why you&#39;ll see in the &lt;a href=&#34;http://swift.openstack.org/deployment_guide.html&#34;&gt;Swift&#39;s Deployment
Guide&lt;/a&gt; this lines listed
under &lt;a href=&#34;http://swift.openstack.org/deployment_guide.html#general-system-tuning&#34;&gt;&#34;general system
tuning&#34;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&#34;prettyprint&#34;&gt;
# disable TIME_WAIT.. wait..
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_reuse=1

# double amount of allowed conntrack
net.ipv4.netfilter.ip_conntrack_max = 262144
&lt;/pre&gt;

&lt;p&gt;In my humble opinion, this is more an ugly hack than a tuning. If you don&#39;t
activate this and if you have a lot of containers on your node, you&#39;ll end
up soon with thousands of connections in &lt;em&gt;TIME_WAIT&lt;/em&gt; state, and you indeed
risk to overload the IP conntrack module.&lt;/p&gt;
&lt;h2&gt;Container deletion&lt;/h2&gt;
&lt;p&gt;We also should talk about container deletion. When a user deletes a
container from its account, the container is &lt;strong&gt;marked as deleted&lt;/strong&gt;. And
that&#39;s it. It&#39;s not deleted. Therefore the SQLite database file representing
the container will continue to be checked for synchronization, over and
over.&lt;/p&gt;
&lt;p&gt;The only way to have a container permanently deleted is to &lt;strong&gt;mark an account
as deleted&lt;/strong&gt;. This way the &lt;em&gt;swift-account-reaper&lt;/em&gt; will delete all its
containers and, finally, the account.&lt;/p&gt;
&lt;h1&gt;Measurement&lt;/h1&gt;
&lt;p&gt;On a pretty big server, I measured the replications to be done at a speed of
around 350 {account,container,object-partitions}/second, which can be a real
problem if you chose to build a lots of partition and you have a low
&lt;em&gt;number_of_node ⁄ number_of_replicas&lt;/em&gt; ratio.&lt;/p&gt;
&lt;p&gt;For example, the default parameters runs the container replication every 30
seconds. To check replication status of 12 000 containers stored on one node
at the speed of 350 containers/seconds, you&#39;ll need around 34 seconds to do
so. In the end, you&#39;ll never stop checking replication of your containers,
and the more you&#39;ll have containers, the more your &lt;strong&gt;inconsistency window
will increase&lt;/strong&gt;.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Until some of the code is fixed (the HTTP connection pooling probably being
the &#34;easiest&#34; one), I warmly recommend to chose correctly the different
Swift parameters for your setup. The replication process optimization
consists in having the minimum amount of partitions per node, which can be
done by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;decreasing the number of partitions&lt;/li&gt;
&lt;li&gt;decreasing the number of replicas&lt;/li&gt;
&lt;li&gt;increasing the number of node&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For very large setups, some code to speed up accounts and containers
synchronization, and remove deleted containers will be required, but this
does not exist yet, as far as I know.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;The opinions expressed here represent my own and not those of my employer.&lt;/small&gt;&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">First release of PyMuninCli</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/pymunincli-0.1"/>
    <updated>2012-04-17T11:06:00Z</updated>
    <published>2012-04-17T11:06:00Z</published>
    <id>/blog/2012/pymunincli-0.1</id>
        <category   scheme="/blog/tags"
                term="python"
                label="Python" />
        <category   scheme="/blog/tags"
                term="munin"
                label="Munin" />
    
    <content type="html">
            &lt;p&gt;Today I release a &lt;a href=&#34;http://python.org&#34;&gt;Python&lt;/a&gt; client library to query
&lt;a href=&#34;http://munin-monitoring.org/&#34;&gt;Munin&lt;/a&gt; servers.&lt;/p&gt;
&lt;p&gt;I wrote it as part of some experiments I did a few weeks ago. I discovered
there was no client library to query a Munin server. There&#39;s
&lt;a href=&#34;http://aouyar.github.com/PyMunin/&#34;&gt;PyMunin&lt;/a&gt; or
&lt;a href=&#34;http://samuelks.com/python-munin/&#34;&gt;python-munin&lt;/a&gt; which help developing
Munin plugins, but nothing to access the &lt;em&gt;munin-node&lt;/em&gt; and retrieve its data.&lt;/p&gt;
&lt;p&gt;So I decided to write a quick and simple one, and it&#39;s released under the
name of &lt;a href=&#34;http://julien.danjou.info/software/pymunincli/&#34;&gt;PyMuninCli&lt;/a&gt;,
providing the &lt;em&gt;munin.client&lt;/em&gt; Python module.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">mod_defensible 1.5 released</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/mod_defensible-1.5"/>
    <updated>2012-04-03T15:48:00Z</updated>
    <published>2012-04-03T15:48:00Z</published>
    <id>/blog/2012/mod_defensible-1.5</id>
        <category   scheme="/blog/tags"
                term="apache"
                label="Apache" />
        <category   scheme="/blog/tags"
                term="dnsbl"
                label="Dnsbl" />
        <category   scheme="/blog/tags"
                term="debian"
                label="Debian" />
    
    <content type="html">
            &lt;p&gt;Apache 2.4 being out, I noticed that my good old
&lt;a href=&#34;http://julien.danjou.info/software/mod_defensible&#34;&gt;mod_defensible&lt;/a&gt; did not
compile anymore.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://httpd.apache.org/docs/2.4/developer/new_api_2_4.html&#34;&gt;changes in the new Apache 2.4
API&lt;/a&gt; were small
for its concern, so it was pretty easy to update this software to make it
compile again.&lt;/p&gt;
&lt;p&gt;Honestly, I&#39;m not sure that this module is really used into the wild, but I
still think that it can serve as a good prototype for doing other things so
I like keeping it around. :-)&lt;/p&gt;
&lt;p&gt;All this has been triggered by the Apache 2.4 arrival into Debian
experimental. Therefore &lt;a href=&#34;http://git.naquadah.org/?p=mod_defensible.git;a=shortlog;h=refs/heads/debian/unstable&#34;&gt;I&#39;ve updated the mod_defensible package to use the
new
dh_apache2&lt;/a&gt;,
and imported it into Git at the same time.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">xpyb 1.3 released</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/xpyb-1.3"/>
    <updated>2012-03-22T22:06:00Z</updated>
    <published>2012-03-22T22:06:00Z</published>
    <id>/blog/2012/xpyb-1.3</id>
        <category   scheme="/blog/tags"
                term="python"
                label="Python" />
        <category   scheme="/blog/tags"
                term="xcb"
                label="Xcb" />
        <category   scheme="/blog/tags"
                term="xpyb"
                label="Xpyb" />
        <category   scheme="/blog/tags"
                term="freedesktop"
                label="Freedesktop" />
        <category   scheme="/blog/tags"
                term="x11"
                label="X11" />
    
    <content type="html">
            &lt;p&gt;It took a while to get it out, but finally, 3 years after the latest release
(1.2), the version of 1.3 of &lt;a href=&#34;http://cgit.freedesktop.org/xcb/xpyb/&#34;&gt;xpyb&lt;/a&gt;
(the &lt;a href=&#34;http://xcb.freedesktop.org&#34;&gt;XCB&lt;/a&gt; Python bindngs) is out.&lt;/p&gt;
&lt;p&gt;This version has a lot of improvement, and major bug fixes (memory
corruption and memory leak were tracked down and fixed).&lt;/p&gt;
&lt;p&gt;One amazing feature that is now shipped with that release, is &lt;a href=&#34;http://julien.danjou.info/blog/2009/python-cairo-and-xcb-support&#34;&gt;my code to
export the xpyb API to other Python
modules&lt;/a&gt;,
allowing to draw with &lt;a href=&#34;http://www.cairographics.org/pycairo/&#34;&gt;Pycairo&lt;/a&gt; in
Python using XCB.&lt;/p&gt;
&lt;p&gt;Here is an example of a Python program that draws a spiral in a window using
xpyb and Pycairo. You need xpyb &amp;gt;= 1.3 and Pycairo &amp;gt;= 1.10 to make this
works.&lt;/p&gt;
&lt;pre class=&#34;prettyprint python&#34;&gt;
import cairo
import xcb
from xcb.xproto import *

WIDTH, HEIGHT = 600, 600

def draw_spiral(ctx, width, height):
    &#34;&#34;&#34;Draw a spiral with lines!&#34;&#34;&#34;
    wd = .02 * width
    hd = .02 * height

    width -= 2
    height -= 2

    ctx.move_to (width + 1, 1-hd)
    for i in range(9):
    ctx.rel_line_to (0, height - hd * (2 * i - 1))
    ctx.rel_line_to (- (width - wd * (2 *i)), 0)
    ctx.rel_line_to (0, - (height - hd * (2*i)))
    ctx.rel_line_to (width - wd * (2 * i + 1), 0)

    ctx.set_source_rgb (0, 0, 1)
    ctx.stroke()

# Connect to the X server
conn = xcb.connect()
# Get the X server setup
setup = conn.get_setup()
# Generate X ID for our X &#34;objects&#34;
window = conn.generate_id()
pixmap = conn.generate_id()
gc = conn.generate_id()
# Create a new window
conn.core.CreateWindow(setup.roots[0].root_depth, window,
                       # Parent is the root window
                       setup.roots[0].root,
                       0, 0, WIDTH, HEIGHT, 0, WindowClass.InputOutput,
                       setup.roots[0].root_visual,
                       CW.BackPixel | CW.EventMask,
                       [ setup.roots[0].white_pixel, EventMask.ButtonPress | EventMask.EnterWindow | EventMask.LeaveWindow | EventMask.Exposure ])

# Create a pixmap: it will be used to draw with cairo
conn.core.CreatePixmap(setup.roots[0].root_depth, pixmap, setup.roots[0].root,
                       WIDTH, HEIGHT)

# We just need a GC to copy later the pixmap on the window, so create one
# very simple
conn.core.CreateGC(gc, setup.roots[0].root, GC.Foreground | GC.Background,
                   [ setup.roots[0].black_pixel, setup.roots[0].white_pixel ])

# Create a cairo surface
surface = cairo.XCBSurface (conn, pixmap,
                            setup.roots[0].allowed_depths[0].visuals[0], WIDTH, HEIGHT)
# Create a cairo context with that surface
ctx = cairo.Context(surface)

# Paint everything in white
ctx.set_source_rgb (1, 1, 1)
ctx.set_operator (cairo.OPERATOR_SOURCE)
ctx.paint()

# Draw our spiral
draw_spiral (ctx, WIDTH, HEIGHT)

# Map the window on the screen so it gets visible
conn.core.MapWindow(window)

# Flush all X requests to the X server
conn.flush()

while True:
    try:
        event = conn.wait_for_event()
    except xcb.ProtocolException, error:
        print &#34;Protocol error %s received!&#34; % error.__class__.__name__
        break
    except:
        break

    # ExposeEvent are received when we need to refresh the content of the
    # window, so we copy the content of the pixmap (where cairo drew) in the
    # window
    if isinstance(event, ExposeEvent):
        conn.core.CopyArea(pixmap, window, gc, 0, 0, 0, 0, WIDTH, HEIGHT)
    # You click, I quit.
    elif isinstance(event, ButtonPressEvent):
        break
    conn.flush()
&lt;/pre&gt;

&lt;p&gt;Seeing the complexity it is to draw something simple with this technology, I
somehow understand why nobody bothered to release or use the code during the
last 3 years.&lt;/p&gt;
&lt;p&gt;But hey, now that it&#39;s out, you can build the next Python based desktop
environment with bleeding edge technologies. :-)&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">10 years as a Debian developer</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/ten-years-as-a-debian-developer"/>
    <updated>2012-02-24T08:55:00Z</updated>
    <published>2012-02-24T08:55:00Z</published>
    <id>/blog/2012/ten-years-as-a-debian-developer</id>
        <category   scheme="/blog/tags"
                term="debian"
                label="Debian" />
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="xen"
                label="Xen" />
        <category   scheme="/blog/tags"
                term="emacs"
                label="Emacs" />
        <category   scheme="/blog/tags"
                term="awesome"
                label="Awesome" />
        <category   scheme="/blog/tags"
                term="vhffs"
                label="Vhffs" />
        <category   scheme="/blog/tags"
                term="tuxfamily"
                label="Tuxfamily" />
    
    <content type="html">
            &lt;p&gt;Ten years ago, I joined the &lt;a href=&#34;http://www.debian.org&#34;&gt;Debian&lt;/a&gt; project as a developer.&lt;/p&gt;
&lt;p&gt;At that time, I was 18 and in my first year at university, hanging out with
the &lt;a href=&#34;http://tuxfamily.org&#34;&gt;TuxFamily&lt;/a&gt; system administrators, which included
3 french Debian developers (sjg, igenibel and creis).&lt;/p&gt;
&lt;p&gt;I was learning Debian packaging while working on &lt;a href=&#34;http://vhffs.org&#34;&gt;VHFFS&lt;/a&gt;,
and decided to package one or two non-yet-packaged software for Debian. My
friends pushed me into the &lt;a href=&#34;http://nm.debian.org&#34;&gt;NM process&lt;/a&gt;, and &lt;a href=&#34;https://nm.debian.org/nmstatus.php?email=acid@hno3.org&#34;&gt;less
than 2 months later&lt;/a&gt;
I was a Debian developer. One have to admit that back in the days, the NM
process was really fast if you were able to reply to the questions quickly.
:-) I think I became the youngest developer among Debian&#39;s ones.&lt;/p&gt;
&lt;p&gt;That was my first steps in a Free Software project, and it was really
exciting.&lt;/p&gt;
&lt;p&gt;In 10 years, I&#39;ve been doing a lot of different things for Debian. Sure,
I&#39;ve been using it all the years long, but let&#39;s recap a bit what I did,
from what I recall.&lt;/p&gt;
&lt;p&gt;My first Debian only project was
&lt;a href=&#34;http://packages.debian.org/apt-build&#34;&gt;apt-build&lt;/a&gt; around 2003, and later
&lt;a href=&#34;http://packages.debian.org/rebuildd&#34;&gt;rebuildd&lt;/a&gt; in 2007.&lt;/p&gt;
&lt;p&gt;I built the &lt;a href=&#34;https://alioth.debian.org/projects/pkg-xen/&#34;&gt;Xen packaging team&lt;/a&gt; in 2005,
I&#39;ve been a Stable Release Manager for a year in 2006, and did heavy bug
squashing to release Etch that same year.&lt;/p&gt;
&lt;p&gt;I also was an &lt;a href=&#34;https://nm.debian.org/whoisam.php&#34;&gt;Application Manager in
2006&lt;/a&gt; and managed the application of 2
Debian developers (&lt;a href=&#34;https://nm.debian.org/nmstatus.php?email=joseparrella%40cantv.net&#34;&gt;Jose
Parrella&lt;/a&gt;
and &lt;a href=&#34;https://nm.debian.org/nmstatus.php?email=debian%40damianv.com.ar&#34;&gt;Damián
Viano&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I admit I&#39;ve been less active in Debian after 2007, mainly because I was
busy working on &lt;a href=&#34;http://awesome.naquadah.org&#34;&gt;awesome&lt;/a&gt;, &lt;a href=&#34;http://www.gnu.org/software/emacs/&#34;&gt;GNU
Emacs&lt;/a&gt; and others software.&lt;/p&gt;
&lt;p&gt;Since 2011, I joined the &lt;a href=&#34;http://alioth.debian.org/projects/openstack/&#34;&gt;OpenStack packaging
team&lt;/a&gt; and I&#39;m working on
OpenStack on a (almost) daily basis.&lt;/p&gt;
&lt;p&gt;I don&#39;t know how many packages I touched, managed or updated, but that
should be one or two hundreds. I still maintain &lt;a href=&#34;http://qa.debian.org/developer.php?login=acid&#34;&gt;53 of
them&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After all, the adventure has been really pleasant, and I had the chance to
work with and meet fabulous and smart people. I always liked this project
and what it&#39;s trying to do.&lt;/p&gt;
&lt;p&gt;After all these years, I&#39;m definitively staying!
See you in another 10 years, folks! :)&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Google Calendar notifications using pynotify</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/google-calendar-pynotify"/>
    <updated>2012-01-03T18:55:00Z</updated>
    <published>2012-01-03T18:55:00Z</published>
    <id>/blog/2012/google-calendar-pynotify</id>
        <category   scheme="/blog/tags"
                term="python"
                label="Python" />
        <category   scheme="/blog/tags"
                term="gtk"
                label="Gtk" />
        <category   scheme="/blog/tags"
                term="notify"
                label="Notify" />
        <category   scheme="/blog/tags"
                term="googlecalendar"
                label="Googlecalendar" />
        <category   scheme="/blog/tags"
                term="gdata"
                label="Gdata" />
    
    <content type="html">
            &lt;p&gt;I use &lt;a href=&#34;http://google.com/calendar&#34;&gt;Google Calendar&lt;/a&gt; to manage my calendars,
and I really missed something to warn me whenever I have an appointment with
an alert set.&lt;/p&gt;
&lt;p&gt;So here is an example of a Python program to do such a thing. It is written using
the &lt;a href=&#34;http://code.google.com/p/gdata-python-client/&#34;&gt;Google Data APIs Python client library&lt;/a&gt;
and pynotify.&lt;/p&gt;
&lt;p&gt;I&#39;ll detail the code here, so you can build your own and adapt it to your needs.&lt;/p&gt;
&lt;p&gt;First, we need to import GTK+ and pynotify, and initialize it.&lt;/p&gt;
&lt;pre class=&#34;prettyprint python&#34;&gt;
import gtk
import pynotify
pynotify.init(sys.argv[0])
&lt;/pre&gt;

&lt;p&gt;Then, we need to import gdata Calendar API and connect to the calendar. I&#39;ll
use the simple email/password way to login, which is clearly not the best,
but it&#39;s also the simplest. Feel free to use OAuth 2.0. :-)&lt;/p&gt;
&lt;pre class=&#34;prettyprint python&#34;&gt;
calendar_service = gdata.calendar.service.CalendarService()
calendar_service.email = &#39;mygooglelogin&#39;
calendar_service.password = &#39;mygooglepassword&#39;
calendar_service.ProgrammaticLogin()
&lt;/pre&gt;

&lt;p&gt;Now we&#39;re ready to request stuff and notify! First, request the events from
the default calendar.&lt;/p&gt;
&lt;pre class=&#34;prettyprint python&#34;&gt;
feed = calendar_service.GetCalendarEventFeed()
&lt;/pre&gt;

&lt;p&gt;Now we can iterate over &lt;em&gt;feed&lt;/em&gt; and do various checks.&lt;/p&gt;
&lt;pre class=&#34;prettyprint python&#34;&gt;
for event in feed.entry:
    # If the event status is not confirmed, go to the next event.
    if event.event_status.value != &#34;CONFIRMED&#34;:
        continue
    # Now iterate over all the event dates (usually it has one)
    for when in event.when:
        # Parse start and end time
        try:
            start_time = datetime.datetime.strptime(when.start_time.split(&#34;.&#34;)[0], &#34;%Y-%m-%dT%H:%M:%S&#34;)
            end_time = datetime.datetime.strptime(when.end_time.split(&#34;.&#34;)[0], &#34;%Y-%m-%dT%H:%M:%S&#34;)
        except ValueError:
            # ValueError happens on parsing error. Parsing errors
            # usually happen for &#34;all day&#34; events since they have
            # not time, but we do not care about this events.
            continue
        now = datetime.datetime.now()
        # Check that the event hasn&#39;t already ended
        if end_time &gt; now:
            # Check each alert
            for reminder in when.reminder:
                # We handle only reminders with method &#34;alert&#34;
                # and whose start time minus the reminder delay has passed
                if reminder.method == &#34;alert&#34; \
                        and start_time - datetime.timedelta(0, 60 * int(reminder.minutes)) &lt; now:
                    # Build the notification
                    notification = pynotify.Notification(summary=event.title.text,
                                                         message=event.content.text)
                    # Set an icon from the GTK+ stock icons
                    notification.set_icon_from_pixbuf(gtk.Label().render_icon(gtk.STOCK_DIALOG_INFO,
                                                                              gtk.ICON_SIZE_LARGE_TOOLBAR))
                    notification.set_timeout(0)
                    # Show the notification
                    notification.show()
&lt;/pre&gt;

&lt;p&gt;Running this program, you should see a notification if an appointment has an
alert to be raised at that time.&lt;/p&gt;
&lt;p&gt;This should be enough to start to build something.&lt;/p&gt;
&lt;p&gt;If you don&#39;t want to program this into Python, you might want to take a look
at &lt;a href=&#34;http://code.google.com/p/gcalcli/wiki/HowTo&#34;&gt;gcalcli&lt;/a&gt;.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Using GTK+ stock icons with pynotify</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2011/python-notify-with-gtk-stock-icon"/>
    <updated>2011-12-27T11:55:00Z</updated>
    <published>2011-12-27T11:55:00Z</published>
    <id>/blog/2011/python-notify-with-gtk-stock-icon</id>
        <category   scheme="/blog/tags"
                term="python"
                label="Python" />
        <category   scheme="/blog/tags"
                term="gtk"
                label="Gtk" />
        <category   scheme="/blog/tags"
                term="notify"
                label="Notify" />
    
    <content type="html">
            &lt;p&gt;It took me a while to find this, so I&#39;m just blogging it so other people
will be able to find it.&lt;/p&gt;
&lt;p&gt;I wanted to send a &lt;a href=&#34;http://www.galago-project.org/specs/notification/&#34;&gt;desktop
notification&lt;/a&gt; using
pynotify, but using a &lt;a href=&#34;http://developer.gnome.org/gtk/2.24/gtk-Stock-Items.html&#34;&gt;GTK+ stock
icons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With the following snippet, I managed to do it.&lt;/p&gt;
&lt;pre class=&#34;prettyprint&#34;&gt;
import pynotify
pynotify.init(&#34;myapp&#34;)
import gtk
n = pynotify.Notification(summary=&#34;Summary&#34;, message=&#34;Message!&#34;)
n.set_icon_from_pixbuf(gtk.Label().render_icon(gtk.STOCK_HARDDISK, gtk.ICON_SIZE_LARGE_TOOLBAR))
n.show()
&lt;/pre&gt;

&lt;p&gt;Note that the use of a &lt;em&gt;Label&lt;/em&gt; is just to have a widget instanciated to use
the &lt;em&gt;render_icon()&lt;/em&gt; method. It could be any widget type as far as I
understand.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">My OpenStack work</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2011/my-openstack-work"/>
    <updated>2011-12-16T17:34:00Z</updated>
    <published>2011-12-16T17:34:00Z</published>
    <id>/blog/2011/my-openstack-work</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="debian"
                label="Debian" />
    
    <content type="html">
            &lt;p&gt;Like I already wrote here last week, I&#39;ve been heavily working on
&lt;a href=&#34;http://openstack.org&#34;&gt;OpenStack&lt;/a&gt; for the last weeks.&lt;/p&gt;
&lt;p&gt;My first assignment was to package OpenStack for Debian. The packages
already present in unstable were mainly done by &lt;a href=&#34;http://thomas.goirand.fr/&#34;&gt;Thomas
Goirand&lt;/a&gt;, who based its work on the one done in
&lt;a href=&#34;http://ubuntu.com&#34;&gt;Ubuntu&lt;/a&gt;. Therefore, the packages where not in a very
good shape for Debian.&lt;/p&gt;
&lt;p&gt;Today Ghe Rivero and I (members of the &lt;a href=&#34;https://alioth.debian.org/projects/openstack&#34;&gt;OpenStack Debian packaging
team&lt;/a&gt;) managed to push the
&lt;a href=&#34;https://launchpad.net/openstack/+milestone/essex-2&#34;&gt;OpenStack Essex 2
milestone&lt;/a&gt; into unstable
with great success. You can now test and deploy OpenStack Essex 2 very easily!&lt;/p&gt;
&lt;p&gt;Packaging OpenStack &lt;a href=&#34;https://review.openstack.org/#dashboard,1669&#34;&gt;made me write several
patches&lt;/a&gt;, mainly related to
packaging, patches which were all accepted and merged by upstream. This is
nice because most of the OpenStack Debian packages lost their
&lt;em&gt;debian/patches&lt;/em&gt; directories now!&lt;/p&gt;
&lt;p&gt;Finally, I&#39;ve finished to implement one blueprint I really missed: the
&lt;a href=&#34;https://blueprints.launchpad.net/nova/+spec/support-kvm-boot-from-iso&#34;&gt;ability to boot from an ISO
image&lt;/a&gt;
using &lt;a href=&#34;http://libvirt.org&#34;&gt;libvirt&lt;/a&gt;. The code still needs a review, but it
should be included in the Essex 3 milestone if everything&#39;s right.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">New job, new blog</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2011/new-job-new-blog"/>
    <updated>2011-12-07T13:47:00Z</updated>
    <published>2011-12-07T13:47:00Z</published>
    <id>/blog/2011/new-job-new-blog</id>
        <category   scheme="/blog/tags"
                term="work"
                label="Work" />
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="python"
                label="Python" />
        <category   scheme="/blog/tags"
                term="debian"
                label="Debian" />
    
    <content type="html">
            &lt;p&gt;It has been a while since I blogged but I&#39;ve been very busy, with my new job and this new blog!&lt;/p&gt;
&lt;h1&gt;New job!&lt;/h1&gt;
&lt;p&gt;I quitted my job last September, and found another one that I started in
October. I&#39;m now the lead developer of &lt;a href=&#34;http://www.enovance.com/fr/produits-solutions/opencloud-opensource/enovance-labs&#34;&gt;eNovance
Labs&lt;/a&gt;,
where I work on the &lt;a href=&#34;http://openstack.org/&#34;&gt;OpenStack&lt;/a&gt; project. So far, this allowed me to contribute heavily to the &lt;a href=&#34;https://alioth.debian.org/projects/openstack&#34;&gt;Debian packaging of OpenStack&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;New blog!&lt;/h1&gt;
&lt;p&gt;In the meantime, I took some time to redesign my personal homepage and this
blog, which is now using &lt;a href=&#34;https://github.com/hyde/hyde&#34;&gt;Hyde&lt;/a&gt;, the
&lt;a href=&#34;http://python.org&#34;&gt;Python&lt;/a&gt; equivalent of &lt;a href=&#34;http://jekyllrb.com/&#34;&gt;Jekyll&lt;/a&gt;,
which is in &lt;a href=&#34;http://www.ruby-lang.org/&#34;&gt;Ruby&lt;/a&gt;. Since I dislike Ruby
(sorry), I preferred to use a Python based generator, and I admit Hyde is really
cool.
Since I really suck at Web design, this one is obviously based on &lt;a href=&#34;http://twitter.github.com/bootstrap/&#34;&gt;Twitter&#39;s bootstrap&lt;/a&gt;&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Google Contacts for Emacs</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2011/google-contacts-for-emacs"/>
    <updated>2011-09-26T13:01:00Z</updated>
    <published>2011-09-26T13:01:00Z</published>
    <id>/blog/2011/google-contacts-for-emacs</id>
        <category   scheme="/blog/tags"
                term="googlecontacts"
                label="Googlecontacts" />
        <category   scheme="/blog/tags"
                term="emacs"
                label="Emacs" />
    
    <content type="html">
            &lt;p&gt;I finally finished a thing I was really missing: accessing my Google
Contacts from Emacs.&lt;/p&gt;
&lt;p&gt;That&#39;s now possible, thanks to my new
&lt;a href=&#34;/software/google-contacts.el&#34;&gt;google-contacts.el&lt;/a&gt;
package.&lt;/p&gt;
&lt;video controls=&#34;controls&#34;&gt;
&lt;source src=&#34;http://julien.danjou.info/images/emacs-google-contacts.ogv&#34; type=&#34;video/ogg&#34; /&gt;
&lt;/video&gt;

&lt;p&gt;It includes searching for any contact and displaying the result in a window.
You can also jump to a contact from &lt;a href=&#34;http://gnus.org&#34;&gt;Gnus&lt;/a&gt; by pressing a
key, and complete e-mail addresses while composing a mail.&lt;/p&gt;
          </content>
  </entry>
  </feed>
