<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://julien.danjou.info">
  <title>
    jd:/dev/blog  </title>
    <link href="http://julien.danjou.info/blog/tags/openstack.xml" rel="self" />
  
    <link href="http://julien.danjou.info/blog/"/>
  
    
  <updated>2013-05-17T15:32:30Z</updated>

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

    <entry>
    <title type="html">Rant about Github pull-request workflow implementation</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/rant-about-github-pull-request-workflow-implementation"/>
    <updated>2013-05-10T17:55:00Z</updated>
    <published>2013-05-10T17:55:00Z</published>
    <id>/blog/2013/rant-about-github-pull-request-workflow-implementation</id>
        <category   scheme="/blog/tags"
                term="github"
                label="Github" />
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="gerrit"
                label="Gerrit" />
    
    <content type="html">
            &lt;p&gt;One of my recent innocent tweet about &lt;em&gt;Gerrit vs Github&lt;/em&gt; triggered much more
reponses and debate that I expected it to. I realize that it might be worth
explaining a bit what I meant, in a text longer than 140 characters.&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet illustration&#34;&gt;
&lt;p&gt;
I&#39;m having a hard time now contributing to projects not using Gerrit. Github
isn&#39;t that good.&lt;/p&gt;&amp;mdash; Julien Danjou (@juldanjou)
&lt;a href=&#34;https://twitter.com/juldanjou/status/332076595521146881&#34;&gt;May 8, 2013&lt;/a&gt;&lt;/blockquote&gt;

&lt;script async src=&#34;//platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;

&lt;h1&gt;The problems with Github pull-requests&lt;/h1&gt;
&lt;p&gt;&lt;img class=&#34;illustration pull-right&#34; width=&#34;150&#34; src=&#34;/media/images/github.svg&#34;&gt;&lt;/p&gt;
&lt;p&gt;I always looked at Github from a distant eye, mainly because I always
disliked their pull-request handling, and saw no value in the social hype it
brings. Why?&lt;/p&gt;
&lt;h2&gt;One click away isn&#39;t one click effort&lt;/h2&gt;
&lt;p&gt;The pull-request system looks like an incredible easy way to contribute to
any project hosted on Github. You&#39;re a click away to send your contribution
to any software. But the problem is that any worthy contribution isn&#39;t an
effort of a single click.&lt;/p&gt;
&lt;p&gt;Doing any proper and useful contribution to a software is never done right
the first time. There&#39;s a dance you will have to play. A slowly rhythmed
back and forth between you and the software maintainer or team. You&#39;ll have
to dance it until your contribution is correct and can be merged.&lt;/p&gt;
&lt;p&gt;But as a software maintainer, not everybody is going to follow you on this
choregraphy, and you&#39;ll end up with pull-request you&#39;ll never get finished
unless you wrap things up yourself. So the gain in pull-requests here, isn&#39;t
really bigger than a good bug report in most cases.&lt;/p&gt;
&lt;p&gt;This is where the social argument of Github isn&#39;t anymore. As soon as you&#39;re
talking about projects bigger than a color theme for your favorite text
editor, this feature is overrated.&lt;/p&gt;
&lt;h2&gt;Contribution rework&lt;/h2&gt;
&lt;p&gt;If you&#39;re lucky enough, your contributor will play along and follow you on
this pull-request review process. You&#39;ll make suggestions, he will listen
and will modify his pull-request to follow your advice.&lt;/p&gt;
&lt;p&gt;At this point, there&#39;s two technics he can use to please you.&lt;/p&gt;
&lt;h3&gt;Technic #1: the Topping&lt;/h3&gt;
&lt;p&gt;Github&#39;s pull-requests invite you to send an entire branch, eclipsing the
fact that it is composed of several commits. The problem is that a lot of
one-click-away contributors do not masterize Git and/or do not make efforts
to build a logical patchset, and nothing warns them that their branch
history is wrong. So they tend to change stuff around, commit, make a
mistake, commit, fix this mistake, commit, etc. This kind of branch is
composed of the whole brain&#39;s construction process of your contributor, and
is a real pain to review. To the point I quite often give up.&lt;/p&gt;
&lt;figure&gt;
&lt;img class=&#34;illustration&#34;
src=&#34;/media/images/blog/2013/github-pull-request-iterative.png&#34;&gt;
&lt;figcaption&gt;
A typical case: 3 commits to build a 4 lines long file.
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Without Github, the old method that all software used, and that many
software still use (e.g. Linux), is to send a patch set over e-mail (or any
other medium like Gerrit). This method has one positive effect, that it
forces the contributor to acknowledge the list of commits he is going to
publish. So, if the contributor he has fixup commits in his history, they
are going to be seen as first class citizen. And nobody is going to want to
see that, neither your contributor, nor the software maintainers. Therefore,
such a system tend to push contributors to write atomic, logical and
self-contained patchset that can be more easily reviewed.&lt;/p&gt;
&lt;h3&gt;Technic #2: the History Rewriter&lt;/h3&gt;
&lt;p&gt;This is actually the good way to build a working and logical patchset using
Git. Rewriting history and amending problematic patches using the famous
&lt;em&gt;git rebase --interactive&lt;/em&gt; trick.&lt;/p&gt;
&lt;p&gt;The problem is that if your contributor does this and then repush the branch
composing your pull-request to Github, you will both lose the previous
review done, each time. There&#39;s no history on the different versions of the
branch that has been pushed.&lt;/p&gt;
&lt;p&gt;In the old alternative system like e-mail, no information is lost when
reworked patches are resent, obviously. This is far better because it eases
the following of the iterative discussions that the patch triggered.&lt;/p&gt;
&lt;p&gt;Of course, it would be possible for Github to enhance this and fix it, but
currently it doesn&#39;t handle this use case correctly..&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&#34;https://github.com/hylang/hy/pull/157&#34;&gt;
&lt;img class=&#34;illusration&#34;
src=&#34;/media/images/blog/2013/hylang-pull-request-157.png&#34;&gt;
&lt;/a&gt;
&lt;figcaption&gt;
Exercise for the doubtful readers: good luck finding all revisions of my
patch in the pull-request #157 of Hy.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h1&gt;A quick look at OpenStack workflow&lt;/h1&gt;
&lt;p&gt;&lt;img class=&#34;illustration pull-right&#34; width=&#34;150&#34; src=&#34;/media/images/projects/openstack.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s not a secret for anyone that I&#39;ve been contributing to OpenStack as a
daily routine for the last 18 months. The more I contribute, the more I like
the contribution workflow and process. It&#39;s already
&lt;a href=&#34;https://wiki.openstack.org/wiki/Gerrit_Workflow&#34;&gt;well and longly described on the wiki&lt;/a&gt;,
so I&#39;ll summarize here my view and what I like about it.&lt;/p&gt;
&lt;h2&gt;Gerrit&lt;/h2&gt;
&lt;p&gt;To send a contribution to any OpenStack project, you need to pass via
Gerrit. This is way simpler than doing a pull-request on Github actually,
all you have to do is do your commit(s), and type
&lt;a href=&#34;https://pypi.python.org/pypi/git-review&#34;&gt;&lt;em&gt;git review&lt;/em&gt;&lt;/a&gt;. That&#39;s it. Your
patch will be pushed to Gerrit and available for review.&lt;/p&gt;
&lt;p&gt;Gerrit allows other developers to review your patch, add comments anywhere
on it, and score your patch up or down. You can build any rule you want for
the score needed for a patch to be merged; OpenStack requires one positive
scoring from two core developers before the patch is merged.&lt;/p&gt;
&lt;p&gt;Until a patch is validated, it can be reworked and amended locally using
Git, and then resent using
&lt;em&gt;git review&lt;/em&gt; again. That simple.
The historic and the different version of the patches are available, with
the whole comments. Gerrit doesn&#39;t lose any historic information on your
workflow.&lt;/p&gt;
&lt;p&gt;Finally, you&#39;ll notice that this is actually the same kind of workflow
projects use when they work by patch sent over e-mail. Gerrit just build a
single place to regroup and keep track of patchsets, which is really handy.
It&#39;s also much easier for people to actually send patch using a command line
tool than their MUA or &lt;em&gt;git send-email&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Gate testing&lt;/h2&gt;
&lt;p&gt;Testing is mandatory for any patch sent to OpenStack. Unit tests and
functionnals test are run for &lt;em&gt;each version of each patch of the patchset&lt;/em&gt;
sent. And until your patch passes all tests, it will be &lt;em&gt;impossible&lt;/em&gt; to
merge it.
Yes, this implies that all patches in a patchset must be working commits and
can be merged on their own, without the entire patchset going in! With such
a restricution, it&#39;s impossible to have &#34;fixup commits&#34; merged in your
project and pollute the history and the testability of the project.&lt;/p&gt;
&lt;p&gt;Once your patch is validated by core developers, the system checks that
there is not any merge conflicts. If there&#39;s not, tests are re-run, since
the branch you are pushing to might have changed, and if everything&#39;s fine,
the patch is merged.&lt;/p&gt;
&lt;p&gt;This is an uncredible force for the quality of the project. This implies
that no broken patchset can ever sneak in, and that the project pass always
all tests.&lt;/p&gt;
&lt;h1&gt;Conclusion: accessibility vs code review&lt;/h1&gt;
&lt;p&gt;In the end, I think that one of the key of any development process, which is
code review, is not well covered by Github pull-request system. It is, along
with history integrity, damaged by the goal of making contributions easier.&lt;/p&gt;
&lt;p&gt;Choosing between these features is probably a trade-off that each project
should do carefully, considering what are its core goals and the quality of
code it want to reach.&lt;/p&gt;
&lt;p&gt;I tend to find that OpenStack found one of the best trade-off available
using Gerrit and plugging testing automation via Jenkins on it, and I would
probably recommend it for any project taking seriously code reviews and
testing.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">OpenStack Design Summit Havana, from a Ceilometer point of view</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/openstack-summit-havana-ceilometer"/>
    <updated>2013-04-25T14:49:45Z</updated>
    <published>2013-04-25T14:49:45Z</published>
    <id>/blog/2013/openstack-summit-havana-ceilometer</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;p&gt;Last week was the
&lt;a href=&#34;https://www.openstack.org/summit/portland-2013/&#34;&gt;OpenStack Design Summit&lt;/a&gt;
in Portland, OR where we, developers, discussed and designed the new
OpenStack release (Havana) coming up.&lt;/p&gt;
&lt;p&gt;&lt;img class=&#34;illustration&#34; src=&#34;/media/images/blog/2013/ods_havana_banner.jpg&#34;&gt;&lt;/p&gt;
&lt;p&gt;The summit has been wonderful. It was my first OpenStack design summit --
even more as a PTL -- and bumping into various people I&#39;ve never met so far
and worked with online only was a real pleasure!&lt;/p&gt;
&lt;figure&gt;
&lt;img width=&#34;500&#34; class=&#34;illustration shadow&#34; src=&#34;/media/images/blog/2013/ods_havana_ceilometer_nijaba_jd_talk.jpg&#34;&gt;
&lt;figcaption&gt;Me and Nick ready to talk about Ceilometer new features.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&#34;http://nicolas.barcet.com/&#34;&gt;Nick Barcet&lt;/a&gt; from
&lt;a href=&#34;http://www.enovance.com&#34;&gt;eNovance&lt;/a&gt;, our dear previous Ceilometer PTL, and
myself, talked about Ceilometer and presented the work that bas been done
for Grizzly, with some previews of what we&#39;ll like to see done for its
Havana release. You can take a look at
&lt;a href=&#34;http://julien.danjou.info/media/Ceilometer%20presentation%20ODS%20Havana.pdf&#34;&gt;the slides&lt;/a&gt;
if you&#39;re curious.&lt;/p&gt;
&lt;h1&gt;Design sessions&lt;/h1&gt;
&lt;p&gt;Ceilometer had his design sessions during the last days of the summit. We
noted a lot of things and commented during the sessions in our
&lt;a href=&#34;https://wiki.openstack.org/wiki/Summit/Havana/Etherpads#Ceilometer&#34;&gt;Etherpads instances&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first session was a description of Ceilometer core architecture for
interested people, and was a wonderful success considering that the room was
packed. Our &lt;a href=&#34;http://doughellmann.com/&#34;&gt;Doug Hellmann&lt;/a&gt; did a wonderful job
introducing people to Ceilometer and answering question.&lt;/p&gt;
&lt;figure&gt;
&lt;img width=&#34;500&#34; class=&#34;illustration shadow&#34; src=&#34;/media/images/blog/2013/ods_havana_ceilometer_dhellmann.jpg&#34;&gt;
&lt;figcaption&gt;Doug explaining Ceilometer architecture.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The next session was about getting feedbacks from our users. We had a lot of
surprise to discover wonderful real use-cases and deployments, like the CERN
using Ceilometer and generating 2 GB of data per day!&lt;/p&gt;
&lt;p&gt;The following sessions ran on Thursday and were much more about new features
discussion. A lot ot already existing blueprints were discussed and quickly
validated during the first morning session. Then,
&lt;a href=&#34;http://www.sandywalsh.com/&#34;&gt;Sandy Walsh&lt;/a&gt; introduced the architecture they
use inside &lt;a href=&#34;https://github.com/rackerlabs/stacktach&#34;&gt;StackTach&lt;/a&gt;, so we can
start thinking about getting things from it into Ceilometer.&lt;/p&gt;
&lt;p&gt;API improvements were discussed without surprises and with a good consensus
on what needs to be done. The four following sessions that occupied a lot of
the days were related to alarming. All were lead by Eoghan Glynn, from
&lt;a href=&#34;http://redhat.com&#34;&gt;RedHat&lt;/a&gt;, who did an amazing job presenting the possible
architectures with theirs pros and cons. Actually, all we had to do was to
nod to his designs and acknowledge the plan on how to build this.&lt;/p&gt;
&lt;p&gt;That last two sessions were about discussing advanced models for billing
where we got some interesting feedback from Daniel Dyer from HP, and then
were a quick follow-up of the StackTach presentation from the morning
session.&lt;/p&gt;
&lt;h1&gt;Havana roadmap&lt;/h1&gt;
&lt;p&gt;The
&lt;a href=&#34;https://blueprints.launchpad.net/ceilometer/havana&#34;&gt;list of blueprints targetting Havana is available&lt;/a&gt;
and should be finished by next week. If you want to propose blueprints,
you&#39;re free to do so and inform us about it so we can validate it. The same
applies if you wish to implement one of them!&lt;/p&gt;
&lt;h2&gt;API extension&lt;/h2&gt;
&lt;p&gt;I do think the API version 2 is going to be heavily extended during this
release cycle. We need more feature, like the
&lt;a href=&#34;https://blueprints.launchpad.net/ceilometer/+spec/api-group-by&#34;&gt;group-by&lt;/a&gt;
functionnality.&lt;/p&gt;
&lt;h2&gt;Healthnmon&lt;/h2&gt;
&lt;p&gt;In parallel of the design sessions, discussions took place in the
unconference room with the Healthnmon developers to figure out a plan in
order to merge some of their efforts into Ceilometer. They should provide a
component to help Ceilometer supports more hypervisors than it currently
does.&lt;/p&gt;
&lt;h2&gt;Alarming&lt;/h2&gt;
&lt;p&gt;Alarming is definitely going to be the next big project for Ceilometer.
Today, Eoghan and I started building blueprints on alarming,
&lt;a href=&#34;https://blueprints.launchpad.net/ceilometer/+spec/alarming&#34;&gt;centralized in a general blueprint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We know this is going to happen for real and very soon, thanks to the
engagements of &lt;a href=&#34;http://enovance.com&#34;&gt;eNovance&lt;/a&gt; and
&lt;a href=&#34;http://redhat.com&#34;&gt;RedHat&lt;/a&gt; who are commiting resources to this amazing
project!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Announcing Climate, the OpenStack capacity leasing project</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/openstack-climate-capacity-leasing"/>
    <updated>2013-03-25T17:49:23Z</updated>
    <published>2013-03-25T17:49:23Z</published>
    <id>/blog/2013/openstack-climate-capacity-leasing</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="climate"
                label="Climate" />
    
    <content type="html">
            &lt;p&gt;While working on the &lt;a href=&#34;http://xlcloud.org/bin/view/Main/&#34;&gt;XLcloud project&lt;/a&gt;
(HPC on cloud) it appeared clear to us that OpenStack was missing a critical
component towards resource reservations.&lt;/p&gt;
&lt;div class=&#34;pull-left&#34;&gt;
    &lt;img width=&#34;120&#34; src=&#34;/media/images/calendar-reservation.jpg&#34;&gt;
&lt;/div&gt;

&lt;p&gt;A capacity leasing service is something really needed by service providers,
especially in the context of cloud platforms dedicated to HPC style
workload. Instead of building something really specific, the decision has
been made to build a new standalone OpenStack components aiming to provide
this kind of functionnality to OpenStack. In the spirit of others OpenStack
components, it will be extensible to fullfil a large panel of needs around
this problematic.&lt;/p&gt;
&lt;div class=&#34;pull-right&#34;&gt;
    &lt;img width=&#34;80&#34; src=&#34;/media/images/projects/openstack.png&#34;&gt;
&lt;/div&gt;

&lt;p&gt;The project is named &lt;a href=&#34;http://launchpad.net/climate&#34;&gt;Climate&lt;/a&gt;, and is hosted
on &lt;a href=&#34;http://ci.openstack.org/stackforge.html&#34;&gt;StackForge&lt;/a&gt;. It will follow the
standard OpenStack development modal. This service will be able to handle a
calendar of reservations for various resources, based on various criteria.&lt;/p&gt;
&lt;p&gt;The project is still at its early design stage, and we plan to have a
unconference session during
&lt;a href=&#34;http://www.openstack.org/summit/portland-2013/&#34;&gt;the next OpenStack summit in Portland&lt;/a&gt;
to present our plans and ideas for the future!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Ceilometer bug squash day &amp;#35;2</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/ceilometer-bug-squash-day-2"/>
    <updated>2013-03-04T11:55:00Z</updated>
    <published>2013-03-04T11:55:00Z</published>
    <id>/blog/2013/ceilometer-bug-squash-day-2</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;p&gt;The Ceilometer team is pleased &lt;a href=&#34;http://lists.openstack.org/pipermail/openstack-dev/2013-March/006188.html&#34;&gt;to
announce&lt;/a&gt;
that tomorrow &lt;a href=&#34;http://wiki.openstack.org/Ceilometer/BugSquashingDay/20130304&#34;&gt;Tuesday 5th March 2013 will be the second bug squash day for
Ceilometer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We wrote an extensive page about &lt;a href=&#34;http://wiki.openstack.org/Ceilometer/Contributing&#34;&gt;how you can contribute to
Ceilometer&lt;/a&gt;, from
updating the documentation, to fixing bugs. There&#39;s a lot you can do. We&#39;ve
good support for Ceilometer built into &lt;a href=&#34;http://devstack.org&#34;&gt;Devstack&lt;/a&gt;, so
installing a development platform is really easy.&lt;/p&gt;
&lt;p&gt;The main goal for this bug day will be to put Ceilometer in the best
possible shape before the &lt;em&gt;grizzly-rc1&lt;/em&gt; release arrives (14th March 2013).
This version of Ceilometer &lt;em&gt;should&lt;/em&gt; be the last one before the final Grizzly
release, so it&#39;s a pretty important one.&lt;/p&gt;
&lt;p&gt;We&#39;ll be hanging out on the &lt;em&gt;#openstack-metering&lt;/em&gt; IRC channel on
&lt;a href=&#34;http://freenode.net&#34;&gt;Freenode&lt;/a&gt;, as usual, so feel free to come by and join
us!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">OpenStack Ceilometer and Heat projects graduated</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/openstack-ceilometer-head-graduated"/>
    <updated>2013-02-27T14:49:45Z</updated>
    <published>2013-02-27T14:49:45Z</published>
    <id>/blog/2013/openstack-ceilometer-head-graduated</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;div class=&#34;pull-right clear&#34;&gt;
  &lt;img width=&#34;150&#34; src=&#34;/media/images/openstack-tech-committee.jpg&#34;&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;a href=&#34;http://www.openstack.org/foundation/technical-committee/&#34;&gt;OpenStack Technical
Committee&lt;/a&gt; have
voted these last weeks about graduation of
&lt;a href=&#34;https://launchpad.net/heat&#34;&gt;Heat&lt;/a&gt; and
&lt;a href=&#34;http://launchpad.net/ceilometer&#34;&gt;Ceilometer&lt;/a&gt;, to change their status from
&lt;strong&gt;incubation&lt;/strong&gt; to &lt;strong&gt;integrated&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The details of the discussion can be found in the
&lt;a href=&#34;http://eavesdrop.openstack.org/meetings/tc/2013/&#34;&gt;TC IRC meetings logs&lt;/a&gt; for
the brave. The results are:&lt;/p&gt;
&lt;dl&gt;
  &lt;dt&gt;Approve graduation of Heat (to be integrated in common Havana release)?&lt;/dt&gt;
  &lt;dd&gt;yes: 10, abstain: 1, no: 1&lt;/dd&gt;
  &lt;dt&gt;Approve graduation of Ceilometer (to be integrated in common Havana release)?&lt;/dt&gt;
  &lt;dd&gt;yes: 11, abstain: 1&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Therefore both projects have been graduated from &lt;em&gt;Incubation&lt;/em&gt; to
&lt;em&gt;Integrated&lt;/em&gt; status. That means that Heat and Ceilometer will be released as
part as OpenStack for the next release cycle &lt;em&gt;Havana&lt;/em&gt;, due in Autumn 2013.&lt;/p&gt;
&lt;p&gt;For people being curious, we the Ceilometer team put up a &lt;a href=&#34;https://wiki.openstack.org/wiki/Ceilometer/Graduation&#34;&gt;nice wiki page
about our status and what we think we were ready to
jump&lt;/a&gt;. For the
curious, The &lt;a href=&#34;https://wiki.openstack.org/wiki/Governance/Foundation/TechnicalCommittee&#34;&gt;OpenStack Technical Committee
charter&lt;/a&gt;
has some explanations about the incubation and integration process.&lt;/p&gt;
&lt;h1&gt;What about Grizzly?&lt;/h1&gt;
&lt;p&gt;Both projects will be released with Grizzly too, obviously, since they
already follow the release process of OpenStack.&lt;/p&gt;
&lt;h1&gt;What about core?&lt;/h1&gt;
&lt;div class=&#34;pull-left clear&#34;&gt;
  &lt;img width=&#34;120&#34; src=&#34;/media/images/projects/openstack.png&#34;&gt;
&lt;/div&gt;

&lt;p&gt;The question that has been raised several times to me is if that means the
projects are becoming &lt;em&gt;Core&lt;/em&gt; projects. The answer is no, because how to
become a &lt;em&gt;Core&lt;/em&gt; project is still under discussion and is more a matter for
the &lt;em&gt;Board of Directors&lt;/em&gt; than the &lt;em&gt;Technical Committee&lt;/em&gt;. But this is
definitely a step in this direction.&lt;/p&gt;
&lt;p&gt;Anyway, from a technical point of view, this means both projects are now
onboard with other OpenStack components so you can enjoy them!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Cloud tools for Debian</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/cloud-init-utils-debian"/>
    <updated>2013-02-13T13:21:45Z</updated>
    <published>2013-02-13T13:21:45Z</published>
    <id>/blog/2013/cloud-init-utils-debian</id>
        <category   scheme="/blog/tags"
                term="debian"
                label="Debian" />
        <category   scheme="/blog/tags"
                term="ubuntu"
                label="Ubuntu" />
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
    
    <content type="html">
            &lt;p&gt;Recently, I&#39;ve worked on the cloud utilities that are provided as standard
in Ubuntu, and I ported them to Debian. Let&#39;s see how that brings Debian to
the cloud!&lt;/p&gt;
&lt;h1&gt;Basics of a cloud image&lt;/h1&gt;
&lt;p&gt;When starting an instance on a IaaS platform, your instance image is raw,
un-configured. Therefore, you need to have a way to configure it
automagically at boot time, based on what you want to do with it. Usually,
IaaS platforms provides for this a metadata server, like
&lt;a href=&#34;http://aws.amazon.com/ec2&#34;&gt;Amazon EC2&lt;/a&gt; does. It&#39;s a special HTTP server
listening on a special and hard-coded IP address that your instance can
request to know basic information about itself, like its hostname, and
retrieve basic user metadata to auto-configure itself. You can check the
&lt;a href=&#34;http://docs.openstack.org/trunk/openstack-compute/admin/content/metadata-service.html&#34;&gt;documentation about the OpenStack metadata service&lt;/a&gt;
for more information.&lt;/p&gt;
&lt;p&gt;Also, image have a predefined size at upload time. So when you run it on a
platform, the disk size you request is usually bigger than the size of your
image disk: you mayneed to resize and grow your image to use the full disk
space that is allocated to your instance.&lt;/p&gt;
&lt;h1&gt;Needed tools&lt;/h1&gt;
&lt;div class=&#34;pull-right&#34;&gt;
    &lt;img width=&#34;120&#34; src=&#34;/media/images/debian-cloud.jpg&#34;&gt;
&lt;/div&gt;

&lt;p&gt;To run a cloud platform, and especially
&lt;a href=&#34;http://aws.amazon.com/ec2&#34;&gt;Amazon EC2&lt;/a&gt; or
&lt;a href=&#34;http://openstack.org&#34;&gt;OpenStack&lt;/a&gt;, you need to configure and update your
image based on the context you&#39;re started in. This also includes extending
your template image disk to use the full available disk size provided to the
running instance.&lt;/p&gt;
&lt;p&gt;Ubuntu provides a set of cloud utils, which is actually composed of
different source packages (&lt;em&gt;cloud-init&lt;/em&gt;, &lt;em&gt;cloud-utils&lt;/em&gt; and
&lt;em&gt;clout-initiramfs-tools&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Combined, these 3 packages will allow you to run a number of step, from disk
resize at boot time to Puppet configuration handling.&lt;/p&gt;
&lt;p&gt;So &lt;em&gt;Ubuntu&lt;/em&gt; got this working right a long time ago, but unfortunately,
Debian was really late on that.&lt;/p&gt;
&lt;p&gt;Until now.&lt;/p&gt;
&lt;p&gt;I&#39;ve worked on getting these into Debian, and you can now find these 3
packages adapted and uploaded to Debian sid.&lt;/p&gt;
&lt;p&gt;All you need to do, is to build a Debian image and then run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;apt-get install cloud-init cloud-tools cloud-initiramfs-growroot&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
And voilà: at the next reboot, your instance will extend its root partition
size to the full available disk size, and ask the metadata server to
configure things like its hostname.&lt;/p&gt;
&lt;p&gt;The packages sources are available on Debian&#39;s git server for
&lt;a href=&#34;http://anonscm.debian.org/gitweb/?p=collab-maint/cloud-utils.git;a=summary&#34;&gt;cloud-utils&lt;/a&gt;
and
&lt;a href=&#34;http://anonscm.debian.org/gitweb/?p=collab-maint/cloud-initramfs-tools.git;a=summary&#34;&gt;cloud-initramfs-tools&lt;/a&gt;
and you can build them yourself until the packages are processed by
ftp-master and get out of the
&lt;a href=&#34;http://ftp-master.debian.org/new.html&#34;&gt;NEW queue&lt;/a&gt;. cloud-init on the other
hand is directly
&lt;a href=&#34;http://packages.debian.org/search?keywords=cloud-init&#34;&gt;available in sid&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of next steps would probably be to build or enhance a tool like
&lt;a href=&#34;https://launchpad.net/vmbuilder&#34;&gt;vmbuilder&lt;/a&gt; to be able to build
cloud-compatible Debian images with a simple command line.&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Going to FOSDEM 2013</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/going-to-fosdem"/>
    <updated>2013-01-29T22:01:20Z</updated>
    <published>2013-01-29T22:01:20Z</published>
    <id>/blog/2013/going-to-fosdem</id>
        <category   scheme="/blog/tags"
                term="fosdem"
                label="Fosdem" />
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
    
    <content type="html">
            &lt;div class=&#34;pull-right&#34;&gt;
    &lt;img width=&#34;120&#34; src=&#34;/media/images/fosdem.png&#34;&gt;
&lt;/div&gt;

&lt;p&gt;For the first time, I&#39;ll be at &lt;a href=&#34;http://fosdem.org&#34;&gt;FOSDEM 2013&lt;/a&gt; in Brussels
on Sunday 2nd February 2013.&lt;/p&gt;
&lt;p&gt;You&#39;ll find me probably hanging out in the
&lt;a href=&#34;https://fosdem.org/2013/schedule/track/cloud&#34;&gt;cloud devroom&lt;/a&gt;
where I&#39;ll
&lt;a href=&#34;https://fosdem.org/2013/schedule/event/openstack_ceilometer/&#34;&gt;talk about Ceilometer&lt;/a&gt;
with my fellow developers &lt;a href=&#34;http://nicolas.barcet.com&#34;&gt;Nick Barcet&lt;/a&gt; and
&lt;a href=&#34;http://eoghang.blogspot.fr/&#34;&gt;Eoghan Glynn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also hope I&#39;ll find time to take a peek at some other talks, like
&lt;a href=&#34;http://tapoueh.org/blog/2013/01/29-FOSDEM-2013.html&#34;&gt;PostgreSQL&#39;s ones that Dimitri Fontaine will handle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See you there!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Extending Swift with middleware: example with ClamAV</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2013/extending-swift-with-a-middleware-clamav"/>
    <updated>2013-01-22T09:37:45Z</updated>
    <published>2013-01-22T09:37:45Z</published>
    <id>/blog/2013/extending-swift-with-a-middleware-clamav</id>
        <category   scheme="/blog/tags"
                term="swift"
                label="Swift" />
        <category   scheme="/blog/tags"
                term="clamav"
                label="Clamav" />
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
    
    <content type="html">
            &lt;div class=&#34;pull-left&#34;&gt;
    &lt;img width=&#34;120&#34; src=&#34;/media/images/openstack-virus.jpg&#34;&gt;
&lt;/div&gt;

&lt;p&gt;In this article, I&#39;m going to explain you how you can extend
&lt;a href=&#34;http://launchpad.net/swift&#34;&gt;Swift&lt;/a&gt;, the &lt;a href=&#34;http://openstack.org&#34;&gt;OpenStack&lt;/a&gt;
Object Storage project, so it performs extra action on files at upload or at
download time.&lt;/p&gt;
&lt;p&gt;We&#39;re going to build an anti-virus filter inside Swift. The goal is to
refuse uploaded data if they contain a virus. To help us with virus
analyses, we&#39;ll use &lt;a href=&#34;http://www.clamav.net&#34;&gt;ClamAV&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;WSGI, paste and middleware&lt;/h1&gt;
&lt;div class=&#34;span3 pull-right thumbnail&#34;&gt;
    &lt;img src=&#34;/media/images/lolcat-tube.jpg&#34;&gt;
    &lt;p&gt;In a pipeline, a black cat can become a white cat with the help of
some middleware.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;To do our content analysis, the best place to hook in the Swift architecture
is at the beginning of every request, on &lt;strong&gt;swift-proxy&lt;/strong&gt;, before the file is
actually stored on the cluster. Swift proxy uses, like many other OpenStack
projects, &lt;a href=&#34;http://pythonpaste.org/&#34;&gt;paste&lt;/a&gt; to build his HTTP architecture.&lt;/p&gt;
&lt;p&gt;Paste uses WSGI and provides an architecture based on a pipeline. The
pipeline is composed of a succession of middleware, ending with one
application. Each middleware has the chance to look at the request or at the
response, can modify it, and then pass it to the following middleware. The
latest component of the pipeline is the real application, and in this case,
the Swift proxy server.&lt;/p&gt;
&lt;p&gt;If you&#39;ve already deployed Swift, you encountered a default pipeline in the
&lt;em&gt;swift-proxy.conf&lt;/em&gt; configuration file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;k&#34;&gt;[pipeline:main]&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;na&#34;&gt;pipeline&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;catch_errors healthcheck cache ratelimit tempauth proxy-logging proxy-server&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
This is a really basic pipeline with a few middleware. The first one catches
error, the second one is in charge to return &lt;em&gt;200 OK&lt;/em&gt; response if you send a
&lt;em&gt;GET /healthcheck&lt;/em&gt; request on your proxy server. The third one is in charge
of caching, the fourth one is used for rate limiting, the fifth for
authentication, the sixth one for logging, and the final one is the actual
proxy server, in charge of proxying the request to the account, container,
or object servers (the others components of Swift). Of course, we could
remove or add any of the middleware here at our convenience.&lt;/p&gt;
&lt;p&gt;Be aware that the order matters: for example, if you put &lt;em&gt;healthcheck&lt;/em&gt; after
&lt;em&gt;tempauth&lt;/em&gt;, you won&#39;t be able to access the &lt;em&gt;/healthcheck&lt;/em&gt; URL without being
authenticated!&lt;/p&gt;
&lt;h1&gt;ClamAV&lt;/h1&gt;
&lt;div class=&#34;pull-left&#34;&gt;
    &lt;img width=&#34;100&#34; src=&#34;/media/images/clamav.png&#34;&gt;
&lt;/div&gt;

&lt;p&gt;If you don&#39;t know &lt;a href=&#34;http://clamav.org&#34;&gt;ClamAV&lt;/a&gt;, it&#39;s an antivirus engine
designed for detecting trojans, viruses, malware and other malicious
threats. Wwe&#39;re going to use it to scan every incoming file. To build the
middleware, we&#39;ll use the Python binding
&lt;a href=&#34;http://pypi.python.org/pypi/clamd&#34;&gt;pyclamd&lt;/a&gt;. The API is quite simple, see:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pyclamd&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init_unix_socket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;/var/run/clamav/clamd.ctl&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scan_stream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EICAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;stream&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scan_stream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;safe!&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;bp&#34;&gt;None&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h1&gt;Anatomy of a WSGI middleware&lt;/h1&gt;
&lt;p&gt;Your WSGI middleware should consist of a callable object. Usually this is
done with a class implementing the &lt;em&gt;__call__&lt;/em&gt; method. Here&#39;s a basic
boilerplate:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Middleware doing virus scan for Swift.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;c&#34;&gt;# app is the final application&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__call__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;filter_factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;global_conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;global_conf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;clamav_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clamav_filter&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
I&#39;m not going to expand more on why this is built this way, but if you want
to have more info on this kind of filter middleware, you can read
&lt;a href=&#34;http://pythonpaste.org/deploy/#paste-filter-factory&#34;&gt;their documentation on Paste&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This middleware will just do nothing as it is. It&#39;s going to simply pass all
requests it receives to the final application, and returns the result.&lt;/p&gt;
&lt;h1&gt;Testing our basic middleware&lt;/h1&gt;
&lt;div class=&#34;pull-right thumbnail&#34;&gt;
    &lt;img width=&#34;180&#34; src=&#34;/media/images/lolcat-testing.jpg&#34;&gt;
&lt;/div&gt;

&lt;p&gt;Now is a really good time to add unit tests. I hope you didn&#39;t think we were
going to write code without some tests, right? It&#39;s really easy to test a
middleware, as we&#39;re going to use &lt;a href=&#34;http://webob.org/&#34;&gt;WebOb&lt;/a&gt; for that.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;unittest&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;webob&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;FakeApp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__call__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;FAKE APP&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TestSwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unittest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TestCase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FakeApp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test_simple_request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;br /&gt;                                 &lt;span class=&#34;s&#34;&gt;&amp;#39;REQUEST_METHOD&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEqual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;quot;FAKE APP&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
We create a FakeApp class, that represents a fake WSGI application. You
could also use a real application, or write a fake application looking like
the one you want to test. It&#39;ll require more time, but your tests will be
closer to the reality.&lt;/p&gt;
&lt;p&gt;Here we write the simplest test we can for our middleware. We&#39;re just
sending a &lt;em&gt;GET /&lt;/em&gt; request to it, so it passes the request to the final
application and returns the result. It is transparent, it does nothing.&lt;/p&gt;
&lt;p&gt;Now, with that solid base we&#39;ll able to add more features and test these
features incrementally.&lt;/p&gt;
&lt;h1&gt;Plugging ClamAV in&lt;/h1&gt;
&lt;p&gt;With our base ready, we can start thinking about how to plug ClamAV in. What
we want to check here, is the content of the file when it&#39;s uploaded.
If we refer to the
&lt;a href=&#34;http://docs.openstack.org/api/openstack-object-storage/1.0/content/&#34;&gt;OpenStack object storage API&lt;/a&gt;
, a file upload is done via a &lt;em&gt;PUT&lt;/em&gt; request, so we&#39;re going to limit the
check to that kind of requests. Obviously, more checks could be added, but
we&#39;ll keep things simple here for the sake of comprehensibility.&lt;/p&gt;
&lt;p&gt;With WSGI, the content of the request is available in &lt;em&gt;env[&#39;wsgi.input&#39;]&lt;/em&gt; as
an object implementing a file interface. We&#39;ll scan that stream with ClamAV
to check for viruses.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pyclamd&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;webob&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;sd&#34;&gt;&amp;quot;&amp;quot;&amp;quot;Middleware doing virus scan for Swift.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init_unix_socket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;/var/run/clamav/clamd.ctl&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;c&#34;&gt;# app is the final application&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__call__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;REQUEST_METHOD&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;quot;PUT&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;c&#34;&gt;# We have to read the whole content in memory because pyclamd&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;c&#34;&gt;# forces us to, but this is a bad idea if the file is huge.&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;n&#34;&gt;scan&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scan_stream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;wsgi.input&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;403&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                                &lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;Virus &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; detected&amp;quot;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;stream&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&lt;br /&gt;                                &lt;span class=&#34;n&#34;&gt;content_type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;filter_factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;global_conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;global_conf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;clamav_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clamav_filter&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
That&#39;s it. We only check for &lt;em&gt;PUT&lt;/em&gt; requests and if there&#39;s a virus in the
file, we return a 403 Forbidden error with the name of the detected virus,
bypassing entirely the rest of the middleware chain and the application
handling.&lt;/p&gt;
&lt;p&gt;Then, we can simply test it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;unittest&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;cStringIO&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;StringIO&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;webob&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;FakeApp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__call__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;FAKE APP&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TestSwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unittest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TestCase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SwiftClamavMiddleware&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FakeApp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test_put_empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;/v1/account/container/object&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;br /&gt;                                 &lt;span class=&#34;s&#34;&gt;&amp;#39;REQUEST_METHOD&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEqual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;quot;FAKE APP&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test_put_no_virus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;/v1/account/container/object&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;br /&gt;                                 &lt;span class=&#34;s&#34;&gt;&amp;#39;REQUEST_METHOD&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                                 &lt;span class=&#34;s&#34;&gt;&amp;#39;wsgi.input&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;StringIO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;foobar&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEqual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;quot;FAKE APP&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;test_put_virus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blank&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#39;/v1/account/container/object&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;br /&gt;                                 &lt;span class=&#34;s&#34;&gt;&amp;#39;REQUEST_METHOD&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;br /&gt;                                 &lt;span class=&#34;s&#34;&gt;&amp;#39;wsgi.input&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;StringIO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pyclamd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EICAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;                             &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;assertEqual&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status_code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;403&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
The first test &lt;em&gt;test_put_empty&lt;/em&gt; simulates an empty &lt;em&gt;PUT&lt;/em&gt; request. The second
one, &lt;em&gt;test_put_no_virus&lt;/em&gt; simulates a regular &lt;em&gt;PUT&lt;/em&gt; request but with a simple
file containing no virus.&lt;/p&gt;
&lt;p&gt;Finally, the third and last test simulates the upload of a virus using the
&lt;a href=&#34;http://www.eicar.org/&#34;&gt;EICAR&lt;/a&gt; test file. This is a special test file that
is recognized as a virus, even if it&#39;s not real one. Very handy for testing
virus detection software!&lt;/p&gt;
&lt;h1&gt;Configuring Swift proxy&lt;/h1&gt;
&lt;p&gt;Our middleware is ready! We can configure Swift&#39;s proxy server to use it. We
need to add the following lines to our &lt;em&gt;swift-proxy.conf&lt;/em&gt; to teach it how to
load the filter:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;k&#34;&gt;[filter:clamav]&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;na&#34;&gt;paste.filter_factory&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;swiftclamav:filter_factory&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
We&#39;ll assume that our Python modules is named &lt;em&gt;swiftclamava&lt;/em&gt; here. Now that
we&#39;ve defined our filter and how to load it, we can use it in our pipeline:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;k&#34;&gt;[pipeline:main]&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;na&#34;&gt;pipeline&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;catch_errors healthcheck cache ratelimit tempauth clamav proxy-logging proxy-server&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
Just before reaching the &lt;em&gt;proxy-server&lt;/em&gt;, and after the user being
authenticated, the content will be scanned for viruses. It&#39;s important here
to put this after authentication for example, because otherwise we may scan
content that will get rejected by the authtemp module, thus scanning for
nothing!&lt;/p&gt;
&lt;h1&gt;Beyond scanning&lt;/h1&gt;
&lt;p&gt;And voilà, we now have a simple middleware testing uploaded content and
refusing infected files. We could enhance it with various other things, like
configuration handling, but I&#39;ll let that as an exercise for the interested
readers.&lt;/p&gt;
&lt;p&gt;We didn&#39;t exploited it here, but note that you can also manipulate request
headers and modify them if needed. For example, we could have added a header
&lt;em&gt;X-Object-Meta-Scanned-By: ClamAV&lt;/em&gt; to indicates that the file has been
scanned by ClamAV.&lt;/p&gt;
&lt;p&gt;You should now be able to build your own middleware doing whatever you want
with uploaded data. Happy hacking!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Ceilometer bug squash day &amp;#35;1</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/ceilometer-bug-squash-day-1"/>
    <updated>2012-12-24T17:45:00Z</updated>
    <published>2012-12-24T17:45:00Z</published>
    <id>/blog/2012/ceilometer-bug-squash-day-1</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;p&gt;In order to start the year in a good mood, what&#39;s the best than squashing
some bugs on OpenStack?&lt;/p&gt;
&lt;p&gt;Therefore, the Ceilometer team is pleased &lt;a href=&#34;http://lists.openstack.org/pipermail/openstack-dev/2012-December/004161.html&#34;&gt;to
announce&lt;/a&gt;
that it organizes a &lt;a href=&#34;http://wiki.openstack.org/Ceilometer/BugSquashingDay/20130104&#34;&gt;bug squashing day on the Friday 4th January
2013&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We wrote an extensive page about &lt;a href=&#34;http://wiki.openstack.org/Ceilometer/Contributing&#34;&gt;how you can contribute to
Ceilometer&lt;/a&gt;, from
updating the documentation, to fixing bugs. There&#39;s a lot you can do. We&#39;ve
good support for Ceilometer built into &lt;a href=&#34;http://devstack.org&#34;&gt;Devstack&lt;/a&gt;, so
installing a development platform is really easy.&lt;/p&gt;
&lt;p&gt;The main goal on this bug day will be put Ceilometer in the best possible
shape before the &lt;em&gt;grizzly-2&lt;/em&gt; milestone arrives (10th January 2013). This
version of Ceilometer will aim to keep compatibility with &lt;em&gt;Folsom&lt;/em&gt;, so early
deployers can enjoy some of our new features before upgrading to &lt;em&gt;Grizzly&lt;/em&gt;.
After that date, we&#39;ll start merging more extensive changes.&lt;/p&gt;
&lt;p&gt;We&#39;ll be hanging out on the &lt;em&gt;#openstack-metering&lt;/em&gt; IRC channel on
&lt;a href=&#34;http://freenode.net&#34;&gt;Freenode&lt;/a&gt;, as usual, so feel free to come by and join
us!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">OpenStack France meetup #2</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/openstack-france-meetup-2"/>
    <updated>2012-11-06T11:37:00Z</updated>
    <published>2012-11-06T11:37:00Z</published>
    <id>/blog/2012/openstack-france-meetup-2</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;p&gt;I was at the &lt;a href=&#34;http://www.meetup.com/OpenStack-France/events/84177022/&#34;&gt;OpenStack France meetup 2&lt;/a&gt; yesterday
evening.&lt;/p&gt;
&lt;p&gt;This has been a wonderful evening, talking about OpenStack and all with
around 30-40 people. I and &lt;a href=&#34;http://nicolas.barcet.com/&#34;&gt;Nick Barcet&lt;/a&gt;
presented &lt;a href=&#34;http://launchpad.net/ceilometer&#34;&gt;Ceilometer&lt;/a&gt; and have received
some good feedbacks about it. We should also thanks
&lt;a href=&#34;http://www.nebula.com/&#34;&gt;Nebula&lt;/a&gt;, who sponsored the evening, and &lt;a href=&#34;http://erwan.com/&#34;&gt;Erwan
Gallen&lt;/a&gt; since it was nicely organized, and free beers are
always enjoyable.&lt;/p&gt;
&lt;p&gt;For people interested, the &lt;a href=&#34;https://docs.google.com/presentation/d/1i30roVZp00Wvo46F4k5CT98sw2uMgaf5Lh3bSfiQ-Cg/edit&#34;&gt;slides of our Ceilometer presentations are
available&lt;/a&gt;.
This is a lighter and fresher version of the &lt;a href=&#34;https://docs.google.com/presentation/d/1ytYhQGR5SxoccZ-wuza2n0H2mS7HniP9HoFDUpuGDiM/edit&#34;&gt;slides used by Nick and Doug
at the OpenStack Design
Summit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/media/images/blog/2012/openstack-france-meetup-2-ceilometer.jpg&#34;
     class=&#34;thumbnail&#34;&gt;&lt;/p&gt;
&lt;iframe src=&#34;https://docs.google.com/presentation/embed?id=1i30roVZp00Wvo46F4k5CT98sw2uMgaf5Lh3bSfiQ-Cg&amp;start=false&amp;loop=false&amp;delayms=3000&#34;
frameborder=&#34;0&#34; height=&#34;479&#34; width=&#34;600&#34;  allowfullscreen=&#34;true&#34;
mozallowfullscreen=&#34;true&#34; webkitallowfullscreen=&#34;true&#34;&gt;&lt;/iframe&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Inside Synaps, a CloudWatch-like implementation for OpenStack</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/openstack-synaps-exploration"/>
    <updated>2012-10-22T11:16:00Z</updated>
    <published>2012-10-22T11:16:00Z</published>
    <id>/blog/2012/openstack-synaps-exploration</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
        <category   scheme="/blog/tags"
                term="heat"
                label="Heat" />
        <category   scheme="/blog/tags"
                term="synaps"
                label="Synaps" />
    
    <content type="html">
            &lt;p&gt;A few days ago, &lt;a href=&#34;http://www.samsung.com/&#34;&gt;Samsung&lt;/a&gt; released the source code
of &lt;a href=&#34;https://github.com/spcs/synaps&#34;&gt;Synaps&lt;/a&gt;, an implementation of the
&lt;a href=&#34;http://aws.amazon.com/cloudwatch/&#34;&gt;Amazon Web Service CloudWatch API&lt;/a&gt; for
&lt;a href=&#34;http://openstack.org&#34;&gt;OpenStack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Being a developer on the &lt;a href=&#34;http://launchpad.net/ceilometer&#34;&gt;Ceilometer&lt;/a&gt;
project, I&#39;ve been curious to look on this project and how it could overlap
with Ceilometer or other projects like &lt;a href=&#34;http://www.heat-api.org&#34;&gt;Heat&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;What is CloudWatch?&lt;/h1&gt;
&lt;p&gt;CloudWatch is a monitoring system provided by Amazon on its Web Services
platform to monitor services. This allows you get notifications and trigger
an action on certain threshold.&lt;/p&gt;
&lt;p&gt;For example, this can be used to scale your architecture by monitoring the
number of requests you get on it and its general load by starting new
servers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/media/images/cloudwatch.jpg&#34;&gt;&lt;/p&gt;
&lt;h1&gt;Synaps&lt;/h1&gt;
&lt;p&gt;Synaps is written in around 7k lines of Python (with 28 % of which are
comments), reuses at least one common module of OpenStack
(&lt;em&gt;openstack.common.cfg&lt;/em&gt;) and copy some modules from Nova. One thing that
strikes me, is that there seems to be only a few unit tests compared to most
OpenStack projects. Also, many parts of the code and documentation contains
text written in korean, which won&#39;t be very helpful for most people! :-)
It uses some external technologies, like &lt;a href=&#34;http://storm-project.net/&#34;&gt;Storm&lt;/a&gt;,
&lt;a href=&#34;http://cassandra.apache.org/&#34;&gt;Cassandra&lt;/a&gt; to store its persistent data and
&lt;a href=&#34;http://pandas.pydata.org/&#34;&gt;Pandas&lt;/a&gt; to do data analysis.&lt;/p&gt;
&lt;p&gt;The API server provides an EC2 compatible API only: no OpenStack specific
API. This is probably not a bad thing for now, since I am not aware of any
work in this direction. The API access directly the Cassandra back-end for
read operation, but relies on RPC to do writes. This way, a set of daemon
handles the write using the Storm part of Synaps and do data aggregation.
The authentication only supports LDAP, but it should still be possible to
add a driver for Keystone.&lt;/p&gt;
&lt;p&gt;A Java and a Python SDK are provided to record metrics into Synaps, but
there&#39;s not enough documentation for it to be useful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/media/images/SynapsDeployment.jpg&#34;&gt;&lt;/p&gt;
&lt;h1&gt;Overlap with Heat&lt;/h1&gt;
&lt;p&gt;For now, there&#39;s not a lot of overlap with Heat, because Heat does not
implement completely the CloudWatch API. Heat actually still misses a lot of
the CloudWatch functions. But as soon as it will implement the CloudWatch
API completely, the overlap will be complete with Synaps in this regard.&lt;/p&gt;
&lt;p&gt;One divergence point however, is that Heat uses RPC to access data from the
storage back-end via its engine (the central daemon), whereas Synaps
directly connects to Cassandra. Also, Heat relies on SQLAlchemy, like most
OpenStack projects needing a database.&lt;/p&gt;
&lt;h1&gt;Overlap with Ceilometer&lt;/h1&gt;
&lt;p&gt;One of the goal of Ceilometer is to provide data probes and pollsters for
all OpenStack components (Nova, Swift, Quantum…) whereas Synaps let the
OpenStack users to put any kind of metric inside it, and therefore doesn&#39;t
provide anything for now.&lt;/p&gt;
&lt;p&gt;But the storage of metrics is the main common point between Synaps and
Ceilometer. Synaps chose only one technology, Cassandra, to store its
metrics, whereas Ceilometer took care of building an abstraction layer for
the storage engine. Ceilometer currently allows an operator to use SQL or
MongoDB, but Cassandra could likely be added.&lt;/p&gt;
&lt;p&gt;Data metric consolidation is done by Synaps. This makes sense, since Synaps
don&#39;t need to have the full data history to trigger alarms. On the opposite,
Ceilometer needs to have a full history to allow things like billing, and
don&#39;t do any aggregation on data.&lt;/p&gt;
&lt;p&gt;Also, in Synaps, the data analysis is done using Pandas. This means the data
used are retrieved from the Cassandra back-end, and then transformed by
Pandas inside Synaps in something else. It&#39;s likely that in such a case,
Synaps should use CQL to achieve that. Ceilometer manipulates the data near
their storage: it means that the computation are done by back-end to be
efficient (SQL, mapreduce…).&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Considering Samsung open-sourced Synaps late in the development process, I
don&#39;t feel like they aimed to have it becoming a core component. This is
always sad, because the effort put into this implementation are big and it
would have probably little to add some abstraction layers to follow what
other OpenStack projects do. But this takes time and energy, and it&#39;s
understandable that Samsung didn&#39;t want to achieve this in a short time
frame.&lt;/p&gt;
&lt;p&gt;There&#39;s a part of the code and architecture that overlaps with Ceilometer
and Heat. Ceilometer is becoming a specialized point to store data metrics
from any source: so it&#39;s sad, but understandable, that Synaps did not tried
to reuse it. Fortunately, Heat is working with Ceilometer to achieve exactly
that. This means OpenStack would have only one metrics storage point, used
for billing, for monitoring and alarming.&lt;/p&gt;
&lt;p&gt;Therefore, I think Synaps is an implementation of CloudWatch that should be
looked at as an inspiration for Heat and Ceilometer to build a better and
more integrated solution!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Ceilometer 0.1 released</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/ceilometer-0.1-released"/>
    <updated>2012-10-12T17:45:00Z</updated>
    <published>2012-10-12T17:45:00Z</published>
    <id>/blog/2012/ceilometer-0.1-released</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;p&gt;After 6 months of development, we are proud to release the first release of
&lt;a href=&#34;http://launchpad.net/ceilometer&#34;&gt;Ceilometer&lt;/a&gt;, the
&lt;a href=&#34;http://openstack.org&#34;&gt;OpenStack&lt;/a&gt; Metering project. Ceilometer. This is a
first and amazing milestone for us: we follow all other projects by
releasing a version for Folsom!&lt;/p&gt;
&lt;p&gt;Using Ceilometer, you should now be able to meter your OpenStack cloud and
retrieve its usage to build statistics or bill your customer!&lt;/p&gt;
&lt;p&gt;You can read &lt;a href=&#34;https://lists.launchpad.net/openstack/msg17410.html&#34;&gt;our announcement on the OpenStack mailing list&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Architecture&lt;/h1&gt;
&lt;p&gt;We spent a good amount of time defining and refining &lt;a href=&#34;http://ceilometer.readthedocs.org/en/latest/architecture.html#high-level-description&#34;&gt;our
architecture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://ceilometer.readthedocs.org/en/latest/architecture.html#high-level-description&#34;&gt;
  &lt;img class=&#34;thumbnail&#34; src=&#34;/media/images/Ceilometer_Architecture.png&#34;&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One of its important point, is that it has been designed to work without
modifying any of the existing core components. Patching OpenStack components
in an intrusive way to meter them was not an option for now, simply because
we had no legitimacy to do so. This may change in the future, and this will
likely be discussed next week during the &lt;a href=&#34;http://www.openstack.org/summit/san-diego-2012/&#34;&gt;OpenStack
Summit&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Meters&lt;/h1&gt;
&lt;p&gt;Initially, we defined a bunch of meters we&#39;d like to have for a first
release, and in the end, most of them are available. Some of them are still
missing, like OpenStack Object Storage (Swift) ones, mainly due to lack of
interest from the involved parties so far.&lt;/p&gt;
&lt;p&gt;Anyhow, with this first release, you should be able to meter your instances,
their network usage, memory, CPU. Images, networks and volumes and their
CRUD operations are metered too. For more detail, you can read the &lt;a href=&#34;http://ceilometer.readthedocs.org/en/latest/measurements.html&#34;&gt;complete
list of implemented
meters&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;REST API&lt;/h1&gt;
&lt;p&gt;The HTTP REST API has been partially implemented. The provided methods
should allow basic integration with a billing system.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://dreamhost.com/&#34;&gt;DreamHost&lt;/a&gt; is using Ceilometer in their deployment
architecture and coupling it with their billing system!&lt;/p&gt;
&lt;h1&gt;Towards Grizzly&lt;/h1&gt;
&lt;p&gt;We don&#39;t have a clear and established road-map for Grizzly yet.&lt;/p&gt;
&lt;p&gt;We already have a couple of patches waiting in the queue to be merged, like
the use of &lt;a href=&#34;https://review.openstack.org/#/c/13989/&#34;&gt;Keystone to authenticate API
request&lt;/a&gt; and the &lt;a href=&#34;https://review.openstack.org/#/c/14185/&#34;&gt;removal of Nova
DB access&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On my side, these last days I&#39;ve been working on a small debug user
interface for the API. Ceilometer API server will return this interface if
your do an API request from a browser (i.e. requesting
&lt;code&gt;text/html&lt;/code&gt; instead of &lt;code&gt;application/json&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/media/images/ceilometer-debug-interface.png&#34; class=&#34;thumbnail&#34;
   style=&#34;width: 500px; margin: auto;&#34;&gt;
  &lt;img src=&#34;/media/images/ceilometer-debug-interface.png&#34;&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I hope this will help to discover Ceilometer API more easily for new comers
and leverage it to build powerful tools!&lt;/p&gt;
&lt;p&gt;Anyhow, we have tons of idea and work to do, and I&#39;m sure the upcoming weeks
will be very interesting. Also, we hope to be able to become an OpenStack
incubated project soon. So stay tuned!&lt;/p&gt;
          </content>
  </entry>
    <entry>
    <title type="html">Ceilometer, the OpenStack metering project</title>
    <author><name>Julien Danjou</name></author>
    <link href="http://julien.danjou.info/blog/2012/openstack-metering-ceilometer"/>
    <updated>2012-07-27T12:37:00Z</updated>
    <published>2012-07-27T12:37:00Z</published>
    <id>/blog/2012/openstack-metering-ceilometer</id>
        <category   scheme="/blog/tags"
                term="openstack"
                label="Openstack" />
        <category   scheme="/blog/tags"
                term="ceilometer"
                label="Ceilometer" />
    
    <content type="html">
            &lt;p&gt;For the last months, I&#39;ve been working on a metering project for
&lt;a href=&#34;http://openstack.org&#34;&gt;OpenStack&lt;/a&gt;, so it&#39;s time to talk a bit about it.&lt;/p&gt;
&lt;p&gt;OpenStack is a growing cloud platform providing
&lt;acronym title=&#34;Infrastructure as a Service&#34;&gt;IaaS&lt;/a&gt;. A problem easily
identified by everyone building a public cloud platform is that nothing is
provided to retrieve the platform usage data. Some data are available in
some places, but not everything is, and you have to do a lot of processing
from the various components to get something useful in the end.
But in order to bill customers that are using your public cloud platform,
you need to do his.&lt;/p&gt;
&lt;p&gt;In this regard, a lot of companies running public OpenStack based
infrastructure wrote their own solution to cover this functional areas, and
to become able to bill theirs customers.&lt;/p&gt;
&lt;p&gt;To avoid everybody doing and maintaining such a stack in their corners, the
&lt;a href=&#34;http://launchpad.net/ceilometer&#34;&gt;Ceilometer&lt;/a&gt; has been created.&lt;/p&gt;
&lt;p&gt;The project aims to cover the metering aspect of the OpenStack components,
pulling usage data from every components and storing them into a single
place. It then offer a retrieving point for this data via a REST API.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://wiki.openstack.org/EfficientMetering&#34;&gt;initial specifications&lt;/a&gt;
have been written in April this year, and actual implementation started in
May. The project is currently worked on by me, Dreamhost and Canonical.&lt;/p&gt;
&lt;p&gt;We already have designed &lt;a href=&#34;http://wiki.openstack.org/EfficientMetering/ArchitectureProposalV1&#34;&gt;an
architecture&lt;/a&gt;
that we are implementing, and we hope to release a first usable version with
Folsom.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Ceilometer architecture&#34; src=&#34;/media/images/ceilometer-architecture.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;I did &lt;a href=&#34;https://docs.google.com/presentation/d/11ALGC4xuWcRvXKTSnnsteJUkArsTfQW-7IAfWRRI5kQ/edit#slide=id.p&#34;&gt;a
presentation&lt;/a&gt;
of this project yesterday at &lt;a href=&#34;http://xlcloud.org/bin/view/Main/&#34;&gt;XLCloud&lt;/a&gt;,
which has been very well received.&lt;/p&gt;
&lt;iframe src=&#34;https://docs.google.com/presentation/embed?id=11ALGC4xuWcRvXKTSnnsteJUkArsTfQW-7IAfWRRI5kQ&amp;start=false&amp;loop=false&amp;delayms=3000&#34;
        frameborder=&#34;0&#34; width=&#34;480&#34; height=&#34;389&#34; allowfullscreen=&#34;true&#34;
        mozallowfullscreen=&#34;true&#34; webkitallowfullscreen=&#34;true&#34;&gt;&lt;/iframe&gt;

&lt;p&gt;If you are interested in helping us and contributing, feel free to join us
during one of our &lt;a href=&#34;http://wiki.openstack.org/Meetings/MeteringAgenda&#34;&gt;weekly IRC
meeting&lt;/a&gt; or fix &lt;a href=&#34;https://bugs.launchpad.net/ceilometer&#34;&gt;some
bugs&lt;/a&gt;. :-)&lt;/p&gt;
          </content>
  </entry>
    <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;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;c&#34;&gt;# Pseudo code describing replication process for accounts&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;c&#34;&gt;# The principle is exactly the same for containers&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;account&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;accounts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;c&#34;&gt;# Determine the partition used to store this account&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;hash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;account&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;number_of_partitions&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;c&#34;&gt;# The number of zone is the number of replicas configured&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_zones_storing_this_partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;c&#34;&gt;# Send a HTTP REPLICATE command to the remote swift-account-server process&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;version_of_account&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;send_HTTP_REPLICATE_for&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;account&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version_of_account&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;account&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;n&#34;&gt;account&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sync_to&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&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;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;c&#34;&gt;# Pseudo code describing replication process for objects&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;partitions_storing_objects&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;c&#34;&gt;# The number of zone is the number of replicas configured&lt;/span&gt;&lt;br /&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_zones_storing_this_partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;c&#34;&gt;# Send a HTTP REPLICATE command to the remote swift-object-server process&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;n&#34;&gt;verion_of_partition&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;send_HTTP_REPLICATE_for&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version_of_partition&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;c&#34;&gt;# Use rsync to synchronize the whole partition&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;c&#34;&gt;# and all its objects&lt;/span&gt;&lt;br /&gt;            &lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rsync_to&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&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;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;c1&#34;&gt;# disable TIME_WAIT.. wait..&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;na&#34;&gt;net.ipv4.tcp_tw_recycle&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;na&#34;&gt;net.ipv4.tcp_tw_reuse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;1&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class=&#34;c1&#34;&gt;# double amount of allowed conntrack&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;na&#34;&gt;net.ipv4.netfilter.ip_conntrack_max&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;262144&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&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">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">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>
  </feed>
