blog.mortenvp.comhttp://blog.mortenvp.com/2015-04-28T14:39:00+02:00Emulating Networks with Mininet2015-04-28T14:39:00+02:00Morten V. Pedersentag:blog.mortenvp.com,2015-04-28:emulating-networks-with-mininet.html<p>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<p>Some time ago I came across <a href="http://mininet.org/">Mininet</a> which allows us setup and emulate networks on a single machine. Mininet uses the <a href="http://en.wikipedia.org/wiki/LXC">Linux container</a> technology (similar to <a href="http://docker.com">Docker</a>) to run multiple lightweight isolated Linux environments on a single Linux host. I never had time to play with it, but recently I wanted to experiment with a new transport protocol and see how it reacted to lossy links and Mininet seemed like an good choice for emulating the network.</p>
<p>Mininet seems quite popular for people how wants to emulate and experiment with newer networking concepts such as SDN (Software Defined Networks). Beause the overhead of running Linux containers is very low, it allows us to emulate large networks with many connected hosts on a single computer (unfeasible with virtual machines). But, it can also be used to emulated simple networks like point-to-point links, with some loss and delay characteristics. Which is what we will do here. Another great strength of Mininet is that it allows us to run existing applications over these networks. So for example on one emulated host I can open the Firefox browser and on another host a webserver. I can even connect hosts on my emulated network to the real network and let them speak to services on the Internet. How great is that!?</p>
<p>Let get this thing up and running.</p>
<h2 id="installing-mininet">Installing mininet</h2>
<p>So the first thing we need to do is to install Mininet. There are a copule of options to choose from the easiest way it to use the prepared virtual machine. You can find additional instructions on how to set it up on the Mininet webpage <a href="http://mininet.org/vm-setup-notes/">here</a>.</p>
<p>Note: The Mininet setup guide suggests that you add a "Host-only Adapter", when I initially tried this, it gave me a "no host only network adapter selected" error. This was resolve following the suggestions described <a href="no%20host%20only%20network%20adapter%20selected">here</a>.</p>
<p>For some reason after booting the Mininet virtual machine the <em>host only</em> network was not brought up, the commands to make it accessible (run the following commands in the virtual machine):</p>
<pre><code>sudo ip link set dev eth1 up
sudo dhclient eth1
ip link show</code></pre>
<p>First we bring up the interface, then ask for an IP using DHCP and finally we show the available interfaces and their IPs.</p>
<p>Alternatively you can add the following to <code>/etc/network/interfaces</code> inside the virtual machine:</p>
<pre><code>auto eth1
iface eth1 inet dhcp</code></pre>
<p>Now I could ssh from my machine to the Mininet virtual machine using:</p>
<pre><code>ssh mininet@192.168.56.101</code></pre>
<p>To make things a bit easier I add the following to <code>.ssh/config</code>:</p>
<pre><code>Host mininet
User mininet
HostName 192.168.56.101</code></pre>
<p>Now I can login using <code>ssh mininet</code>, see the bottom of the <a href="http://mininet.org/vm-setup-notes/">Mininet VM setup notes</a> guide to see how to setup password less login.</p>
<h3 id="shared-folders">Shared folders</h3>
<p>To make it easier to move files between the virtual machine and the host computer you can setup a shared folder. In Virtualbox go to the settings for the Mininet virtual machine and you should be able to select a host folder that you want to share in the virtual machine. In my case is just created a folder called <code>vm_mininet</code> in my home directory.</p>
<p>To access shared folders inside the virtual machine we need to install the Virtualbox Guest Additions. Fortunately there is a package for that, so inside the Mininet virtual machine run:</p>
<pre><code>sudo apt-get install virtualbox-guest-utils</code></pre>
<p>As a final step you need to add the <code>mininet</code> user to the <code>vboxsf</code> group to have access to the folder:</p>
<pre><code>usermod -aG vboxsf mininet</code></pre>
<p>Rebooting the virtual machine, and my shared folder shows up under <code>/media/sf_vm_mininet</code>.</p>
<h2 id="creating-the-network">Creating the network</h2>
<p>Now that we have the Mininet virtual machine up and running we can start to emulate some networks. One of the really cool things about Mininet is how easy it is to define network topologies using Python scripts. As an example lets try to create a network with two hosts and ping from one to the other.</p>
<p>The script for setting this up is available <a href="https://cdn.rawgit.com/mortenvp/mortenvp-pelican/master/content/notebooks/emulating_networks_with_mininet/simple_ping.py">here</a>. The <code>%%file simple_ping.py</code> is an iPython cell magic command that will save the content of the cell to a file called <code>simple_ping.py</code>, so that we later can runn the script in our Mininet virtual machine.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">
In [8]:
</div>
<div class="input_area box-flex1">
<div class="highlight-ipynb"><pre class="ipynb"><span class="o">%%</span><span class="k">file</span> <span class="n">simple_ping</span><span class="o">.</span><span class="n">py</span>
<span class="c">#!/usr/bin/python</span>
<span class="sd">"""</span>
<span class="sd">Simple example of point-to-point link running ping</span>
<span class="sd">"""</span>
<span class="kn">from</span> <span class="nn">mininet.topo</span> <span class="kn">import</span> <span class="n">Topo</span>
<span class="kn">from</span> <span class="nn">mininet.net</span> <span class="kn">import</span> <span class="n">Mininet</span>
<span class="kn">from</span> <span class="nn">mininet.node</span> <span class="kn">import</span> <span class="n">OVSController</span>
<span class="kn">from</span> <span class="nn">mininet.link</span> <span class="kn">import</span> <span class="n">TCLink</span>
<span class="kn">from</span> <span class="nn">mininet.util</span> <span class="kn">import</span> <span class="n">pmonitor</span>
<span class="kn">from</span> <span class="nn">mininet.log</span> <span class="kn">import</span> <span class="n">setLogLevel</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">setLogLevel</span><span class="p">(</span><span class="s">'info'</span><span class="p">)</span>
<span class="c"># Build the topology we want to play with:</span>
<span class="c">#</span>
<span class="c"># +-----------------------+</span>
<span class="c"># | 10Mbit/s link |</span>
<span class="c"># | 5ms delay |</span>
<span class="c"># | 10% packet loss |</span>
<span class="c"># | |</span>
<span class="c"># +-----------+-----------+</span>
<span class="c"># |</span>
<span class="c"># |</span>
<span class="c"># |</span>
<span class="c"># |</span>
<span class="c"># +-------------+ v +-------------+</span>
<span class="c"># | | | |</span>
<span class="c"># | host 1 (h1) +------------------+ host 2 (h2) |</span>
<span class="c"># | | | |</span>
<span class="c"># +-------------+ +-------------+</span>
<span class="c"># </span>
<span class="n">topo</span> <span class="o">=</span> <span class="n">Topo</span><span class="p">()</span>
<span class="n">topo</span><span class="o">.</span><span class="n">addHost</span><span class="p">(</span><span class="s">'h1'</span><span class="p">)</span>
<span class="n">topo</span><span class="o">.</span><span class="n">addHost</span><span class="p">(</span><span class="s">'h2'</span><span class="p">)</span>
<span class="n">topo</span><span class="o">.</span><span class="n">addLink</span><span class="p">(</span><span class="s">'h1'</span><span class="p">,</span><span class="s">'h2'</span><span class="p">,</span> <span class="n">bw</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">delay</span><span class="o">=</span><span class="s">'5ms'</span><span class="p">,</span> <span class="n">loss</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
<span class="c"># The TCLink is needed for use to set the bandwidth, delay and loss</span>
<span class="c"># constraints on the link</span>
<span class="c">#</span>
<span class="c"># waitConnected</span>
<span class="n">net</span> <span class="o">=</span> <span class="n">Mininet</span><span class="p">(</span><span class="n">topo</span><span class="o">=</span><span class="n">topo</span><span class="p">,</span>
<span class="n">link</span><span class="o">=</span><span class="n">TCLink</span><span class="p">,</span>
<span class="n">waitConnected</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">net</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">h1</span><span class="p">,</span> <span class="n">h2</span> <span class="o">=</span> <span class="n">net</span><span class="o">.</span><span class="n">getNodeByName</span><span class="p">(</span><span class="s">'h1'</span><span class="p">,</span> <span class="s">'h2'</span><span class="p">)</span>
<span class="c"># Will open the two ping processes on both hosts and read the output</span>
<span class="n">popens</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">popens</span><span class="p">[</span><span class="n">h1</span><span class="p">]</span> <span class="o">=</span> <span class="n">h1</span><span class="o">.</span><span class="n">popen</span><span class="p">(</span><span class="s">'ping -c10 {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">h2</span><span class="o">.</span><span class="n">IP</span><span class="p">()))</span>
<span class="n">popens</span><span class="p">[</span><span class="n">h2</span><span class="p">]</span> <span class="o">=</span> <span class="n">h2</span><span class="o">.</span><span class="n">popen</span><span class="p">(</span><span class="s">'ping -c10 {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">h1</span><span class="o">.</span><span class="n">IP</span><span class="p">()))</span>
<span class="k">for</span> <span class="n">host</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">pmonitor</span><span class="p">(</span><span class="n">popens</span><span class="p">):</span>
<span class="k">if</span> <span class="n">host</span><span class="p">:</span>
<span class="c"># Output each line written as "<hX>: some output", where X</span>
<span class="c"># will be replaced by the host number i.e. 1 or 2.</span>
<span class="k">print</span><span class="p">(</span><span class="s">"<{}>: {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">host</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()))</span>
<span class="n">net</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
</pre></div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area"><div class="prompt"></div>
<div class="box-flex1 output_subarea output_stream output_stdout">
<pre class="ipynb">Overwriting simple_ping.py
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<p>To run the above network simply copy the <code>simple_ping.py</code> file to the Mininet virtual machine and run it (e.g. using the shared folder):</p>
<pre><code>sudo ./simple_ping.py</code></pre>
<p>The script should now generate a bunch of output, giving you diagnostics about the network and output from the two ping processes.</p>
<pre><code>mininet@mininet-vm:/media/sf_vm_mininet$ sudo ./simple_ping.py
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
*** Adding links:
(10.00Mbit 5ms delay 10% loss) (10.00Mbit 5ms delay 10% loss) (h1, h2)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 0 switches
*** Waiting for switches to connect
<h2>: PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
<h2>: 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=12.5 ms
<h1>: PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
<h1>: 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=17.8 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=10.4 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=10.2 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=10.2 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=4 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=5 ttl=64 time=10.1 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=6 ttl=64 time=10.2 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=6 ttl=64 time=10.2 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=7 ttl=64 time=10.1 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=8 ttl=64 time=10.1 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=8 ttl=64 time=10.1 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=9 ttl=64 time=10.1 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=9 ttl=64 time=11.0 ms
<h2>: 64 bytes from 10.0.0.1: icmp_seq=10 ttl=64 time=10.1 ms
<h2>:
<h2>: --- 10.0.0.1 ping statistics ---
<h2>: 10 packets transmitted, 10 received, 0% packet loss, time 9014ms
<h2>: rtt min/avg/max/mdev = 10.139/10.442/12.594/0.725 ms
<h1>: 64 bytes from 10.0.0.2: icmp_seq=10 ttl=64 time=10.1 ms
<h1>:
<h1>: --- 10.0.0.2 ping statistics ---
<h1>: 10 packets transmitted, 8 received, 20% packet loss, time 9024ms
<h1>: rtt min/avg/max/mdev = 10.144/11.294/17.819/2.484 ms
*** Stopping 1 controllers
c0
*** Stopping 0 switches
*** Stopping 1 links
*** Stopping 2 hosts
h1 h2
*** Done</code></pre>
<p>Pretty cool right?</p>
<p>The code was tested with Mininet version <code>2.2.0</code>, to see which version you have run:</p>
<pre><code>>>> import mininet
>>> print(mininet.net.VERSION)
2.2.0</code></pre>
<p>For more examples see the official <a href="https://github.com/mininet/mininet/tree/master/examples">Mininet examples</a>. Also you can find lots more information on the API etc. on the Mininet pages.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<p><em>This blog post was written entirely in the IPython Notebook. The full notebook can be downloaded <a href="https://cdn.rawgit.com/mortenvp/mortenvp-pelican/master/content/notebooks/emulating_networks_with_mininet/emulating_networks_with_mininet.ipynb">here</a>, or viewed statically <a href="http://nbviewer.ipython.org/url/github.com/mortenvp/mortenvp-pelican/blob/master/content/notebooks/emulating_networks_with_mininet/emulating_networks_with_mininet.ipynb">here</a>.</em></p>
</div>
</div>
</div></p>Creating slides using the iPython notebook2015-03-03T12:43:00+01:00Morten V. Pedersentag:blog.mortenvp.com,2015-03-03:creating-slides-using-the-ipython-notebook.html<p>This semester I'm teaching a free study activity called C++11/14 basic
introduction and as an experiment I decided to try to create my slides
using an iPython notebook.</p>
<p>Turns out this works pretty well, and</p>
<h2>Hosting the slides</h2>
<p>Since the slides are basically a html website with some javascript we can
easily make the slides available for online viewing.</p>
<p>For the slides to work properly we also have to host the
<a href="https://github.com/hakimel/reveal.js">reveal.js</a> javascript library. To
ensure that it will work as expected you should use the same version of
reveal.js as the iPython However, this did not work straight out of the box
so here is a small tip.</p>
<div class="highlight"><pre>ipython nbconvert --to slides lecture1.ipynb --post serve --log-level=INFO
</pre></div>
<p>This will generate some output and show where the in-built webserver
forwards requests to the reveal.js library:</p>
<div class="highlight"><pre>[NbConvertApp] Redirecting reveal.js requests to https://cdn.jsdelivr.net/reveal.js/2.5.0
</pre></div>
<p>So if we want to use the same version of reveal.js we should use version 2.5.0.</p>Creating a Pelican powered iPython notebook blog2015-02-26T15:00:00+01:00Morten V. Pedersentag:blog.mortenvp.com,2015-02-26:creating-a-pelican-powered-ipython-notebook-blog.html<p>
<div class="cell border-box-sizing text_cell rendered">
<div class="prompt input_prompt">
</div>
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<p>During the last couple of months I've started to use the iPython notebook for more and more stuff. Inspired by <a href="https://jakevdp.github.io/blog/2013/05/07/migrating-from-octopress-to-pelican/">Python Perambulations</a> by Jake VanderPlas, I decided to also try to use it for blogging. In the following I'll try to document how I've setup things.</p>
<h2 id="tools-involved">Tools involved</h2>
<ul>
<li><a href="http://getpelican.com">Pelican</a>: Pelican is a static site generator, written in Python.</li>
<li><a href="http://ipython.com">iPython</a>: What we will use to write our notebooks in.</li>
<li><a href="http://docker.com">Docker</a>: Container for the tools</li>
<li><a href="http://fabfile.org">Fabric</a>: Automates tasks like building and publishing the site.</li>
</ul>
<h2 id="installing-pelican">Installing Pelican</h2>
<p>On the GitHub README.md for the Pythonic Perambulations blog Jake VanderPlas recommonds using Pelican 3.3+. Fortunatly Ubuntu 14.10 ships with 3.4.0 (run: <code>sudo aptitude show python-pelican</code> to check). So installation is easy:</p>
<pre><code>sudo aptitude install python-pelican</code></pre>
<h2 id="hosting-the-site">Hosting the site</h2>
<p>Here I will again follow the approach taken by Jake VanderPlas for his <a href="http://jakevpd.github.io">Python Perambulations</a> blog.</p>
<p>The basic idea is to create two <a href="http://github.com">GitHub</a> reporitories. The first contains the Pelican project files, notebooks, and other material used when generating the site. The second contains the generated html files etc. With this approach we can use <a href="https://pages.github.com/">GitHub Pages</a> for the second repository to make the site available on the web.</p>
<p>The first repoitory I named <a href="https://github.com/mortenvp/mortenvp-pelican">mortenvp-pelican</a> and the second <a href="https://github.com/mortenvp/mortenvp.github.io">mortenvp.github.io</a> which according to the GitHub Pages documentation is the mandtory name for a personal pages repository.</p>
<h2 id="generating-the-initial-project">Generating the initial project</h2>
<p>In this case I just follow the Pelican <a href="http://docs.getpelican.com/en/3.5.0/quickstart.html">quickstart</a> section to setup a basic site.</p>
<pre><code>git clone <git-url-to-mortenvp-pelican.git>
cd mortenvp-pelican
pelican-quickstart</code></pre>
<p>The quick start will ask you a bunch of questions, these were my answers:</p>
<pre><code>Welcome to pelican-quickstart v3.4.0.
This script will help you create a new Pelican-based website.
Please answer the following questions so this script can generate the files
needed by Pelican.
> Where do you want to create your new web site? [.]
> What will be the title of this web site? blog.mortenvp.com
> Who will be the author of this web site? Morten V. Pedersen
> What will be the default language of this web site? [en]
> Do you want to specify a URL prefix? e.g., http://example.com (Y/n)
> What is your URL prefix? (see above example; no trailing slash) http://blog.mortenvp.com
> Do you want to enable article pagination? (Y/n)
> How many articles per page do you want? [10]
> Do you want to generate a Fabfile/Makefile to automate generation and publishing? (Y/n)
> Do you want an auto-reload & simpleHTTP script to assist with theme and site development? (Y/n)
> Do you want to upload your website using FTP? (y/N)
> Do you want to upload your website using SSH? (y/N)
> Do you want to upload your website using Dropbox? (y/N)
> Do you want to upload your website using S3? (y/N)
> Do you want to upload your website using Rackspace Cloud Files? (y/N)
> Do you want to upload your website using GitHub Pages? (y/N) Y
> Is this your personal page (username.github.io)? (y/N) y
Done. Your new project is available at /home/mvp/dev/mortenvp-pelican</code></pre>
<h2 id="plugin-enabeling-ipython-notebooks">Plugin enabeling iPython notebooks</h2>
<p>Support for including an iPython notebook is available in the <a href="https://github.com/getpelican/pelican-plugins/tree/master/liquid_tags">Liquid-style Tags</a> plugin. Following the <a href="https://github.com/getpelican/pelican-plugins">README</a> in the Pelican Plugins we add the pelican-plugins repository as a submodule:</p>
<pre><code>git submodule add git@github.com:getpelican/pelican-plugins.git</code></pre>
<p>and activate the liquid-style Tags:</p>
<pre><code>PLUGIN_PATHS = ['pelican-plugins']
PLUGINS = ['liquid_tags.img', 'liquid_tags.video',
'liquid_tags.youtube', 'liquid_tags.vimeo',
'liquid_tags.include_code', 'liquid_tags.notebook']</code></pre>
<p>You can choose the location for the notebooks by specifying:</p>
<pre><code>NOTEBOOK_DIR = 'notebooks'</code></pre>
<h3 id="including-the-notebook-css-in-our-choosen-theme">Including the notebook CSS in our choosen theme</h3>
<p>At the top of the pelicanconf.py add:</p>
<pre><code>from __future__ import unicode_literals
import os</code></pre>
<p>Which enables us to use (copied from <a href="https://github.com/jakevdp/PythonicPerambulations/blob/master/pelicanconf.py">here</a>):</p>
<pre><code># The theme file should be updated so that the base header contains the line:
#
# {% if EXTRA_HEADER %}
# {{ EXTRA_HEADER }}
# {% endif %}
#
# This header file is automatically generated by the notebook plugin
if not os.path.exists('_nb_header.html'):
import warnings
warnings.warn("_nb_header.html not found. "
"Rerun make html to finalize build.")
else:
EXTRA_HEADER = open('_nb_header.html').read().decode('utf-8')
</code></pre>
<h2 id="choosing-a-theme">Choosing a theme</h2>
<p>I'm not a big fan of the default Pelican theme, so instead I chose to use the <a href="https://github.com/fle/pelican-sober">Pelican-sober</a> theme.</p>
<p>Since the <code>EXTRA_HEADER</code> section (as described above) isn't present in the Pelican-sober theme's <code>templates/base.html</code>, we need to add it. To do this I forked the repository to my user and made the changes there.</p>
<p>Once this is done we can use our chosen theme by editing the <code>THEME</code> variable in <code>pelican.conf</code> (read about this <a href="http://nafiulis.me/making-a-static-blog-with-pelican.html#themes">here</a>), so to summarize:</p>
<ol style="list-style-type: decimal">
<li>Select a theme and ensure that it has the <code>EXTRA_HEADER</code> section.</li>
<li>Add the theme git repository as a submodule to our site repository <code>git submodule add git@github.com:url-to-repo.git</code></li>
<li>Add a <code>THEME = 'pelican-sober'</code> vaiable inside <code>pelicanconf.py</code></li>
</ol>
<h2 id="adding-a-post">Adding a post</h2>
<p>Adding a post is pretty easy, create a simple markdown file with some basic tags. As an example <code>constent/first_post.md</code>:</p>
<pre><code>Title: My fist post
Date: 2015-02-26 15:00
# About
Here we can write the blog post using markdown</code></pre>
<h2 id="including-a-notebook">Including a notebook</h2>
<p>To include a notebook called <code>filename.ipynb</code> copy it to the <code>content/notebooks/</code> directory and add it to a post using:</p>
<pre><code>{% notebook filename.ipynb %}</code></pre>
<h2 id="generating-the-site">Generating the site</h2>
<p>I generate the site using <a href="http://docker.com">Docker</a> and <a href="http://fabfile.org">Fabric</a>.</p>
<h3 id="docker">Docker</h3>
<p>Initially when I wanted to generate the site I got and error <code>Incomplete format</code></p>
<p>Searching the web the error seems to be related to a Mardown <a href="https://github.com/getpelican/pelican-plugins/pull/321">issue</a> with the pelican pugin. The issue was resolved, however comments later indicated that it did not work for everybody, including me.</p>
<p>One user confirmed that the error occured with Markdown 2.5 and that downgrading to 2.4 solved the issue. To verify this fix I decided to install pelican and the dependencies in a Docker container to not interfer with the system packages already on my system.</p>
<p>The Docker container I created for this purpose is available <a href="https://registry.hub.docker.com/u/mortenvp/mortenvp-docker/">here</a>. The container is invoked using fabric described next.</p>
<h3 id="fabric">Fabric</h3>
<p>Is a tool that can be use to automate performing some tasks either on the local or a remote machine. The <code>pelican-quickstart</code> already creates a <code>fabfile</code> for generating and deploying the site. I just adapted it to use If you check the <a href="https://github.com/mortenvp/mortenvp-pelican/blob/master/fabfile.py">fabfile</a> in the <a href="https://github.com/mortenvp/mortenvp-pelican">repository</a> for this site you will see how I use the docker container to generate the content and then move it to the gihub.io repository.</p>
</div>
</div>
</div></p>