diff --git a/slapos/recipe/erp5testnode/SlapOSControler.py b/slapos/recipe/erp5testnode/SlapOSControler.py
deleted file mode 100644
index e9ed42f711874efb143e05787cf30f4cc6787eac..0000000000000000000000000000000000000000
--- a/slapos/recipe/erp5testnode/SlapOSControler.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import slapos.slap, subprocess, os, time
-from xml_marshaller import xml_marshaller
-
-class SlapOSControler(object):
-
-  def __init__(self, config, process_group_pid_set=None):
-    self.config = config
-    # By erasing everything, we make sure that we are able to "update"
-    # existing profiles. This is quite dirty way to do updates...
-    if os.path.exists(config['proxy_database']):
-      os.unlink(config['proxy_database'])
-    proxy = subprocess.Popen([config['slapproxy_binary'],
-      config['slapos_config']], close_fds=True, preexec_fn=os.setsid)
-    process_group_pid_set.add(proxy.pid)
-    # XXX: dirty, giving some time for proxy to being able to accept
-    # connections
-    time.sleep(10)
-    slap = slapos.slap.slap()
-    slap.initializeConnection(config['master_url'])
-    # register software profile
-    self.software_profile = config['custom_profile_path']
-    slap.registerSupply().supply(
-        self.software_profile,
-        computer_guid=config['computer_id'])
-    computer = slap.registerComputer(config['computer_id'])
-    # create partition and configure computer
-    partition_reference = config['partition_reference']
-    partition_path = os.path.join(config['instance_root'], partition_reference)
-    if not os.path.exists(partition_path):
-      os.mkdir(partition_path)
-      os.chmod(partition_path, 0750)
-    computer.updateConfiguration(xml_marshaller.dumps({
- 'address': config['ipv4_address'],
- 'instance_root': config['instance_root'],
- 'netmask': '255.255.255.255',
- 'partition_list': [{'address_list': [{'addr': config['ipv4_address'],
-                                       'netmask': '255.255.255.255'},
-                                      {'addr': config['ipv6_address'],
-                                       'netmask': 'ffff:ffff:ffff::'},
-                      ],
-                     'path': partition_path,
-                     'reference': partition_reference,
-                     'tap': {'name': partition_reference},
-                     }
-                    ],
- 'reference': config['computer_id'],
- 'software_root': config['software_root']}))
-
-  def runSoftwareRelease(self, config, environment, process_group_pid_set=None,
-                         stdout=None, stderr=None):
-    print "SlapOSControler.runSoftwareRelease"
-    cpu_count = os.sysconf("SC_NPROCESSORS_ONLN")
-    os.putenv('MAKEFLAGS', '-j%s' % cpu_count)
-    os.environ['PATH'] = environment['PATH']
-    slapgrid = subprocess.Popen([config['slapgrid_software_binary'], '-v', '-c',
-      #'--buildout-parameter',"'-U -N' -o",
-      config['slapos_config']],
-      stdout=stdout, stderr=stderr,
-      close_fds=True, preexec_fn=os.setsid)
-    process_group_pid_set.add(slapgrid.pid)
-    slapgrid.wait()
-    stdout.seek(0)
-    stderr.seek(0)
-    process_group_pid_set.remove(slapgrid.pid)
-    status_dict = {'status_code':slapgrid.returncode,
-                    'stdout':stdout.read(),
-                    'stderr':stderr.read()}
-    stdout.close()
-    stderr.close()
-    return status_dict
-
-  def runComputerPartition(self, config, environment,
-                           process_group_pid_set=None,
-                           stdout=None, stderr=None):
-    print "SlapOSControler.runSoftwareRelease"
-    slap = slapos.slap.slap()
-    slap.registerOpenOrder().request(self.software_profile,
-        partition_reference='testing partition',
-        partition_parameter_kw=config['instance_dict'])
-    slapgrid = subprocess.Popen([config['slapgrid_partition_binary'],
-      config['slapos_config'], '-c', '-v'],
-      stdout=stdout, stderr=stderr,
-      close_fds=True, preexec_fn=os.setsid)
-    process_group_pid_set.add(slapgrid.pid)
-    slapgrid.wait()
-    stdout.seek(0)
-    stderr.seek(0)
-    process_group_pid_set.remove(slapgrid.pid)
-    status_dict = {'status_code':slapgrid.returncode,
-                    'stdout':stdout.read(),
-                    'stderr':stderr.read()}
-    stdout.close()
-    stderr.close()
-    return status_dict
diff --git a/slapos/recipe/erp5testnode/Updater.py b/slapos/recipe/erp5testnode/Updater.py
deleted file mode 100644
index 83ea60a1e7738bdd55316ce4bb7c5937f838203c..0000000000000000000000000000000000000000
--- a/slapos/recipe/erp5testnode/Updater.py
+++ /dev/null
@@ -1,189 +0,0 @@
-import os, sys, subprocess, re, threading
-from testnode import SubprocessError
-
-_format_command_search = re.compile("[[\\s $({?*\\`#~';<>&|]").search
-_format_command_escape = lambda s: "'%s'" % r"'\''".join(s.split("'"))
-def format_command(*args, **kw):
-  cmdline = []
-  for k, v in sorted(kw.items()):
-    if _format_command_search(v):
-      v = _format_command_escape(v)
-    cmdline.append('%s=%s' % (k, v))
-  for v in args:
-    if _format_command_search(v):
-      v = _format_command_escape(v)
-    cmdline.append(v)
-  return ' '.join(cmdline)
-
-def subprocess_capture(p, quiet=False):
-  def readerthread(input, output, buffer):
-    while True:
-      data = input.readline()
-      if not data:
-        break
-      output(data)
-      buffer.append(data)
-  if p.stdout:
-    stdout = []
-    output = quiet and (lambda data: None) or sys.stdout.write
-    stdout_thread = threading.Thread(target=readerthread,
-                                     args=(p.stdout, output, stdout))
-    stdout_thread.setDaemon(True)
-    stdout_thread.start()
-  if p.stderr:
-    stderr = []
-    stderr_thread = threading.Thread(target=readerthread,
-                                     args=(p.stderr, sys.stderr.write, stderr))
-    stderr_thread.setDaemon(True)
-    stderr_thread.start()
-  if p.stdout:
-    stdout_thread.join()
-  if p.stderr:
-    stderr_thread.join()
-  p.wait()
-  return (p.stdout and ''.join(stdout),
-          p.stderr and ''.join(stderr))
-
-GIT_TYPE = 'git'
-SVN_TYPE = 'svn'
-
-class Updater(object):
-
-  _git_cache = {}
-  realtime_output = True
-  stdin = file(os.devnull)
-
-  def __init__(self, repository_path, revision=None, git_binary=None):
-    self.revision = revision
-    self._path_list = []
-    self.repository_path = repository_path
-    self.git_binary = git_binary
-
-  def getRepositoryPath(self):
-    return self.repository_path
-
-  def getRepositoryType(self):
-    try:
-      return self.repository_type
-    except AttributeError:
-      # guess the type of repository we have
-      if os.path.isdir(os.path.join(
-                       self.getRepositoryPath(), '.git')):
-        repository_type = GIT_TYPE
-      elif os.path.isdir(os.path.join(
-                       self.getRepositoryPath(), '.svn')):
-        repository_type = SVN_TYPE
-      else:
-        raise NotImplementedError
-      self.repository_type = repository_type
-      return repository_type
-
-  def deletePycFiles(self, path):
-    """Delete *.pyc files so that deleted/moved files can not be imported"""
-    for path, dir_list, file_list in os.walk(path):
-      for file in file_list:
-        if file[-4:] in ('.pyc', '.pyo'):
-          # allow several processes clean the same folder at the same time
-          try:
-            os.remove(os.path.join(path, file))
-          except OSError, e:
-            if e.errno != errno.ENOENT:
-              raise
-
-  def spawn(self, *args, **kw):
-    quiet = kw.pop('quiet', False)
-    env = kw and dict(os.environ, **kw) or None
-    command = format_command(*args, **kw)
-    print '\n$ ' + command
-    sys.stdout.flush()
-    p = subprocess.Popen(args, stdin=self.stdin, stdout=subprocess.PIPE,
-                         stderr=subprocess.PIPE, env=env,
-                         cwd=self.getRepositoryPath())
-    if self.realtime_output:
-      stdout, stderr = subprocess_capture(p, quiet)
-    else:
-      stdout, stderr = p.communicate()
-      if not quiet:
-        sys.stdout.write(stdout)
-      sys.stderr.write(stderr)
-    result = dict(status_code=p.returncode, command=command,
-                  stdout=stdout, stderr=stderr)
-    if p.returncode:
-      raise SubprocessError(result)
-    return result
-
-  def _git(self, *args, **kw):
-    return self.spawn(self.git_binary, *args, **kw)['stdout'].strip()
-
-  def _git_find_rev(self, ref):
-    try:
-      return self._git_cache[ref]
-    except KeyError:
-      if os.path.exists('.git/svn'):
-        r = self._git('svn', 'find-rev', ref)
-        assert r
-        self._git_cache[ref[0] != 'r' and 'r%u' % int(r) or r] = ref
-      else:
-        r = self._git('rev-list', '--topo-order', '--count', ref), ref
-      self._git_cache[ref] = r
-      return r
-
-  def getRevision(self, *path_list):
-    if not path_list:
-      path_list = self._path_list
-    if self.getRepositoryType() == GIT_TYPE:
-      h = self._git('log', '-1', '--format=%H', '--', *path_list)
-      return self._git_find_rev(h)
-    elif self.getRepositoryType() == SVN_TYPE:
-      stdout = self.spawn('svn', 'info', *path_list)['stdout']
-      return str(max(map(int, SVN_CHANGED_REV.findall(stdout))))
-    raise NotImplementedError
-
-  def checkout(self, *path_list):
-    if not path_list:
-      path_list = '.',
-    revision = self.revision
-    if self.getRepositoryType() == GIT_TYPE:
-      # edit .git/info/sparse-checkout if you want sparse checkout
-      if revision:
-        if type(revision) is str:
-          h = revision
-        else:
-          h = revision[1]
-        if h != self._git('rev-parse', 'HEAD'):
-          self.deletePycFiles('.')
-          self._git('reset', '--merge', h)
-      else:
-        self.deletePycFiles('.')
-        if os.path.exists('.git/svn'):
-          self._git('svn', 'rebase')
-        else:
-          self._git('pull', '--ff-only')
-        self.revision = self._git_find_rev(self._git('rev-parse', 'HEAD'))
-    elif self.getRepositoryType() == SVN_TYPE:
-      # following code allows sparse checkout
-      def svn_mkdirs(path):
-        path = os.path.dirname(path)
-        if path and not os.path.isdir(path):
-          svn_mkdirs(path)
-          self.spawn(*(args + ['--depth=empty', path]))
-      for path in path_list:
-        args = ['svn', 'up', '--force', '--non-interactive']
-        if revision:
-          args.append('-r%s' % revision)
-        svn_mkdirs(path)
-        args += '--set-depth=infinity', path
-        self.deletePycFiles(path)
-        try:
-          status_dict = self.spawn(*args)
-        except SubprocessError, e:
-          if 'cleanup' not in e.stderr:
-            raise
-          self.spawn('svn', 'cleanup', path)
-          status_dict = self.spawn(*args)
-        if not revision:
-          self.revision = revision = SVN_UP_REV.findall(
-            status_dict['stdout'].splitlines()[-1])[0]
-    else:
-      raise NotImplementedError
-    self._path_list += path_list
diff --git a/slapos/recipe/erp5testnode/__init__.py b/slapos/recipe/erp5testnode/__init__.py
index cd8f0f53b69913c533cfb7b276a8fe0c50a923aa..af7177dc2b0ef95e2c56373f4c37a9aa10182f97 100644
--- a/slapos/recipe/erp5testnode/__init__.py
+++ b/slapos/recipe/erp5testnode/__init__.py
@@ -31,129 +31,79 @@ import zc.buildout
 import zc.recipe.egg
 import sys
 
-CONFIG = dict(
-  proxy_port='5000',
-  computer_id='COMPUTER',
-  partition_reference='test0',
-)
-
 class Recipe(BaseSlapRecipe):
   def __init__(self, buildout, name, options):
     self.egg = zc.recipe.egg.Egg(buildout, options['recipe'], options)
     BaseSlapRecipe.__init__(self, buildout, name, options)
 
-  def installSlapOs(self):
+  def _install(self):
+    self.requirements, self.ws = self.egg.working_set()
+    path_list = []
+    CONFIG = dict()
     CONFIG['slapos_directory'] = self.createDataDirectory('slapos')
     CONFIG['working_directory'] = self.createDataDirectory('testnode')
-    CONFIG['software_root'] = os.path.join(CONFIG['slapos_directory'],
-        'software')
-    CONFIG['instance_root'] = os.path.join(CONFIG['slapos_directory'],
-        'instance')
-    CONFIG['proxy_database'] = os.path.join(CONFIG['slapos_directory'],
-        'proxy.db')
+    CONFIG['test_suite_directory'] = self.createDataDirectory('test_suite')
     CONFIG['proxy_host'] = self.getLocalIPv4Address()
-    CONFIG['master_url'] = 'http://%s:%s' % (CONFIG['proxy_host'],
-        CONFIG['proxy_port'])
-    self._createDirectory(CONFIG['software_root'])
-    self._createDirectory(CONFIG['instance_root'])
-    CONFIG['slapos_config'] = self.createConfigurationFile('slapos.cfg',
-        self.substituteTemplate(pkg_resources.resource_filename(__name__,
-          'template/slapos.cfg.in'), CONFIG))
-    self.path_list.append(CONFIG['slapos_config'])
-
-  def setupRunningWrapper(self):
-    self.path_list.extend(zc.buildout.easy_install.scripts([(
-      'testnode',
-        __name__+'.testnode', 'run')], self.ws,
-          sys.executable, self.wrapper_directory, arguments=[
-            dict(
-              computer_id=CONFIG['computer_id'],
-              instance_dict=eval(self.parameter_dict.get('instance_dict', '{}')),
-              instance_root=CONFIG['instance_root'],
-              ipv4_address=self.getLocalIPv4Address(),
-              ipv6_address=self.getGlobalIPv6Address(),
-              master_url=CONFIG['master_url'],
-              profile_path=self.parameter_dict['profile_path'],
-              proxy_database=CONFIG['proxy_database'],
-              proxy_port=CONFIG['proxy_port'],
-              slapgrid_partition_binary=self.options['slapgrid_partition_binary'],
-              slapgrid_software_binary=self.options['slapgrid_software_binary'],
-              slapos_config=CONFIG['slapos_config'],
-              slapproxy_binary=self.options['slapproxy_binary'],
-              git_binary=self.options['git_binary'],
-              software_root=CONFIG['software_root'],
-              working_directory=CONFIG['working_directory'],
-              vcs_repository_list=eval(self.parameter_dict.get('vcs_repository_list'),),
-              node_quantity=self.parameter_dict.get('node_quantity', '1'),
-              test_suite_master_url=self.parameter_dict.get(
-                                'test_suite_master_url', None),
-              test_suite=self.parameter_dict.get('test_suite'),
-              test_suite_title=self.parameter_dict.get('test_suite_title'),
-              test_node_title=self.parameter_dict.get('test_node_title'),
-              project_title=self.parameter_dict.get('project_title'),
-              bin_directory=self.bin_directory,
-              # botenvironemnt is splittable string of key=value to substitute
-              # environment of running bot
-              bot_environment=self.parameter_dict.get('bot_environment', ''),
-              partition_reference=CONFIG['partition_reference'],
-              environment=dict(PATH=os.environ['PATH']),
-              vcs_authentication_list=eval(self.parameter_dict.get(
-                     'vcs_authentication_list', 'None')),
-            )
-          ]))
-
-  def installLocalGit(self):
-    git_dict = dict(git_binary = self.options['git_binary'])
-    git_dict.update(self.parameter_dict)
-    double_slash_end_position = 1
-    # XXX, this should be provided by slapos
-    print "bin_directory : %r" % self.bin_directory
-    home_directory = os.path.join(*os.path.split(self.bin_directory)[0:-1])
-    print "home_directory : %r" % home_directory
-    git_dict.setdefault("git_server_name", "git.erp5.org")
-    if git_dict.get('vcs_authentication_list', None) is not None:
-      vcs_authentication_list = eval(git_dict['vcs_authentication_list'])
-      netrc_file = open(os.path.join(home_directory, '.netrc'), 'w')
-      for vcs_authentication_dict in vcs_authentication_list:
-        netrc_file.write("""
-machine %(host)s
-login %(user_name)s
-password %(password)s
-""" % vcs_authentication_dict)
-      netrc_file.close()
+    CONFIG['proxy_port'] = '5000'
+    CONFIG['log_directory'] = self.createDataDirectory('testnodelog')
+    CONFIG['run_directory'] = self.createDataDirectory('testnoderun')
+    CONFIG['test_suite_title'] = self.parameter_dict.get('test_suite_title')
+    CONFIG['test_node_title'] = self.parameter_dict.get('test_node_title')
+    CONFIG['test_suite'] = self.parameter_dict.get('test_suite')
+    CONFIG['node_quantity'] = self.parameter_dict.get('node_quantity', '1')
+    CONFIG['project_title'] = self.parameter_dict.get('project_title')
+    CONFIG['ipv4_address'] = self.getLocalIPv4Address()
+    CONFIG['ipv6_address'] = self.getGlobalIPv6Address()
+    CONFIG['test_suite_master_url'] = self.parameter_dict.get(
+                                'test_suite_master_url', None)
+    CONFIG['git_binary'] = self.options['git_binary']
+    CONFIG['slapgrid_partition_binary'] = self.options[
+      'slapgrid_partition_binary']
+    CONFIG['slapgrid_software_binary'] = self.options[
+      'slapgrid_software_binary']
+    CONFIG['slapproxy_binary'] = self.options['slapproxy_binary']
+    CONFIG['zip_binary'] = self.options['zip_binary']
+    CONFIG['PATH'] = os.environ['PATH']
+    additional_bt5_repository_id = \
+        self.parameter_dict.get('additional_bt5_repository_id')
 
-  def installLocalRepository(self):
-    self.installLocalGit()
+    CONFIG['bt5_path'] = None
+    if additional_bt5_repository_id is not None:
+      CONFIG['bt5_path'] = ""
+      additional_bt5_repository_id_list = additional_bt5_repository_id.split(",")
+      for id in additional_bt5_repository_id_list:
+        id_path = os.path.join(CONFIG['slapos_directory'], id)
+        bt_path = os.path.join(id_path, "bt5")
+        CONFIG['bt5_path'] += "%s,%s," % (id_path, bt_path)
+    CONFIG['instance_dict'] = ''
+    if 'instance_dict' in self.parameter_dict:
+      CONFIG['instance_dict'] = '[instance_dict]\n'
+      for k,v in eval(self.parameter_dict['instance_dict']).iteritems():
+        CONFIG['instance_dict'] += '%s = %s\n' % (k,v)
 
-  def installLocalZip(self):
-    zip = os.path.join(self.bin_directory, 'zip')
-    if os.path.lexists(zip):
-      os.unlink(zip)
-    os.symlink(self.options['zip_binary'], zip)
-
-  def installLocalPython(self):
-    """Installs local python fully featured with eggs"""
-    self.path_list.extend(zc.buildout.easy_install.scripts([], self.ws,
-          sys.executable, self.bin_directory, scripts=None,
-          interpreter='python'))
-
-  def installLocalRunUnitTest(self):
-    link = os.path.join(self.bin_directory, 'runUnitTest')
-    destination = os.path.join(CONFIG['instance_root'],
-        CONFIG['partition_reference'], 'bin', 'runUnitTest')
-    if os.path.lexists(link):
-      if not os.readlink(link) != destination:
-        os.unlink(link)
-    if not os.path.lexists(link):
-      os.symlink(destination, link)
-
-  def _install(self):
-    self.requirements, self.ws = self.egg.working_set()
-    self.path_list = []
-    self.installSlapOs()
-    self.setupRunningWrapper()
-    self.installLocalRepository()
-    self.installLocalZip()
-    self.installLocalPython()
-    self.installLocalRunUnitTest()
-    return self.path_list
+    CONFIG['repository_list'] = ''
+    i = 0
+    for repository in eval(self.parameter_dict['vcs_repository_list']):
+      CONFIG['repository_list'] += '[vcs_repository_%s]\n' % i
+      CONFIG['repository_list'] += 'url = %s\n' % repository['url']
+      if 'branch' in repository:
+        CONFIG['repository_list'] += 'branch = %s\n' % repository['branch']
+      if 'profile_path' in repository:
+        CONFIG['repository_list'] += 'profile_path = %s\n' % repository[
+          'profile_path']
+      if 'buildout_section_id' in repository:
+        CONFIG['repository_list'] += 'buildout_section_id = %s\n' % repository[
+          'buildout_section_id']
+      CONFIG['repository_list'] += '\n'
+      i += 1
+    testnode_config = self.createConfigurationFile('erp5testnode.cfg',
+        self.substituteTemplate(pkg_resources.resource_filename(__name__,
+          'template/erp5testnode.cfg.in'), CONFIG))
+    testnode_log = os.path.join(self.log_directory, 'erp5testnode.log')
+    wrapper = zc.buildout.easy_install.scripts([('erp5testnode',
+     'slapos.recipe.librecipe.execute', 'executee')], self.ws, sys.executable,
+      self.wrapper_directory, arguments=[[self.options['testnode'], '-l',
+      testnode_log, testnode_config], {'GIT_SSL_NO_VERIFY': '1'}])[0]
+    path_list.append(testnode_config)
+    path_list.append(wrapper)
+    return path_list
diff --git a/slapos/recipe/erp5testnode/template/erp5testnode.cfg.in b/slapos/recipe/erp5testnode/template/erp5testnode.cfg.in
new file mode 100644
index 0000000000000000000000000000000000000000..08c4619a211392c6e48094b9b0f333e4ceaa9bb6
--- /dev/null
+++ b/slapos/recipe/erp5testnode/template/erp5testnode.cfg.in
@@ -0,0 +1,31 @@
+[testnode]
+slapos_directory = %(slapos_directory)s
+working_directory = %(slapos_directory)s
+test_suite_directory = %(test_suite_directory)s
+log_directory = %(log_directory)s
+run_directory = %(run_directory)s
+proxy_host = %(proxy_host)s
+proxy_port = %(proxy_port)s
+test_suite_title = %(test_suite_title)s
+test_suite = %(test_suite)s
+node_quantity = %(node_quantity)s
+test_node_title = %(test_node_title)s
+project_title= %(project_title)s
+ipv4_address = %(ipv4_address)s
+ipv6_address = %(ipv6_address)s
+test_suite_master_url = %(test_suite_master_url)s
+bt5_path = %(bt5_path)s
+
+# Binaries
+git_binary = %(git_binary)s
+slapgrid_partition_binary = %(slapgrid_partition_binary)s
+slapgrid_software_binary = %(slapgrid_software_binary)s
+slapproxy_binary = %(slapproxy_binary)s
+zip_binary = %(zip_binary)s
+
+[environment]
+PATH = %(PATH)s
+
+%(instance_dict)s
+
+%(repository_list)s
diff --git a/slapos/recipe/erp5testnode/template/slapos.cfg.in b/slapos/recipe/erp5testnode/template/slapos.cfg.in
deleted file mode 100644
index 713f719a322502bca230db83a0c2aa4c6678607c..0000000000000000000000000000000000000000
--- a/slapos/recipe/erp5testnode/template/slapos.cfg.in
+++ /dev/null
@@ -1,10 +0,0 @@
-[slapos]
-software_root = %(software_root)s
-instance_root = %(instance_root)s
-master_url = %(master_url)s
-computer_id = %(computer_id)s
-
-[slapproxy]
-host = %(proxy_host)s
-port = %(proxy_port)s
-database_uri = %(proxy_database)s
diff --git a/slapos/recipe/erp5testnode/testnode.py b/slapos/recipe/erp5testnode/testnode.py
deleted file mode 100644
index f090a3a8668fcc91f4a1012dcac5593156a0334b..0000000000000000000000000000000000000000
--- a/slapos/recipe/erp5testnode/testnode.py
+++ /dev/null
@@ -1,245 +0,0 @@
-from xml_marshaller import xml_marshaller
-import os, xmlrpclib, time, imp
-from glob import glob
-import signal
-import slapos.slap
-import subprocess
-import sys
-import socket
-import pprint
-from SlapOSControler import SlapOSControler
-
-
-class SubprocessError(EnvironmentError):
-  def __init__(self, status_dict):
-    self.status_dict = status_dict
-  def __getattr__(self, name):
-    return self.status_dict[name]
-  def __str__(self):
-    return 'Error %i' % self.status_code
-
-
-from Updater import Updater
-
-process_group_pid_set = set()
-process_pid_file_list = []
-process_command_list = []
-def sigterm_handler(signal, frame):
-  for pgpid in process_group_pid_set:
-    try:
-      os.killpg(pgpid, signal.SIGTERM)
-    except:
-      pass
-  for pid_file in process_pid_file_list:
-    try:
-      os.kill(int(open(pid_file).read().strip()), signal.SIGTERM)
-    except:
-      pass
-  for p in process_command_list:
-    try:
-      subprocess.call(p)
-    except:
-      pass
-  sys.exit(1)
-
-signal.signal(signal.SIGTERM, sigterm_handler)
-
-def safeRpcCall(function, *args):
-  retry = 64
-  while True:
-    try:
-      return function(*args)
-    except (socket.error, xmlrpclib.ProtocolError), e:
-      print >>sys.stderr, e
-      pprint.pprint(args, file(function._Method__name, 'w'))
-      time.sleep(retry)
-      retry += retry >> 1
-
-def getInputOutputFileList(config, command_name):
-  stdout = open(os.path.join(
-                config['instance_root'],'.%s_out' % command_name),
-                'w+')
-  stdout.write("%s\n" % command_name)
-  stderr = open(os.path.join(
-                config['instance_root'],'.%s_err' % command_name),
-                'w+')
-  return (stdout, stderr)
-
-slapos_controler = None
-
-def run(args):
-  config = args[0]
-  slapgrid = None
-  supervisord_pid_file = os.path.join(config['instance_root'], 'var', 'run',
-        'supervisord.pid')
-  subprocess.check_call([config['git_binary'],
-                "config", "--global", "http.sslVerify", "false"])
-  previous_revision = None
-
-  run_software = True
-  # Write our own software.cfg to use the local repository
-  custom_profile_path = os.path.join(config['working_directory'], 'software.cfg')
-  config['custom_profile_path'] = custom_profile_path
-  vcs_repository_list = config['vcs_repository_list']
-  profile_content = None
-  assert len(vcs_repository_list), "we must have at least one repository"
-  for vcs_repository in vcs_repository_list:
-    url = vcs_repository['url']
-    buildout_section_id = vcs_repository.get('buildout_section_id', None)
-    repository_id = buildout_section_id or \
-                                  url.split('/')[-1].split('.')[0]
-    repository_path = os.path.join(config['working_directory'],repository_id)
-    vcs_repository['repository_id'] = repository_id
-    vcs_repository['repository_path'] = repository_path
-    if profile_content is None:
-      profile_content = """
-[buildout]
-extends = %(software_config_path)s
-""" %  {'software_config_path': os.path.join(repository_path,
-                                          config['profile_path'])}
-    if not(buildout_section_id is None):
-      profile_content += """
-[%(buildout_section_id)s]
-repository = %(repository_path)s
-branch = %(branch)s
-""" %  {'buildout_section_id': buildout_section_id,
-        'repository_path' : repository_path,
-        'branch' : vcs_repository.get('branch','master')}
-
-  custom_profile = open(custom_profile_path, 'w')
-  custom_profile.write(profile_content)
-  custom_profile.close()
-  config['repository_path'] = repository_path
-  sys.path.append(repository_path)
-  test_suite_title = config['test_suite_title'] or config['test_suite']
-
-  retry_software = False
-  try:
-    while True:
-      # kill processes from previous loop if any
-      try:
-        for pgpid in process_group_pid_set:
-          try:
-            os.killpg(pgpid, signal.SIGTERM)
-          except:
-            pass
-        process_group_pid_set.clear()
-        full_revision_list = []
-        # Make sure we have local repository
-        for vcs_repository in vcs_repository_list:
-          repository_path = vcs_repository['repository_path']
-          repository_id = vcs_repository['repository_id']
-          if not os.path.exists(repository_path):
-            parameter_list = [config['git_binary'], 'clone',
-                              vcs_repository['url']]
-            if vcs_repository.get('branch') is not None:
-              parameter_list.extend(['-b',vcs_repository.get('branch')])
-            parameter_list.append(repository_path)
-            subprocess.check_call(parameter_list)
-          # Make sure we have local repository
-          updater = Updater(repository_path, git_binary=config['git_binary'])
-          updater.checkout()
-          revision = "-".join(updater.getRevision())
-          full_revision_list.append('%s=%s' % (repository_id, revision))
-        revision = ','.join(full_revision_list)
-        if previous_revision == revision:
-          time.sleep(120)
-          if not(retry_software):
-            continue
-        retry_software = False
-        previous_revision = revision
-
-        print config
-        portal_url = config['test_suite_master_url']
-        test_result_path = None
-        test_result = (test_result_path, revision)
-        if portal_url:
-          if portal_url[-1] != '/':
-            portal_url += '/'
-          portal = xmlrpclib.ServerProxy("%s%s" %
-                      (portal_url, 'portal_task_distribution'),
-                      allow_none=1)
-          master = portal.portal_task_distribution
-          assert master.getProtocolRevision() == 1
-          test_result = safeRpcCall(master.createTestResult,
-            config['test_suite'], revision, [],
-            False, test_suite_title,
-            config['test_node_title'], config['project_title'])
-        print "testnode, test_result : %r" % (test_result,)
-        if test_result:
-          test_result_path, test_revision = test_result
-          if revision != test_revision:
-            for i, repository_revision in enumerate(test_revision.split(',')):
-              vcs_repository = vcs_repository_list[i]
-              repository_path = vcs_repository['repository_path']
-              # other testnodes on other boxes are already ready to test another
-              # revision
-              updater = Updater(repository_path, git_binary=config['git_binary'],
-                                revision=repository_revision.split('-')[1])
-              updater.checkout()
-
-          # Now prepare the installation of SlapOS and create instance
-          slapos_controler = SlapOSControler(config,
-            process_group_pid_set=process_group_pid_set)
-          for method_name in ("runSoftwareRelease", "runComputerPartition"):
-            stdout, stderr = getInputOutputFileList(config, method_name)
-            slapos_method = getattr(slapos_controler, method_name)
-            status_dict = slapos_method(config,
-              environment=config['environment'],
-              process_group_pid_set=process_group_pid_set,
-              stdout=stdout, stderr=stderr
-              )
-            if status_dict['status_code'] != 0:
-              break
-          if status_dict['status_code'] != 0:
-            safeRpcCall(master.reportTaskFailure,
-              test_result_path, status_dict, config['test_node_title'])
-            retry_software = True
-            continue
-
-          partition_path = os.path.join(config['instance_root'],
-                                        config['partition_reference'])
-          run_test_suite_path = os.path.join(partition_path, 'bin',
-                                            'runTestSuite')
-          if not os.path.exists(run_test_suite_path):
-            raise ValueError('No %r provided' % run_test_suite_path)
-
-          run_test_suite_revision = revision
-          if isinstance(revision, tuple):
-            revision = ','.join(revision)
-          # Deal with Shebang size limitation
-          file_object = open(run_test_suite_path, 'r')
-          line = file_object.readline()
-          file_object.close()
-          invocation_list = []
-          if line[:2] == '#!':
-            invocation_list = line[2:].split()
-          invocation_list.extend([run_test_suite_path,
-                                  '--test_suite', config['test_suite'],
-                                  '--revision', revision,
-                                  '--test_suite_title', test_suite_title,
-                                  '--node_quantity', config['node_quantity'],
-                                  '--master_url', config['test_suite_master_url']])
-          run_test_suite = subprocess.Popen(invocation_list)
-          process_group_pid_set.add(run_test_suite.pid)
-          run_test_suite.wait()
-          process_group_pid_set.remove(run_test_suite.pid)
-      except SubprocessError:
-        time.sleep(120)
-        continue
-
-  finally:
-    # Nice way to kill *everything* generated by run process -- process
-    # groups working only in POSIX compilant systems
-    # Exceptions are swallowed during cleanup phase
-    print "going to kill %r" % (process_group_pid_set,)
-    for pgpid in process_group_pid_set:
-      try:
-        os.killpg(pgpid, signal.SIGTERM)
-      except:
-        pass
-    try:
-      if os.path.exists(supervisord_pid_file):
-        os.kill(int(open(supervisord_pid_file).read().strip()), signal.SIGTERM)
-    except:
-      pass
\ No newline at end of file
diff --git a/software/erp5testnode/instance.cfg b/software/erp5testnode/instance.cfg
index 2b0c318416e2e56226958a4c1174bfadc828c8db..5c75e01d537f21a362dc5e16e83070017998f5af 100644
--- a/software/erp5testnode/instance.cfg
+++ b/software/erp5testnode/instance.cfg
@@ -8,11 +8,11 @@ develop-eggs-directory = ${buildout:develop-eggs-directory}
 [testnode]
 recipe = slapos.cookbook:erp5testnode
 
-buildbot_binary = ${buildout:bin-directory}/buildbot
+git_binary = ${git:location}/bin/git
 slapgrid_partition_binary = ${buildout:bin-directory}/slapgrid-cp
 slapgrid_software_binary = ${buildout:bin-directory}/slapgrid-sr
 slapproxy_binary = ${buildout:bin-directory}/slapproxy
 svn_binary = ${subversion:location}/bin/svn
-git_binary = ${git:location}/bin/git
 svnversion_binary = ${subversion:location}/bin/svnversion
+testnode = ${buildout:bin-directory}/testnode
 zip_binary = ${zip:location}/bin/zip
diff --git a/software/erp5testnode/software.cfg b/software/erp5testnode/software.cfg
index 3fdec1ac67996817ffdbf8143f38dd0b00b8e6f1..7060022cf5486fabdeb783a6093016207d7b7a17 100644
--- a/software/erp5testnode/software.cfg
+++ b/software/erp5testnode/software.cfg
@@ -1,8 +1,4 @@
 [buildout]
-extensions =
-  slapos.rebootstrap
-  slapos.zcbworkarounds
-  mr.developer
 
 find-links = http://www.nexedi.org/static/packages/source/slapos.buildout/
     http://dist.repoze.org
@@ -14,10 +10,10 @@ include-site-packages = false
 exec-sitecustomize = false
 
 versions = versions
-rebootstrap-section = python2.6
 
 extends =
-  ../../component/python-2.6/buildout.cfg
+  ../../stack/shacache-client.cfg
+  ../../component/python-2.7/buildout.cfg
   ../../component/subversion/buildout.cfg
   ../../component/git/buildout.cfg
   ../../component/lxml-python/buildout.cfg
@@ -25,37 +21,125 @@ extends =
 
 parts =
   template
-  bootstrap
+  lxml-python
   eggs
   subversion
   zip
   git
+  checkrecipe
 
-[bootstrap]
-recipe = zc.recipe.egg
-eggs = zc.buildout
-suffix =
-scripts =
-    buildout=bootstrap2.6
-arguments = sys.argv[1:] + ["bootstrap"]
+# Separate from site eggs
+allowed-eggs-from-site-packages =
+include-site-packages = false
+exec-sitecustomize = false
+
+# Use only quite well working sites.
+allow-hosts =
+  *.nexedi.org
+  *.python.org
+  *.sourceforge.net
+  dist.repoze.org
+  effbot.org
+  github.com
+  peak.telecommunity.com
+  psutil.googlecode.com
+  www.dabeaz.com
+
+develop =
+  ${:parts-directory}/slapos.cookbook-repository
+  ${:parts-directory}/erp5.util-repository
 
-[rebootstrap]
-section = python2.6
-version = 1
+[checkrecipe]
+recipe = plone.recipe.command
+stop-on-error = true
+update-command = ${:command}
+command = grep parts ${buildout:develop-eggs-directory}/slapos.cookbook.egg-link && grep parts ${buildout:develop-eggs-directory}/erp5.util.egg-link
+
+[slapos.cookbook-repository]
+recipe = plone.recipe.command
+location = ${buildout:parts-directory}/${:_buildout_section_name_}
+stop-on-error = true
+repository = http://git.erp5.org/repos/slapos.git
+branch = erp5testnode
+revision =
+command = ${git:location}/bin/git clone --quiet -b ${:branch} ${:repository} ${:location} && if [ -n ${:revision} ]; then cd ${:location} && ${git:location}/bin/git reset --quiet --hard ${:revision} ; fi
+update-command = cd ${:location} && ${git:location}/bin/git pull --quiet && if [ -n ${:revision} ]; then cd ${:location} && ${git:location}/bin/git reset --quiet --hard ${:revision} ; fi
+
+[erp5.util-repository]
+recipe = plone.recipe.command
+location = ${buildout:parts-directory}/${:_buildout_section_name_}
+stop-on-error = true
+repository = http://git.erp5.org/repos/erp5.git
+branch = master
+revision =
+command = ${git:location}/bin/git clone --quiet -b ${:branch} ${:repository} ${:location} && if [ -n ${:revision} ]; then cd ${:location} && ${git:location}/bin/git reset --quiet --hard ${:revision} ; fi
+update-command = cd ${:location} && ${git:location}/bin/git pull --quiet && if [ -n ${:revision} ]; then cd ${:location} && ${git:location}/bin/git reset --quiet --hard ${:revision} ; fi
 
 [versions]
 # Use SlapOS patched zc.buildout
-zc.buildout = 1.5.3-dev-SlapOS-001
+zc.buildout = 1.5.3-dev-SlapOS-009
 
 [eggs]
 recipe = zc.recipe.egg
+# Just so buildout executes [slapos.cookbook-repository] before [eggs], as
+# - [eggs] references [slapos.cookbook-repository]
+# - [instance-recipe] needs [slapos.cookbook-repository] to be finished
+# - we cannot rely on anything else being executed before [eggs]
+dummy =
+  ${slapos.cookbook-repository:location}
+  ${erp5.util-repository:location}
 eggs =
   ${lxml-python:egg}
+  zc.buildout
+  slapos.libnetworkcache
   slapos.core
   slapos.cookbook
+  erp5.util[testnode]
+
+scripts =
+  testnode = erp5.util.testnode:main
+  slapgrid-cp = slapos.grid.slapgrid:runComputerPartition
+  slapgrid-sr = slapos.grid.slapgrid:runSoftwareRelease
+  slapproxy = slapos.proxy:main
+
+python = python2.7
+
+[lxml-python]
+python = python2.7
 
 [template]
 recipe = slapos.recipe.template
 url = ${:_profile_base_location_}/instance.cfg
 output = ${buildout:directory}/template.cfg
 mode = 0644
+md5sum = 08e3f92bce41efc5bfe044bb9d354786
+
+[networkcache]
+# Romain Courteaud + Sebastien Robin signature certificate
+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-----
+  MIIB8jCCAVugAwIBAgIJAPu2zchZ2BxoMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV
+  BAMMB3RzeGRldjMwHhcNMTExMDE0MTIxNjIzWhcNMTIxMDEzMTIxNjIzWjASMRAw
+  DgYDVQQDDAd0c3hkZXYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrPbh+
+  YGmo6mWmhVb1vTqX0BbeU0jCTB8TK3i6ep3tzSw2rkUGSx3niXn9LNTFNcIn3MZN
+  XHqbb4AS2Zxyk/2tr3939qqOrS4YRCtXBwTCuFY6r+a7pZsjiTNddPsEhuj4lEnR
+  L8Ax5mmzoi9nE+hiPSwqjRwWRU1+182rzXmN4QIDAQABo1AwTjAdBgNVHQ4EFgQU
+  /4XXREzqBbBNJvX5gU8tLWxZaeQwHwYDVR0jBBgwFoAU/4XXREzqBbBNJvX5gU8t
+  LWxZaeQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA07q/rKoE7fAda
+  FED57/SR00OvY9wLlFEF2QJ5OLu+O33YUXDDbGpfUSF9R8l0g9dix1JbWK9nQ6Yd
+  R/KCo6D0sw0ZgeQv1aUXbl/xJ9k4jlTxmWbPeiiPZEqU1W9wN5lkGuLxV4CEGTKU
+  hJA/yXa1wbwIPGvX3tVKdOEWPRXZLg==
+  -----END CERTIFICATE-----