diff --git a/CHANGES.txt b/CHANGES.txt
index c87df4074c215e69fe7295f197c67a316460068b..7c7a38458e224a4f4c423db31c427649ee1cfc25 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,40 @@
 Changes
 =======
 
+0.46 (Unreleased)
+-----------------
+
+  * No change yet.
+
+0.45 (2012-03-29)
+-----------------
+
+  * slaprunner: change number of available partitions to 7 [Alain Takoudjou]
+
+0.44 (2012-03-28)
+-----------------
+
+  * minor: apachephp: update apache configuration to work with Apache2.4
+
+0.43 (2012-03-28)
+-----------------
+
+  * minor: erp5: add missing .zcml files into egg. [Cedric de Saint Martin]
+
+0.42 (2012-03-26)
+-----------------
+
+ * erp5: Add web_checker recipe. [Tatuya Kamada]
+ * erp5: Add generic_varnish recipe. [Tatuya Kamada]
+ * erp5: Simplify erp5_update to only create the ERP5 site. [Romain Courteaud]
+ * erp5: Allow to pass CA parameters from section. [艁ukasz Nowak]
+
+0.41 (2012-03-21)
+-----------------
+
+ * Release new "generic" version of KVM, includes frontend.
+   [Cedric de Saint Martin]
+
 0.40.1 (2012-03-01)
 -----------------
 
diff --git a/MANIFEST.in b/MANIFEST.in
index 7a7a46ad5854e867ebfe05a2d61bd67914c1cc51..baa42c13a7d5241c999f964b98c679ceaa9b02cb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,7 @@
 include CHANGES.txt
-include slapos/recipe/generic_zope/template/site.zcml
 include slapos/recipe/apache_frontend/template/notfound.html
 recursive-include slapos/recipe *.in
 recursive-include slapos/recipe *.bin
 recursive-include slapos/recipe README.*.txt
+recursive-include slapos/recipe *.js
+recursive-include slapos/recipe *.zcml
diff --git a/component/alsa/buildout.cfg b/component/alsa/buildout.cfg
index 3c91b1fdfd3e392dc589c33b54bf42eaad54cf81..452bb71207ad6caa76e781c837974ca44ffafdeb 100644
--- a/component/alsa/buildout.cfg
+++ b/component/alsa/buildout.cfg
@@ -6,7 +6,7 @@ parts =
 # Contains libasound
 recipe = hexagonit.recipe.cmmi
 url = ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.0.24.1.tar.bz2
-#md5sum = d55a9d7d2a79d738a1b7a511cffda4b6
+md5sum = 7cc05f25e1d5b65da8fb3fdcd540f226
 configure-options =
   --disable-static
   --disable-aload
@@ -18,4 +18,4 @@ configure-options =
   --disable-ucm
   --disable-alisp
   --disable-old-symbols
-  --disable-python
\ No newline at end of file
+  --disable-python
diff --git a/component/apache-php/buildout.cfg b/component/apache-php/buildout.cfg
index 7dcc0f5a7df281068643f53d393dc901b9f27c05..dcf667fa68463892ceee1eb0e6b76f4cc874597f 100644
--- a/component/apache-php/buildout.cfg
+++ b/component/apache-php/buildout.cfg
@@ -3,6 +3,7 @@ parts = apache-php
 
 extends =
   ../apache/buildout.cfg
+  ../bzip2/buildout.cfg
   ../cclient/buildout.cfg
   ../curl/buildout.cfg
   ../freetype/buildout.cfg
@@ -20,12 +21,13 @@ extends =
 # Note: Shall react on each build of apache and reinstall itself
 recipe = hexagonit.recipe.cmmi
 url = http://fr2.php.net/distributions/php-5.3.10.tar.gz
-md5sum = 816259e5ca7d0a7e943e56a3bb32b17f
+md5sum = 2b3d2d0ff22175685978fb6a5cbcdc13
 configure-options =
   --with-apxs2=${apache:location}/bin/apxs
   --with-libxml-dir=${libxml2:location}
   --with-mysql=${mariadb:location}
   --with-zlib-dir=${zlib:location}
+  --with-bz2-dir=${bzip2:location}
   --with-mcrypt=${libmcrypt:location}
   --with-gd
   --with-jpeg-dir=${libjpeg:location}
@@ -48,12 +50,13 @@ configure-options =
   --enable-session
   --enable-exif
   --enable-zip
+  --enable-bz2
   --enable-ftp
 
 environment =
   PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig
-  PATH=${pkgconfig:location}/bin:${libxml2:location}/bin:%(PATH)s
-  LDFLAGS =-L${libtool:location}/lib -Wl,-rpath -Wl,${libtool:location}/lib -L${mariadb:location}/lib -Wl,-rpath -Wl,${mariadb:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -L${libmcrypt:location}/lib -Wl,-rpath -Wl,${libmcrypt:location}/libblkid
+  PATH=${pkgconfig:location}/bin:${bzip2:location}/bin:${libxml2:location}/bin:%(PATH)s
+  LDFLAGS =-L${bzip2:location}/lib -Wl,-rpath -Wl,${bzip2:location}/lib -L${libtool:location}/lib -Wl,-rpath -Wl,${libtool:location}/lib -L${mariadb:location}/lib -Wl,-rpath -Wl,${mariadb:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -L${libmcrypt:location}/lib -Wl,-rpath -Wl,${libmcrypt:location}/libblkid
 
 
 [libmcrypt]
@@ -64,4 +67,4 @@ md5sum = c4f491dd411a09e9de3b8702ea6f73eb
 [xml-rpc]
 recipe = hexagonit.recipe.cmmi
 url = http://downloads.sourceforge.net/project/phpxmlrpc/phpxmlrpc/2.2.2/xmlrpc-2.2.2.tar.gz
-md5sum = 59a644c636c6d98267d0c99b406ae9e8
\ No newline at end of file
+md5sum = 59a644c636c6d98267d0c99b406ae9e8
diff --git a/component/cloud9/buildout.cfg b/component/cloud9/buildout.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..71f299a6b3d56290a39ec8bedf92fa95f71e9a70
--- /dev/null
+++ b/component/cloud9/buildout.cfg
@@ -0,0 +1,45 @@
+[buildout]
+extends =
+  ../dcron/buildout.cfg
+  ../libxml2/buildout.cfg
+  ../logrotate/buildout.cfg
+  ../rdiff-backup/buildout.cfg
+  ../nodejs/buildout.cfg
+
+parts =
+  nodejs
+  npm
+  cloud9
+
+[cloud9]
+<= cloud9-git
+
+[cloud9-git]
+# Online IDE written in javascript/node.js
+# URL : c9.io
+# You can use it using the following command :
+# NODE_PATH=${:destination}/node_modules ${nodejs:node_location} ${:cloud9_js_location} 
+recipe = plone.recipe.command
+stop-on-error = true
+commit = 97db1467c517d265438684bd2a70b0b76ee282f6
+repository = https://github.com/ajaxorg/cloud9.git
+location = ${buildout:parts-directory}/${:_buildout_section_name_}
+git-binary = ${git:location}/bin/git
+npm-binary = ${nodejs-0.4:location}/bin/node ${npm:location}/bin/npm
+command = export GIT_SSL_NO_VERIFY=true; (${:git-binary} clone --quiet ${:repository} ${:location} && cd ${:location} && ${:git-binary} reset --hard ${:commit} && ${:git-binary} submodule update --init && cd support/jsdav && PATH=${nodejs-0.4:location}/bin:$PATH LDFLAGS=-L${libxml2:location}/lib ${:npm-binary} install) || (rm -fr ${:location}; exit 1)
+update-command =
+
+[cloud9-npm]
+# Online IDE written in javascript/node.js
+# URL : c9.io 
+# You can use it using the following command :
+# NODE_PATH=${:destination}/node_modules ${nodejs:node_location} ${:cloud9_js_location} 
+recipe = slapos.recipe.npm
+# Node part has to be specified, otherwise system node is used.
+node = nodejs-0.6
+# List of packages to install
+packages =
+  cloud9
+# Specify environment jsDAV (dependency of cloud9) needs libxml2
+environment = 
+  LDFLAGS=-L${libxml2:location}/lib -Wl,-rpath=${libxml2:location}/lib
diff --git a/component/firefox/buildout.cfg b/component/firefox/buildout.cfg
index 4b8bf8a4d5d48b1144835ecc1d9124c81cc813b1..b55f70653aa8868102cf284871f62e3dfeabd41b 100644
--- a/component/firefox/buildout.cfg
+++ b/component/firefox/buildout.cfg
@@ -20,8 +20,8 @@ depends =
   ${liberation-fonts:location}
   ${ipaex-fonts:location}
 
-x86 = http://releases.mozilla.org/pub/mozilla.org/firefox/releases/7.0.1/linux-i686/fr/firefox-7.0.1.tar.bz2 42c2559892f26ed2a0563faaf693a00f
-x86-64 = http://releases.mozilla.org/pub/mozilla.org/firefox/releases/7.0.1/linux-x86_64/en-US/firefox-7.0.1.tar.bz2 20d6c8e3dfc97d08d1dec7d0479f924f
+x86 = http://releases.mozilla.org/pub/mozilla.org/firefox/releases/11.0/linux-i686/fr/firefox-11.0.tar.bz2 a7e9c614ddac993476ef771afaedf568
+x86-64 = http://releases.mozilla.org/pub/mozilla.org/firefox/releases/11.0/linux-x86_64/fr/firefox-11.0.tar.bz2 b358865c08145211314a62660e871614
 
 script =
   if not self.options.get('url'): self.options['url'], self.options['md5sum'] = self.options[guessPlatform()].split(' ')
diff --git a/component/noVNC/buildout.cfg b/component/noVNC/buildout.cfg
index 00dad6c804d8c077bcb44ffe946a889fa95b14a1..41b6f8ae2886031efe5dc3c11497f4420a370ec0 100644
--- a/component/noVNC/buildout.cfg
+++ b/component/noVNC/buildout.cfg
@@ -3,6 +3,6 @@ parts =
   noVNC
 
 [noVNC]
-recipe = hexagonit.recipe.download
-url = https://github.com/kanaka/noVNC/tarball/master
+recipe = slapos.recipe.build:download-unpacked
+url = https://github.com/kanaka/noVNC/tarball/v0.2
 strip-top-level-dir = true
diff --git a/component/nodejs/buildout.cfg b/component/nodejs/buildout.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..dddcf4bef82274e0e4d7c02f1c275fac93574b0c
--- /dev/null
+++ b/component/nodejs/buildout.cfg
@@ -0,0 +1,50 @@
+[buildout]
+extends =
+  ../git/buildout.cfg
+  ../pkgconfig/buildout.cfg
+  ../openssl/buildout.cfg
+  ../python-2.7/buildout.cfg
+  ../zlib/buildout.cfg
+
+parts =
+  nodejs
+
+[nodejs]
+# Server-side Javascript.
+recipe = hexagonit.recipe.cmmi
+url = http://nodejs.org/dist/v0.6.12/node-v0.6.12.tar.gz
+md5sum = a12766ae4003c9712927d1fa134ed9f6
+configure-options =
+  --openssl-includes=${openssl:location}/include
+  --openssl-libpath=${openssl:location}/lib
+environment =
+  PATH=${pkgconfig:location}/bin:${python2.7:location}/bin:%(PATH)s
+  PKG_CONFIG_PATH=${openssl:location}/lib/pkgconfig/
+  CPPFLAGS=-I${zlib:location}/include
+  LDFLAGS=-Wl,-rpath=${openssl:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
+
+[nodejs-0.4]
+recipe = hexagonit.recipe.cmmi
+url = http://nodejs.org/dist/node-v0.4.12.tar.gz
+md5sum = a6375eaa43db5356bf443e25b828ae16
+configure-options =
+  --openssl-includes=${openssl:location}/include
+  --openssl-libpath=${openssl:location}/lib
+environment =
+  PATH=${pkgconfig:location}/bin:${python2.7:location}/bin:%(PATH)s
+  PKG_CONFIG_PATH=${openssl:location}/lib/pkgconfig/
+  CPPFLAGS=-I${zlib:location}/include
+  LDFLAGS=-Wl,-rpath=${openssl:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
+
+[npm]
+# Node.js Package Manager
+# Deprecated. Included in node >= 0.6.3.
+recipe = plone.recipe.command
+location = ${buildout:parts-directory}/${:_buildout_section_name_}
+stop-on-error = true
+commit = 3136abc5c6b3ed332c4700ece24450fada63639b
+origin = https://github.com/isaacs/npm.git
+git-bin = ${git:location}/bin/git
+node-bin = ${nodejs-0.4:location}/bin/node
+command = (GIT_SSL_NO_VERIFY=true ${:git-bin} clone --quiet ${:origin} ${:location} && cd ${:location} && ${:git-bin} reset --hard ${:commit} && ${:location}/configure --prefix=${:location} && GIT_SSL_NO_VERIFY=true ${:git-bin} submodule update --init --recursive && ${:node-bin} cli.js install npm@1.0.106 -g -f) || (rm -fr ${:location}; exit 1)
+update-command =
diff --git a/component/slapos/buildout.cfg b/component/slapos/buildout.cfg
index 3552cffc575883ef0a55ca640cd789424aaabc5a..0088c4709030c33159f60734cb6acb5402071282 100644
--- a/component/slapos/buildout.cfg
+++ b/component/slapos/buildout.cfg
@@ -120,17 +120,17 @@ Werkzeug = 0.8.3
 buildout-versions = 1.7
 collective.recipe.template = 1.9
 hexagonit.recipe.cmmi = 1.5.0
-lxml = 2.3.3
+lxml = 2.3.4
 meld3 = 0.6.8
 netaddr = 0.7.6
-slapos.core = 0.23
+slapos.core = 0.24
 slapos.libnetworkcache = 0.12
 xml-marshaller = 0.9.7
-z3c.recipe.scripts = 1.0.1 
+z3c.recipe.scripts = 1.0.1
 zc.recipe.egg = 1.3.2
 
 # Required by:
-# slapos.core==0.23
+# slapos.core==0.24
 Flask = 0.8
 
 # Required by:
@@ -138,11 +138,11 @@ Flask = 0.8
 hexagonit.recipe.download = 1.5.0
 
 # Required by:
-# slapos.core==0.23
+# slapos.core==0.24
 netifaces = 0.8
 
 # Required by:
-# slapos.core==0.23
+# slapos.core==0.24
 # slapos.libnetworkcache==0.12
 # supervisor==3.0a12
 # zc.buildout==1.6.0-dev-SlapOS-004
@@ -150,9 +150,9 @@ netifaces = 0.8
 setuptools = 0.6c12dev-r88846
 
 # Required by:
-# slapos.core==0.23
+# slapos.core==0.24
 supervisor = 3.0a12
 
 # Required by:
-# slapos.core==0.23
+# slapos.core==0.24
 zope.interface = 3.8.0
diff --git a/component/stunnel/buildout.cfg b/component/stunnel/buildout.cfg
index 8321200e7becbc25dbb364092978c176574c2a6e..63eb5e6b098c8e9086eca629de947eb923238972 100644
--- a/component/stunnel/buildout.cfg
+++ b/component/stunnel/buildout.cfg
@@ -17,8 +17,8 @@ filename = stunnel-4-hooks.py
 
 [stunnel-4]
 recipe = hexagonit.recipe.cmmi
-url = http://mirror.bit.nl/stunnel/stunnel-4.52.tar.gz
-md5sum = f5e713dda0e8efa659f372832ecd0c2c
+url = http://mirror.bit.nl/stunnel/stunnel-4.53.tar.gz
+md5sum = ab3bfc915357d67da18c73f73610d593
 pre-configure-hook = ${stunnel-4-hook-download:location}/${stunnel-4-hook-download:filename}:pre_configure_hook
 configure-options =
   --enable-ipv6
diff --git a/setup.py b/setup.py
index ee7523d52672dff85c181be7938df4214730fd10..df5eb713bd231d5f26de3d7eb8834a4815365ee0 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 import glob
 import os
 
-version = '0.40.1'
+version = '0.46-dev'
 name = 'slapos.cookbook'
 long_description = open("README.txt").read() + "\n" + \
     open("CHANGES.txt").read() + "\n"
@@ -46,6 +46,7 @@ setup(name=name,
           'apache.zope.backend = slapos.recipe.apache_zope_backend:Recipe',
           'certificate_authority = slapos.recipe.certificate_authority:Recipe',
           'certificate_authority.request = slapos.recipe.certificate_authority:Request',
+          'check_port_listening = slapos.recipe.check_port_listening:Recipe',
           'cron = slapos.recipe.dcron:Recipe',
           'cron.d = slapos.recipe.dcron:Part',
           'davstorage = slapos.recipe.davstorage:Recipe',
@@ -56,14 +57,18 @@ setup(name=name,
           'erp5scalabilitytestbed = slapos.recipe.erp5scalabilitytestbed:Recipe',
           'equeue = slapos.recipe.equeue:Recipe',
           'erp5testnode = slapos.recipe.erp5testnode:Recipe',
+          'generate.mac = slapos.recipe.generatemac:Recipe',
+          'nbdserver = slapos.recipe.nbdserver:Recipe',
+          'generic.onetimeupload = slapos.recipe.generic_onetimeupload:Recipe',
           'helloworld = slapos.recipe.helloworld:Recipe',
           'generic.cloudooo = slapos.recipe.generic_cloudooo:Recipe',
           'fontconfig = slapos.recipe.fontconfig:Recipe',
           'java = slapos.recipe.java:Recipe',
           'kumofs = slapos.recipe.kumofs:Recipe',
+          'kvm = slapos.recipe.kvm:Recipe',
+          'kvm.frontend = slapos.recipe.kvm_frontend:Recipe',
           'generic.kumofs = slapos.recipe.generic_kumofs:Recipe',
           'haproxy = slapos.recipe.haproxy:Recipe',
-          'kvm = slapos.recipe.kvm:Recipe',
           'libcloud = slapos.recipe.libcloud:Recipe',
           'libcloudrequest = slapos.recipe.libcloudrequest:Recipe',
           'lockfile = slapos.recipe.lockfile:Recipe',
@@ -73,11 +78,11 @@ setup(name=name,
           'mydumper = slapos.recipe.mydumper:Recipe',
           'generic.mysql = slapos.recipe.generic_mysql:Recipe',
           'mkdirectory = slapos.recipe.mkdirectory:Recipe',
-          'nbdserver = slapos.recipe.nbdserver:Recipe',
           'nosqltestbed = slapos.recipe.nosqltestbed:NoSQLTestBed',
           'notifier = slapos.recipe.notifier:Recipe',
           'notifier.callback = slapos.recipe.notifier:Callback',
           'notifier.notify = slapos.recipe.notifier:Notify',
+          'novnc = slapos.recipe.novnc:Recipe',
           'lamp = slapos.recipe.lamp:Request',
           'lamp.request = slapos.recipe.lamp:Request',
           'lamp.static = slapos.recipe.lamp:Static',
diff --git a/slapos/recipe/README.apache_frontend.txt b/slapos/recipe/README.apache_frontend.txt
index f701b6591426405201b92a3437759362b574eeba..89da1b88f1d91025b3cff543c0025a5d39dd727a 100644
--- a/slapos/recipe/README.apache_frontend.txt
+++ b/slapos/recipe/README.apache_frontend.txt
@@ -1,4 +1,36 @@
 apache_frontend
 ==========
 
-Frontend using Apache, allowing to rewrite and proxy URLs like myinstance.myfrontenddomainname.com to real IP/URL of myinstance.
\ No newline at end of file
+Frontend using Apache, allowing to rewrite and proxy URLs like
+myinstance.myfrontenddomainname.com to real IP/URL of myinstance.
+
+apache_frontend works using the master instance / slave instance design.
+It means that a single main instance of Apache will be used to act as frontend
+for many slaves.
+
+
+How to use
+========
+First, you will need to request a "master" instance of Apache Frontend with
+"domain" parameter, like : 
+<?xml version='1.0' encoding='utf-8'?>
+<instance>
+ <parameter id="domain">moulefrite.com</parameter>
+ <parameter id="port">443</parameter>
+</instance>
+
+Then, it is possible to request many slave instances
+(currently only from slapconsole, UI doesn't work yet)
+of Apache Frontend, like : 
+instance = request(
+       software_release=apache_frontend,
+       partition_reference='frontend2',
+       shared=True,
+       partition_parameter_kw={"url":"https://[1:2:3:4]:1234/someresource"}
+     )
+Those slave instances will be redirected to the "master" instance,
+and you will see on the "master" instance the associated RewriteRules of
+all slave instances.
+
+Finally, the slave instance will be accessible from :
+https://someidentifier.moulefrite.com.
diff --git a/slapos/recipe/README.kvm_frontend.txt b/slapos/recipe/README.kvm_frontend.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b600221c6a89767798aa1a94834e786b53f680ab
--- /dev/null
+++ b/slapos/recipe/README.kvm_frontend.txt
@@ -0,0 +1,56 @@
+kvm_frontend
+===
+
+Introduction
+------------
+
+The ``slapos.recipe.kvm_frontend`` aims to provide proxy server to KVM instances.
+
+It allows HTTPS IPv4/IPv6 proxying (with or without domain name), and supports
+the WebSocket technology needed for VNC-in-webapplication noVNC.
+
+It works following the master/slave instances system. A master instance is
+created, containing all what is needed to run the proxy. Slave instances
+are later created, adding one line in the master instance's proxy configuration
+that specify the IP/port to proxy to the KVM.
+
+The slave instance (kvm) is then accessible from
+http://[masterinstanceIPorhostname]/[randomgeneratednumber]
+
+
+Instance parameters
+------------
+
+Incoming master instance parameters
++++++++
+
+``port``                - Port of server, optional, defaults to 4443.
+``domain``              - domain name to use, optional, default to
+                          "host.vifib.net".
+``redirect_plain_http`` - if value is one of ['y', 'yes', '1', 'true'], instance
+                          will try to create a simple http server on port 80
+                          redirecting to the proxy. Optional.
+
+Incoming slave instance parameters
++++++++
+
+``host``    - KVM instance IP or hostname. Mandatory.
+``port``    - KVM instance port, Mandatory.
+``https``   - if value is one of ['n', 'no', '0', 'false'], will try to connect
+              to target in plain http. Optional.
+
+Connection parameters
+-------------
+
+Outgoing master connection parameters
++++++++
+
+``domain_ipv6_address``  - Proxy IP
+``site_url``             - Proxy URL
+
+Outgoing slave connection parameters are :
++++++++
+
+``site_url``             - URL of instance
+``domain_name``          - Domain name of proxy
+``port``                 - Port of proxy
diff --git a/slapos/recipe/apachephp/template/apache.in b/slapos/recipe/apachephp/template/apache.in
index 8dc3ef81d383ccbeb5d1fbd269f4013f64ec7622..a354890425d6f96e946627a659c65299e4c0091c 100644
--- a/slapos/recipe/apachephp/template/apache.in
+++ b/slapos/recipe/apachephp/template/apache.in
@@ -3,7 +3,6 @@
 
 # Basic server configuration
 PidFile "%(pid_file)s"
-LockFile "%(lock_file)s"
 Listen %(ip)s:%(port)s
 PHPINIDir %(php_ini_dir)s
 ServerAdmin someone@email
@@ -25,20 +24,21 @@ CustomLog "%(access_log)s" common
 <Directory />
     Options FollowSymLinks
     AllowOverride None
-    Order deny,allow
-    Deny from all
+    Require all denied
 </Directory>
 
 <Directory %(document_root)s>
   Options FollowSymLinks
   AllowOverride All
-  Order allow,deny
-  Allow from all
+  Require all granted
 </Directory>
 DocumentRoot %(document_root)s
 DirectoryIndex index.html index.php
 
 # List of modules
+LoadModule unixd_module modules/mod_unixd.so
+LoadModule access_compat_module modules/mod_access_compat.so
+LoadModule authz_core_module modules/mod_authz_core.so
 LoadModule authz_host_module modules/mod_authz_host.so
 LoadModule log_config_module modules/mod_log_config.so
 LoadModule setenvif_module modules/mod_setenvif.so
diff --git a/slapos/recipe/check_port_listening/__init__.py b/slapos/recipe/check_port_listening/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..542072d68f8b9e331dd52f88e8557cc1e406d250
--- /dev/null
+++ b/slapos/recipe/check_port_listening/__init__.py
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+from slapos.recipe.librecipe import GenericBaseRecipe
+import sys
+
+class Recipe(GenericBaseRecipe):
+  """
+  Check listening port promise
+  """
+
+  def install(self):
+    config = dict(
+      hostname=self.options['hostname'],
+      port=self.options['port'],
+      python_path=sys.executable,
+    )
+
+    vnc_promise = self.createExecutable(
+      self.options['path'],
+      self.substituteTemplate(
+        self.getTemplateFilename('socket_connection_attempt.py.in'),
+        config))
+
+    return [vnc_promise]
diff --git a/slapos/recipe/check_port_listening/template/socket_connection_attempt.py.in b/slapos/recipe/check_port_listening/template/socket_connection_attempt.py.in
new file mode 100644
index 0000000000000000000000000000000000000000..7c7a2699123fffd451900f6f604b2c74133dd68f
--- /dev/null
+++ b/slapos/recipe/check_port_listening/template/socket_connection_attempt.py.in
@@ -0,0 +1,21 @@
+#!%(python_path)s
+# BEWARE: This file is operated by slapgrid
+# BEWARE: It will be overwritten automatically
+import socket
+import sys
+
+hostname = "%(hostname)s"
+port = %(port)s
+
+connection_okay = False
+
+try:
+  s = socket.create_connection((hostname, port))
+  connection_okay = True
+  s.close()
+except (socket.error, socket.timeout):
+  connection_okay = False
+
+if not connection_okay:
+  print >> sys.stderr, "%(port)s on %(hostname)s isn't listening"
+  sys.exit(127)
diff --git a/slapos/recipe/generatemac.py b/slapos/recipe/generatemac.py
new file mode 100644
index 0000000000000000000000000000000000000000..45abf6d2ee8961b025539b442403af8a62ee2036
--- /dev/null
+++ b/slapos/recipe/generatemac.py
@@ -0,0 +1,40 @@
+
+##############################################################################
+#
+# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+import random
+
+from slapos.recipe.librecipe import GenericBaseRecipe
+
+class Recipe(GenericBaseRecipe):
+
+  def __init__(self, buildout, name, options):
+    # First octet has to represent a locally administered address
+    octet_list = [254] + [random.randint(0x00, 0xff) for x in range(5)]
+    options['mac-address'] = ':'.join(['%02x' % x for x in octet_list])
+
+  def install(self):
+    return []
diff --git a/slapos/recipe/generic_onetimeupload/__init__.py b/slapos/recipe/generic_onetimeupload/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..97ef798220e045de31c3d917ba7daf1167b182b1
--- /dev/null
+++ b/slapos/recipe/generic_onetimeupload/__init__.py
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+from slapos.recipe.librecipe import GenericBaseRecipe
+import binascii
+import os
+import sys
+
+class Recipe(GenericBaseRecipe):
+  """
+  kvm instance configuration.
+  """
+
+  def __init__(self, buildout, name, options):
+    options['key'] = binascii.hexlify(os.urandom(24))
+    return GenericBaseRecipe.__init__(self, buildout, name, options)
+
+  def install(self):
+    config = dict(
+      ip=self.options['ip'],
+      port=self.options['port'],
+      onetimeupload_path=self.options['onetimeupload-path'],
+      shell_path=self.options['shell-path'],
+      log_path=self.options['log-path'],
+      image=self.options['image-path'],
+      key=self.options['key'],
+    )
+
+    # Runners
+    runner_path = self.createExecutable(
+      self.options['path'],
+      self.substituteTemplate(self.getTemplateFilename('onetimeupload_run.in'),
+                              config))
+
+    return [runner_path]
+
diff --git a/slapos/recipe/nbdserver/template/onetimeupload_run.in b/slapos/recipe/generic_onetimeupload/template/onetimeupload_run.in
similarity index 90%
rename from slapos/recipe/nbdserver/template/onetimeupload_run.in
rename to slapos/recipe/generic_onetimeupload/template/onetimeupload_run.in
index 9d6cfe22248f543a22fc43e9c0b5deb2080500a3..a09627d5501af0b3e2d71835ea345c71efc1b37b 100644
--- a/slapos/recipe/nbdserver/template/onetimeupload_run.in
+++ b/slapos/recipe/generic_onetimeupload/template/onetimeupload_run.in
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!%(shell_path)s
 # BEWARE: This file is operated by slapgrid
 # BEWARE: It will be overwritten automatically
 exec %(onetimeupload_path)s -l %(log_path)s %(ip)s %(port)s %(image)s %(key)s 
diff --git a/slapos/recipe/kvm/__init__.py b/slapos/recipe/kvm/__init__.py
index e4d5688562bd5d1590f8922d144ef582d86965ec..54ec9a442a48014717616d341d37e705fcd53bd3 100644
--- a/slapos/recipe/kvm/__init__.py
+++ b/slapos/recipe/kvm/__init__.py
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
+# Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
 #
 # WARNING: This program as such is intended to be used by professional
 # programmers who take the whole responsibility of assessing all potential
@@ -24,333 +24,53 @@
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #
 ##############################################################################
+from slapos.recipe.librecipe import GenericBaseRecipe
+import binascii
 import os
 import sys
-from slapos.recipe.librecipe import BaseSlapRecipe
-import subprocess
-import binascii
-import random
-import zc.buildout
-import pkg_resources
-import ConfigParser
-import hashlib
-
-class Recipe(BaseSlapRecipe):
-
-  # To avoid magic numbers
-  VNC_BASE_PORT = 5900
-
-  def _install(self):
-    """
-    Set the connection dictionnary for the computer partition and create a list
-    of paths to the different wrappers
-
-    Parameters : none
-
-    Returns    : List path_list
-    """
-    self.path_list = []
-
-    self.requirements, self.ws           = self.egg.working_set()
-    self.cron_d                          = self.installCrond()
-
-    self.ca_conf                         = self.installCertificateAuthority()
-    self.key_path, self.certificate_path = self.requestCertificate('noVNC')
-
-    # Install the socket_connection_attempt script
-    catcher = zc.buildout.easy_install.scripts(
-      [('check_port_listening', 'slapos.recipe.kvm.socket_connection_attempt',
-        'connection_attempt')],
-      self.ws,
-      sys.executable,
-      self.bin_directory,
-    )
-    # Save the check_port_listening script path
-    check_port_listening_script = catcher[0]
-    # Get the port_listening_promise template path, and save it
-    self.port_listening_promise_path = pkg_resources.resource_filename(
-      __name__, 'template/port_listening_promise.in')
-    self.port_listening_promise_conf = dict(
-     check_port_listening_script=check_port_listening_script,
-    )
-
-    kvm_conf = self.installKvm(vnc_ip = self.getLocalIPv4Address())
-
-    vnc_port = Recipe.VNC_BASE_PORT + kvm_conf['vnc_display']
-
-    noVNC_conf = self.installNoVnc(source_ip   = self.getGlobalIPv6Address(),
-                                   source_port = 6080,
-                                   target_ip   = kvm_conf['vnc_ip'],
-                                   target_port = vnc_port)
-
-    self.linkBinary()
-    self.computer_partition.setConnectionDict(dict(
-        url = "https://[%s]:%s/vnc_auto.html?host=[%s]&port=%s&encrypt=1" % (
-            noVNC_conf['source_ip'],
-            noVNC_conf['source_port'],
-            noVNC_conf['source_ip'],
-            noVNC_conf['source_port']),
-        password = kvm_conf['vnc_passwd']))
-
-    return self.path_list
-
-  def installKvm(self, vnc_ip):
-    """
-    Create kvm configuration dictionnary and instanciate a wrapper for kvm and
-    kvm controller
-
-    Parameters : IP the vnc server is listening on
-
-    Returns    : Dictionnary kvm_conf
-    """
-    kvm_conf = dict(vnc_ip = vnc_ip)
-
-    connection_found = False
-    for tap_interface, dummy in self.parameter_dict['ip_list']:
-      # Get an ip associated to a tap interface
-      if tap_interface:
-        connection_found = True
-    if not connection_found:
-      raise NotImplementedError("Do not support ip without tap interface")
-
-    kvm_conf['tap_interface'] = tap_interface
-
-    # Disk path
-    kvm_conf['disk_path'] = os.path.join(self.data_root_directory,
-        'virtual.qcow2')
-    kvm_conf['socket_path'] = os.path.join(self.var_directory, 'qmp_socket')
-    # XXX Weak password
-    ##XXX -Vivien: add an option to generate one password for all instances
-    # and/or to input it yourself
-    kvm_conf['vnc_passwd'] = binascii.hexlify(os.urandom(4))
-
-    #XXX pid_file path, database_path, path to python binary and xml path
-    kvm_conf['pid_file_path'] = os.path.join(self.run_directory, 'pid_file')
-    kvm_conf['database_path'] = os.path.join(self.data_root_directory,
-        'slapmonitor_database')
-    kvm_conf['python_path']   = sys.executable
-    kvm_conf['qemu_path']     = self.options['qemu_path']
-    #xml_path = os.path.join(self.var_directory, 'slapreport.xml' )
 
-    # Create disk if needed
-    if not os.path.exists(kvm_conf['disk_path']):
-      retcode = subprocess.call(["%s create -f qcow2 %s %iG" % (
-          self.options['qemu_img_path'], kvm_conf['disk_path'],
-          int(self.options['disk_size']))], shell=True)
-      if retcode != 0:
-        raise OSError, "Disk creation failed!"
-
-    # Options nbd_ip and nbd_port are provided by slapos master
-    kvm_conf['nbd_ip']   = self.parameter_dict['nbd_ip']
-    kvm_conf['nbd_port'] = self.parameter_dict['nbd_port']
-
-    # First octet has to represent a locally administered address
-    octet_list         = [254] + [random.randint(0x00, 0xff) for x in range(5)]
-    kvm_conf['mac_address'] = ':'.join(['%02x' % x for x in octet_list])
-
-    kvm_conf['hostname']    = "slaposkvm"
-    kvm_conf['smp_count']   = self.options['smp_count']
-    kvm_conf['ram_size']    = self.options['ram_size']
-
-    kvm_conf['vnc_display'] = 1
-
-    # Instanciate KVM
-    kvm_template_location = pkg_resources.resource_filename(
-                                             __name__, os.path.join(
-                                             'template', 'kvm_run.in'))
-
-    kvm_runner_path = self.createRunningWrapper("kvm",
-          self.substituteTemplate(kvm_template_location,
-                                  kvm_conf))
-
-    self.path_list.append(kvm_runner_path)
-
-    # Instanciate KVM controller
-    kvm_controller_template_location = pkg_resources.resource_filename(
-                                             __name__, os.path.join(
-                                             'template',
-                                             'kvm_controller_run.in' ))
-
-    kvm_controller_runner_path = self.createRunningWrapper("kvm_controller",
-          self.substituteTemplate(kvm_controller_template_location,
-                                  kvm_conf))
-
-    self.path_list.append(kvm_controller_runner_path)
-
-    # Instanciate Slapmonitor
-    ##slapmonitor_runner_path = self.instanciate_wrapper("slapmonitor",
-    #    [database_path, pid_file_path, python_path])
-    # Instanciate Slapreport
-    ##slapreport_runner_path = self.instanciate_wrapper("slapreport",
-    #    [database_path, python_path])
-
-    # Add VNC promise
-    self.port_listening_promise_conf.update(
-      hostname=kvm_conf['vnc_ip'],
-      port=Recipe.VNC_BASE_PORT + kvm_conf['vnc_display'],
+class Recipe(GenericBaseRecipe):
+  """
+  kvm instance configuration.
+  """
+
+  def __init__(self, buildout, name, options):
+    options['passwd'] = binascii.hexlify(os.urandom(4))
+    return GenericBaseRecipe.__init__(self, buildout, name, options)
+
+  def install(self):
+    config = dict(
+      tap_interface=self.options['tap'],
+      vnc_ip=self.options['vnc-ip'],
+      vnc_port=self.options['vnc-port'],
+      nbd_ip=self.options['nbd-ip'],
+      nbd_port=self.options['nbd-port'],
+      disk_path=self.options['disk-path'],
+      disk_size=self.options['disk-size'],
+      mac_address=self.options['mac-address'],
+      smp_count=self.options['smp-count'],
+      ram_size=self.options['ram-size'],
+      socket_path=self.options['socket-path'],
+      pid_file_path=self.options['pid-path'],
+      python_path=sys.executable,
+      shell_path=self.options['shell-path'],
+      qemu_path=self.options['qemu-path'],
+      qemu_img_path=self.options['qemu-img-path'],
+      # XXX Weak password
+      vnc_passwd=self.options['passwd']
     )
-    self.createPromiseWrapper("vnc_promise",
-        self.substituteTemplate(self.port_listening_promise_path,
-                                self.port_listening_promise_conf,
-                               )
-                             )
 
-    return kvm_conf
+    # Runners
+    runner_path = self.createExecutable(
+      self.options['runner-path'],
+      self.substituteTemplate(self.getTemplateFilename('kvm_run.in'),
+                              config))
 
-  def installNoVnc(self, source_ip, source_port, target_ip, target_port):
-    """
-    Create noVNC configuration dictionnary and instanciate Websockify proxy
+    controller_path = self.createExecutable(
+      self.options['controller-path'],
+      self.substituteTemplate(self.getTemplateFilename('kvm_controller_run.in'),
+                              config))
 
-    Parameters : IP of the proxy, port on which is situated the proxy,
-                 IP of the vnc server, port on which is situated the vnc server,
-                 path to python binary
-
-    Returns    : noVNC configuration dictionnary
-    """
-
-    noVNC_conf = {}
-
-    noVNC_conf['source_ip']   = source_ip
-    noVNC_conf['source_port'] = source_port
-
-    execute_arguments = [[
-        self.options['websockify'].strip(),
-        '--web',
-        self.options['noVNC_location'],
-        '--key=%s' % (self.key_path),
-        '--cert=%s' % (self.certificate_path),
-        '--ssl-only',
-        '%s:%s' % (source_ip, source_port),
-        '%s:%s' % (target_ip, target_port)],
-        [self.certificate_path, self.key_path]]
-
-    self.path_list.extend(zc.buildout.easy_install.scripts([('websockify',
-      'slapos.recipe.librecipe.execute', 'execute_wait')], self.ws, sys.executable,
-      self.wrapper_directory, arguments=execute_arguments))
-
-    # Add noVNC promise
-    self.port_listening_promise_conf.update(hostname=noVNC_conf['source_ip'],
-                                            port=noVNC_conf['source_port'],
-                                           )
-    self.createPromiseWrapper("novnc_promise",
-        self.substituteTemplate(self.port_listening_promise_path,
-                                self.port_listening_promise_conf,
-                               )
-                             )
-
-    return noVNC_conf
-
-  def linkBinary(self):
-    """Links binaries to instance's bin directory for easier exposal"""
-    for linkline in self.options.get('link_binary_list', '').splitlines():
-      if not linkline:
-        continue
-      target = linkline.split()
-      if len(target) == 1:
-        target = target[0]
-        path, linkname = os.path.split(target)
-      else:
-        linkname = target[1]
-        target = target[0]
-      link = os.path.join(self.bin_directory, linkname)
-      if os.path.lexists(link):
-        if not os.path.islink(link):
-          raise zc.buildout.UserError(
-              'Target link already %r exists but it is not link' % link)
-        os.unlink(link)
-      os.symlink(target, link)
-      self.logger.debug('Created link %r -> %r' % (link, target))
-      self.path_list.append(link)
-
-  def installCertificateAuthority(self, ca_country_code='XX',
-      ca_email='xx@example.com', ca_state='State', ca_city='City',
-      ca_company='Company'):
-    backup_path = self.createBackupDirectory('ca')
-    self.ca_dir = os.path.join(self.data_root_directory, 'ca')
-    self._createDirectory(self.ca_dir)
-    self.ca_request_dir = os.path.join(self.ca_dir, 'requests')
-    self._createDirectory(self.ca_request_dir)
-    config = dict(ca_dir=self.ca_dir, request_dir=self.ca_request_dir)
-    self.ca_private = os.path.join(self.ca_dir, 'private')
-    self.ca_certs = os.path.join(self.ca_dir, 'certs')
-    self.ca_crl = os.path.join(self.ca_dir, 'crl')
-    self.ca_newcerts = os.path.join(self.ca_dir, 'newcerts')
-    self.ca_key_ext = '.key'
-    self.ca_crt_ext = '.crt'
-    for d in [self.ca_private, self.ca_crl, self.ca_newcerts, self.ca_certs]:
-      self._createDirectory(d)
-    for f in ['crlnumber', 'serial']:
-      if not os.path.exists(os.path.join(self.ca_dir, f)):
-        open(os.path.join(self.ca_dir, f), 'w').write('01')
-    if not os.path.exists(os.path.join(self.ca_dir, 'index.txt')):
-      open(os.path.join(self.ca_dir, 'index.txt'), 'w').write('')
-    openssl_configuration = os.path.join(self.ca_dir, 'openssl.cnf')
-    config.update(
-        working_directory=self.ca_dir,
-        country_code=ca_country_code,
-        state=ca_state,
-        city=ca_city,
-        company=ca_company,
-        email_address=ca_email,
-    )
-    self._writeFile(openssl_configuration, pkg_resources.resource_string(
-      __name__, 'template/openssl.cnf.ca.in') % config)
-    self.path_list.extend(zc.buildout.easy_install.scripts([
-      ('certificate_authority',
-        __name__ + '.certificate_authority', 'runCertificateAuthority')],
-        self.ws, sys.executable, self.wrapper_directory, arguments=[dict(
-          openssl_configuration=openssl_configuration,
-          openssl_binary=self.options['openssl_binary'],
-          certificate=os.path.join(self.ca_dir, 'cacert.pem'),
-          key=os.path.join(self.ca_private, 'cakey.pem'),
-          crl=os.path.join(self.ca_crl),
-          request_dir=self.ca_request_dir
-          )]))
-    # configure backup
-    backup_cron = os.path.join(self.cron_d, 'ca_rdiff_backup')
-    open(backup_cron, 'w').write(
-        '''0 0 * * * %(rdiff_backup)s %(source)s %(destination)s'''%dict(
-          rdiff_backup=self.options['rdiff_backup_binary'],
-          source=self.ca_dir,
-          destination=backup_path))
-    self.path_list.append(backup_cron)
-
-    return dict(
-      ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'),
-      ca_crl=os.path.join(config['ca_dir'], 'crl'),
-      certificate_authority_path=config['ca_dir']
-    )
 
-  def requestCertificate(self, name):
-    hash = hashlib.sha512(name).hexdigest()
-    key = os.path.join(self.ca_private, hash + self.ca_key_ext)
-    certificate = os.path.join(self.ca_certs, hash + self.ca_crt_ext)
-    parser = ConfigParser.RawConfigParser()
-    parser.add_section('certificate')
-    parser.set('certificate', 'name', name)
-    parser.set('certificate', 'key_file', key)
-    parser.set('certificate', 'certificate_file', certificate)
-    parser.write(open(os.path.join(self.ca_request_dir, hash), 'w'))
-    return key, certificate
+    return [runner_path, controller_path]
 
-  def installCrond(self):
-    timestamps = self.createDataDirectory('cronstamps')
-    cron_output = os.path.join(self.log_directory, 'cron-output')
-    self._createDirectory(cron_output)
-    catcher = zc.buildout.easy_install.scripts([('catchcron',
-      __name__ + '.catdatefile', 'catdatefile')], self.ws, sys.executable,
-      self.bin_directory, arguments=[cron_output])[0]
-    self.path_list.append(catcher)
-    cron_d = os.path.join(self.etc_directory, 'cron.d')
-    crontabs = os.path.join(self.etc_directory, 'crontabs')
-    self._createDirectory(cron_d)
-    self._createDirectory(crontabs)
-    # Use execute from erp5.
-    wrapper = zc.buildout.easy_install.scripts([('crond',
-      'slapos.recipe.librecipe.execute', 'execute')], self.ws, sys.executable,
-      self.wrapper_directory, arguments=[
-        self.options['dcrond_binary'].strip(), '-s', cron_d, '-c', crontabs,
-        '-t', timestamps, '-f', '-l', '5', '-M', catcher]
-      )[0]
-    self.path_list.append(wrapper)
-    return cron_d
diff --git a/slapos/recipe/kvm/certificate_authority.py b/slapos/recipe/kvm/certificate_authority.py
deleted file mode 100755
index d05a460649c01edefd09e5caa09f3feab899ddcf..0000000000000000000000000000000000000000
--- a/slapos/recipe/kvm/certificate_authority.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import os
-import subprocess
-import time
-import ConfigParser
-import uuid
-
-
-def popenCommunicate(command_list, input=None):
-  subprocess_kw = dict(stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-  if input is not None:
-    subprocess_kw.update(stdin=subprocess.PIPE)
-  popen = subprocess.Popen(command_list, **subprocess_kw)
-  result = popen.communicate(input)[0]
-  if popen.returncode is None:
-    popen.kill()
-  if popen.returncode != 0:
-    raise ValueError('Issue during calling %r, result was:\n%s' % (
-      command_list, result))
-  return result
-
-
-class CertificateAuthority:
-  def __init__(self, key, certificate, openssl_binary,
-      openssl_configuration, request_dir):
-    self.key = key
-    self.certificate = certificate
-    self.openssl_binary = openssl_binary
-    self.openssl_configuration = openssl_configuration
-    self.request_dir = request_dir
-
-  def checkAuthority(self):
-    file_list = [ self.key, self.certificate ]
-    ca_ready = True
-    for f in file_list:
-      if not os.path.exists(f):
-        ca_ready = False
-        break
-    if ca_ready:
-      return
-    for f in file_list:
-      if os.path.exists(f):
-        os.unlink(f)
-    try:
-      # no CA, let us create new one
-      popenCommunicate([self.openssl_binary, 'req', '-nodes', '-config',
-          self.openssl_configuration, '-new', '-x509', '-extensions', 'v3_ca',
-          '-keyout', self.key, '-out', self.certificate, '-days', '10950'],
-          # Authority name will be random, so no instance has the same issuer
-          'Certificate Authority %s\n' % uuid.uuid1())
-    except:
-      try:
-        for f in file_list:
-          if os.path.exists(f):
-            os.unlink(f)
-      except:
-        # do not raise during cleanup
-        pass
-      raise
-
-  def _checkCertificate(self, common_name, key, certificate):
-    file_list = [key, certificate]
-    ready = True
-    for f in file_list:
-      if not os.path.exists(f):
-        ready = False
-        break
-    if ready:
-      return False
-    for f in file_list:
-      if os.path.exists(f):
-        os.unlink(f)
-    csr = certificate + '.csr'
-    try:
-      popenCommunicate([self.openssl_binary, 'req', '-config',
-        self.openssl_configuration, '-nodes', '-new', '-keyout',
-        key, '-out', csr, '-days', '3650'],
-        common_name + '\n')
-      try:
-        popenCommunicate([self.openssl_binary, 'ca', '-batch', '-config',
-          self.openssl_configuration, '-out', certificate,
-          '-infiles', csr])
-      finally:
-        if os.path.exists(csr):
-          os.unlink(csr)
-    except:
-      try:
-        for f in file_list:
-          if os.path.exists(f):
-            os.unlink(f)
-      except:
-        # do not raise during cleanup
-        pass
-      raise
-    else:
-      return True
-
-  def checkRequestDir(self):
-    for request_file in os.listdir(self.request_dir):
-      parser = ConfigParser.RawConfigParser()
-      parser.readfp(open(os.path.join(self.request_dir, request_file), 'r'))
-      if self._checkCertificate(parser.get('certificate', 'name'),
-          parser.get('certificate', 'key_file'), parser.get('certificate',
-            'certificate_file')):
-        print 'Created certificate %r' % parser.get('certificate', 'name')
-
-def runCertificateAuthority(args):
-  ca_conf = args[0]
-  ca = CertificateAuthority(ca_conf['key'], ca_conf['certificate'],
-      ca_conf['openssl_binary'], ca_conf['openssl_configuration'],
-      ca_conf['request_dir'])
-  while True:
-    ca.checkAuthority()
-    ca.checkRequestDir()
-    time.sleep(60)
diff --git a/slapos/recipe/kvm/socket_connection_attempt.py b/slapos/recipe/kvm/socket_connection_attempt.py
deleted file mode 100644
index dfd9fad4b930518c6ef94a88c69ed40ce6b22539..0000000000000000000000000000000000000000
--- a/slapos/recipe/kvm/socket_connection_attempt.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import socket
-import sys
-
-def connection_attempt():
-
-  try:
-    hostname, port = sys.argv[1:3]
-  except ValueError:
-    print >> sys.stderr, """Bad command line.
-  Usage: %s hostname|ip port""" % sys.argv[0]
-    sys.exit(1)
-
-  connection_okay = False
-
-  try:
-    s = socket.create_connection((hostname, port))
-    connection_okay = True
-    s.close()
-  except (socket.error, socket.timeout):
-    connection_okay = False
-
-  if not connection_okay:
-    print >> sys.stderr, "%(port)s on %(ip)s isn't listening" % {
-      'port': port, 'ip': hostname
-    }
-    sys.exit(127)
diff --git a/slapos/recipe/kvm/template/kvm_run.in b/slapos/recipe/kvm/template/kvm_run.in
index 3987f1df1135c832be80d5e0b19a9dc9dc3192ea..d4f770a2d6f471b0ab208f67e57816b93a8f82c7 100644
--- a/slapos/recipe/kvm/template/kvm_run.in
+++ b/slapos/recipe/kvm/template/kvm_run.in
@@ -1,17 +1,55 @@
-#!/bin/sh
+#!%(python_path)s
 # BEWARE: This file is operated by slapgrid
 # BEWARE: It will be overwritten automatically
 
-# TODO: -net nic,model=virtio, but OS installer has to provide the virtio_net
-# module
-exec %(qemu_path)s \
-  -net nic,macaddr=%(mac_address)s \
-  -net tap,ifname=%(tap_interface)s,script=no,downscript=no \
-  -smp %(smp_count)s \
-  -m %(ram_size)s \
-  -cdrom nbd:[%(nbd_ip)s]:%(nbd_port)s \
-  -drive file=%(disk_path)s,if=virtio,boot=on \
-  -vnc %(vnc_ip)s:1,ipv4,password \
-  -boot menu=on \
-  -qmp unix:%(socket_path)s,server \
-  -pidfile %(pid_file_path)s
+# Echo client program
+import os
+import socket
+import subprocess
+
+def getSocketStatus(host, port):
+  s = None
+  for res in socket.getaddrinfo(host, port,
+      socket.AF_UNSPEC, socket.SOCK_STREAM):
+    af, socktype, proto, canonname, sa = res
+    try:
+      s = socket.socket(af, socktype, proto)
+    except socket.error, msg:
+      s = None
+      continue
+    try:
+      s.connect(sa)
+    except socket.error, msg:
+      s.close()
+      s = None
+      continue
+    break
+  return s
+
+# create disk if doesn't exist
+disk_path = '%(disk_path)s'
+if not os.path.exists(disk_path):
+  subprocess.Popen(['%(qemu_img_path)s', 'create' ,'-f', 'qcow2',
+      '%(disk_path)s', '%(disk_size)sG'])
+
+kvm_argument_list = ['kvm', '-net', 'nic,macaddr=%(mac_address)s',
+  '-net', 'tap,ifname=%(tap_interface)s,script=no,downscript=no',
+  '-smp', '%(smp_count)s',
+  '-m', '%(ram_size)s',
+  '-drive', 'file=%(disk_path)s,if=virtio,boot=on',
+  '-vnc', '%(vnc_ip)s:1,ipv4,password',
+  '-boot', 'menu=on',
+  '-qmp', 'unix:%(socket_path)s,server',
+  '-pidfile', '%(pid_file_path)s',
+]
+
+# Try to connect to NBD server
+s = getSocketStatus('%(nbd_ip)s', %(nbd_port)s)
+if s is None:
+  # NBD is not available : launch kvm without it
+  print 'Warning : Nbd is not available.'
+  os.execv('%(qemu_path)s', kvm_argument_list)
+else:
+  # NBD is available
+  kvm_argument_list.extend(['-cdrom', 'nbd:[%(nbd_ip)s]:%(nbd_port)s'])
+  os.execv('%(qemu_path)s', kvm_argument_list)
diff --git a/slapos/recipe/kvm/template/openssl.cnf.ca.in b/slapos/recipe/kvm/template/openssl.cnf.ca.in
deleted file mode 100644
index 67067178b951729004cc20c0156bc76d8d968d23..0000000000000000000000000000000000000000
--- a/slapos/recipe/kvm/template/openssl.cnf.ca.in
+++ /dev/null
@@ -1,350 +0,0 @@
-#
-# OpenSSL example configuration file.
-# This is mostly being used for generation of certificate requests.
-#
-
-# This definition stops the following lines choking if HOME isn't
-# defined.
-HOME			= .
-RANDFILE		  = $ENV::HOME/.rnd
-
-# Extra OBJECT IDENTIFIER info:
-#oid_file      		  = $ENV::HOME/.oid
-oid_section		    = new_oids
-
-# To use this configuration file with the "-extfile" option of the
-# "openssl x509" utility, name here the section containing the
-# X.509v3 extensions to use:
-# extensions	     	= 
-# (Alternatively, use a configuration file that has only
-# X.509v3 extensions in its main [= default] section.)
-
-[ new_oids ]
-
-# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
-# Add a simple OID like this:
-# testoid1=1.2.3.4
-# Or use config file substitution like this:
-# testoid2=${testoid1}.5.6
-
-# Policies used by the TSA examples.
-tsa_policy1 = 1.2.3.4.1
-tsa_policy2 = 1.2.3.4.5.6
-tsa_policy3 = 1.2.3.4.5.7
-
-####################################################################
-[ ca ]
-default_ca	= CA_default		# The default ca section
-
-####################################################################
-[ CA_default ]
-
-dir		= %(working_directory)s		# Where everything is kept
-certs		= $dir/certs			  # Where the issued certs are kept
-crl_dir		= $dir/crl			    # Where the issued crl are kept
-database	= $dir/index.txt		    # database index file.
-#unique_subject	= no			      	       # Set to 'no' to allow creation of
-		      					       	     # several ctificates with same subject.
-new_certs_dir	= $dir/newcerts	       # default place for new certs.
-
-certificate	= $dir/cacert.pem 	       # The CA certificate
-serial		= $dir/serial 	       	 # The current serial number
-crlnumber	= $dir/crlnumber		 # the current crl number
-				       # must be commented out to leave a V1 CRL
-crl		= $dir/crl.pem        # The current CRL
-private_key	= $dir/private/cakey.pem # The private key
-RANDFILE	= $dir/private/.rand	  # private random number file
-
-x509_extensions	= usr_cert		    # The extentions to add to the cert
-
-# Comment out the following two lines for the "traditional"
-# (and highly broken) format.
-name_opt      = ca_default		# Subject Name options
-cert_opt      = ca_default		  # Certificate field options
-
-# Extension copying option: use with caution.
-# copy_extensions = copy
-
-# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
-# so this is commented out by default to leave a V1 CRL.
-# crlnumber must also be commented out to leave a V1 CRL.
-# crl_extensions = crl_ext
-
-default_days	 = 3650			# how long to certify for
-default_crl_days = 30			      # how long before next CRL
-default_md	 = default		      	# use public key default MD
-preserve	 = no				      # keep passed DN ordering
-
-# A few difference way of specifying how similar the request should look
-# For type CA, the listed attributes must be the same, and the optional
-# and supplied fields are just that :-)
-policy	       	      = policy_match
-
-# For the CA policy
-[ policy_match ]
-countryName	= match
-stateOrProvinceName	= match
-organizationName	= match
-organizationalUnitName	= optional
-commonName		= supplied
-emailAddress		= optional
-
-# For the 'anything' policy
-# At this point in time, you must list all acceptable 'object'
-# types.
-[ policy_anything ]
-countryName		= optional
-stateOrProvinceName	= optional
-localityName		= optional
-organizationName	= optional
-organizationalUnitName	= optional
-commonName		= supplied
-emailAddress		= optional
-
-####################################################################
-[ req ]
-default_bits		= 2048
-default_md		= sha1
-default_keyfile 	= privkey.pem
-distinguished_name	= req_distinguished_name
-#attributes		= req_attributes
-x509_extensions		= v3_ca	# The extentions to add to the self signed cert
-
-# Passwords for private keys if not present they will be prompted for
-# input_password = secret
-# output_password = secret
-
-# This sets a mask for permitted string types. There are several options. 
-# default: PrintableString, T61String, BMPString.
-# pkix	    : PrintableString, BMPString (PKIX recommendation before 2004)
-# utf8only: only UTF8Strings (PKIX recommendation after 2004).
-# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
-# MASK:XXXX a literal mask value.
-# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
-string_mask = utf8only
-
-# req_extensions = v3_req # The extensions to add to a certificate request
-
-[ req_distinguished_name ]
-countryName				= Country Name (2 letter code)
-countryName_value			= %(country_code)s
-countryName_min				= 2
-countryName_max				= 2
-
-stateOrProvinceName			= State or Province Name (full name)
-stateOrProvinceName_value		= %(state)s
-
-localityName				= Locality Name (eg, city)
-localityName_value			= %(city)s
-
-0.organizationName			= Organization Name (eg, company)
-0.organizationName_value		= %(company)s
-
-# we can do this but it is not needed normally :-)
-#1.organizationName  	= Second Organization Name (eg, company)
-#1.organizationName_default	 = World Wide Web Pty Ltd
-
-commonName	   = Common Name (eg, your name or your server\'s hostname)
-commonName_max	   = 64
-
-emailAddress	   = Email Address
-emailAddress_value = %(email_address)s
-emailAddress_max   = 64
-
-# SET-ex3	   = SET extension number 3
-
-#[ req_attributes ]
-#challengePassword	= A challenge password
-#challengePassword_min	= 4
-#challengePassword_max	= 20
-#
-#unstructuredName	= An optional company name
-
-[ usr_cert ]
-
-# These extensions are added when 'ca' signs a request.
-
-# This goes against PKIX guidelines but some CAs do it and some software
-# requires this to avoid interpreting an end user certificate as a CA.
-
-basicConstraints=CA:FALSE
-
-# Here are some examples of the usage of nsCertType. If it is omitted
-# the certificate can be used for anything *except* object signing.
-
-# This is OK for an SSL server.
-# nsCertType 	    = server
-
-# For an object signing certificate this would be used.
-# nsCertType = objsign
-
-# For normal client use this is typical
-# nsCertType = client, email
-
-# and for everything including object signing:
-# nsCertType = client, email, objsign
-
-# This is typical in keyUsage for a client certificate.
-# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-
-# This will be displayed in Netscape's comment listbox.
-nsComment      		 = "OpenSSL Generated Certificate"
-
-# PKIX recommendations harmless if included in all certificates.
-subjectKeyIdentifier=hash
-authorityKeyIdentifier=keyid,issuer
-
-# This stuff is for subjectAltName and issuerAltname.
-# Import the email address.
-# subjectAltName=email:copy
-# An alternative to produce certificates that aren't
-# deprecated according to PKIX.
-# subjectAltName=email:move
-
-# Copy subject details
-# issuerAltName=issuer:copy
-
-#nsCaRevocationUrl		= http://www.domain.dom/ca-crl.pem
-#nsBaseUrl
-#nsRevocationUrl
-#nsRenewalUrl
-#nsCaPolicyUrl
-#nsSslServerName
-
-# This is required for TSA certificates.
-# extendedKeyUsage = critical,timeStamping
-
-[ v3_req ]
-
-# Extensions to add to a certificate request
-
-basicConstraints = CA:FALSE
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-
-[ v3_ca ]
-
-
-# Extensions for a typical CA
-
-
-# PKIX recommendation.
-
-subjectKeyIdentifier=hash
-
-authorityKeyIdentifier=keyid:always,issuer
-
-# This is what PKIX recommends but some broken software chokes on critical
-# extensions.
-#basicConstraints = critical,CA:true
-# So we do this instead.
-basicConstraints = CA:true
-
-# Key usage: this is typical for a CA certificate. However since it will
-# prevent it being used as an test self-signed certificate it is best
-# left out by default.
-# keyUsage = cRLSign, keyCertSign
-
-# Some might want this also
-# nsCertType = sslCA, emailCA
-
-# Include email address in subject alt name: another PKIX recommendation
-# subjectAltName=email:copy
-# Copy issuer details
-# issuerAltName=issuer:copy
-
-# DER hex encoding of an extension: beware experts only!
-# obj=DER:02:03
-# Where 'obj' is a standard or added object
-# You can even override a supported extension:
-# basicConstraints= critical, DER:30:03:01:01:FF
-
-[ crl_ext ]
-
-# CRL extensions.
-# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
-
-# issuerAltName=issuer:copy
-authorityKeyIdentifier=keyid:always
-
-[ proxy_cert_ext ]
-# These extensions should be added when creating a proxy certificate
-
-# This goes against PKIX guidelines but some CAs do it and some software
-# requires this to avoid interpreting an end user certificate as a CA.
-
-basicConstraints=CA:FALSE
-
-# Here are some examples of the usage of nsCertType. If it is omitted
-# the certificate can be used for anything *except* object signing.
-
-# This is OK for an SSL server.
-# nsCertType 	    = server
-
-# For an object signing certificate this would be used.
-# nsCertType = objsign
-
-# For normal client use this is typical
-# nsCertType = client, email
-
-# and for everything including object signing:
-# nsCertType = client, email, objsign
-
-# This is typical in keyUsage for a client certificate.
-# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-
-# This will be displayed in Netscape's comment listbox.
-nsComment      		 = "OpenSSL Generated Certificate"
-
-# PKIX recommendations harmless if included in all certificates.
-subjectKeyIdentifier=hash
-authorityKeyIdentifier=keyid,issuer
-
-# This stuff is for subjectAltName and issuerAltname.
-# Import the email address.
-# subjectAltName=email:copy
-# An alternative to produce certificates that aren't
-# deprecated according to PKIX.
-# subjectAltName=email:move
-
-# Copy subject details
-# issuerAltName=issuer:copy
-
-#nsCaRevocationUrl		= http://www.domain.dom/ca-crl.pem
-#nsBaseUrl
-#nsRevocationUrl
-#nsRenewalUrl
-#nsCaPolicyUrl
-#nsSslServerName
-
-# This really needs to be in place for it to be a proxy certificate.
-proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
-
-####################################################################
-[ tsa ]
-
-default_tsa = tsa_config1	# the default TSA section
-
-[ tsa_config1 ]
-
-# These are used by the TSA reply generation only.
-dir	                = /etc/pki/tls  	  # TSA root directory
-serial	                = $dir/tsaserial	  # The current serial number (mandatory)
-crypto_device           = builtin		    # OpenSSL engine to use for signing
-signer_cert             = $dir/tsacert.pem    # The TSA signing certificate
-	      		      	  # (optional)
-certs	                = $dir/cacert.pem	# Certificate chain to include in reply
-		  	      # (optional)
-signer_key              = $dir/private/tsakey.pem # The TSA private key (optional)
-
-default_policy          = tsa_policy1		  # Policy if request did not specify it
-					    	   # (optional)
-other_policies          = tsa_policy2, tsa_policy3 # acceptable policies (optional)
-digests	                = md5, sha1  	      # Acceptable message digests (mandatory)
-accuracy                = secs:1, millisecs:500, microsecs:100	   # (optional)
-clock_precision_digits  = 0	    # number of digits after dot. (optional)
-ordering		= yes	    # Is ordering defined for timestamps?
-			  	       # (optional, default: no)
-tsa_name		= yes	    # Must the TSA name be included in the reply?
-			  	      # (optional, default: no)
-ess_cert_id_chain	= no	   # Must the ESS cert id chain be included?
-				     # (optional, default: no)
diff --git a/slapos/recipe/kvm/template/port_listening_promise.in b/slapos/recipe/kvm/template/port_listening_promise.in
deleted file mode 100644
index 15fa390d01e38096cfb8a9d01356bf1687e3f740..0000000000000000000000000000000000000000
--- a/slapos/recipe/kvm/template/port_listening_promise.in
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env sh
-
-"%(check_port_listening_script)s" "%(hostname)s" "%(port)s"
-exit $?
diff --git a/slapos/recipe/kvm/template/slapmonitor_run.in b/slapos/recipe/kvm/template/slapmonitor_run.in
deleted file mode 100644
index c790e0904af68481fb1b6f13d9cdbd64454b4ad7..0000000000000000000000000000000000000000
--- a/slapos/recipe/kvm/template/slapmonitor_run.in
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# BEWARE: This file is operated by slapgrid
-# BEWARE: It will be overwritten automatically
-exec %(python_path)s %(slapmonitor_path)s %(pid_file_path)s %(database_path)s
diff --git a/slapos/recipe/kvm/template/slapreport_run.in b/slapos/recipe/kvm/template/slapreport_run.in
deleted file mode 100644
index 4b3dd99a377d0dcbcf256255c056877811dce397..0000000000000000000000000000000000000000
--- a/slapos/recipe/kvm/template/slapreport_run.in
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# BEWARE: This file is operated by slapgrid
-# BEWARE: It will be overwritten automatically
-exec %(python_path)s %(slapreport_path)s $1 %(database_path)s
diff --git a/slapos/recipe/kvm_frontend/__init__.py b/slapos/recipe/kvm_frontend/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0da2573480b73e3aa0d0c62c739991a0ebc5cd87
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/__init__.py
@@ -0,0 +1,137 @@
+##############################################################################
+#
+# Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+from slapos.recipe.librecipe import GenericBaseRecipe, GenericSlapRecipe
+import json
+import zc.buildout
+
+class Recipe(GenericSlapRecipe):
+  """
+  kvm frontend instance configuration.
+  """
+
+  def _getRewriteRuleContent(self, slave_instance_list):
+    """Generate rewrite rules list from slaves list"""
+    rewrite_rule_list = []
+    for slave_instance in slave_instance_list:
+      self.logger.info("Processing slave instance %s..." %
+          slave_instance['slave_reference'])
+      # Check for mandatory fields
+      if slave_instance.get('host', None) is None:
+        self.logger.warn('No "host" parameter is defined for %s slave'\
+            'instance. Ignoring it.' % slave_instance['slave_reference'])
+        continue
+      if slave_instance.get('port', None) is None:
+        self.logger.warn('No "host" parameter is defined for %s slave'\
+            'instance. Ignoring it.' % slave_instance['slave_reference'])
+        continue
+
+      current_slave_dict = dict()
+
+      # Get host, and if IPv6 address, remove "[" and "]"
+      current_slave_dict['host'] = slave_instance['host'].\
+          replace('[', '').replace(']', '')
+      current_slave_dict['port'] = slave_instance['port']
+
+      # Check if target is https or http
+      current_slave_dict['https'] = slave_instance.get('https', 'true')
+      if current_slave_dict['https'] in GenericBaseRecipe.FALSE_VALUES:
+        current_slave_dict['https'] = 'false'
+      # Set reference and resource url
+      # Reference is raw reference from SlapOS Master, resource is
+      # URL-compatible name
+      reference = slave_instance.get('slave_reference')
+      current_slave_dict['reference'] = reference
+      current_slave_dict['resource'] = reference.replace('-', '')
+      rewrite_rule_list.append(current_slave_dict)
+    return rewrite_rule_list
+
+  def _getProxyTableContent(self, rewrite_rule_list):
+    """Generate proxy table file content from rewrite rules list"""
+    proxy_table = dict()
+    for rewrite_rule in rewrite_rule_list:
+      proxy_table[rewrite_rule['resource']] = {
+          'port': rewrite_rule['port'],
+          'host': rewrite_rule['host'],
+          'https': rewrite_rule['https'],
+      }
+
+    proxy_table_content = json.dumps(proxy_table)
+    return proxy_table_content
+
+  def _install(self):
+    # Check for mandatory field
+    if self.options.get('domain', None) is None:
+      raise zc.buildout.UserError('No domain name specified. Please define '
+          'the "domain" instance parameter.')
+    # Generate rewrite rules
+    rewrite_rule_list = self._getRewriteRuleContent(
+      json.loads(self.options['slave-instance-list']))
+    # Create Map
+    map_content = self._getProxyTableContent(rewrite_rule_list)
+    map_file = self.createFile(self.options['map-path'], map_content)
+
+    # Create configuration
+    conf = open(self.getTemplateFilename('kvm-proxy.js'), 'r')
+    conf_file = self.createFile(self.options['conf-path'], conf.read())
+    conf.close()
+
+    # Do we create http dummy server used to redirect to https?
+    if self.options['http-redirection'] in GenericBaseRecipe.TRUE_VALUES:
+      http_redirect_server = '1'
+    else:
+      http_redirect_server = ''
+
+    config = dict(
+      ip=self.options['ip'],
+      port=self.options['port'],
+      key=self.options['ssl-key-path'],
+      certificate=self.options['ssl-cert-path'],
+      name=self.options['domain'],
+      shell_path=self.options['shell-path'],
+      node_path=self.options['node-binary'],
+      node_env=self.options['node-env'],
+      conf_path=conf_file,
+      map_path=map_file,
+      plain_http=http_redirect_server,
+    )
+
+    runner_path = self.createExecutable(
+      self.options['wrapper-path'],
+      self.substituteTemplate(self.getTemplateFilename('nodejs_run.in'),
+                              config))
+
+    # Send connection parameters of slave instances
+    site_url = "https://%s:%s/" % (self.options['domain'], self.options['port'])
+    for slave in rewrite_rule_list:
+      self.setConnectionDict(
+          dict(url="%s%s" % (site_url, slave['resource']),
+               domainname=self.options['domain'],
+               port=self.options['port'],
+               resource=slave['resource']),
+          slave['reference'])
+
+    return [map_file, conf_file, runner_path]
diff --git a/slapos/recipe/kvm_frontend/template/kvm-proxy.js b/slapos/recipe/kvm_frontend/template/kvm-proxy.js
new file mode 100644
index 0000000000000000000000000000000000000000..9c83d6930e1732ae9aa1efb75ecced8b93274004
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/template/kvm-proxy.js
@@ -0,0 +1,128 @@
+/*****************************************************************************
+*
+* Copyright (c) 2012 Vifib SARL and Contributors. All Rights Reserved.
+*
+* WARNING: This program as such is intended to be used by professional
+* programmers who take the whole responsibility of assessing all potential
+* consequences resulting from its eventual inadequacies and bugs
+* End users who are looking for a ready-to-use solution with commercial
+* guarantees and support are strongly adviced to contract a Free Software
+* Service Company
+*
+* This program is Free Software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 3
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*
+*****************************************************************************/
+
+/* Wrapper used to configure the httpproxy node package to proxy
+   http://myhost/myinstance
+   to real IP/URL of myinstance
+*/
+
+var fs = require('fs'),
+    util = require('util'),
+    colors = require('colors'),
+    http = require('http'),
+    httpProxy = require('http-proxy'),
+    proxyByUrl = require('proxy-by-url');
+
+var listenInterface = process.argv[2],
+    port = process.argv[3],
+    sslKeyFile = process.argv[4],
+    sslCertFile = process.argv[5],
+    proxyTable = process.argv[6],
+    redirect = process.argv[7] || false,
+    isRawIPv6;
+
+if (process.argv.length < 7) {
+    console.error("Too few arguments. Exiting.");
+  process.exit(1);
+}
+
+isRawIPv6 = function checkipv6(str) {
+  // Inspired by http://forums.intermapper.com/viewtopic.php?t=452
+  return (/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/.test(str));
+}(listenInterface);
+
+/**
+ * Dummy middleware that throws 404 not found. Does not contain websocket
+ * middleware.
+ */
+var middlewareNotFound = function(req, res, proxy) {
+  res.statusCode = 404;
+  res.setHeader('Content-Type', 'text/plain');
+  res.end('This URL is not known. Please check your URL or contact your ' +
+      'SlapOS administrator.');
+};
+
+/**
+ * Create server
+ */
+var proxyServer = httpProxy.createServer(
+  // We declare our proxyByUrl middleware
+  proxyByUrl(proxyTable),
+  // Then we add your dummy middleware, called when proxyByUrl doesn't find url.
+  middlewareNotFound,
+  // And we set HTTPS options for server. HTTP will be forbidden.
+  {
+    https: {
+      key: fs.readFileSync(
+        sslKeyFile,
+        'utf8'
+      ),
+      cert: fs.readFileSync(
+        sslCertFile,
+        'utf8'
+      )
+    },
+    source: {
+    host: listenInterface,
+    port: port
+  }}
+);
+
+console.log('HTTPS server starting and trying to listen on ' +
+            listenInterface + ':' + port);
+// Release the beast.
+proxyServer.listen(port, listenInterface);
+
+// Dummy HTTP server redirecting to HTTPS. Only has sense if we can use port 80
+if (redirect === '1') {
+  console.log('HTTP redirect server starting and trying to listen on ' +
+              listenInterface + ':' + httpPort);
+  try {
+    var httpPort = 80;
+    http.createServer(function(req, res) {
+      var url;
+      if (isRawIPv6 === true) {
+        url = 'https://[' + listenInterface + ']';
+      } else {
+        url = 'https://' + listenInterface;
+      }
+      // If non standard port : need to specify it
+      if (port !== 443) {
+        url = url + ':' + port;
+      }
+      // Add last part of URL
+      url = url + req.url;
+      console.log(url);
+      // Anwser "permanently redirected"
+      res.statusCode = 301;
+      res.setHeader('Location', url);
+      res.end();
+    }).listen(httpPort, listenInterface);
+  } catch (error) {
+    console.log('Couldn\'t start plain HTTP redirection server : ' + error)
+  }
+}
diff --git a/slapos/recipe/kvm_frontend/template/nodejs_run.in b/slapos/recipe/kvm_frontend/template/nodejs_run.in
new file mode 100644
index 0000000000000000000000000000000000000000..8384f05deba13f9e6be0a9417dd54a2b945a9eb9
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/template/nodejs_run.in
@@ -0,0 +1,5 @@
+#!%(shell_path)s
+# BEWARE: This file is operated by slapgrid
+# BEWARE: It will be overwritten automatically
+export NODE_PATH=%(node_env)s
+exec %(node_path)s %(conf_path)s %(ip)s %(port)s %(key)s %(certificate)s %(map_path)s %(plain_http)s
diff --git a/slapos/recipe/lamp/__init__.py b/slapos/recipe/lamp/__init__.py
index 55ae815bcc6c859406b57b352b13c3c0eeef2bfa..710cc076032c2692df652dd9e354ef53695572b0 100644
--- a/slapos/recipe/lamp/__init__.py
+++ b/slapos/recipe/lamp/__init__.py
@@ -33,6 +33,8 @@ import sys
 import zc.recipe.egg
 import urlparse
 
+# Warning : this recipe is deprecated and has been replaced by apachephp.
+
 class BaseRecipe(BaseSlapRecipe):
   def getTemplateFilename(self, template_name):
     return pkg_resources.resource_filename(__name__,
diff --git a/slapos/recipe/librecipe/generic.py b/slapos/recipe/librecipe/generic.py
index 86829c378ceea8061730cf1add7811beff75173c..19729c6352d1018967c7c586c5999d9b646c5776 100644
--- a/slapos/recipe/librecipe/generic.py
+++ b/slapos/recipe/librecipe/generic.py
@@ -38,6 +38,7 @@ import zc.buildout
 class GenericBaseRecipe(object):
 
   TRUE_VALUES = ['y', 'yes', '1', 'true']
+  FALSE_VALUES = ['n', 'no', '0', 'false']
 
   def __init__(self, buildout, name, options):
     """Recipe initialisation"""
diff --git a/slapos/recipe/nbdserver/__init__.py b/slapos/recipe/nbdserver/__init__.py
index 3b7e654b90dfa737f885b30c31c102b9ff068474..6f35add02be7a81cd558b7464ca6c125eb7469b0 100644
--- a/slapos/recipe/nbdserver/__init__.py
+++ b/slapos/recipe/nbdserver/__init__.py
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
+# Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
 #
 # WARNING: This program as such is intended to be used by professional
 # programmers who take the whole responsibility of assessing all potential
@@ -24,59 +24,29 @@
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #
 ##############################################################################
-import os
+from slapos.recipe.librecipe import GenericBaseRecipe
 import binascii
-from slapos.recipe.librecipe import BaseSlapRecipe
-
-import pkg_resources
-
-class Recipe(BaseSlapRecipe):
-
-  def _install(self):
-
-    # Image path
-    cdrom_iso = os.path.join(self.data_root_directory, 'cdrom.iso')
-
-    #Get the IP list
-    ip = self.getGlobalIPv6Address()
-    http_port = 9999
-    nbd_port = 1024
-
-    # Instanciate onetimeupload
-    onetimeupload_config = {}
-    onetimeupload_config.update(self.options)
-    onetimeupload_config['port'] = http_port
-    onetimeupload_config['ip'] = ip
-    onetimeupload_config['image'] = cdrom_iso
-    onetimeupload_config['key'] = binascii.hexlify(os.urandom(24))
-    onetimeupload_config['log_path'] = os.path.join(self.log_directory, 
-                                                    'onetimeupload.log')
-
-    wrapper_template_location = pkg_resources.resource_filename(
-                                       __name__, os.path.join(
-                                       'template', 'onetimeupload_run.in'))
-    onetimeupload_runner_path = self.createRunningWrapper("onetimeupload",
-        self.substituteTemplate(wrapper_template_location, 
-                                onetimeupload_config))
-
-    # Instanciate qemu
-    qemu_config = {}
-    qemu_config.update(self.options)
-    qemu_config['ip'] = ip
-    qemu_config['port'] = nbd_port
-    qemu_config['image'] = cdrom_iso
-
-    wrapper_template_location = pkg_resources.resource_filename(
-                                             __name__, os.path.join(
-                                             'template', 'nbdserver_run.in'))
-    nbdserver_runner_path = self.createRunningWrapper("nbdserver",
-        self.substituteTemplate(wrapper_template_location, qemu_config))
-
-    # Publish connection dict
-    self.computer_partition.setConnectionDict(dict(
-      upload_connection_string="https://[%s]:%s/" % (ip, http_port),
-      upload_key=onetimeupload_config['key'],
-      nbd_connection_string="nbd:[%s]:%s" % (ip, nbd_port),
-      ))
-
-    return [onetimeupload_runner_path, nbdserver_runner_path]
+import os
+import sys
+
+class Recipe(GenericBaseRecipe):
+  """
+  nbd instance configuration.
+  """
+
+  def install(self):
+    config = dict(
+      ip=self.options['ip'],
+      port=self.options['port'],
+      image_path=self.options['image-path'],
+      qemu_path=self.options['qemu-path'],
+      shell_path=self.options['shell-path'],
+    )
+
+    # Runners
+    runner_path = self.createExecutable(
+      self.options['path'],
+      self.substituteTemplate(self.getTemplateFilename('nbdserver_run.in'),
+                              config))
+
+    return [runner_path]
diff --git a/slapos/recipe/nbdserver/template/nbdserver_run.in b/slapos/recipe/nbdserver/template/nbdserver_run.in
index ca93f73b70fd273c59c559104e05eb7cdab3f968..dd7068a0e0e39f1f5e78e5f6fddb5fd5a9f76d48 100644
--- a/slapos/recipe/nbdserver/template/nbdserver_run.in
+++ b/slapos/recipe/nbdserver/template/nbdserver_run.in
@@ -1,6 +1,6 @@
-#!/bin/sh
+#!%(shell_path)s
 # BEWARE: This file is operated by slapgrid
 # BEWARE: It will be overwritten automatically
 
 # 32767 is the maximum number of connections allowed by the nbd server
-exec %(qemu_path)s -b %(ip)s %(image)s -r -t -p %(port)s -e 32767
+exec %(qemu_path)s -b %(ip)s %(image_path)s -r -t -p %(port)s -e 32767
diff --git a/slapos/recipe/novnc/__init__.py b/slapos/recipe/novnc/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a24588d54c40e571e5c97ab508506356429f096f
--- /dev/null
+++ b/slapos/recipe/novnc/__init__.py
@@ -0,0 +1,54 @@
+##############################################################################
+#
+# Copyright (c) 2011 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+from slapos.recipe.librecipe import GenericBaseRecipe
+import binascii
+import os
+import sys
+
+class Recipe(GenericBaseRecipe):
+  """
+  novnc instance configuration.
+  """
+
+  def install(self):
+    runner_path = self.createPythonScript(
+      self.options['path'],
+      'slapos.recipe.librecipe.execute.execute_wait',
+      [[
+        self.options['websockify-path'],
+        '--web',
+        self.options['novnc-location'],
+        '--key=%s' % self.options['ssl-key-path'],
+        '--cert=%s' % self.options['ssl-cert-path'],
+        '--ssl-only',
+        '%s:%s' % (self.options['ip'], self.options['port']),
+        '%s:%s' % (self.options['vnc-ip'], self.options['vnc-port']),
+      ],
+      [self.options['ssl-key-path'], self.options['ssl-cert-path']]],
+    )
+
+    return [runner_path]
diff --git a/slapos/recipe/request.py b/slapos/recipe/request.py
index 1aeb72fbb28d08b2cad311628e1171ec93875d56..52c8454f9bb2fa9c836c30d7e12b31bf93204fac 100644
--- a/slapos/recipe/request.py
+++ b/slapos/recipe/request.py
@@ -72,14 +72,15 @@ class Recipe(object):
         partition_parameter_kw[config_parameter] = \
             options['config-%s' % config_parameter]
 
-    instance = self.request(options['software-url'], software_type,
+    self.instance = self.request(options['software-url'], software_type,
       options.get('name', name), partition_parameter_kw=partition_parameter_kw,
       filter_kw=filter_kw, shared=self.isSlave)
 
     self.failed = None
     for param in self.return_parameters:
       try:
-        options['connection-%s' % param] = str(instance.getConnectionParameter(param))
+        options['connection-%s' % param] = str(
+            self.instance.getConnectionParameter(param))
       except slapmodule.NotFoundError:
         options['connection-%s' % param] = ''
         if self.failed is None:
@@ -87,7 +88,16 @@ class Recipe(object):
 
   def install(self):
     if self.failed is not None:
-      raise KeyError("Connection parameter %r not found." % self.failed)
+      # Check instance status to know if instance has been deployed
+      try:
+        status = self.instance.getState()
+      except slapmodule.NotFoundError:
+        status = "not ready yet, please try again"
+      # XXX-Cedric : currently raise an error. So swallow it...
+      except AttributeError:
+        status = "unknown"
+      raise KeyError("Connection parameter %s not found. "
+          "Status of requested instance is : %s." % (self.failed, status))
     return []
 
   update = install
diff --git a/slapos/recipe/slaprunner/__init__.py b/slapos/recipe/slaprunner/__init__.py
index 3c74cb5b21bde688d1163612fcff06280a629428..347c13ef9e06c238dc01e6ef326336a684ce4078 100644
--- a/slapos/recipe/slaprunner/__init__.py
+++ b/slapos/recipe/slaprunner/__init__.py
@@ -40,6 +40,7 @@ class Recipe(BaseSlapRecipe):
     ipv6 = self.getGlobalIPv6Address()
     proxy_port = '50000'
     runner_port = '50000'
+    cloud9_port = '30000'
     workdir = self.createDataDirectory('runner')
     software_root = os.path.join(workdir, 'software')
     instance_root = os.path.join(workdir, 'instance')
@@ -48,7 +49,7 @@ class Recipe(BaseSlapRecipe):
         instance_root=instance_root,
         master_url='http://%s:%s/' % (ipv4, proxy_port),
         computer_id='slaprunner',
-        partition_amount=2,
+        partition_amount=7,
         slapgrid_sr=self.options['slapgrid_sr'],
         slapgrid_cp=self.options['slapgrid_cp'],
         slapproxy=self.options['slapproxy'],
@@ -64,22 +65,33 @@ class Recipe(BaseSlapRecipe):
         proxy_port=proxy_port,
         proxy_database=os.path.join(workdir, 'proxy.db'),
 	git=self.options['git'],
+	cloud9_url='http://[%s]:%s' % (ipv6, cloud9_port),
 	ssh_client=self.options['ssh_client'],
 	public_key=self.options['public_key'],
-	private_key=self.options['private_key']
+	private_key=self.options['private_key'],
+
     )
     config_file = self.createConfigurationFile('slapos.cfg',
         self.substituteTemplate(pkg_resources.resource_filename(__name__,
           'template/slapos.cfg.in'), configuration))
     self.path_list.append(config_file)
-    
+
     environment = dict(
         PATH=os.path.dirname(self.options['git']) + ':' + os.environ['PATH'],
         GIT_SSH=self.options['ssh_client']
     )
+    workdir = os.path.join(workdir, 'project')
+    if not os.path.exists(workdir):
+      os.mkdir(workdir)
     launch_args = [self.options['slaprunner'].strip(), config_file, '--debug']
+    cloud9_args = [self.options['node-bin'].strip(), self.options['cloud9'].strip(),
+                   '-l', ipv6, '-p', cloud9_port, '-w', workdir]
     self.path_list.extend(zc.buildout.easy_install.scripts([('slaprunner',
       'slapos.recipe.librecipe.execute', 'executee')], self.ws, sys.executable,
       self.wrapper_directory, arguments=[launch_args, environment]))
-    self.setConnectionDict(dict(url='http://[%s]:%s' % (ipv6, runner_port)))
+    self.path_list.extend(zc.buildout.easy_install.scripts([('cloud9IDE',
+      'slapos.recipe.librecipe.execute', 'executee')], self.ws, sys.executable,
+      self.wrapper_directory, arguments=[cloud9_args, environment]))
+    self.setConnectionDict(dict(slaprunner_url='http://[%s]:%s' % (ipv6, runner_port),
+                            cloud9_url='http://[%s]:%s' % (ipv6, cloud9_port)))
     return self.path_list
diff --git a/slapos/recipe/slaprunner/template/slapos.cfg.in b/slapos/recipe/slaprunner/template/slapos.cfg.in
index 5dc00060eccae8c5842ae6500da3459e4bb9e200..a10da0cf5e11a414df22ed6f8dd6adb7955b6e4b 100644
--- a/slapos/recipe/slaprunner/template/slapos.cfg.in
+++ b/slapos/recipe/slaprunner/template/slapos.cfg.in
@@ -31,3 +31,6 @@ private_key = %(private_key)s
 
 [gitclient]
 git = %(git)s
+
+[cloud9_IDE]
+cloud9 = %(cloud9_url)s
\ No newline at end of file
diff --git a/slapos/recipe/softwaretype.py b/slapos/recipe/softwaretype.py
index 43ff451f1e8a30c1dfba1b107f22abdb8ed7d655..825a153f1a91a5a48729b6f76232e83a15a53ad0 100644
--- a/slapos/recipe/softwaretype.py
+++ b/slapos/recipe/softwaretype.py
@@ -64,6 +64,15 @@ class Recipe:
     # XXX: Lack checking for globality of address
     return self._getIpAddress(netaddr.valid_ipv6)
 
+  def getNetworkInterface(self):
+    """Returns the network interface available on partition"""
+    if not 'ip_list' in self.parameter_dict:
+      raise AttributeError
+    for name, ip in self.parameter_dict['ip_list']:
+      if name:
+        return name
+    raise AttributeError, "Not network interface found"
+
   def install(self):
     slap = slapos.slap.slap()
     slap_connection = self.buildout['slap_connection']
@@ -111,6 +120,8 @@ class Recipe:
                  self.getLocalIPv4Address())
     buildout.set('slap-network-information', 'global-ipv6',
                  self.getGlobalIPv6Address())
+    buildout.set('slap-network-information', 'network-interface', 
+                 self.getNetworkInterface())
 
     # Copy/paste slap_connection
     buildout.add_section('slap-connection')
diff --git a/software/kvm/README.txt b/software/kvm/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..17e9a2cdf5ade017b1a82899e829168524d47291
--- /dev/null
+++ b/software/kvm/README.txt
@@ -0,0 +1,80 @@
+kvm
+===
+
+Introduction
+------------
+
+This software release is used to deploy KVM instances, NBD instances and
+Frontend instances of KVM.
+
+Examples
+--------
+
+The following examples listhow to request different possible instances of KVM
+Software Release from slap console or command line.
+
+KVM instance (1GB of RAM, 10GB of SSD, one core)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Note that the KVM instance will request a frontend slave instance in order
+to be accessible from IPv4.
+
+KVM instance needs a NBD to fetch disk image at first boot. Working NBD IP/port
+has to be specified.
+
+::
+  myawesomekvm = request(
+      software_release=kvm,
+      partition_reference="myawesomekvm",
+      partition_parameter_kw={
+          "ndb_ip":"2a01:e35:2e27:460:e2cb:4eff:fed9:48dc",
+          "ndb_port": 1024
+      }
+  )
+
+
+KVM+ instance (2GB of RAM, 20GB of SSD, two cores)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+  myevenmoreawesomekvm = request(
+      software_release=kvm,
+      partition_reference="myevenmoreawesomekvm",
+      partition_parameter_kw={
+          "ndb_ip":"2a01:e35:2e27:460:e2cb:4eff:fed9:48dc",
+          "ndb_port": 1024
+      },
+      software_type="kvm+",
+  )
+
+
+NBD instance
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This type of instance will allow to host a disk image that will be used by
+any KVM instance.
+
+::
+  mynbd = request(
+      software_release=kvm,
+      partition_reference="mynbd",
+      software_type="nbd",
+  )
+
+
+KVM Frontend Master Instance (will host all frontend Slave Instances)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This type of instance will allow to host any frontend slave instance requested
+by KVM instances. Slave instances (and thus KVM instance) will be accessible
+at : https://mydomain.com/instancereference .
+
+::
+  mykvmfrontend = request(
+      software_release=kvm,
+      partition_reference="mykvmfrontend",
+      partition_parameter_kw={
+          "domain":"mydomain.com"
+      },
+      software_type="frontend",
+  )
diff --git a/software/kvm/instance-frontend.cfg b/software/kvm/instance-frontend.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..277674c850734d60e80fecbc604b724bf59aa44b
--- /dev/null
+++ b/software/kvm/instance-frontend.cfg
@@ -0,0 +1,148 @@
+#############################
+#
+# Instanciate kvm frontend
+#
+#############################
+[buildout]
+parts =
+  logrotate
+#   logrotate-entry-frontend
+  cron
+  cron-entry-logrotate
+  ca-frontend
+  certificate-authority
+  frontend-promise
+  publish-kvm-frontend-connection-information
+
+eggs-directory = ${buildout:eggs-directory}
+develop-eggs-directory = ${buildout:develop-eggs-directory}
+offline = true
+
+[rootdirectory]
+recipe = slapos.cookbook:mkdirectory
+etc = $${buildout:directory}/etc
+bin = $${buildout:directory}/bin
+srv = $${buildout:directory}/srv
+var = $${buildout:directory}/var
+
+[basedirectory]
+recipe = slapos.cookbook:mkdirectory
+services = $${rootdirectory:etc}/run
+promises = $${rootdirectory:etc}/promise
+nodejs-conf = $${rootdirectory:etc}/nodejs
+run = $${rootdirectory:var}/run
+log = $${rootdirectory:var}/log
+ca-dir = $${rootdirectory:srv}/ssl
+backup = $${rootdirectory:srv}/backup
+
+[directory]
+recipe = slapos.cookbook:mkdirectory
+cron-entries = $${rootdirectory:etc}/cron.d
+crontabs = $${rootdirectory:etc}/crontabs
+cronstamps = $${rootdirectory:etc}/cronstamps
+ca-dir = $${rootdirectory:srv}/ssl
+logrotate-backup = $${basedirectory:backup}/logrotate
+logrotate-entries = $${rootdirectory:etc}/logrotate.d
+
+[frontend-instance]
+recipe = slapos.cookbook:kvm.frontend
+domain = $${ca-frontend:name}
+# port = $${slap-parameter:port}
+ip = $${slap-network-information:global-ipv6}
+port = $${slap-parameter:port}
+http-redirection = $${slap-parameter:http-redirection}
+ssl-key-path = $${ca-frontend:key-file}
+ssl-cert-path = $${ca-frontend:cert-file}
+slave-instance-list = $${slap-parameter:slave_instance_list}
+map-path = $${basedirectory:nodejs-conf}/proxy_table.json
+conf-path = $${basedirectory:nodejs-conf}/kvm-proxy.js
+wrapper-path = $${rootdirectory:bin}/kvm_frontend
+node-binary = ${nodejs:location}/bin/node
+node-env = ${buildout:parts-directory}:${npm-modules:location}/node_modules
+shell-path = ${dash:location}/bin/dash
+
+[frontend-promise]
+recipe = slapos.cookbook:check_port_listening
+path = $${basedirectory:promises}/frontend_promise
+hostname = $${frontend-instance:ip}
+port = $${frontend-instance:port}
+
+[certificate-authority]
+recipe = slapos.cookbook:certificate_authority
+openssl-binary = ${openssl:location}/bin/openssl
+ca-dir = $${basedirectory:ca-dir}
+requests-directory = $${cadirectory:requests}
+wrapper = $${basedirectory:services}/certificate_authority
+ca-private = $${cadirectory:private}
+ca-certs = $${cadirectory:certs}
+ca-newcerts = $${cadirectory:newcerts}
+ca-crl = $${cadirectory:crl}
+
+[cadirectory]
+recipe = slapos.cookbook:mkdirectory
+requests = $${basedirectory:ca-dir}/requests/
+private = $${basedirectory:ca-dir}/private/
+certs = $${basedirectory:ca-dir}/certs/
+newcerts = $${basedirectory:ca-dir}/newcerts/
+crl = $${basedirectory:ca-dir}/crl/
+
+[ca-frontend]
+<= certificate-authority
+recipe = slapos.cookbook:certificate_authority.request
+key-file = $${basedirectory:nodejs-conf}/nodejs.key
+cert-file = $${basedirectory:nodejs-conf}/nodejs.crt
+executable = $${frontend-instance:wrapper-path}
+wrapper = $${basedirectory:services}/nodejs
+# Put domain name
+name = $${slap-parameter:domain}
+
+[cron]
+recipe = slapos.cookbook:cron
+dcrond-binary = ${dcron:location}/sbin/crond
+cron-entries = $${directory:cron-entries}
+crontabs = $${directory:crontabs}
+cronstamps = $${directory:cronstamps}
+catcher = $${cron-simplelogger:wrapper}
+binary = $${basedirectory:services}/crond
+
+[cron-simplelogger]
+recipe = slapos.cookbook:simplelogger
+wrapper = $${rootdirectory:bin}/cron_simplelogger
+log = $${basedirectory:log}/cron.log
+
+[cron-entry-logrotate]
+<= cron
+recipe = slapos.cookbook:cron.d
+name = logrotate
+frequency = 0 0 * * *
+command = $${logrotate:wrapper}
+
+[logrotate]
+recipe = slapos.cookbook:logrotate
+# Binaries
+logrotate-binary = ${logrotate:location}/usr/sbin/logrotate
+gzip-binary = ${gzip:location}/bin/gzip
+gunzip-binary = ${gzip:location}/bin/gunzip
+# Directories
+wrapper = $${rootdirectory:bin}/logrotate
+conf = $${rootdirectory:etc}/logrotate.conf
+logrotate-entries = $${directory:logrotate-entries}
+backup = $${directory:logrotate-backup}
+state-file = $${rootdirectory:srv}/logrotate.status
+
+[publish-kvm-frontend-connection-information]
+recipe = slapos.cookbook:publish
+ip = $${frontend-instance:ip}
+port = $${frontend-instance:port}
+
+[slap-parameter]
+# Default value if no port is specified
+port = 4443
+http-redirection = 0
+
+# [logrotate-entry-frontend]
+# <= logrotate
+# recipe = slapos.cookbook:logrotate.d
+# name = frontend
+# log = $${mariadb-instance:error-log} $${mariadb-instance:slow-query-log}
+# post = $${mariadb-instance:mysql-binary} --no-defaults -B --socket=$${mariadb-instance:socket} -e "FLUSH LOGS"
diff --git a/software/kvm/instance-kvm.cfg b/software/kvm/instance-kvm.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bfd9715ba9cbaecca2f66a5bf87a8e699daa6ea8
--- /dev/null
+++ b/software/kvm/instance-kvm.cfg
@@ -0,0 +1,136 @@
+#############################
+#
+# Instanciate kvm
+#
+#############################
+[buildout]
+parts =
+  request-slave-frontend
+  certificate-authority
+  kvm-promise
+  novnc-promise
+  publish-kvm-connection-information
+
+eggs-directory = ${buildout:eggs-directory}
+develop-eggs-directory = ${buildout:develop-eggs-directory}
+offline = true
+
+[rootdirectory]
+recipe = slapos.cookbook:mkdirectory
+etc = $${buildout:directory}/etc
+bin = $${buildout:directory}/bin
+srv = $${buildout:directory}/srv
+var = $${buildout:directory}/var
+
+[basedirectory]
+recipe = slapos.cookbook:mkdirectory
+services = $${rootdirectory:etc}/run
+promises = $${rootdirectory:etc}/promise
+novnc-conf = $${rootdirectory:etc}/novnc
+run = $${rootdirectory:var}/run
+ca-dir = $${rootdirectory:srv}/ssl
+
+[create-mac]
+recipe = slapos.cookbook:generate.mac
+
+[kvm-instance]
+recipe = slapos.cookbook:kvm
+vnc-ip = $${slap-network-information:local-ipv4}
+vnc-port = 5901
+nbd-ip = $${slap-parameter:nbd_ip}
+nbd-port = $${slap-parameter:nbd_port}
+tap = $${slap-network-information:network-interface}
+disk-path = $${rootdirectory:srv}/virtual.qcow2
+disk-size = 10
+socket-path = $${rootdirectory:var}/qmp_socket
+pid-path = $${basedirectory:run}/pid_file
+smp-count = 1
+ram-size = 1024
+mac-address = $${create-mac:mac-address}
+runner-path = $${basedirectory:services}/kvm
+controller-path = $${basedirectory:services}/kvm_controller
+shell-path = ${dash:location}/bin/dash
+qemu-path = ${kvm:location}/bin/qemu-system-x86_64
+qemu-img-path = ${kvm:location}/bin/qemu-img
+
+[kvm-promise]
+recipe = slapos.cookbook:check_port_listening
+path = $${basedirectory:promises}/vnc_promise
+hostname = $${kvm-instance:vnc-ip}
+port = $${kvm-instance:vnc-port}
+
+[novnc-instance]
+recipe = slapos.cookbook:novnc
+path = $${ca-novnc:executable}
+ip = $${slap-network-information:global-ipv6}
+port = 6080
+vnc-ip = $${kvm-instance:vnc-ip}
+vnc-port = $${kvm-instance:vnc-port}
+novnc-location = ${noVNC:location}
+websockify-path = ${buildout:directory}/bin/websockify
+ssl-key-path = $${ca-novnc:key-file}
+ssl-cert-path = $${ca-novnc:cert-file}
+
+[certificate-authority]
+recipe = slapos.cookbook:certificate_authority
+openssl-binary = ${openssl:location}/bin/openssl
+ca-dir = $${basedirectory:ca-dir}
+requests-directory = $${cadirectory:requests}
+wrapper = $${basedirectory:services}/certificate_authority
+ca-private = $${cadirectory:private}
+ca-certs = $${cadirectory:certs}
+ca-newcerts = $${cadirectory:newcerts}
+ca-crl = $${cadirectory:crl}
+
+[cadirectory]
+recipe = slapos.cookbook:mkdirectory
+requests = $${basedirectory:ca-dir}/requests/
+private = $${basedirectory:ca-dir}/private/
+certs = $${basedirectory:ca-dir}/certs/
+newcerts = $${basedirectory:ca-dir}/newcerts/
+crl = $${basedirectory:ca-dir}/crl/
+
+[ca-novnc]
+<= certificate-authority
+recipe = slapos.cookbook:certificate_authority.request
+key-file = $${basedirectory:novnc-conf}/novnc.key
+cert-file = $${basedirectory:novnc-conf}/novnc.crt
+executable = $${rootdirectory:bin}/novnc
+wrapper = $${basedirectory:services}/websockify
+
+[novnc-promise]
+recipe = slapos.cookbook:check_port_listening
+path = $${basedirectory:promises}/novnc_promise
+hostname = $${novnc-instance:ip}
+port = $${novnc-instance:port}
+
+[kvm-monitor]
+recipe = slapos.cookbook:generic.slapmonitor
+db-path = $${rootdirectory:srv}/slapmonitor_database
+
+[request-common]
+recipe = slapos.cookbook:request
+software-url = $${slap-connection:software-release-url}
+sla = computer_guid
+sla-computer_guid = $${slap-connection:computer-id}
+server-url = $${slap-connection:server-url}
+key-file = $${slap-connection:key-file}
+cert-file = $${slap-connection:cert-file}
+computer-id = $${slap-connection:computer-id}
+partition-id = $${slap-connection:partition-id}
+
+[request-slave-frontend]
+<=request-common
+name = SlaveFrontend
+software-type = frontend
+slave = true
+config = host port
+config-host = $${novnc-instance:ip}
+config-port = $${novnc-instance:port}
+return = url resource port domainname
+
+[publish-kvm-connection-information]
+recipe = slapos.cookbook:publish
+backend_url = https://[$${novnc-instance:ip}]:$${novnc-instance:port}/vnc_auto.html?host=[$${novnc-instance:ip}]&port=$${novnc-instance:port}&encrypt=1
+url = $${request-slave-frontend:connection-url}/vnc_auto.html?host=$${request-slave-frontend:connection-domainname}&port=$${request-slave-frontend:connection-port}&encrypt=1&path=$${request-slave-frontend:connection-resource}
+password = $${kvm-instance:passwd}
diff --git a/software/kvm/instance-kvmplus.cfg b/software/kvm/instance-kvmplus.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..1babe8bf0c4de089a06c9505d9148b0bf03b4a87
--- /dev/null
+++ b/software/kvm/instance-kvmplus.cfg
@@ -0,0 +1,27 @@
+#############################
+#
+# Instanciate kvm+
+#
+#############################
+[buildout]
+extends = instance-kvm.cfg
+
+[kvm-instance]
+recipe = slapos.cookbook:kvm
+vnc-ip = $${slap-network-information:local-ipv4}
+vnc-port = 5901
+nbd-ip = $${slap-parameter:nbd_ip}
+nbd-port = $${slap-parameter:nbd_port}
+tap = $${slap-network-information:network-interface}
+disk-path = $${rootdirectory:srv}/virtual.qcow2
+disk-size = 20
+socket-path = $${rootdirectory:var}/qmp_socket
+pid-path = $${basedirectory:run}/pid_file
+smp-count = 2
+ram-size = 2048
+mac-address = $${create-mac:mac-address}
+runner-path = $${basedirectory:services}/kvm
+controller-path = $${basedirectory:services}/kvm_controller
+shell-path = ${dash:location}/bin/dash
+qemu-path = ${kvm:location}/bin/qemu-system-x86_64
+qemu-img-path = ${kvm:location}/bin/qemu-img
diff --git a/software/kvm/instance-nbd.cfg b/software/kvm/instance-nbd.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9df5cfcf508fd4dab3bb10e32eadbc041979c748
--- /dev/null
+++ b/software/kvm/instance-nbd.cfg
@@ -0,0 +1,63 @@
+#############################
+#
+# Instanciate nbdserver
+#
+#############################
+[buildout]
+parts =
+  nbd-promise
+  onetimeupload-promise
+  publish-connection-information
+
+eggs-directory = ${buildout:eggs-directory}
+develop-eggs-directory = ${buildout:develop-eggs-directory}
+offline = true
+
+[rootdirectory]
+recipe = slapos.cookbook:mkdirectory
+etc = $${buildout:directory}/etc
+srv = $${buildout:directory}/srv
+log = $${buildout:directory}/log
+
+[basedirectory]
+recipe = slapos.cookbook:mkdirectory
+services = $${rootdirectory:etc}/run
+promises = $${rootdirectory:etc}/promise
+
+[nbd-instance]
+recipe = slapos.cookbook:nbdserver
+ip = $${slap-network-information:global-ipv6}
+port = 1024
+image-path = $${onetimeupload-instance:image-path}
+qemu-path = ${kvm:location}/bin/qemu-nbd
+shell-path = ${dash:location}/bin/dash
+# XXX TODO: Wait for the iso to be uploaded (execute_wait)
+path = $${basedirectory:services}/nbdserver
+
+[nbd-promise]
+recipe = slapos.cookbook:check_port_listening
+path = $${basedirectory:promises}/nbd_promise
+hostname = $${nbd-instance:ip}
+port = $${nbd-instance:port}
+
+[onetimeupload-instance]
+recipe = slapos.cookbook:generic.onetimeupload
+ip = $${slap-network-information:global-ipv6}
+port = 9999
+image-path = $${rootdirectory:srv}/cdrom.iso
+log-path = $${rootdirectory:log}/onetimeupload.log
+shell-path = ${dash:location}/bin/dash
+onetimeupload-path = ${buildout:bin-directory}/onetimeupload
+path = $${basedirectory:services}/onetimeupload
+
+[onetimeupload-promise]
+recipe = slapos.cookbook:check_port_listening
+path = $${basedirectory:promises}/onetimeupload_promise
+hostname = $${onetimeupload-instance:ip}
+port = $${onetimeupload-instance:port}
+
+[publish-connection-information]
+recipe = slapos.cookbook:publish
+nbd_url = nbd://[$${nbd-instance:ip}]:$${nbd-instance:port}
+upload_url = http://[$${onetimeupload-instance:ip}]:$${onetimeupload-instance:port}/
+upload_key = $${onetimeupload-instance:key}
diff --git a/software/kvm/instance.cfg b/software/kvm/instance.cfg
index 65041a3f9aff4c2f506ad283d6b5520aec5233ce..8d44ea5663dd9c71d101a97d0a149a16602a6964 100644
--- a/software/kvm/instance.cfg
+++ b/software/kvm/instance.cfg
@@ -1,22 +1,24 @@
 [buildout]
 parts =
-  kvminstance
+  switch-softwaretype
 
 eggs-directory = ${buildout:eggs-directory}
-develop-eggs-directory = ${buildout:develop-eggs-directory} 
+develop-eggs-directory = ${buildout:develop-eggs-directory}
+offline = true
 
-[kvminstance]
-recipe = slapos.cookbook:kvm
-qemu_path = ${kvm:location}/bin/qemu-system-x86_64
-qemu_img_path = ${kvm:location}/bin/qemu-img
-#slapmonitor_path = ${buildout:bin-directory}/slapmonitor
-#slapreport_path = ${buildout:bin-directory}/slapreport
-websockify = ${buildout:directory}/bin/websockify
-noVNC_location = ${noVNC:location}
-openssl_binary = ${openssl:location}/bin/openssl
-rdiff_backup_binary = ${buildout:bin-directory}/rdiff-backup
-dcrond_binary = ${dcron:location}/sbin/crond
+[switch-softwaretype]
+recipe = slapos.cookbook:softwaretype
+default = ${template-kvm:output}
+kvm = ${template-kvm:output}
+kvm+ = ${template-kvmplus:output}
+nbd = ${template-nbd:output}
+frontend = ${template-frontend:output}
 
-smp_count = 1
-ram_size = 1024
-disk_size = 10
+[slap-connection]
+# part to migrate to new - separated words
+computer-id = $${slap_connection:computer_id}
+partition-id = $${slap_connection:partition_id}
+server-url = $${slap_connection:server_url}
+software-release-url = $${slap_connection:software_release_url}
+key-file = $${slap_connection:key_file}
+cert-file = $${slap_connection:cert_file}
diff --git a/software/kvm/software.cfg b/software/kvm/software.cfg
index 541d80f78fdac489e51223edd05641e564ccdf69..5100d46b330b8ffff16631e5af90e1494637068a 100644
--- a/software/kvm/software.cfg
+++ b/software/kvm/software.cfg
@@ -3,46 +3,37 @@ extensions =
   buildout-versions
   
 extends =
+  ../../component/gzip/buildout.cfg
   ../../component/dcron/buildout.cfg
+  ../../component/logrotate/buildout.cfg
   ../../component/git/buildout.cfg
   ../../component/gnutls/buildout.cfg
   ../../component/libpng/buildout.cfg
   ../../component/libuuid/buildout.cfg
-  ../../component/lxml-python/buildout.cfg
   ../../component/noVNC/buildout.cfg
   ../../component/openssl/buildout.cfg
-  ../../component/python-2.7/buildout.cfg
-  ../../component/rdiff-backup/buildout.cfg
-  ../../stack/shacache-client.cfg
+  ../../component/dash/buildout.cfg
+  ../../component/lxml-python/buildout.cfg
+  ../../stack/nodejs.cfg
 
 develop =
   ${:parts-directory}/websockify
 
 parts =
   template
+  dash
   kvm
   eggs
   check-local-eggs
-
-find-links +=
-  http://www.nexedi.org/static/packages/source/slapos.buildout/
+  nodejs
+  http-proxy
+  proxy-by-url
+  npm-modules
+  dcron
+  logrotate
 
 versions = versions
 
-# Use only quite well working sites.
-allow-hosts =
-  *.nexedi.org
-  *.python.org
-  *.sourceforge.net
-  alastairs-place.net
-  dist.repoze.org
-  effbot.org
-  github.com
-  peak.telecommunity.com
-  psutil.googlecode.com
-  www.dabeaz.com
-  www.owlfish.com
-
 #XXX-Cedric : Currently, one can only access to KVM using noVNC.
 #             Ideally one should be able to access KVM by using either NoVNC or VNC.
 #             Problem is : no native crypto support in web browsers. So we have to disable ssl
@@ -54,15 +45,14 @@ allow-hosts =
 #            Websockify (socket <-> websocket proxy server) when it is ready.
 #            May solve previous XXX depending on the implementation.
 
-#XXX-Cedric: Check status of 
-#            https://www.tiolive.com/nexedi/bug_module/20110819-11F4F70 for
-#            Chrome >= 14 and Firefox >=7 can access to noVNC. (should be solved)
-
 #XXX-Cedric : add list of keyboard layouts (azerty/us querty/...) parameter to qemu
 
 [kvm]
 recipe = hexagonit.recipe.cmmi
 url = http://downloads.sourceforge.net/project/kvm/qemu-kvm/0.15.1/qemu-kvm-0.15.1.tar.gz
+# XXX-Cedric : Upgrade to 1.0
+# url = http://downloads.sourceforge.net/project/kvm/qemu-kvm/1.0/qemu-kvm-1.0.tar.gz
+# md5sum = 00a825db46a70ba8ef9fc95da9cc7c1e
 md5sum = 8800a7d6b3aa4a168ea7f78dc66c0320
 configure-options =
   --disable-sdl
@@ -76,7 +66,7 @@ configure-options =
   --enable-vnc-png
   --disable-vnc-jpeg
   --extra-cflags="-I${gnutls:location}/include -I${libuuid:location}/include -I${zlib:location}/include -I${libpng:location}/include"
-  --extra-ldflags="-Wl,-rpath -Wl,${glib:location}/lib -L${glib:location}/lib -Wl,-rpath -Wl,${gnutls:location}/lib -L${gnutls:location}/lib -L${gettext:location}/lib -Wl,-rpath -Wl,${gettext:location}/lib -Wl,-rpath -Wl,${libpng:location}/lib -L${libpng:location}/lib -L${libuuid:location}/lib -Wl,-rpath -Wl,${libuuid:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -lpng -lz -lgnutls"
+  --extra-ldflags="-Wl,-rpath -Wl,${glib:location}/lib -L${glib:location}/lib -Wl,-rpath -Wl,${gnutls:location}/lib -L${gnutls:location}/lib -Wl,-rpath -Wl,${gpg-error:location}/lib -L${gpg-error:location}/lib -L${gettext:location}/lib -Wl,-rpath -Wl,${gettext:location}/lib -Wl,-rpath -Wl,${libpng:location}/lib -L${libpng:location}/lib -L${libuuid:location}/lib -Wl,-rpath -Wl,${libuuid:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -lpng -lz -lgnutls"
   --disable-werror
 environment =
   PATH=${pkgconfig:location}/bin:%(PATH)s
@@ -86,8 +76,8 @@ environment =
 # XXX-Cedric : use official egg from pypi when it is released
 recipe = plone.recipe.command
 stop-on-error = true
-commit = e7363f43443deb9982bdb5c3db50eec475584b06
-repository = https://github.com/desaintmartin/websockify.git
+commit = 301f3ae580557da47fa5ea2050aa671ce9c5a1a0
+repository = https://github.com/SlapOS/websockify.git
 location = ${buildout:parts-directory}/${:_buildout_section_name_}
 git-binary = ${git:location}/bin/git
 command = export GIT_SSL_NO_VERIFY=true; (${:git-binary} clone --quiet ${:repository} ${:location} && cd ${:location} && ${:git-binary} reset --hard ${:commit}) || (rm -fr ${:location}; exit 1)
@@ -101,83 +91,199 @@ command = grep parts ${buildout:develop-eggs-directory}/websockify.egg-link
 depends = ${eggs:dummy}
 
 [eggs]
-python = python2.7
 recipe = z3c.recipe.scripts
 dummy =
   ${websockify:location}
 eggs =
   ${lxml-python:egg}
-  slapos.cookbook
   websockify
-  
+  slapos.cookbook
+  slapos.toolbox
+
+[http-proxy]
+# https://github.com/nodejitsu/node-http-proxy
+recipe = slapos.recipe.build:download-unpacked
+#XXX-Cedric : use upstream when merged
+url = https://nodeload.github.com/desaintmartin/node-http-proxy/zipball/master
+md5sum = 20204d0b29c2cef26e1c91e99eedca6b
+
+[proxy-by-url]
+# https://github.com/dominictarr/proxy-by-url
+recipe = slapos.recipe.build:download-unpacked
+#XXX-Cedric : use upstream when merged
+url = https://nodeload.github.com/desaintmartin/proxy-by-url/zipball/master
+md5sum = c2609948aa708581f93b981b23880314
+
+[npm-modules]
+recipe = plone.recipe.command
+destination = ${buildout:parts-directory}/${:_buildout_section_name_}
+location = ${buildout:parts-directory}/${:_buildout_section_name_}
+command =
+  rm -fr ${:destination} &&
+  mkdir -p ${:destination} &&
+  cd ${:destination} &&
+  ${nodejs:location}/bin/node ${nodejs:location}/bin/npm install colors@0.6.0-1 &&
+  ${nodejs:location}/bin/node ${nodejs:location}/bin/npm install socket.io@0.8.7 &&
+  ${nodejs:location}/bin/node ${nodejs:location}/bin/npm install socket.io-client@0.8.7 &&
+  ${nodejs:location}/bin/node ${nodejs:location}/bin/npm install optimist@0.3.1 &&
+  ${nodejs:location}/bin/node ${nodejs:location}/bin/npm install pkginfo@0.2.3
+
+
+[template-kvm]
+recipe = slapos.recipe.template
+url = ${:_profile_base_location_}/instance-kvm.cfg
+md5sum = b6572c018e44d4676e76805116bcade0
+output = ${buildout:directory}/template-kvm.cfg
+mode = 0644
+
+[template-kvmplus]
+recipe = slapos.recipe.template
+url = ${:_profile_base_location_}/instance-kvmplus.cfg
+md5sum = 2e35c5b2ac9ee51d8f98fb1199f011c4
+output = ${buildout:directory}/template-kvmplus.cfg
+mode = 0644
+
+[template-nbd]
+recipe = slapos.recipe.template
+url = ${:_profile_base_location_}/instance-nbd.cfg
+md5sum = 7691fadfc8d4392c58ac1bf0ebd5aaf2
+output = ${buildout:directory}/template-nbd.cfg
+mode = 0644
+
+[template-frontend]
+recipe = slapos.recipe.template
+url = ${:_profile_base_location_}/instance-frontend.cfg
+md5sum = 123bf4e5bea9e86c03b62e9afb8ca04b
+output = ${buildout:directory}/template-frontend.cfg
+mode = 0644
+
 [template]
 recipe = slapos.recipe.template
 url = ${:_profile_base_location_}/instance.cfg
-md5sum = 298b146e4efce41bfd58b3f85d064ff1
+md5sum = 68788763d23f70f24b9e575871c903a8
 output = ${buildout:directory}/template.cfg
 mode = 0644
 
-[versions]
-zc.buildout = 1.5.3-dev-SlapOS-010
+[networkcache]
+# signature certificates of the following uploaders.
+#   Romain Courteaud
+#   Cedric de Saint Martin
+signature-certificate-list =
+  -----BEGIN CERTIFICATE-----
+  MIIB4DCCAUkCADANBgkqhkiG9w0BAQsFADA5MQswCQYDVQQGEwJGUjEZMBcGA1UE
+  CBMQRGVmYXVsdCBQcm92aW5jZTEPMA0GA1UEChMGTmV4ZWRpMB4XDTExMDkxNTA5
+  MDAwMloXDTEyMDkxNTA5MDAwMlowOTELMAkGA1UEBhMCRlIxGTAXBgNVBAgTEERl
+  ZmF1bHQgUHJvdmluY2UxDzANBgNVBAoTBk5leGVkaTCBnzANBgkqhkiG9w0BAQEF
+  AAOBjQAwgYkCgYEApYZv6OstoqNzxG1KI6iE5U4Ts2Xx9lgLeUGAMyfJLyMmRLhw
+  boKOyJ9Xke4dncoBAyNPokUR6iWOcnPHtMvNOsBFZ2f7VA28em3+E1JRYdeNUEtX
+  Z0s3HjcouaNAnPfjFTXHYj4um1wOw2cURSPuU5dpzKBbV+/QCb5DLheynisCAwEA
+  ATANBgkqhkiG9w0BAQsFAAOBgQBCZLbTVdrw3RZlVVMFezSHrhBYKAukTwZrNmJX
+  mHqi2tN8tNo6FX+wmxUUAf3e8R2Ymbdbn2bfbPpcKQ2fG7PuKGvhwMG3BlF9paEC
+  q7jdfWO18Zp/BG7tagz0jmmC4y/8akzHsVlruo2+2du2freE8dK746uoMlXlP93g
+  QUUGLQ==
+  -----END CERTIFICATE-----
+  -----BEGIN CERTIFICATE-----
+  MIIB9jCCAV+gAwIBAgIJAO4V/jiMoICoMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
+  BAMMCENPTVAtMjMyMCAXDTEyMDIxNjExMTAyM1oYDzIxMTIwMTIzMTExMDIzWjAT
+  MREwDwYDVQQDDAhDT01QLTIzMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+  wi/3Z8W9pUiegUXIk/AiFDQ0UJ4JFAwjqr+HSRUirlUsHHT+8DzH/hfcTDX1I5BB
+  D1ADk+ydXjMm3OZrQcXjn29OUfM5C+g+oqeMnYQImN0DDQIOcUyr7AJc4xhvuXQ1
+  P2pJ5NOd3tbd0kexETa1LVhR6EgBC25LyRBRae76qosCAwEAAaNQME4wHQYDVR0O
+  BBYEFMDmW9aFy1sKTfCpcRkYnP6zUd1cMB8GA1UdIwQYMBaAFMDmW9aFy1sKTfCp
+  cRkYnP6zUd1cMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAskbFizHr
+  b6d3iIyN+wffxz/V9epbKIZVEGJd/6LrTdLiUfJPec7FaxVCWNyKBlCpINBM7cEV
+  Gn9t8mdVQflNqOlAMkOlUv1ZugCt9rXYQOV7rrEYJBWirn43BOMn9Flp2nibblby
+  If1a2ZoqHRxoNo2yTmm7TSYRORWVS+vvfjY=
+  -----END CERTIFICATE-----
 
-slapos.cookbook = 0.37
+[versions]
 Jinja2 = 2.6
-Werkzeug = 0.8.1
+Werkzeug = 0.8.3
+apache-libcloud = 0.8.0
+async = 0.6.1
 buildout-versions = 1.7
+gitdb = 0.5.4
 hexagonit.recipe.cmmi = 1.5.0
-lxml = 2.3.2
-meld3 = 0.6.7
+lxml = 2.3.3
+meld3 = 0.6.8
 plone.recipe.command = 1.1
+pycrypto = 2.5
+slapos.cookbook = 0.41
+slapos.recipe.build = 0.7
 slapos.recipe.template = 2.2
+slapos.toolbox = 0.18
+smmap = 0.8.2
 z3c.recipe.scripts = 1.0.1
 
 # Required by:
-# slapos.core==0.20
+# slapos.core==0.23
+# slapos.toolbox==0.18
 Flask = 0.8
 
 # Required by:
-# slapos.cookbook==0.37
+# slapos.toolbox==0.18
+GitPython = 0.3.2.RC1
+
+# Required by:
+# slapos.cookbook==0.41
 PyXML = 0.8.4
 
 # Required by:
-# hexagonit.recipe.cmmi==1.5.0
-hexagonit.recipe.download = 1.5.0
+# slapos.toolbox==0.18
+atomize = 0.1.1
+
+# Required by:
+# slapos.toolbox==0.18
+feedparser = 5.1.1
+
+# Required by:
+# slapos.cookbook==0.41
+inotifyx = 0.2.0
 
 # Required by:
-# slapos.cookbook==0.37
+# slapos.cookbook==0.41
 netaddr = 0.7.6
 
 # Required by:
-# slapos.core==0.20
-netifaces = 0.6
+# slapos.core==0.23
+netifaces = 0.8
 
 # Required by:
 # websockify==0.1-dev
 numpy = 1.6.1
 
 # Required by:
-# slapos.cookbook==0.37
-# slapos.core==0.20
-# zc.buildout==1.5.3-dev-SlapOS-010
-# zc.recipe.egg==1.3.2
+# slapos.toolbox==0.18
+paramiko = 1.7.7.1
+
+# Required by:
+# slapos.toolbox==0.18
+psutil = 0.4.1
+
+# Required by:
+# slapos.cookbook==0.41
+# slapos.core==0.23
+# slapos.toolbox==0.18
 setuptools = 0.6c12dev-r88846
 
 # Required by:
-# slapos.cookbook==0.37
-slapos.core = 0.20
+# slapos.cookbook==0.41
+# slapos.toolbox==0.18
+slapos.core = 0.23
 
 # Required by:
-# slapos.core==0.20
-supervisor = 3.0a10
+# slapos.core==0.23
+supervisor = 3.0a12
 
 # Required by:
-# slapos.cookbook==0.37
+# slapos.cookbook==0.41
+# slapos.toolbox==0.18
 xml-marshaller = 0.9.7
 
 # Required by:
-# slapos.cookbook==0.37
+# slapos.cookbook==0.41
 zc.recipe.egg = 1.3.2
 
 # Required by:
-# slapos.core==0.20
+# slapos.core==0.23
 zope.interface = 3.8.0
\ No newline at end of file
diff --git a/software/nbd/instance.cfg b/software/nbd/instance.cfg
deleted file mode 100644
index dcc2d0a62cecc520e7d3eb37c39af67d713d091e..0000000000000000000000000000000000000000
--- a/software/nbd/instance.cfg
+++ /dev/null
@@ -1,11 +0,0 @@
-[buildout]
-parts =
-  nbdserverinstance
-
-eggs-directory = ${buildout:eggs-directory}
-develop-eggs-directory = ${buildout:develop-eggs-directory} 
-
-[nbdserverinstance]
-recipe = ${instance-recipe:egg}:${instance-recipe:module}
-qemu_path = ${nbdserver:location}/bin/qemu-nbd
-onetimeupload_path = ${buildout:bin-directory}/onetimeupload
diff --git a/software/nbd/software.cfg b/software/nbd/software.cfg
deleted file mode 100644
index 2735046e37ff296efde2ea571b7584131d6fe314..0000000000000000000000000000000000000000
--- a/software/nbd/software.cfg
+++ /dev/null
@@ -1,20 +0,0 @@
-[buildout]
-
-extends =
-  ../../stack/nbd.cfg
-  ../../stack/shacache-client.cfg
-
-parts +=
-  template
-
-[template]
-recipe = slapos.recipe.template
-url = ${:_profile_base_location_}/instance.cfg
-md5sum = 82e948e1c0cb0d5540ef185edeef3ec3
-output = ${buildout:directory}/template.cfg
-mode = 0644
-
-[versions]
-# XXX-CEDRIC Quick and dirty workaround to avoid m2crypto problems.
-# should not be used elsewhere unless for urgent cases. 
-slapos.libnetworkcache = 0.2
diff --git a/software/slaprunner/instance.cfg b/software/slaprunner/instance.cfg
index f9427ba5b784e6b1a80dd1c813a584f300376d98..182887a686342b03b7a68db2bd14de3dc3cf0242 100644
--- a/software/slaprunner/instance.cfg
+++ b/software/slaprunner/instance.cfg
@@ -16,6 +16,8 @@ slapgrid_cp = ${buildout:directory}/bin/slapgrid-cp
 slapproxy = ${buildout:directory}/bin/slapproxy
 supervisor = ${buildout:directory}/bin/slapgrid-supervisorctl
 git = ${git:location}/bin/git
+node-bin = ${nodejs-0.4:location}/bin/node
+cloud9 = ${cloud9:location}/bin/cloud9.js
 ssh_client = $${sshkeys-dropbear:wrapper}
 public_key = $${sshkeys-dropbear:public-key}
 private_key = $${sshkeys-dropbear:private-key}
@@ -62,4 +64,4 @@ bin = $${buildout:directory}/bin/
 recipe = slapos.cookbook:mkdirectory
 sshkeys = $${rootdirectory:srv}/sshkeys
 services = $${rootdirectory:etc}/run/
-ssh = $${rootdirectory:etc}/ssh/
+ssh = $${rootdirectory:etc}/ssh/
\ No newline at end of file
diff --git a/software/slaprunner/software.cfg b/software/slaprunner/software.cfg
index 8a2e90bb72e9f594dcafa5111ebc8b20e8f445e2..e6ee0edd7a7d68ece8108b9dee768aeca0d2030e 100644
--- a/software/slaprunner/software.cfg
+++ b/software/slaprunner/software.cfg
@@ -1,4 +1,5 @@
 [buildout]
+
 extensions =
   buildout-versions
 
@@ -8,6 +9,7 @@ extends =
   ../../stack/shacache-client.cfg
   ../../component/dropbear/buildout.cfg
   ../../component/git/buildout.cfg
+  ../../component/cloud9/buildout.cfg
 
 parts =
   template
@@ -31,7 +33,7 @@ recipe = slapos.recipe.template
 url = ${:_profile_base_location_}/instance.cfg
 output = ${buildout:directory}/template.cfg
 mode = 0644
-md5sum = cd69efd5c3a7e9adca7387b9a401590a
+md5sum = 7cfd248cdc6fa6cbb4957d25a0aed884
 
 [eggs]
 eggs +=
@@ -60,6 +62,7 @@ signature-certificate-list =
 [versions]
 # Use SlapOS patched zc.buildout
 zc.buildout = 1.6.0-dev-SlapOS-003
+
 Jinja2 = 2.6
 Werkzeug = 0.8.3
 apache-libcloud = 0.8.0
@@ -68,68 +71,65 @@ buildout-versions = 1.7
 gitdb = 0.5.4
 hexagonit.recipe.cmmi = 1.5.0
 meld3 = 0.6.8
+plone.recipe.command = 1.1
 pycrypto = 2.5
-slapos.cookbook = 0.39
+slapos.cookbook = 0.45
 slapos.libnetworkcache = 0.12
-slapos.recipe.template = 2.2
-slapos.toolbox = 0.18
+slapos.recipe.template = 2.3
+slapos.toolbox = 0.20
 smmap = 0.8.2
 
 # Required by:
-# slapos.core==0.22
+# slapos.core==0.24
 Flask = 0.8
 
 # Required by:
-# slapos.toolbox==0.18
+# slapos.toolbox==0.20
 GitPython = 0.3.2.RC1
 
 # Required by:
-# slapos.cookbook==0.39
+# slapos.cookbook==0.45
 PyXML = 0.8.4
 
 # Required by:
-# slapos.toolbox==0.18
+# slapos.toolbox==0.20
 atomize = 0.1.1
 
 # Required by:
-# slapos.toolbox==0.18
-feedparser = 5.1
-
-# Required by:
-# hexagonit.recipe.cmmi==1.5.0
-hexagonit.recipe.download = 1.5.0
+# slapos.toolbox==0.20
+feedparser = 5.1.1
 
 # Required by:
-# slapos.cookbook==0.39
+# slapos.cookbook==0.45
 inotifyx = 0.2.0
 
 # Required by:
-# slapos.cookbook==0.39
-# slapos.core==0.22
+# slapos.cookbook==0.45
+# slapos.core==0.24
 # xml-marshaller==0.9.7
-lxml = 2.3.3
+lxml = 2.3.4
 
 # Required by:
-# slapos.cookbook==0.39
+# slapos.cookbook==0.45
 netaddr = 0.7.6
 
 # Required by:
-# slapos.core==0.22
+# slapos.core==0.24
 netifaces = 0.8
 
 # Required by:
-# slapos.toolbox==0.18
+# slapos.toolbox==0.20
 paramiko = 1.7.7.1
 
 # Required by:
-# slapos.toolbox==0.18
+# slapos.toolbox==0.20
 psutil = 0.4.1
 
 # Required by:
-# slapos.cookbook==0.39
-# slapos.core==0.22
+# slapos.cookbook==0.45
+# slapos.core==0.24
 # slapos.libnetworkcache==0.12
-# slapos.toolbox==0.18
+# slapos.toolbox==0.20
 # supervisor==3.0a12
 # zc.buildout==1.6.0-dev-SlapOS-003
 # zc.recipe.egg==1.3.2
@@ -137,21 +137,21 @@ psutil = 0.4.1
 setuptools = 0.6c12dev-r88846
 
 # Required by:
-# slapos.cookbook==0.39
-slapos.core = 0.22
+# slapos.cookbook==0.45
+slapos.core = 0.24
 
 # Required by:
-# slapos.core==0.22
+# slapos.core==0.24
 supervisor = 3.0a12
 
 # Required by:
-# slapos.cookbook==0.39
+# slapos.cookbook==0.45
 xml-marshaller = 0.9.7
 
 # Required by:
-# slapos.cookbook==0.39
+# slapos.cookbook==0.45
 zc.recipe.egg = 1.3.2
 
 # Required by:
-# slapos.core==0.22
-zope.interface = 3.8.0
+# slapos.core==0.24
+zope.interface = 3.8.0
\ No newline at end of file
diff --git a/stack/lamp/buildout.cfg b/stack/lamp/buildout.cfg
index b17f8a7b38267a00e274c9e55d7fd8479a80a20f..51d91cad404946c79c030e8e4ca72597704303ca 100644
--- a/stack/lamp/buildout.cfg
+++ b/stack/lamp/buildout.cfg
@@ -51,6 +51,7 @@ extends =
   ../../component/mydumper/buildout.cfg
   ../../component/mysql-python/buildout.cfg
   ../../component/dropbear/buildout.cfg
+  ../slapos.cfg
 
 versions = versions
 
@@ -83,7 +84,7 @@ mode = 0644
 recipe = slapos.recipe.template
 url = ${:_profile_base_location_}/instance-apache-php.cfg
 output = ${buildout:directory}/template-apache-php.cfg
-md5sum = 45bc82dc468e7f418d95c846d1a33d74
+md5sum = 8ebed1e26127c066e5b69372e69e6c38
 mode = 0644
 
 [template-apache-backup]
diff --git a/stack/lamp/instance-apache-php.cfg b/stack/lamp/instance-apache-php.cfg
index 77e266f21def3d68a1ee1cfbe374e9eb4bb86ea6..d77125de685bf8ea09133792e50442de11f4489a 100644
--- a/stack/lamp/instance-apache-php.cfg
+++ b/stack/lamp/instance-apache-php.cfg
@@ -12,6 +12,7 @@ parts =
   logrotate-entry-stunnel
   cron
   cron-entry-logrotate
+  promise
 
 eggs-directory = ${buildout:eggs-directory}
 develop-eggs-directory = ${buildout:develop-eggs-directory}
@@ -21,6 +22,12 @@ offline = true
 recipe = slapos.cookbook:publishurl
 url = http://[$${apache-php:ip}]:$${apache-php:port}/
 
+[promise]
+recipe = slapos.cookbook:check_port_listening
+path = $${basedirectory:promises}/apache
+hostname = $${apache-php:ip}
+port = $${apache-php:port}
+
 [mariadb-urlparse]
 recipe = slapos.cookbook:urlparse
 url = $${request-mariadb:connection-url}
@@ -43,8 +50,6 @@ tmp-dir = $${directory:tmp-php}
 httpd-conf = $${rootdirectory:etc}/apache.conf
 wrapper = $${basedirectory:services}/apache
 
-promise = $${basedirectory:promises}/apache
-
 httpd-binary = ${apache:location}/bin/httpd
 
 mysql-username = $${mariadb-urlparse:username}
diff --git a/stack/nodejs.cfg b/stack/nodejs.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..04b47bc9b8947ab717c769451a4d73c8178613cd
--- /dev/null
+++ b/stack/nodejs.cfg
@@ -0,0 +1,18 @@
+[buildout]
+extends =
+  ../component/nodejs/buildout.cfg
+  ../component/lxml-python/buildout.cfg
+  ../stack/slapos.cfg
+
+versions = versions
+
+parts =
+  eggs
+  nodejs
+  npm
+
+[eggs]
+recipe = zc.recipe.egg
+eggs =
+  slapos.cookbook
+  ${lxml-python:egg}
diff --git a/stack/slapos.cfg b/stack/slapos.cfg
index 83267a377f3333aa6929e326378a7f406c737f63..2054ec53eaaea2f8c5b4eb9f70eb57f1bb43fe1f 100644
--- a/stack/slapos.cfg
+++ b/stack/slapos.cfg
@@ -18,6 +18,7 @@ exec-sitecustomize = false
 # Add location for modified non-official slapos.buildout
 find-links +=
   http://www.nexedi.org/static/packages/source/slapos.buildout/
+  http://www.nexedi.org/static/packages/source/hexagonit.recipe.download/
 
 # Use only quite well working sites.
 allow-hosts +=
@@ -38,3 +39,12 @@ allow-hosts +=
 # Unzippig of eggs is required, as SlapOS do not yet provide nicely working
 # development / fast switching environment for whole software
 unzip = true
+
+versions = versions
+
+[versions]
+# Use patched hexagonit.recipe.download from
+# https://github.com/SlapOS/hexagonit.recipe.download
+hexagonit.recipe.download = 1.5.1-dev-slapos-001
+# Use SlapOS patched zc.buildout
+zc.buildout = 1.6.0-dev-SlapOS-004