Commit 35b650da authored by Jean-Baptiste Petre's avatar Jean-Baptiste Petre

Merge branch 'master' into xwiki

parents 3bafc19a 7cc1f011
Changes Changes
======= =======
0.17 (unreleased) 0.25 (unreleased)
----------------- -----------------
* No changes yet. * No changes yet.
0.24 (2011-09-19)
-----------------
* mysql: Unhardcode the requested url. [Antoine Catton]
0.23 (2011-09-19)
-----------------
* Clean code in mysql recipe [Cedric de Saint Martin]
* librecipe: Provide createPromiseWrapper method. [Antoine Catton]
* kvm: Expose promisee checks to slapgrid. [Antoine Catton]
* davstorage: Initial version. [Antoine Catton]
* mysql: Support DAV backup. [Antoine Catton]
0.22 (2011-09-12)
-----------------
* Fix haproxy setup for erp5 [Sebastien Robin]
0.21 (2011-09-12)
-----------------
* Update PHP configuration to set session and date options.
[Alain Takoudjou Kamdem]
* Improve logrotate policy and haproxy config for erp5
[Sebastien Robin]
0.20 (2011-09-07)
-----------------
* Update and fix KVM/noVNC installation to be compatible with new WebSocket
protocol (HyBi-10) required by Chrome >= 14 and Firefox >= 7.
[Cedric de Saint Martin]
0.19 (2011-09-06)
-----------------
* Update PHP configuration to disable debug logging. [Cedric de Saint Martin]
0.18 (2011-08-25)
-----------------
* Repackage egg to include needed .bin files. [Cedric de Saint Martin]
0.17 (2011-08-25)
-----------------
* Add XWiki software release [Cedric de Saint Martin]
0.16 (2011-07-15) 0.16 (2011-07-15)
----------------- -----------------
......
include CHANGES.txt include CHANGES.txt
include slapos/recipe/erp5/template/site.zcml include slapos/recipe/erp5/template/site.zcml
recursive-include slapos/recipe *.in recursive-include slapos/recipe *.in
recursive-include slapos/recipe *.bin
recursive-include slapos/recipe README.*.txt recursive-include slapos/recipe README.*.txt
...@@ -15,9 +15,9 @@ configure-options = ...@@ -15,9 +15,9 @@ configure-options =
--with-mysql=${mariadb:location} --with-mysql=${mariadb:location}
--with-zlib-dir=${zlib:location} --with-zlib-dir=${zlib:location}
--with-mcrypt=${libmcrypt:location} --with-mcrypt=${libmcrypt:location}
--enable-libxml
--enable-mbstring --enable-mbstring
--enable-session --enable-session
--disable-all
environment = environment =
PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig
......
...@@ -16,8 +16,8 @@ extends = ...@@ -16,8 +16,8 @@ extends =
[apache-no-ssl] [apache-no-ssl]
# inspired on http://old.aclark.net/team/aclark/blog/a-lamp-buildout-for-wordpress-and-other-php-apps/ # inspired on http://old.aclark.net/team/aclark/blog/a-lamp-buildout-for-wordpress-and-other-php-apps/
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = http://mir2.ovh.net/ftp.apache.org/dist//httpd/httpd-2.2.19.tar.bz2 url = http://mir2.ovh.net/ftp.apache.org/dist//httpd/httpd-2.2.20.tar.bz2
md5sum = 832f96a6ec4b8fc7cf49b9efd4e89060 md5sum = 1ac251431c8c4285f6b085c1d156bb56
configure-options = --enable-authn-alias configure-options = --enable-authn-alias
--enable-bucketeer --enable-bucketeer
--enable-cache --enable-cache
...@@ -41,14 +41,15 @@ configure-options = --enable-authn-alias ...@@ -41,14 +41,15 @@ configure-options = --enable-authn-alias
--enable-proxy-http --enable-proxy-http
--enable-proxy-scgi --enable-proxy-scgi
--enable-so --enable-so
--enable-dav
--enable-dav-fs
--disable-ssl --disable-ssl
--with-included-apr --with-included-apr
--with-z=${zlib:location} --with-z=${zlib:location}
--with-expat=${libexpat:location} --with-expat=${libexpat:location}
--with-pcre=${pcre:location} --with-pcre=${pcre:location}
--with-sqlite3=${sqlite3:location} --with-sqlite3=${sqlite3:location}
--with-dbm=gdbm --with-gdbm=${gdbm:location}
--with-gdm=${gdbm:location}
--without-ssl --without-ssl
--without-lber --without-lber
--without-ldap --without-ldap
...@@ -65,8 +66,8 @@ configure-options = --enable-authn-alias ...@@ -65,8 +66,8 @@ configure-options = --enable-authn-alias
[apache] [apache]
# inspired on http://old.aclark.net/team/aclark/blog/a-lamp-buildout-for-wordpress-and-other-php-apps/ # inspired on http://old.aclark.net/team/aclark/blog/a-lamp-buildout-for-wordpress-and-other-php-apps/
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = http://mir2.ovh.net/ftp.apache.org/dist//httpd/httpd-2.2.19.tar.bz2 url = http://mir2.ovh.net/ftp.apache.org/dist//httpd/httpd-2.2.20.tar.bz2
md5sum = 832f96a6ec4b8fc7cf49b9efd4e89060 md5sum = 1ac251431c8c4285f6b085c1d156bb56
configure-options = --disable-static configure-options = --disable-static
--enable-authn-alias --enable-authn-alias
--enable-bucketeer --enable-bucketeer
...@@ -90,6 +91,8 @@ configure-options = --disable-static ...@@ -90,6 +91,8 @@ configure-options = --disable-static
--enable-proxy-ftp --enable-proxy-ftp
--enable-proxy-http --enable-proxy-http
--enable-proxy-scgi --enable-proxy-scgi
--enable-dav
--enable-dav-fs
--enable-so --enable-so
--enable-ssl --enable-ssl
--with-included-apr --with-included-apr
...@@ -98,8 +101,7 @@ configure-options = --disable-static ...@@ -98,8 +101,7 @@ configure-options = --disable-static
--with-expat=${libexpat:location} --with-expat=${libexpat:location}
--with-pcre=${pcre:location} --with-pcre=${pcre:location}
--with-sqlite3=${sqlite3:location} --with-sqlite3=${sqlite3:location}
--with-dbm=gdbm --with-gdbm=${gdbm:location}
--with-gdm=${gdbm:location}
--without-lber --without-lber
--without-ldap --without-ldap
--without-ndbm --without-ndbm
......
########################################################
# Dropbear - a secure shell client and server for *nix #
# #
# http://matt.ucc.asn.au/dropbear/dropbear.html #
########################################################
[buildout]
extends =
../zlib/buildout.cfg
parts =
dropbear
[dropbear-userspace-patch]
recipe = hexagonit.recipe.download
md5sum = 89f575b9a9586b04ef9073c9c3af13ae
url = ${:_profile_base_location_}/${:filename}
filename = userspace.patch
download-only = true
[dropbear-ipv6-patch]
recipe = hexagonit.recipe.download
md5sum = b30dd58d68829a80eee69188134382ef
url = ${:_profile_base_location_}/${:filename}
filename = ipv6-support.patch
download-only = true
[dropbear]
recipe = hexagonit.recipe.cmmi
md5sum = 0284ea239083f04c8b874e08e1aca243
url = http://matt.ucc.asn.au/dropbear/dropbear-0.53.1.tar.bz2
configure-options =
--prefix=${buildout:parts-directory}/${:_buildout_section_name_}
--with-zlib=${zlib:location}
CFLAGS="-DENABLE_SINGLEUSER "
environment =
CPPFLAGS =-I${zlib:location}/include
LDFLAGS =-Wl,-rpath=${zlib:location}/lib -L${zlib:location}/lib
patches=
${dropbear-userspace-patch:location}/${dropbear-userspace-patch:filename}
${dropbear-ipv6-patch:location}/${dropbear-ipv6-patch:filename}
patch-options=
-p1
@@ -0,0 +1,56 @@
--- dropbear-0.52.orig/svr-runopts.c Wed May 13 20:56:03 2009
+++ dropbear-0.52/svr-runopts.c Wed May 13 22:20:22 2009
@@ -311,27 +311,39 @@
static void addportandaddress(char* spec) {
char *myspec = NULL;
+ char *p = NULL;
if (svr_opts.portcount < DROPBEAR_MAX_PORTS) {
/* We don't free it, it becomes part of the runopt state */
myspec = m_strdup(spec);
- /* search for ':', that separates address and port */
- svr_opts.ports[svr_opts.portcount] = strchr(myspec, ':');
-
- if (svr_opts.ports[svr_opts.portcount] == NULL) {
- /* no ':' -> the whole string specifies just a port */
- svr_opts.ports[svr_opts.portcount] = myspec;
- } else {
- /* Split the address/port */
- svr_opts.ports[svr_opts.portcount][0] = '\0';
- svr_opts.ports[svr_opts.portcount]++;
+ /* [ipv6]:port */
+ if (myspec[0] == '[' && (p = strchr(myspec, ']')) != NULL && *(p+1) == ':') {
+ *p = '\0';
+ p+=2;
+ myspec++;
+ svr_opts.ports[svr_opts.portcount] = p;
svr_opts.addresses[svr_opts.portcount] = myspec;
- }
-
- if (svr_opts.addresses[svr_opts.portcount] == NULL) {
- /* no address given -> fill in the default address */
+ } else if ((p = strchr(myspec, '.')) != NULL) {
+ if ((p = strchr(p, ':')) == NULL) {
+ /* ipv4 */
+ svr_opts.ports[svr_opts.portcount] = m_strdup(DROPBEAR_DEFPORT);
+ svr_opts.addresses[svr_opts.portcount] = myspec;
+ } else {
+ /* ipv4:port */
+ *p = '\0';
+ p++;
+ svr_opts.ports[svr_opts.portcount] = p;
+ svr_opts.addresses[svr_opts.portcount] = myspec;
+ }
+ } else if ((p = strchr(myspec, ':')) != NULL && (p = strchr(p, ':')) != NULL) {
+ /* ipv6 */
+ svr_opts.ports[svr_opts.portcount] = m_strdup(DROPBEAR_DEFPORT);
+ svr_opts.addresses[svr_opts.portcount] = myspec;
+ } else {
+ /* port */
+ svr_opts.ports[svr_opts.portcount] = myspec;
svr_opts.addresses[svr_opts.portcount] = m_strdup(DROPBEAR_DEFADDRESS);
}
diff --git a/options.h b/options.h
index d309ab4..63048e1 100644
--- a/options.h
+++ b/options.h
@@ -287,6 +287,12 @@ be overridden at runtime with -I. 0 disables idle timeouts */
/* The default path. This will often get replaced by the shell */
#define DEFAULT_PATH "/usr/bin:/bin"
+/* The prefix of dropbear environment variable overriding. */
+#define DROPBEAR_OVERRIDE_PREFIX "DROPBEAR_OVERRIDE_"
+#define DROPBEAR_OVERRIDE_PASSWORD DROPBEAR_OVERRIDE_PREFIX "PASSWORD"
+#define DROPBEAR_OVERRIDE_HOME DROPBEAR_OVERRIDE_PREFIX "HOME"
+#define DROPBEAR_OVERRIDE_SHELL DROPBEAR_OVERRIDE_PREFIX "SHELL"
+
/* Some other defines (that mostly should be left alone) are defined
* in sysoptions.h */
#include "sysoptions.h"
diff --git a/runopts.h b/runopts.h
index 83b5861..f8999b9 100644
--- a/runopts.h
+++ b/runopts.h
@@ -86,6 +86,15 @@ typedef struct svr_runopts {
int noauthpass;
int norootpass;
+#ifdef ENABLE_SINGLEUSER
+ int singleuser;
+ struct {
+ char *password;
+ char *home;
+ char *shell;
+ } singleuserenv;
+#endif /* ifdef ENABLE_SINGLEUSER */
+
#ifdef ENABLE_SVR_REMOTETCPFWD
int noremotetcp;
#endif
diff --git a/svr-auth.c b/svr-auth.c
index 87e3c5e..adb2e8b 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -126,6 +126,14 @@ void recv_msg_userauth_request() {
username = buf_getstring(ses.payload, &userlen);
+#ifdef ENABLE_SINGLEUSER
+ /* If userspace enabled, ignore username */
+ if (svr_opts.singleuser) {
+ m_free(username);
+ /* Get the current login of the user running dropbear */
+ username = m_strdup(getlogin());
+ }
+#endif /* ifdef ENABLE_SINGLEUSER */
servicename = buf_getstring(ses.payload, &servicelen);
methodname = buf_getstring(ses.payload, &methodlen);
@@ -228,6 +236,18 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
}
authclear();
fill_passwd(username);
+#ifdef ENABLE_SINGLEUSER
+ if (svr_opts.singleuser) {
+ if (svr_opts.singleuserenv.home != NULL) {
+ m_free(ses.authstate.pw_dir);
+ ses.authstate.pw_dir = m_strdup(svr_opts.singleuserenv.home);
+ }
+ if (svr_opts.singleuserenv.shell != NULL) {
+ m_free(ses.authstate.pw_shell);
+ ses.authstate.pw_shell = m_strdup(svr_opts.singleuserenv.shell);
+ }
+ }
+#endif /* ifdef ENABLE_SINGLEUSER */
ses.authstate.username = m_strdup(username);
}
diff --git a/svr-runopts.c b/svr-runopts.c
index 2e836d2..1c21d7c 100644
--- a/svr-runopts.c
+++ b/svr-runopts.c
@@ -83,6 +83,9 @@ static void printhelp(const char * progname) {
"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
"-K <keepalive> (0 is never, default %d)\n"
"-I <idle_timeout> (0 is never, default %d)\n"
+#ifdef ENABLE_SINGLEUSER
+ "-n Enable the single user mode.\n"
+#endif /* ifdef ENABLE_SINGLEUSER */
#ifdef DEBUG_TRACE
"-v verbose (compiled with DEBUG_TRACE)\n"
#endif
@@ -128,6 +131,12 @@ void svr_getopts(int argc, char ** argv) {
#ifndef DISABLE_ZLIB
opts.enable_compress = 1;
#endif
+#ifdef ENABLE_SINGLEUSER
+ svr_opts.singleuser = 0;
+ svr_opts.singleuserenv.password = NULL;
+ svr_opts.singleuserenv.home = NULL;
+ svr_opts.singleuserenv.shell = NULL;
+#endif /* ifdef ENABLE_SINGLEUSER */
/* not yet
opts.ipv4 = 1;
opts.ipv6 = 1;
@@ -242,6 +251,17 @@ void svr_getopts(int argc, char ** argv) {
case 'u':
/* backwards compatibility with old urandom option */
break;
+#ifdef ENABLE_SINGLEUSER
+ case 'n':
+#ifndef ENABLE_SINGLEUSER_ROOT
+ /* If current user is root */
+ if (getuid() == 0) {
+ dropbear_log(LOG_ERR, "Can't enable singleuser mode as root.");
+ }
+#endif /* ifndef ENABLE_SINGLEUSER_ROOT */
+ svr_opts.singleuser = 1;
+ break;
+#endif /* ifdef ENABLE_SINGLEUSER */
#ifdef DEBUG_TRACE
case 'v':
debug_trace = 1;
@@ -313,6 +333,20 @@ void svr_getopts(int argc, char ** argv) {
}
opts.idle_timeout_secs = val;
}
+#ifdef ENABLE_SINGLEUSER
+ if (svr_opts.singleuser) {
+ dropbear_log(LOG_INFO, "Starting dropbear as single user mode.");
+ svr_opts.singleuserenv.password = getenv(DROPBEAR_OVERRIDE_PASSWORD);
+ svr_opts.singleuserenv.home = getenv(DROPBEAR_OVERRIDE_HOME);
+ if (svr_opts.singleuserenv.home != NULL) {
+ dropbear_log(LOG_INFO, "Single user home is '%s'", svr_opts.singleuserenv.home);
+ }
+ svr_opts.singleuserenv.shell = getenv(DROPBEAR_OVERRIDE_SHELL);
+ if (svr_opts.singleuserenv.shell != NULL) {
+ dropbear_log(LOG_INFO, "Single user shell is '%s'", svr_opts.singleuserenv.shell);
+ }
+ }
+#endif /* ifdef ENABLE_SINGLEUSER */
}
static void addportandaddress(char* spec) {
[buildout]
extends =
../librsync/buildout.cfg
../patch/buildout.cfg
parts =
duplicity
[duplicity]
recipe = slapos.recipe.build
url = http://code.launchpad.net/duplicity/0.6-series/0.6.15/+download/duplicity-0.6.15.tar.gz
md5sum = 88f3c990f41fde86cd7d5af5a1bc7b81
patch = ${:_profile_base_location_}/duplicity-ipv6.patch 32c44816a9a59401e233ef622bf50223
script =
import os
import tempfile
import sys
workingdir = guessworkdir(self.extract(self.download(%(url)r, %(md5sum)r)))
os.chdir(workingdir)
self.applyPatchList('${:patch}', patch_binary='${patch:location}/bin/patch', patch_options='-p0', cwd=workingdir)
call([sys.executable, os.path.join(workingdir, 'setup.py'),
'build_ext',
'--include-dirs', os.path.join('${librsync:location}', 'include'),
'--library-dirs', os.path.join('${librsync:location}', 'lib'),
'--rpath', os.path.join('${librsync:location}', 'lib')])
call([sys.executable, os.path.join(workingdir, 'setup.py'),
'install',
'--prefix', %(location)r])
os.chdir(os.path.join(%(location)r, 'bin'))
paths = [os.path.join(%(location)r, 'lib', 'python%%s.%%s' %% sys.version_info[:2], 'site-packages')]
from zc.buildout.easy_install import scripts
scripts([('duplicity', '__builtin__', 'execfile')], [], sys.executable, '${buildout:bin-directory}', arguments='%%r' %% os.path.abspath('duplicity'), extra_paths=paths)
scripts([('rdiffdir', '__builtin__', 'execfile')], [], sys.executable, '${buildout:bin-directory}', arguments='%%r' %% os.path.abspath('rdiffdir'), extra_paths=paths)
=== modified file 'src/urlparse_2_5.py'
--- src/urlparse_2_5.py 2011-03-06 15:12:33 +0000
+++ src/urlparse_2_5.py 2011-08-31 14:21:06 +0000
@@ -109,18 +109,19 @@
password = property(get_password)
def get_hostname(self):
- netloc = self.netloc
- if "@" in netloc:
- netloc = _rsplit(netloc, "@", 1)[1]
- if ":" in netloc:
- netloc = netloc.split(":", 1)[0]
- return netloc.lower() or None
+ netloc = self.netloc.split('@')[-1]
+ if '[' in netloc and ']' in netloc:
+ return netloc.split(']')[0][1:].lower()
+ elif ':' in netloc:
+ return netloc.split(':')[0].lower()
+ elif netloc == '':
+ return None
+ else:
+ return netloc.lower()
hostname = property(get_hostname)
def get_port(self):
- netloc = self.netloc
- if "@" in netloc:
- netloc = _rsplit(netloc, "@", 1)[1]
+ netloc = self.netloc.split('@')[-1].split(']')[-1]
if ":" in netloc:
port = netloc.split(":", 1)[1]
return int(port, 10)
...@@ -13,7 +13,9 @@ parts = ...@@ -13,7 +13,9 @@ parts =
[git] [git]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = http://kernel.org/pub/software/scm/git/git-1.7.4.5.tar.bz2 # url = http://kernel.org/pub/software/scm/git/git-1.7.4.5.tar.bz2
# Circumvent kernel.org downtime
url = http://ftp.free.fr/mirrors/ftp.kernel.org/software/scm/git/git-1.7.4.5.tar.bz2
md5sum = 2fa6c4c847ed87523cf55de54af457eb md5sum = 2fa6c4c847ed87523cf55de54af457eb
configure-options = configure-options =
--with-curl=${curl:location} --with-curl=${curl:location}
......
...@@ -19,6 +19,7 @@ install-pyuno-egg = no ...@@ -19,6 +19,7 @@ install-pyuno-egg = no
hack-openoffice-python = no hack-openoffice-python = no
flavour = libreoffice flavour = libreoffice
# here, two %s are used, first one is for directory name (eg. x86_64), and second one is for filename (eg. x86-64). # here, two %s are used, first one is for directory name (eg. x86_64), and second one is for filename (eg. x86-64).
# base-url = http://download.documentfoundation.org/libreoffice/stable/3.3.2/rpm/%s/LibO_3.3.2_Linux_%s_install-rpm_en-US.tar.gz #base-url = http://download.documentfoundation.org/libreoffice/stable/3.3.2/rpm/%s/LibO_3.3.2_Linux_%s_install-rpm_en-US.tar.gz
base-url = http://download.documentfoundation.org/libreoffice/stable/3.3.4/rpm/%s/LibO_3.3.4_Linux_%s_install-rpm_en-US.tar.gz
cpio = ${cpio:location}/bin/cpio cpio = ${cpio:location}/bin/cpio
...@@ -9,7 +9,9 @@ parts = ...@@ -9,7 +9,9 @@ parts =
[libtiff] [libtiff]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = http://download.osgeo.org/libtiff/tiff-3.9.5.tar.gz #url = http://download.osgeo.org/libtiff/tiff-3.9.5.tar.gz
# server is down - circumvent
url = http://www.imagemagick.org/download/delegates/tiff-3.9.5.tar.gz
md5sum = 8fc7ce3b4e1d0cc8a319336967815084 md5sum = 8fc7ce3b4e1d0cc8a319336967815084
configure-options = configure-options =
--disable-static --disable-static
......
...@@ -4,7 +4,7 @@ parts = ...@@ -4,7 +4,7 @@ parts =
[libuuid] [libuuid]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = http://ftp.kernel.org/pub/linux/utils/util-linux/v2.18/util-linux-ng-2.18.tar.bz2 url = http://mirror.be.gbxs.net/pub/linux/utils/util-linux-ng/v2.18/util-linux-ng-2.18.tar.bz2
md5sum = 2f5f71e6af969d041d73ab778c141a77 md5sum = 2f5f71e6af969d041d73ab778c141a77
configure-options = configure-options =
--disable-static --disable-static
......
...@@ -12,9 +12,9 @@ parts = ...@@ -12,9 +12,9 @@ parts =
[mariadb] [mariadb]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
version = 5.2.7 version = 5.3.1-beta
url = http://www.percona.com/downloads/MariaDB/mariadb-${:version}/kvm-tarbake-jaunty-x86/mariadb-${:version}.tar.gz url = http://downloads.askmonty.org/f/mariadb-${:version}/kvm-tarbake-jaunty-x86/mariadb-${:version}.tar.gz/from/http://mirror.layerjet.com/mariadb
md5sum = 06b9b102946a3606b38348c0ebf18367 md5sum = 5b3a94de1c1fcaa193edbbc8d7f8ffe4
# compile directory is required to build mysql plugins. # compile directory is required to build mysql plugins.
keep-compile-dir = true keep-compile-dir = true
# configure: how to avoid searching for my.cnf? # configure: how to avoid searching for my.cnf?
......
...@@ -27,27 +27,37 @@ download-only = true ...@@ -27,27 +27,37 @@ download-only = true
md5sum = 3418477f64500cd2a8dce046f5d72fec md5sum = 3418477f64500cd2a8dce046f5d72fec
[memcached] [memcached]
<= memcached-1.4.5 <= memcached-1.4.6
[memcached-1.4.6]
<= memcached-common
url = http://memcached.googlecode.com/files/memcached-1.4.6.tar.gz
md5sum = 243e5d82de27e6e45caf0ebfd400e41a
patches =
${memcached-fix-array-subscript-is-above-array-bounds:location}/${memcached-fix-array-subscript-is-above-array-bounds:filename}
[memcached-1.4.5] [memcached-1.4.5]
<= memcached-common <= memcached-common
url = http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz url = http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz
md5sum = 583441a25f937360624024f2881e5ea8 md5sum = 583441a25f937360624024f2881e5ea8
patches =
${memcached-strict-aliasing-patch:location}/${memcached-strict-aliasing-patch:filename}
${memcached-fix-array-subscript-is-above-array-bounds:location}/${memcached-fix-array-subscript-is-above-array-bounds:filename}
${memcached-gcc4.6.patch:location}/${memcached-gcc4.6.patch:filename}
[memcached-1.4.4] [memcached-1.4.4]
<= memcached-common <= memcached-common
url = http://memcached.googlecode.com/files/memcached-1.4.4.tar.gz url = http://memcached.googlecode.com/files/memcached-1.4.4.tar.gz
md5sum = 5ca5b24de347e97ac1f48f3785b4178a md5sum = 5ca5b24de347e97ac1f48f3785b4178a
patches =
${memcached-strict-aliasing-patch:location}/${memcached-strict-aliasing-patch:filename}
${memcached-fix-array-subscript-is-above-array-bounds:location}/${memcached-fix-array-subscript-is-above-array-bounds:filename}
${memcached-gcc4.6.patch:location}/${memcached-gcc4.6.patch:filename}
[memcached-common] [memcached-common]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
configure-options = configure-options =
--with-libevent=${libevent:location} --with-libevent=${libevent:location}
patches =
${memcached-strict-aliasing-patch:location}/${memcached-strict-aliasing-patch:filename}
${memcached-fix-array-subscript-is-above-array-bounds:location}/${memcached-fix-array-subscript-is-above-array-bounds:filename}
${memcached-gcc4.6.patch:location}/${memcached-gcc4.6.patch:filename}
patch-options = -p1 patch-options = -p1
environment = environment =
LDFLAGS =-Wl,-rpath=${libevent:location}/lib LDFLAGS =-Wl,-rpath=${libevent:location}/lib
################################################################
# OpenSSH Portable - a secure shell client and server for *nix #
# #
# http://www.openssh.com/ #
################################################################
[buildout]
extends =
../openssl/buildout.cfg
parts =
openssh
[openssh]
recipe = hexagonit.recipe.cmmi
md5sum = 0541579adf9d55abb15ef927048d372e
url = http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-5.8p2.tar.gz
configure-option =
-I${zlib:location}/include
-L${zlib:location}/lib
-I${openssl:location}/include
-L${openssl:location}/lib
--with-ssl-dir=${openssl:location}/lib
--prefix=${buildout:parts-directory}/${:_buildout_section_name_}
--libdir=lib
[buildout] [buildout]
extends = extends =
../../stack/shacache-client.cfg ../../stack/shacache-client.cfg
../m2crypto/buildout.cfg ../bison/buildout.cfg
../bzip2/buildout.cfg
../gdbm/buildout.cfg
../gettext/buildout.cfg
../glib/buildout.cfg
../libxml2/buildout.cfg
../libxslt/buildout.cfg
../lxml-python/buildout.cfg ../lxml-python/buildout.cfg
../m4/buildout.cfg
../ncurses/buildout.cfg
../openssl/buildout.cfg
../pkgconfig/buildout.cfg
../popt/buildout.cfg
../python-2.7/buildout.cfg ../python-2.7/buildout.cfg
../readline/buildout.cfg
../sqlite3/buildout.cfg
../swig/buildout.cfg
../zlib/buildout.cfg
parts = parts =
slapos slapos
cfg-environment
sh-environment
find-links = find-links =
http://www.nexedi.org/static/packages/source/slapos.buildout/ http://www.nexedi.org/static/packages/source/slapos.buildout/
...@@ -27,22 +44,58 @@ include-site-packages = false ...@@ -27,22 +44,58 @@ include-site-packages = false
exec-sitecustomize = false exec-sitecustomize = false
allowed-eggs-from-site-packages = allowed-eggs-from-site-packages =
[lxml-python] [environment]
python = python2.7 # Note: For now original PATH is appeneded to the end, as not all tools are
# provided by SlapOS
PATH=${bison:location}/bin:${bzip2:location}/bin:${gettext:location}/bin:${glib:location}/bin:${libxml2:location}/bin:${libxslt:location}/bin:${m4:location}/bin:${ncurses:location}/bin:${openssl:location}/bin:${pkgconfig:location}/bin:${python2.7:location}/bin:${readline:location}/bin:${sqlite3:location}/bin:${swig:location}/bin:${buildout:bin-directory}:$PATH
CFLAGS=-I${bzip2:location}/include -I${gdbm:location}/include -I${gettext:location}/include -I${glib:location}/include -I${libxml2:location}/include -I${libxslt:location}/include -I${ncurses:location}/include -I${openssl:location}/include -I${popt:location}/include -I${readline:location}/include -I${sqlite3:location}/include -I${zlib:location}/include
CPPFLAGS=${:CFLAGS}
LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath=${bzip2:location}/lib -L${gdbm:location}/lib -Wl,-rpath=${gdbm:location}/lib -L${gettext:location}/lib -Wl,-rpath=${gettext:location}/lib -L${glib:location}/lib -Wl,-rpath=${glib:location}/lib -L${libxml2:location}/lib -Wl,-rpath=${libxml2:location}/lib -L${libxslt:location}/lib -Wl,-rpath=${libxslt:location}/lib -L${ncurses:location}/lib -Wl,-rpath=${ncurses:location}/lib -L${openssl:location}/lib -Wl,-rpath=${openssl:location}/lib -L${popt:location}/lib -Wl,-rpath=${popt:location}/lib -L${readline:location}/lib -Wl,-rpath=${readline:location}/lib -L${sqlite3:location}/lib -Wl,-rpath=${sqlite3:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${libxml2:location}/lib/pkgconfig:${libxslt:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig:${popt:location}/lib/pkgconfig:${python2.7:location}/lib/pkconfig:${sqlite3:location}/lib/pkconfig
LD_LIBRARY_PATH=${bzip2:location}/lib:${gdbm:location}/lib:${gettext:location}/lib:${glib:location}/lib:${libxml2:location}/lib:${libxslt:location}/lib:${ncurses:location}/lib:${openssl:location}/lib:${popt:location}/lib:${readline:location}/lib:${sqlite3:location}/lib:${zlib:location}/lib
[cfg-environment]
# Section exposes SlapOS default environment as configuration file.
recipe = collective.recipe.template
input = inline:
[environment]
PATH=${environment:PATH}
CFLAGS=${environment:CFLAGS}
CPPFLAGS=${environment:CPPFLAGS}
LDFLAGS=${environment:LDFLAGS}
PKG_CONFIG_PATH=${environment:PKG_CONFIG_PATH}
LD_LIBRARY_PATH=${environment:LD_LIBRARY_PATH}
output = ${buildout:directory}/environment.cfg
[M2Crypto] [sh-environment]
# Section exposes SlapOS default environment as sh file.
recipe = collective.recipe.template
input = inline:
export PATH="${environment:PATH}"
export CFLAGS="${environment:CFLAGS}"
export CPPFLAGS="${environment:CPPFLAGS}"
export LDFLAGS="${environment:LDFLAGS}"
export PKG_CONFIG_PATH="${environment:PKG_CONFIG_PATH}"
export LD_LIBRARY_PATH="${environment:LD_LIBRARY_PATH}"
export PS1="[SlapOS env Active] $PS1"
output = ${buildout:directory}/environment.sh
[lxml-python]
python = python2.7 python = python2.7
[slapos] [slapos]
recipe = z3c.recipe.scripts recipe = z3c.recipe.scripts
python = python2.7 python = python2.7
eggs = eggs =
${M2Crypto:egg}
slapos.libnetworkcache slapos.libnetworkcache
zc.buildout zc.buildout
${lxml-python:egg} ${lxml-python:egg}
slapos.core slapos.core
initialization =
import os
os.environ['PATH'] = os.path.expandvars('${environment:PATH}')
# control scripts generation in order to avoid reinstalling bin/buildout # control scripts generation in order to avoid reinstalling bin/buildout
scripts = scripts =
generate-signature-key = slapos.signature:run generate-signature-key = slapos.signature:run
...@@ -50,32 +103,31 @@ scripts = ...@@ -50,32 +103,31 @@ scripts =
slapos-request = slapos.console:request slapos-request = slapos.console:request
slapformat = slapos.format:main slapformat = slapos.format:main
slapgrid = slapos.grid.slapgrid:run slapgrid = slapos.grid.slapgrid:run
slapgrid-sr = slapos.grid.slapgrid:runSoftwareRelease
slapgrid-cp = slapos.grid.slapgrid:runComputerPartition slapgrid-cp = slapos.grid.slapgrid:runComputerPartition
slapgrid-sr = slapos.grid.slapgrid:runSoftwareRelease
slapgrid-ur = slapos.grid.slapgrid:runUsageReport slapgrid-ur = slapos.grid.slapgrid:runUsageReport
slapgrid-supervisorctl = slapos.grid.svcbackend:supervisorctl slapgrid-supervisorctl = slapos.grid.svcbackend:supervisorctl
slapgrid-supervisord = slapos.grid.svcbackend:supervisord slapgrid-supervisord = slapos.grid.svcbackend:supervisord
slapproxy = slapos.proxy:main slapproxy = slapos.proxy:main
[versions] [versions]
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
Jinja2 = 2.6 Jinja2 = 2.6
M2Crypto = 0.21.1
Werkzeug = 0.7.1 Werkzeug = 0.7.1
buildout-versions = 1.6 collective.recipe.template = 1.9
hexagonit.recipe.cmmi = 1.5.0 hexagonit.recipe.cmmi = 1.5.0
lxml = 2.3 lxml = 2.3
meld3 = 0.6.7 meld3 = 0.6.7
netaddr = 0.7.5 netaddr = 0.7.5
setuptools = 0.6c12dev-r88846 setuptools = 0.6c12dev-r88846
slapos.core = 0.12 slapos.core = 0.14
slapos.libnetworkcache = 0.4 slapos.libnetworkcache = 0.9
xml-marshaller = 0.9.7 xml-marshaller = 0.9.7
z3c.recipe.scripts = 1.0.1 z3c.recipe.scripts = 1.0.1
zc.recipe.egg = 1.3.2 zc.recipe.egg = 1.3.2
# Required by: # Required by:
# slapos.core==0.12 # slapos.core==0.14
Flask = 0.7.2 Flask = 0.7.2
# Required by: # Required by:
...@@ -83,13 +135,14 @@ Flask = 0.7.2 ...@@ -83,13 +135,14 @@ Flask = 0.7.2
hexagonit.recipe.download = 1.5.0 hexagonit.recipe.download = 1.5.0
# Required by: # Required by:
# slapos.core==0.12 # slapos.core==0.14
netifaces = 0.5 netifaces = 0.5
# Required by: # Required by:
# slapos.core==0.12 # slapos.core==0.14
supervisor = 3.0a10 supervisor = 3.0a10
# Required by: # Required by:
# slapos.core==0.12 # slapos.core==0.14
zope.interface = 3.7.0 zope.interface = 3.7.0
...@@ -17,8 +17,8 @@ filename = stunnel-4-hooks.py ...@@ -17,8 +17,8 @@ filename = stunnel-4-hooks.py
[stunnel-4] [stunnel-4]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = ftp://ftp.stunnel.org/stunnel/stunnel-4.39.tar.gz url = ftp://ftp.bit.nl/mirror/stunnel/obsolete/4.x/stunnel-4.42.tar.gz
md5sum = 853739119a8364daea750154af6d7e79 md5sum = f64a7426166d397dd826deef33bb6aa0
pre-configure-hook = ${stunnel-4-hook-download:location}/${stunnel-4-hook-download:filename}:pre_configure_hook pre-configure-hook = ${stunnel-4-hook-download:location}/${stunnel-4-hook-download:filename}:pre_configure_hook
configure-options = configure-options =
--enable-ipv6 --enable-ipv6
......
From 700c7896b832d6e4fb0185f0d5382b01f94e7141 Mon Sep 17 00:00:00 2001
From: Alan Hourihane <alanh@vmware.com>
Date: Fri, 25 Feb 2011 11:05:27 +0000
Subject: Add _X_HIDDEN to xgeExtRegister to fix build problems on 64bit
---
diff --git a/src/Xge.c b/src/Xge.c
index 0655e00..d28a4f0 100644
--- a/src/Xge.c
+++ b/src/Xge.c
@@ -292,7 +292,7 @@ _xgeEventToWire(Display* dpy, XEvent* re, xEvent* event)
* Extensions need to register callbacks for their events.
*/
Bool
-xgeExtRegister(Display* dpy, int offset, XExtensionHooks* callbacks)
+_X_HIDDEN xgeExtRegister(Display* dpy, int offset, XExtensionHooks* callbacks)
{
XGEExtNode* newExt;
XGEData* xge_data;
--
cgit v0.8.3-6-g21f6
...@@ -114,7 +114,8 @@ patch-options = -p1 ...@@ -114,7 +114,8 @@ patch-options = -p1
[libXext-patch-link-error] [libXext-patch-link-error]
# Fixes libXext.la link error # Fixes libXext.la link error
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = http://cgit.freedesktop.org/xorg/lib/libXext/patch/?id=700c7896b832d6e4fb0185f0d5382b01f94e7141 #url = http://cgit.freedesktop.org/xorg/lib/libXext/patch/?id=700c7896b832d6e4fb0185f0d5382b01f94e7141
url = ${:_profile_base_location_}/${:filename}
download-only = true download-only = true
filename = 700c7896b832d6e4fb0185f0d5382b01f94e7141.patch filename = 700c7896b832d6e4fb0185f0d5382b01f94e7141.patch
md5sum = 52635ef694ee6f1acb642a77ee8eb010 md5sum = 52635ef694ee6f1acb642a77ee8eb010
......
...@@ -23,9 +23,9 @@ versions = versions ...@@ -23,9 +23,9 @@ versions = versions
[xtrabackup-build-patch-download] [xtrabackup-build-patch-download]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/${:filename} url = ${:_profile_base_location_}/${:filename}
md5sum = e018df8bb3ed672891388556b8e91e35 md5sum = 95b2c2ef625f88d85bf8876269a19372
download-only = true download-only = true
filename = xtrabackup_build.patch filename = xtrabackup-1.6.2_build.patch
[allow_force_ibbackup.patch] [allow_force_ibbackup.patch]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
...@@ -36,8 +36,8 @@ filename = ${:_buildout_section_name_} ...@@ -36,8 +36,8 @@ filename = ${:_buildout_section_name_}
[xtrabackup] [xtrabackup]
recipe = hexagonit.recipe.cmmi recipe = hexagonit.recipe.cmmi
url = http://www.percona.com/redir/downloads/XtraBackup/XtraBackup-1.6/source/xtrabackup-1.6.tar.gz url = http://www.percona.com/redir/downloads/XtraBackup/xtrabackup-1.6.2/source/xtrabackup-1.6.2.tar.gz
md5sum = 7c263723312cba36539df4cd7a119744 md5sum = 933243ae362156c98f1211eb87b3b4ea
make-binary = true make-binary = true
patches = patches =
${xtrabackup-build-patch-download:location}/${xtrabackup-build-patch-download:filename} ${xtrabackup-build-patch-download:location}/${xtrabackup-build-patch-download:filename}
......
diff --git a/Makefile b/Makefile diff -ur xtrabackup-1.6.2.orig/Makefile xtrabackup-1.6.2/Makefile
index dbb15d5..852a627 100644 --- xtrabackup-1.6.2.orig/Makefile 2011-07-19 05:16:22.000000000 +0900
--- a/Makefile +++ xtrabackup-1.6.2/Makefile 2011-07-27 17:58:38.108925111 +0900
+++ b/Makefile @@ -137,5 +137,5 @@
@@ -137,5 +137,5 @@ $(TARGET): xtrabackup.o $(INNODBOBJS) $(MYSQLOBJS)
clean: clean:
rm -f *.o xtrabackup_* rm -f *.o xtrabackup_*
install: install:
- install -m 755 innobackupex-1.5.1 $(BIN_DIR) - install -m 755 innobackupex-1.5.1 $(BIN_DIR)
+ install -m 755 innobackupex-1.5.1 $(BIN_DIR)/innobackupex + install -m 755 innobackupex-1.5.1 $(BIN_DIR)/innobackupex
install -m 755 xtrabackup_* $(BIN_DIR) install -m 755 xtrabackup_* $(BIN_DIR)
diff --git a/utils/build.sh b/utils/build.sh diff -ur xtrabackup-1.6.2.orig/utils/build.sh xtrabackup-1.6.2/utils/build.sh
index 3f0cee7..c41dc89 100755 --- xtrabackup-1.6.2.orig/utils/build.sh 2011-07-19 05:16:22.000000000 +0900
--- a/utils/build.sh +++ xtrabackup-1.6.2/utils/build.sh 2011-07-27 18:01:53.809212142 +0900
+++ b/utils/build.sh @@ -21,12 +21,14 @@
@@ -21,12 +21,14 @@ function usage()
{ {
echo "Build an xtrabackup binary against the specified InnoDB flavor." echo "Build an xtrabackup binary against the specified InnoDB flavor."
echo echo
...@@ -29,7 +27,7 @@ index 3f0cee7..c41dc89 100755 ...@@ -29,7 +27,7 @@ index 3f0cee7..c41dc89 100755
exit -1 exit -1
} }
@@ -79,7 +81,12 @@ function build_server() @@ -79,7 +81,12 @@
{ {
echo "Configuring the server" echo "Configuring the server"
cd $server_dir cd $server_dir
...@@ -43,19 +41,22 @@ index 3f0cee7..c41dc89 100755 ...@@ -43,19 +41,22 @@ index 3f0cee7..c41dc89 100755
eval $configure_cmd eval $configure_cmd
echo "Building the server" echo "Building the server"
@@ -92,9 +99,10 @@ function build_xtrabackup() @@ -92,12 +99,13 @@
echo "Building XtraBackup" echo "Building XtraBackup"
mkdir $build_dir mkdir $build_dir
cp $top_dir/Makefile $top_dir/xtrabackup.c $build_dir cp $top_dir/Makefile $top_dir/xtrabackup.c $build_dir
+ cp $top_dir/innobackupex $build_dir/innobackupex-1.5.1 + cp $top_dir/innobackupex $build_dir/innobackupex-1.5.1
# Read XTRABACKUP_VERSION from the VERSION file
. $top_dir/VERSION
cd $build_dir cd $build_dir
- $MAKE_CMD $xtrabackup_target - $MAKE_CMD $xtrabackup_target XTRABACKUP_VERSION=$XTRABACKUP_VERSION
+ $MAKE_CMD PREFIX=$1 $xtrabackup_target + $MAKE_CMD $xtrabackup_target XTRABACKUP_VERSION=$XTRABACKUP_VERSION PREFIX=$1
cd $top_dir cd $top_dir
} }
@@ -103,11 +111,36 @@ function build_tar4ibd() @@ -106,11 +114,36 @@
echo "Building tar4ibd" echo "Building tar4ibd"
unpack_and_patch libtar-1.2.11.tar.gz tar4ibd_libtar-1.2.11.patch unpack_and_patch libtar-1.2.11.tar.gz tar4ibd_libtar-1.2.11.patch
cd libtar-1.2.11 cd libtar-1.2.11
...@@ -93,7 +94,7 @@ index 3f0cee7..c41dc89 100755 ...@@ -93,7 +94,7 @@ index 3f0cee7..c41dc89 100755
################################################################################ ################################################################################
# Do all steps to build the server, xtrabackup and tar4ibd # Do all steps to build the server, xtrabackup and tar4ibd
# Expects the following variables to be set before calling: # Expects the following variables to be set before calling:
@@ -136,9 +169,15 @@ function build_all() @@ -141,9 +174,15 @@
build_server build_server
...@@ -111,7 +112,7 @@ index 3f0cee7..c41dc89 100755 ...@@ -111,7 +112,7 @@ index 3f0cee7..c41dc89 100755
} }
if ! test -f xtrabackup.c if ! test -f xtrabackup.c
@@ -148,6 +187,15 @@ then @@ -153,6 +192,15 @@
fi fi
type=$1 type=$1
...@@ -127,20 +128,20 @@ index 3f0cee7..c41dc89 100755 ...@@ -127,20 +128,20 @@ index 3f0cee7..c41dc89 100755
top_dir=`pwd` top_dir=`pwd`
case "$type" in case "$type" in
@@ -161,9 +209,10 @@ case "$type" in @@ -166,9 +214,10 @@
--with-plugins=innobase \ --with-plugins=innobase \
--with-zlib-dir=bundled \ --with-zlib-dir=bundled \
--enable-shared \ --enable-shared \
- --with-extra-charsets=complex" - --with-extra-charsets=all"
+ --with-extra-charsets=complex \ + --with-extra-charsets=all \
+ --prefix=$2" + --prefix=$2"
- build_all - build_all
+ build_all $2 + build_all $2
;; ;;
"innodb55" | "5.5") "innodb55" | "5.5")
@@ -225,7 +274,7 @@ case "$type" in @@ -230,7 +279,7 @@
build_server build_server
......
...@@ -2,7 +2,7 @@ from setuptools import setup, find_packages ...@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
import glob import glob
import os import os
version = '0.17-dev' version = '0.25-dev'
name = 'slapos.cookbook' name = 'slapos.cookbook'
long_description = open("README.txt").read() + "\n" + \ long_description = open("README.txt").read() + "\n" + \
open("CHANGES.txt").read() + "\n" open("CHANGES.txt").read() + "\n"
...@@ -40,6 +40,7 @@ setup(name=name, ...@@ -40,6 +40,7 @@ setup(name=name,
entry_points={ entry_points={
'zc.buildout': [ 'zc.buildout': [
'download = slapos.recipe.download:Recipe', 'download = slapos.recipe.download:Recipe',
'davstorage = slapos.recipe.davstorage:Recipe',
'erp5 = slapos.recipe.erp5:Recipe', 'erp5 = slapos.recipe.erp5:Recipe',
'erp5testnode = slapos.recipe.erp5testnode:Recipe', 'erp5testnode = slapos.recipe.erp5testnode:Recipe',
'helloworld = slapos.recipe.helloworld:Recipe', 'helloworld = slapos.recipe.helloworld:Recipe',
...@@ -52,10 +53,10 @@ setup(name=name, ...@@ -52,10 +53,10 @@ setup(name=name,
'mysql = slapos.recipe.mysql:Recipe', 'mysql = slapos.recipe.mysql:Recipe',
'nbdserver = slapos.recipe.nbdserver:Recipe', 'nbdserver = slapos.recipe.nbdserver:Recipe',
'nosqltestbed = slapos.recipe.nosqltestbed:NoSQLTestBed', 'nosqltestbed = slapos.recipe.nosqltestbed:NoSQLTestBed',
'osoeslaptraining = slapos.recipe.osoeslaptraining:Request', 'lamp = slapos.recipe.lamp:Request',
'osoeslaptraining.request = slapos.recipe.osoeslaptraining:Request', 'lamp.request = slapos.recipe.lamp:Request',
'osoeslaptraining.static = slapos.recipe.osoeslaptraining:Static', 'lamp.static = slapos.recipe.lamp:Static',
'osoeslaptraining.simple = slapos.recipe.osoeslaptraining:Simple', 'lamp.simple = slapos.recipe.lamp:Simple',
'proactive = slapos.recipe.proactive:Recipe', 'proactive = slapos.recipe.proactive:Recipe',
'sheepdogtestbed = slapos.recipe.sheepdogtestbed:SheepDogTestBed', 'sheepdogtestbed = slapos.recipe.sheepdogtestbed:SheepDogTestBed',
'siptester = slapos.recipe.siptester:SipTesterRecipe', 'siptester = slapos.recipe.siptester:SipTesterRecipe',
......
##############################################################################
#
# 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.
#
##############################################################################
from slapos.recipe.librecipe import BaseSlapRecipe
import os
import subprocess
import pkg_resources
import zc.buildout
import zc.recipe.egg
import sys
class Recipe(BaseSlapRecipe):
def getTemplateFilename(self, template_name):
return pkg_resources.resource_filename(__name__,
'template/%s' % template_name)
def _install(self):
self.path_list = []
self.requirements, self.ws = self.egg.working_set()
document_root = self.createDataDirectory('www')
self.apache_config = self.installApache(document_root)
self.setConnectionDict(
dict(url='https://[%s]:%s/' % (self.apache_config['ip'],
self.apache_config['port']),
user=self.apache_config['user'],
password=self.apache_config['password']),
)
return self.path_list
def installApache(self, document_root, ip=None, port=None):
if ip is None:
ip=self.getGlobalIPv6Address()
if port is None:
port = '9080'
htpasswd_config = self.createHtpasswd()
ssl_config = self.createCertificate(size=2048)
apache_config = dict(
pid_file=os.path.join(self.run_directory, 'httpd.pid'),
lock_file=os.path.join(self.run_directory, 'httpd.lock'),
davlock_db=os.path.join(self.run_directory, 'davdb.lock'),
ip=ip,
port=port,
error_log=os.path.join(self.log_directory, 'httpd-error.log'),
access_log=os.path.join(self.log_directory, 'httpd-access.log'),
document_root=document_root,
modules_dir=self.options['apache_modules_dir'],
mime_types=self.options['apache_mime_file'],
server_root=self.work_directory,
email_address='admin@vifib.net',
htpasswd_file=htpasswd_config['htpasswd_file'],
ssl_certificate=ssl_config['certificate'],
ssl_key=ssl_config['key'],
)
httpd_config_file = self.createConfigurationFile('httpd.conf',
self.substituteTemplate(self.getTemplateFilename('httpd.conf.in'),
apache_config))
self.path_list.append(httpd_config_file)
apache_runner = zc.buildout.easy_install.scripts(
[('httpd', 'slapos.recipe.librecipe.execute', 'execute')],
self.ws, sys.executable, self.wrapper_directory,
arguments=[self.options['apache_binary'],
'-f', httpd_config_file,
'-DFOREGROUND',
]
)[0]
self.path_list.append(apache_runner)
return dict(ip=apache_config['ip'],
port=apache_config['port'],
user=htpasswd_config['user'],
password=htpasswd_config['password']
)
def createHtpasswd(self):
htpasswd = self.createConfigurationFile('htpasswd', '')
self.path_list.append(htpasswd)
password = self.generatePassword()
user = 'user'
subprocess.check_call([self.options['apache_htpasswd'],
'-bc', htpasswd,
user, password
])
return dict(htpasswd_file=htpasswd,
user=user,
password=password)
def createCertificate(self, size=1024, subject='/C=FR/L=Marcq-en-Baroeul/O=Nexedi'):
key_file = os.path.join(self.etc_directory, 'httpd.key')
self.path_list.append(key_file)
certificate_file = os.path.join(self.etc_directory, 'httpd.crt')
self.path_list.append(certificate_file)
subprocess.check_call([self.options['openssl_binary'],
'req', '-x509', '-nodes',
'-newkey', 'rsa:%s' % size,
'-subj', str(subject),
'-out', certificate_file,
'-keyout', key_file
])
return dict(key=key_file,
certificate=certificate_file)
ServerRoot "%(server_root)s"
Listen [%(ip)s]:%(port)s
# Needed modules
LoadModule authn_file_module "%(modules_dir)s/mod_authn_file.so"
LoadModule authz_host_module "%(modules_dir)s/mod_authz_host.so"
LoadModule authz_user_module "%(modules_dir)s/mod_authz_user.so"
LoadModule auth_basic_module "%(modules_dir)s/mod_auth_basic.so"
LoadModule auth_digest_module "%(modules_dir)s/mod_auth_digest.so"
LoadModule log_config_module "%(modules_dir)s/mod_log_config.so"
LoadModule headers_module "%(modules_dir)s/mod_headers.so"
LoadModule setenvif_module "%(modules_dir)s/mod_setenvif.so"
LoadModule ssl_module "%(modules_dir)s/mod_ssl.so"
LoadModule mime_module "%(modules_dir)s/mod_mime.so"
LoadModule dav_module "%(modules_dir)s/mod_dav.so"
LoadModule dav_fs_module "%(modules_dir)s/mod_dav_fs.so"
LoadModule dir_module "%(modules_dir)s/mod_dir.so"
ServerAdmin %(email_address)s
# Quiet Server header (if not, Apache give its life history)
# It's safer
ServerTokens ProductOnly
DocumentRoot "%(document_root)s"
PidFile "%(pid_file)s"
LockFile "%(lock_file)s"
DavLockDB "%(davlock_db)s"
<Directory />
Options FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
</Directory>
<Directory %(document_root)s>
Options Indexes MultiViews
AllowOverride None
Order allow,deny
Allow from all
Dav On
# Security Rules to avoid DDoS Attacks
DavDepthInfinity Off
LimitXMLRequestBody 0
# Cross-Origin Resources Sharing
Header always set Access-Control-Max-Age "0"
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "OPTIONS, GET, HEAD, POST, PUT, DELETE, PROPFIND"
Header always set Access-Control-Allow-Headers "Content-Type, X-Requested-With, X-HTTP-Method-Override, Accept, Authorization, Depth"
SetEnvIf Origin "(.+)" ORIGIN=$1
Header always set Access-Control-Allow-Origin %%{ORIGIN}e
AuthType Basic
AuthName "WebDAV Storage"
AuthUserFile "%(htpasswd_file)s"
<LimitExcept OPTIONS>
Require valid-user
</LimitExcept>
</Directory>
ErrorLog "%(error_log)s"
LogLevel warn
LogFormat "%%h %%l %%u %%t \"%%r\" %%>s %%b \"%%{Referer}i\" \"%%{User-Agent}i\"" combined
LogFormat "%%h %%l %%u %%t \"%%r\" %%>s %%b" common
CustomLog "%(access_log)s" common
DefaultType text/plain
TypesConfig "%(mime_types)s"
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLEngine on
SSLCertificateFile "%(ssl_certificate)s"
SSLCertificateKeyFile "%(ssl_key)s"
...@@ -546,13 +546,23 @@ class Recipe(BaseSlapRecipe): ...@@ -546,13 +546,23 @@ class Recipe(BaseSlapRecipe):
} }
def installHaproxy(self, ip, port, name, server_check_path, url_list): def installHaproxy(self, ip, port, name, server_check_path, url_list):
server_template = """ server %(name)s %(address)s cookie %(name)s check inter 20s rise 2 fall 4""" # inter must be quite short in order to detect quickly an unresponsive node
# and to detect quickly a node which is back
# rise must be minimal possible : 1, indeed, a node which is back don't need
# to sleep more time and we can give him work immediately
# fall should be quite sort. with inter at 3, and fall at 2, a node will be
# considered as dead after 6 seconds.
# maxconn should be set as the maximum thread we have per zope, like this
# haproxy will manage the queue of request with the possibility to
# move a request to another node if the initially selected one is dead
server_template = """ server %(name)s %(address)s cookie %(name)s check inter 3s rise 1 fall 2 maxconn %(cluster_zope_thread_amount)s"""
config = dict(name=name, ip=ip, port=port, config = dict(name=name, ip=ip, port=port,
server_check_path=server_check_path,) server_check_path=server_check_path,)
i = 1 i = 1
server_list = [] server_list = []
for url in url_list: for url in url_list:
server_list.append(server_template % dict(name='%s_%s' % (name, i), server_list.append(server_template % dict(name='%s_%s' % (name, i),
cluster_zope_thread_amount=self.options.get('cluster_zope_thread_amount', 1),
address=url)) address=url))
i += 1 i += 1
config['server_text'] = '\n'.join(server_list) config['server_text'] = '\n'.join(server_list)
......
...@@ -34,9 +34,9 @@ RequestHeader unset REMOTE_USER ...@@ -34,9 +34,9 @@ RequestHeader unset REMOTE_USER
# Log configuration # Log configuration
ErrorLog "%(error_log)s" ErrorLog "%(error_log)s"
LogFormat "%%h %%{REMOTE_USER}i %%l %%u %%t \"%%r\" %%>s %%b \"%%{Referer}i\" \"%%{User-Agent}i\"" combined # Default apache log format with request time in microsecond at the end
LogFormat "%%h %%{REMOTE_USER}i %%l %%u %%t \"%%r\" %%>s %%b" common LogFormat "%%h %%l %%u %%t \"%%r\" %%>s %%b \"%%{Referer}i\" \"%%{User-Agent}i\" %%D" combined
CustomLog "%(access_log)s" common CustomLog "%(access_log)s" combined
# Directory protection # Directory protection
<Directory /> <Directory />
......
%(file_list)s { %(file_list)s {
daily daily
dateext dateext
rotate 30
compress compress
notifempty notifempty
sharedscripts sharedscripts
......
...@@ -37,6 +37,9 @@ import hashlib ...@@ -37,6 +37,9 @@ import hashlib
class Recipe(BaseSlapRecipe): class Recipe(BaseSlapRecipe):
# To avoid magic numbers
VNC_BASE_PORT = 5900
def _install(self): def _install(self):
""" """
Set the connection dictionnary for the computer partition and create a list Set the connection dictionnary for the computer partition and create a list
...@@ -49,43 +52,58 @@ class Recipe(BaseSlapRecipe): ...@@ -49,43 +52,58 @@ class Recipe(BaseSlapRecipe):
self.path_list = [] self.path_list = []
self.requirements, self.ws = self.egg.working_set() self.requirements, self.ws = self.egg.working_set()
self.cron_d = self.installCrond() self.cron_d = self.installCrond()
self.ca_conf = self.installCertificateAuthority() self.ca_conf = self.installCertificateAuthority()
self.key_path, self.certificate_path = self.requestCertificate('noVNC') self.key_path, self.certificate_path = self.requestCertificate('noVNC')
# Install the socket_connection_attempt script
catcher = zc.buildout.easy_install.scripts(
[('check_port_listening', __name__ + '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()) kvm_conf = self.installKvm(vnc_ip = self.getLocalIPv4Address())
vnc_port = 5900 + kvm_conf['vnc_display'] vnc_port = Recipe.VNC_BASE_PORT + kvm_conf['vnc_display']
noVNC_conf = self.installNoVnc(source_ip = self.getGlobalIPv6Address(), noVNC_conf = self.installNoVnc(source_ip = self.getGlobalIPv6Address(),
source_port = 6080, source_port = 6080,
target_ip = kvm_conf['vnc_ip'], target_ip = kvm_conf['vnc_ip'],
target_port = vnc_port, target_port = vnc_port)
python_path = kvm_conf['python_path'])
self.linkBinary() self.linkBinary()
self.computer_partition.setConnectionDict(dict( self.computer_partition.setConnectionDict(dict(
url = "https://[%s]:%s/vnc.html?host=[%s]&port=%s&encrypt=1" % (noVNC_conf['source_ip'], url = "https://[%s]:%s/vnc.html?host=[%s]&port=%s&encrypt=1" % (noVNC_conf['source_ip'],
noVNC_conf['source_port'], noVNC_conf['source_port'],
noVNC_conf['source_ip'], noVNC_conf['source_ip'],
noVNC_conf['source_port'] noVNC_conf['source_port']
), ),
password = kvm_conf['vnc_passwd'])) password = kvm_conf['vnc_passwd']))
return self.path_list return self.path_list
def installKvm(self, vnc_ip): def installKvm(self, vnc_ip):
""" """
Create kvm configuration dictionnary and instanciate a wrapper for kvm and Create kvm configuration dictionnary and instanciate a wrapper for kvm and
kvm controller kvm controller
Parameters : IP the vnc server is listening on Parameters : IP the vnc server is listening on
Returns : Dictionnary kvm_conf Returns : Dictionnary kvm_conf
""" """
kvm_conf = dict(vnc_ip = vnc_ip) kvm_conf = dict(vnc_ip = vnc_ip)
connection_found = False connection_found = False
for tap_interface, dummy in self.parameter_dict['ip_list']: for tap_interface, dummy in self.parameter_dict['ip_list']:
# Get an ip associated to a tap interface # Get an ip associated to a tap interface
...@@ -95,13 +113,13 @@ class Recipe(BaseSlapRecipe): ...@@ -95,13 +113,13 @@ class Recipe(BaseSlapRecipe):
raise NotImplementedError("Do not support ip without tap interface") raise NotImplementedError("Do not support ip without tap interface")
kvm_conf['tap_interface'] = tap_interface kvm_conf['tap_interface'] = tap_interface
# Disk path # Disk path
kvm_conf['disk_path'] = os.path.join(self.data_root_directory, kvm_conf['disk_path'] = os.path.join(self.data_root_directory,
'virtual.qcow2') 'virtual.qcow2')
kvm_conf['socket_path'] = os.path.join(self.var_directory, 'qmp_socket') kvm_conf['socket_path'] = os.path.join(self.var_directory, 'qmp_socket')
# XXX Weak password # XXX Weak password
##XXX -Vivien: add an option to generate one password for all instances ##XXX -Vivien: add an option to generate one password for all instances
# and/or to input it yourself # and/or to input it yourself
kvm_conf['vnc_passwd'] = binascii.hexlify(os.urandom(4)) kvm_conf['vnc_passwd'] = binascii.hexlify(os.urandom(4))
...@@ -120,7 +138,7 @@ class Recipe(BaseSlapRecipe): ...@@ -120,7 +138,7 @@ class Recipe(BaseSlapRecipe):
int(self.options['disk_size']))], shell=True) int(self.options['disk_size']))], shell=True)
if retcode != 0: if retcode != 0:
raise OSError, "Disk creation failed!" raise OSError, "Disk creation failed!"
# Options nbd_ip and nbd_port are provided by slapos master # Options nbd_ip and nbd_port are provided by slapos master
kvm_conf['nbd_ip'] = self.parameter_dict['nbd_ip'] kvm_conf['nbd_ip'] = self.parameter_dict['nbd_ip']
kvm_conf['nbd_port'] = self.parameter_dict['nbd_port'] kvm_conf['nbd_port'] = self.parameter_dict['nbd_port']
...@@ -134,41 +152,48 @@ class Recipe(BaseSlapRecipe): ...@@ -134,41 +152,48 @@ class Recipe(BaseSlapRecipe):
kvm_conf['ram_size'] = self.options['ram_size'] kvm_conf['ram_size'] = self.options['ram_size']
kvm_conf['vnc_display'] = 1 kvm_conf['vnc_display'] = 1
# Instanciate KVM # Instanciate KVM
kvm_template_location = pkg_resources.resource_filename( kvm_template_location = pkg_resources.resource_filename(
__name__, os.path.join( __name__, 'template/kvm_run.in')
'template', 'kvm_run.in'))
kvm_runner_path = self.createRunningWrapper("kvm",
kvm_runner_path = self.createRunningWrapper("kvm",
self.substituteTemplate(kvm_template_location, self.substituteTemplate(kvm_template_location,
kvm_conf)) kvm_conf))
self.path_list.append(kvm_runner_path) self.path_list.append(kvm_runner_path)
# Instanciate KVM controller # Instanciate KVM controller
kvm_controller_template_location = pkg_resources.resource_filename( kvm_controller_template_location = pkg_resources.resource_filename(
__name__, os.path.join( __name__, 'template/kvm_controller_run.in')
'template',
'kvm_controller_run.in' )) kvm_controller_runner_path = self.createRunningWrapper("kvm_controller",
kvm_controller_runner_path = self.createRunningWrapper("kvm_controller",
self.substituteTemplate(kvm_controller_template_location, self.substituteTemplate(kvm_controller_template_location,
kvm_conf)) kvm_conf))
self.path_list.append(kvm_controller_runner_path) self.path_list.append(kvm_controller_runner_path)
# Instanciate Slapmonitor # Instanciate Slapmonitor
##slapmonitor_runner_path = self.instanciate_wrapper("slapmonitor", ##slapmonitor_runner_path = self.instanciate_wrapper("slapmonitor",
# [database_path, pid_file_path, python_path]) # [database_path, pid_file_path, python_path])
# Instanciate Slapreport # Instanciate Slapreport
##slapreport_runner_path = self.instanciate_wrapper("slapreport", ##slapreport_runner_path = self.instanciate_wrapper("slapreport",
# [database_path, python_path]) # [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'],
)
self.createPromiseWrapper("vnc_promise",
self.substituteTemplate(self.port_listening_promise_path,
self.port_listening_promise_conf,
)
)
return kvm_conf return kvm_conf
def installNoVnc(self, source_ip, source_port, target_ip, target_port, def installNoVnc(self, source_ip, source_port, target_ip, target_port):
python_path):
""" """
Create noVNC configuration dictionnary and instanciate Websockify proxy Create noVNC configuration dictionnary and instanciate Websockify proxy
...@@ -181,14 +206,20 @@ class Recipe(BaseSlapRecipe): ...@@ -181,14 +206,20 @@ class Recipe(BaseSlapRecipe):
noVNC_conf = {} noVNC_conf = {}
noVNC_conf['source_ip'] = source_ip noVNC_conf['source_ip'] = source_ip
noVNC_conf['source_port'] = source_port noVNC_conf['source_port'] = source_port
# Install numpy.
# XXX-Cedric : this looks like a hack. Do we have better solution, knowing
# That websockify is not an egg?
numpy = zc.buildout.easy_install.install(['numpy'], self.options['eggs-directory'])
environment = dict(PYTHONPATH='%s' % numpy.entries[0])
# Instanciate Websockify # Instanciate Websockify
websockify_runner_path = zc.buildout.easy_install.scripts([('websockify', websockify_runner_path = zc.buildout.easy_install.scripts([('websockify',
'slapos.recipe.librecipe.execute', 'execute_wait')], self.ws, 'slapos.recipe.librecipe.execute', 'executee_wait')], self.ws,
sys.executable, self.wrapper_directory, arguments=[ sys.executable, self.wrapper_directory, arguments=[
[python_path.strip(), [sys.executable.strip(),
self.options['websockify_path'], self.options['websockify_path'],
'--web', '--web',
self.options['noVNC_location'], self.options['noVNC_location'],
...@@ -197,11 +228,22 @@ class Recipe(BaseSlapRecipe): ...@@ -197,11 +228,22 @@ class Recipe(BaseSlapRecipe):
'--ssl-only', '--ssl-only',
'%s:%s' % (source_ip, source_port), '%s:%s' % (source_ip, source_port),
'%s:%s' % (target_ip, target_port)], '%s:%s' % (target_ip, target_port)],
[self.certificate_path, self.key_path]] [self.certificate_path, self.key_path],
environment]
)[0] )[0]
self.path_list.append(websockify_runner_path) self.path_list.append(websockify_runner_path)
# 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 return noVNC_conf
def linkBinary(self): def linkBinary(self):
...@@ -225,7 +267,7 @@ class Recipe(BaseSlapRecipe): ...@@ -225,7 +267,7 @@ class Recipe(BaseSlapRecipe):
os.symlink(target, link) os.symlink(target, link)
self.logger.debug('Created link %r -> %r' % (link, target)) self.logger.debug('Created link %r -> %r' % (link, target))
self.path_list.append(link) self.path_list.append(link)
def installCertificateAuthority(self, ca_country_code='XX', def installCertificateAuthority(self, ca_country_code='XX',
ca_email='xx@example.com', ca_state='State', ca_city='City', ca_email='xx@example.com', ca_state='State', ca_city='City',
ca_company='Company'): ca_company='Company'):
...@@ -284,7 +326,7 @@ class Recipe(BaseSlapRecipe): ...@@ -284,7 +326,7 @@ class Recipe(BaseSlapRecipe):
ca_crl=os.path.join(config['ca_dir'], 'crl'), ca_crl=os.path.join(config['ca_dir'], 'crl'),
certificate_authority_path=config['ca_dir'] certificate_authority_path=config['ca_dir']
) )
def requestCertificate(self, name): def requestCertificate(self, name):
hash = hashlib.sha512(name).hexdigest() hash = hashlib.sha512(name).hexdigest()
key = os.path.join(self.ca_private, hash + self.ca_key_ext) key = os.path.join(self.ca_private, hash + self.ca_key_ext)
...@@ -296,7 +338,7 @@ class Recipe(BaseSlapRecipe): ...@@ -296,7 +338,7 @@ class Recipe(BaseSlapRecipe):
parser.set('certificate', 'certificate_file', certificate) parser.set('certificate', 'certificate_file', certificate)
parser.write(open(os.path.join(self.ca_request_dir, hash), 'w')) parser.write(open(os.path.join(self.ca_request_dir, hash), 'w'))
return key, certificate return key, certificate
def installCrond(self): def installCrond(self):
timestamps = self.createDataDirectory('cronstamps') timestamps = self.createDataDirectory('cronstamps')
cron_output = os.path.join(self.log_directory, 'cron-output') cron_output = os.path.join(self.log_directory, 'cron-output')
......
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)
#!/usr/bin/env sh
"%(check_port_listening_script)s" "%(hostname)s" "%(port)s"
exit $?
...@@ -124,7 +124,7 @@ class BaseRecipe(BaseSlapRecipe): ...@@ -124,7 +124,7 @@ class BaseRecipe(BaseSlapRecipe):
self.path_list.append(config_file) self.path_list.append(config_file)
self.path_list.append(self.createConfigurationFile('php.ini', self.path_list.append(self.createConfigurationFile('php.ini',
self.substituteTemplate(pkg_resources.resource_filename(__name__, self.substituteTemplate(pkg_resources.resource_filename(__name__,
'template/php.ini.in'), {}))) 'template/php.ini.in'), dict(tmp_directory=self.tmp_directory))))
self.path_list.extend(zc.buildout.easy_install.scripts([( self.path_list.extend(zc.buildout.easy_install.scripts([(
'httpd', 'httpd',
__name__ + '.apache', 'runApache')], self.ws, __name__ + '.apache', 'runApache')], self.ws,
......
[PHP] [PHP]
engine = On engine = On
safe_mode = Off safe_mode = Off
expose_php = On expose_php = Off
error_reporting = E_ALL | E_STRICT error_reporting = E_ALL | E_STRICT
display_errors = On display_errors = Off
display_startup_errors = On display_startup_errors = Off
log_errors = On log_errors = On
log_errors_max_len = 1024 log_errors_max_len = 1024
ignore_repeated_errors = Off ignore_repeated_errors = Off
ignore_repeated_source = Off ignore_repeated_source = Off
session.save_path = "%(tmp_directory)s"
session.auto_start = 1
date.timezone = Europe/Paris
\ No newline at end of file
...@@ -60,6 +60,7 @@ class BaseSlapRecipe: ...@@ -60,6 +60,7 @@ class BaseSlapRecipe:
'xml_report') 'xml_report')
self.destroy_script_location = os.path.join(self, self.work_directory, self.destroy_script_location = os.path.join(self, self.work_directory,
'sbin', 'destroy') 'sbin', 'destroy')
self.promise_directory = os.path.join(self.etc_directory, 'promise')
# default directory structure information # default directory structure information
self.default_directory_list = [ self.default_directory_list = [
...@@ -71,6 +72,7 @@ class BaseSlapRecipe: ...@@ -71,6 +72,7 @@ class BaseSlapRecipe:
self.etc_directory, # CP/etc - configuration container self.etc_directory, # CP/etc - configuration container
self.wrapper_directory, # CP/etc/run - for wrappers self.wrapper_directory, # CP/etc/run - for wrappers
self.wrapper_report_directory, # CP/etc/report - for report wrappers self.wrapper_report_directory, # CP/etc/report - for report wrappers
self.promise_directory, # CP/etc/promise - for promise checking scripts
self.var_directory, # CP/var - partition "internal" container for logs, self.var_directory, # CP/var - partition "internal" container for logs,
# and another metadata # and another metadata
self.wrapper_xml_report_directory, # CP/var/xml_report - for xml_report wrappers self.wrapper_xml_report_directory, # CP/var/xml_report - for xml_report wrappers
...@@ -243,3 +245,14 @@ class BaseSlapRecipe: ...@@ -243,3 +245,14 @@ class BaseSlapRecipe:
def _install(self): def _install(self):
"""Hook which shall be implemented in children class""" """Hook which shall be implemented in children class"""
raise NotImplementedError('Shall be implemented by subclass') raise NotImplementedError('Shall be implemented by subclass')
def createPromiseWrapper(self, promise_name, file_content):
"""Create a promise wrapper.
This wrapper aim to check if the software release is doing its job.
Return the promise file path.
"""
promise_path = os.path.join(self.promise_directory, promise_name)
self._writeExecutable(promise_path, file_content)
return promise_path
...@@ -39,6 +39,25 @@ def executee(args): ...@@ -39,6 +39,25 @@ def executee(args):
env[k] = v env[k] = v
os.execve(exec_list[0], exec_list + sys.argv[1:], env) os.execve(exec_list[0], exec_list + sys.argv[1:], env)
def executee_wait(args):
"""Portable execution with process replacement and environment manipulation"""
exec_list = list(args[0])
file_list = list(args[1])
environment = args[2]
env = os.environ.copy()
for k,v in environment.iteritems():
env[k] = v
sleep = 60
while True:
ready = True
for f in file_list:
if not os.path.exists(f):
print 'File %r does not exists, sleeping for %s' % (f, sleep)
ready = False
if ready:
break
time.sleep(sleep)
os.execve(exec_list[0], exec_list + sys.argv[1:], env)
def sig_handler(signal, frame): def sig_handler(signal, frame):
print 'Received signal %r, killing children and exiting' % signal print 'Received signal %r, killing children and exiting' % signal
......
...@@ -31,6 +31,8 @@ import pkg_resources ...@@ -31,6 +31,8 @@ import pkg_resources
import sys import sys
import zc.buildout import zc.buildout
import ConfigParser import ConfigParser
import re
import urlparse
class Recipe(BaseSlapRecipe): class Recipe(BaseSlapRecipe):
def getTemplateFilename(self, template_name): def getTemplateFilename(self, template_name):
...@@ -44,17 +46,18 @@ class Recipe(BaseSlapRecipe): ...@@ -44,17 +46,18 @@ class Recipe(BaseSlapRecipe):
# self.cron_d is a directory, where cron jobs can be registered # self.cron_d is a directory, where cron jobs can be registered
self.cron_d = self.installCrond() self.cron_d = self.installCrond()
self.logrotate_d, self.logrotate_backup = self.installLogrotate() self.logrotate_d, self.logrotate_backup = self.installLogrotate()
mysql_conf = self.installMysqlServer(self.getLocalIPv4Address(), 45678) mysql_conf = self.installMysqlServer(self.getLocalIPv4Address(), 45678)
self.mysql_backup_directory = mysql_conf['backup_directory']
ca_conf = self.installCertificateAuthority() ca_conf = self.installCertificateAuthority()
key, certificate = self.requestCertificate('MySQL') key, certificate = self.requestCertificate('MySQL')
stunnel_conf = self.installStunnel(self.getGlobalIPv6Address(), stunnel_conf = self.installStunnel(self.getGlobalIPv6Address(),
self.getLocalIPv4Address(), 12345, mysql_conf['tcp_port'], self.getLocalIPv4Address(), 12345, mysql_conf['tcp_port'],
certificate, key, ca_conf['ca_crl'], certificate, key, ca_conf['ca_crl'],
ca_conf['certificate_authority_path']) ca_conf['certificate_authority_path'])
self.linkBinary() self.linkBinary()
self.setConnectionDict(dict( self.setConnectionDict(dict(
stunnel_ip = stunnel_conf['public_ip'], stunnel_ip = stunnel_conf['public_ip'],
...@@ -107,7 +110,7 @@ class Recipe(BaseSlapRecipe): ...@@ -107,7 +110,7 @@ class Recipe(BaseSlapRecipe):
)[0] )[0]
self.path_list.append(wrapper) self.path_list.append(wrapper)
return cron_d return cron_d
def installLogrotate(self): def installLogrotate(self):
"""Installs logortate main configuration file and registers its to cron""" """Installs logortate main configuration file and registers its to cron"""
logrotate_d = os.path.abspath(os.path.join(self.etc_directory, logrotate_d = os.path.abspath(os.path.join(self.etc_directory,
...@@ -232,7 +235,7 @@ class Recipe(BaseSlapRecipe): ...@@ -232,7 +235,7 @@ class Recipe(BaseSlapRecipe):
self.path_list.append(wrapper) self.path_list.append(wrapper)
return stunnel_conf return stunnel_conf
def installMysqlServer(self, ip, port, database='db', user='user', def installMysqlServer(self, ip, port, database='db', user='user',
template_filename=None, mysql_conf=None): template_filename=None, mysql_conf=None):
if mysql_conf is None: if mysql_conf is None:
...@@ -267,16 +270,11 @@ class Recipe(BaseSlapRecipe): ...@@ -267,16 +270,11 @@ class Recipe(BaseSlapRecipe):
mysql_conf)) mysql_conf))
mysql_script_list = [] mysql_script_list = []
for x_database, x_user, x_password in \ mysql_script_list.append(pkg_resources.resource_string(__name__,
[(mysql_conf['mysql_database'], 'template/initmysql.sql.in') % {
mysql_conf['mysql_user'], 'mysql_database': mysql_conf['mysql_database'],
mysql_conf['mysql_password']), 'mysql_user': mysql_conf['mysql_user'],
]: 'mysql_password': mysql_conf['mysql_password']})
mysql_script_list.append(pkg_resources.resource_string(__name__,
'template/initmysql.sql.in') % {
'mysql_database': x_database,
'mysql_user': x_user,
'mysql_password': x_password})
mysql_script_list.append('EXIT') mysql_script_list.append('EXIT')
mysql_script = '\n'.join(mysql_script_list) mysql_script = '\n'.join(mysql_script_list)
self.path_list.extend(zc.buildout.easy_install.scripts([('mysql_update', self.path_list.extend(zc.buildout.easy_install.scripts([('mysql_update',
...@@ -329,7 +327,31 @@ class Recipe(BaseSlapRecipe): ...@@ -329,7 +327,31 @@ class Recipe(BaseSlapRecipe):
incremental_backup])[0] incremental_backup])[0]
self.path_list.append(backup_controller) self.path_list.append(backup_controller)
mysql_backup_cron = os.path.join(self.cron_d, 'mysql_backup') mysql_backup_cron = os.path.join(self.cron_d, 'mysql_backup')
open(mysql_backup_cron, 'w').write('0 0 * * * ' + backup_controller) open(mysql_backup_cron, 'w').write('0 0 * * * %r' % str(backup_controller))
self.path_list.append(mysql_backup_cron) self.path_list.append(mysql_backup_cron)
mysql_conf.update(backup_directory=incremental_backup)
# The return could be more explicit database, user ... # The return could be more explicit database, user ...
remote_url = self.installWebDAVBackup()
remote_backup_cron = os.path.join(self.cron_d, 'remote_backup')
with open(remote_backup_cron, 'w') as file_:
file_.write('1 0 * * * %s' % ' '.join([
'%r' % str(self.options['duplicity_binary']),
'--no-encryption',
'%r' % str(backup_directory), '%r' % str(remote_url),
]))
return mysql_conf return mysql_conf
def installWebDAVBackup(self):
computer_partition = self.request(
self.options['davstorage-software-url'],
'davstorage',
'mysql_backup',
)
url = re.sub('^http', 'webdav', computer_partition.getConnectionParameter('url'))
url = list(urlparse.urlparse(url))
url[1] = '%(user)s:%(password)s@%(netloc)s' % {
'user': computer_partition.getConnectionParameter('user'),
'password': computer_partition.getConnectionParameter('password'),
'netloc': url[1],
}
return urlparse.urlunparse(url)
[buildout]
parts =
instance
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
[instance]
recipe = ${instance-recipe:egg}:${instance-recipe:module}
dcrond_binary = ${dcron:location}/sbin/crond
logrotate_binary = ${logrotate:location}/usr/sbin/logrotate
apache_binary = ${apache:location}/bin/httpd
apache_modules_dir = ${apache:location}/modules/
apache_mime_file = ${apache:location}/conf/mime.types
apache_htpasswd = ${apache:location}/bin/htpasswd
openssl_binary = ${openssl:location}/bin/openssl
[buildout]
find-links +=
http://www.nexedi.org/static/packages/source/slapos.buildout/
versions = versions
extends =
../../component/apache/buildout.cfg
../../component/dcron/buildout.cfg
../../component/logrotate/buildout.cfg
../../stack/shacache-client.cfg
../../component/lxml-python/buildout.cfg
../../component/python-2.7/buildout.cfg
# 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
parts =
template
lxml-python
apache
logrotate
dcron
eggs
instance-recipe-egg
unzip= true
[eggs]
recipe = zc.recipe.egg
eggs =
[instance-recipe]
egg = slapos.cookbook
module = davstorage
[instance-recipe-egg]
recipe = zc.recipe.egg
python = python2.7
eggs = ${instance-recipe:egg}
[template]
# Default template for the instance.
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance.cfg
md5sum = 51b6213889573ae7b1dec0bd65384432
output = ${buildout:directory}/template.cfg
mode = 0644
[lxml-python]
python = python2.7
[versions]
zc.buildout = 1.5.3-dev-SlapOS-009
Jinja2 = 2.6
Werkzeug = 0.7.1
buildout-versions = 1.6
hexagonit.recipe.cmmi = 1.5.0
hexagonit.recipe.download = 1.5.0
meld3 = 0.6.7
slapos.cookbook = 0.24
slapos.recipe.template = 1.1
# Required by:
# slapos.core==0.14
Flask = 0.7.2
# Required by:
# slapos.cookbook==0.24
PyXML = 0.8.4
# Required by:
# slapos.recipe.template==1.1
collective.recipe.template = 1.9
# Required by:
# slapos.cookbook==0.24
# slapos.core==0.14
# xml-marshaller==0.9.7
lxml = 2.3
# Required by:
# slapos.cookbook==0.24
netaddr = 0.7.6
# Required by:
# slapos.core==0.14
netifaces = 0.4
# Required by:
# slapos.cookbook==0.24
# slapos.core==0.14
# zc.buildout==1.5.3-dev-SlapOS-009
# zc.recipe.egg==1.3.2
setuptools = 0.6c12dev-r88846
# Required by:
# slapos.cookbook==0.24
slapos.core = 0.14
# Required by:
# slapos.core==0.14
supervisor = 3.0a10
# Required by:
# slapos.cookbook==0.24
xml-marshaller = 0.9.7
# Required by:
# slapos.cookbook==0.24
zc.recipe.egg = 1.3.2
# Required by:
# slapos.core==0.14
zope.interface = 3.7.0
...@@ -77,4 +77,4 @@ xml-marshaller = 0.9.7 ...@@ -77,4 +77,4 @@ xml-marshaller = 0.9.7
setuptools = 0.6c12dev-r88795 setuptools = 0.6c12dev-r88795
# Use SlapOS patched zc.buildout # Use SlapOS patched zc.buildout
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
...@@ -15,71 +15,63 @@ command = ...@@ -15,71 +15,63 @@ command =
update-command = update-command =
[versions] [versions]
Jinja2 = 2.5.5 Jinja2 = 2.6
Werkzeug = 0.6.2 Werkzeug = 0.7.1
buildout-versions = 1.6
hexagonit.recipe.cmmi = 1.5.0 hexagonit.recipe.cmmi = 1.5.0
lxml = 2.3 lxml = 2.3
meld3 = 0.6.7 meld3 = 0.6.7
numpy = 1.6.1
plone.recipe.command = 1.1 plone.recipe.command = 1.1
slapos.cookbook = 0.15
slapos.recipe.template = 1.1 slapos.recipe.template = 1.1
z3c.recipe.scripts = 1.0.1
# Required by: # Required by:
# slapos.core==0.9 # slapos.core==0.14
Flask = 0.7.2 Flask = 0.7.2
# Required by: # Required by:
# slapos.cookbook==0.15 # slapos.cookbook==0.20
PyXML = 0.8.4 PyXML = 0.8.4
# Required by: # Required by:
# slapos.recipe.template==1.1 # slapos.recipe.template==1.1
collective.recipe.template = 1.8 collective.recipe.template = 1.9
# Required by: # Required by:
# hexagonit.recipe.cmmi==1.5.0 # hexagonit.recipe.cmmi==1.5.0
hexagonit.recipe.download = 1.5.0 hexagonit.recipe.download = 1.5.0
# Required by: # Required by:
# slapos.cookbook==0.15 # slapos.cookbook==0.20
netaddr = 0.7.5 netaddr = 0.7.5
# Required by: # Required by:
# slapos.core==0.9 # slapos.core==0.14
netifaces = 0.5 netifaces = 0.5
# Required by: # Required by:
# slapos.cookbook==0.15 # slapos.cookbook==0.20
# slapos.core==0.9 # slapos.core==0.14
# zc.buildout==1.5.3-dev-SlapOS-005 # zc.buildout==1.5.3-dev-SlapOS-005
# zc.recipe.egg==1.3.2 # zc.recipe.egg==1.3.2
setuptools = 0.6c12dev-r88846 setuptools = 0.6c12dev-r88846
# Required by: # Required by:
# slapos.cookbook==0.15 # slapos.cookbook==0.20
slapos.core = 0.9 slapos.core = 0.14
# Required by: # Required by:
# slapos.core==0.9 # slapos.core==0.14
supervisor = 3.0a10 supervisor = 3.0a10
# Required by: # Required by:
# slapos.cookbook==0.15 # slapos.cookbook==0.20
xml-marshaller = 0.9.7 xml-marshaller = 0.9.7
# Required by: # Required by:
# slapos.cookbook==0.15 # slapos.cookbook==0.20
zc.recipe.egg = 1.3.2 zc.recipe.egg = 1.3.2
# Required by: # Required by:
# slapos.core==0.9 # slapos.core==0.14
zope.interface = 3.6.4 zope.interface = 3.7.0
# Required by:
# novnc==0.1
numpy = 1.6.1
# XXX-CEDRIC Quick and dirty workaround to avoid m2crypto problems.
# should not be used elsewhere unless for urgent cases.
slapos.libnetworkcache = 0.2
...@@ -22,7 +22,7 @@ url = Student shall put here url of zipped or tarballed web page or application ...@@ -22,7 +22,7 @@ url = Student shall put here url of zipped or tarballed web page or application
[instance-recipe] [instance-recipe]
egg = slapos.cookbook egg = slapos.cookbook
module = osoeslaptraining.static module = lamp.static
[template] [template]
# Default template for the instance. # Default template for the instance.
......
...@@ -34,7 +34,7 @@ location = Student shall put here relative path to application top level directo ...@@ -34,7 +34,7 @@ location = Student shall put here relative path to application top level directo
[instance-recipe] [instance-recipe]
egg = slapos.cookbook egg = slapos.cookbook
module = osoeslaptraining.simple module = lamp.simple
[template] [template]
# Default template for the instance. # Default template for the instance.
......
#!%(bash_binary)s
# Didn't we already restore the database ?
[ -f %(lock_file)r ] && exit 127
touch %(lock_file)r
dbname=db
# Wait for MySQL to be started
while ! %(mysql_binary)r --socket=%(mysql_socket)r -u root -e "use $dbname;"
do
sleep 5
done
# Restore dump
%(duplicity_binary)r restore --no-encryption %(parameter_remote_backup)r %(local_directory)r
zcat %(dump_name)r | %(mysql_binary)r --socket=%(mysql_socket)r -D $dbname -u root
...@@ -18,3 +18,5 @@ openssl_binary = ${openssl:location}/bin/openssl ...@@ -18,3 +18,5 @@ openssl_binary = ${openssl:location}/bin/openssl
perl_binary = ${perl:location}/bin/perl perl_binary = ${perl:location}/bin/perl
rdiff_backup_binary = ${buildout:bin-directory}/rdiff-backup rdiff_backup_binary = ${buildout:bin-directory}/rdiff-backup
stunnel_binary = ${stunnel:location}/bin/stunnel stunnel_binary = ${stunnel:location}/bin/stunnel
duplicity_binary = ${buildout:bin-directory}/duplicity
davstorage-software-url = http://git.erp5.org/gitweb/slapos.git/blob/refs/tags/slapos-0.44:/software/davstorage/software.cfg
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
extensions = extensions =
slapos.zcbworkarounds slapos.zcbworkarounds
slapos.rebootstrap
find-links += find-links +=
http://www.nexedi.org/static/packages/source/slapos.buildout/ http://www.nexedi.org/static/packages/source/slapos.buildout/
...@@ -18,6 +17,7 @@ extends = ...@@ -18,6 +17,7 @@ extends =
../../component/rdiff-backup/buildout.cfg ../../component/rdiff-backup/buildout.cfg
../../component/lxml-python/buildout.cfg ../../component/lxml-python/buildout.cfg
../../stack/shacache-client.cfg ../../stack/shacache-client.cfg
../../component/duplicity/buildout.cfg
# Use only quite well working sites. # Use only quite well working sites.
allow-hosts = allow-hosts =
...@@ -33,7 +33,7 @@ allow-hosts = ...@@ -33,7 +33,7 @@ allow-hosts =
versions = versions versions = versions
parts += parts =
# Create instance template # Create instance template
#TODO : list here all parts. #TODO : list here all parts.
template template
...@@ -70,12 +70,12 @@ eggs = ...@@ -70,12 +70,12 @@ eggs =
# Default template for the instance. # Default template for the instance.
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance.cfg url = ${:_profile_base_location_}/instance.cfg
md5sum = 69c32a67c5640d36ee042d2cfc35843d md5sum = de50b4841247cc049686ca44cef1184b
output = ${buildout:directory}/template.cfg output = ${buildout:directory}/template.cfg
mode = 0644 mode = 0644
[versions] [versions]
slapos.cookbook = 0.9 slapos.cookbook = 0.24
# Required by slapos.cookbook==0.9 # Required by slapos.cookbook==0.9
slapos.core = 0.4 slapos.core = 0.4
...@@ -89,4 +89,4 @@ hexagonit.recipe.download = 1.5.0 ...@@ -89,4 +89,4 @@ hexagonit.recipe.download = 1.5.0
plone.recipe.command = 1.1 plone.recipe.command = 1.1
# Use SlapOS patched zc.buildout # Use SlapOS patched zc.buildout
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
...@@ -86,4 +86,4 @@ hexagonit.recipe.download = 1.5.0 ...@@ -86,4 +86,4 @@ hexagonit.recipe.download = 1.5.0
plone.recipe.command = 1.1 plone.recipe.command = 1.1
# Use SlapOS patched zc.buildout # Use SlapOS patched zc.buildout
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
...@@ -80,4 +80,4 @@ hexagonit.recipe.download = 1.5.0 ...@@ -80,4 +80,4 @@ hexagonit.recipe.download = 1.5.0
plone.recipe.command = 1.1 plone.recipe.command = 1.1
# Use SlapOS patched zc.buildout # Use SlapOS patched zc.buildout
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
\ No newline at end of file
...@@ -39,7 +39,7 @@ location = config.inc.php ...@@ -39,7 +39,7 @@ location = config.inc.php
[instance-recipe] [instance-recipe]
egg = slapos.cookbook egg = slapos.cookbook
module = osoeslaptraining.simple module = lamp.simple
[instance-recipe-egg] [instance-recipe-egg]
recipe = zc.recipe.egg recipe = zc.recipe.egg
......
...@@ -51,4 +51,4 @@ plone.recipe.command = 1.1 ...@@ -51,4 +51,4 @@ plone.recipe.command = 1.1
slapos.libnetworkcache = 0.2 slapos.libnetworkcache = 0.2
# Use SlapOS patched zc.buildout # Use SlapOS patched zc.buildout
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
...@@ -30,7 +30,7 @@ location = wp-config.php ...@@ -30,7 +30,7 @@ location = wp-config.php
[instance-recipe] [instance-recipe]
egg = slapos.cookbook egg = slapos.cookbook
module = osoeslaptraining.request module = lamp.request
[instance-recipe-egg] [instance-recipe-egg]
recipe = zc.recipe.egg recipe = zc.recipe.egg
......
...@@ -400,6 +400,12 @@ scripts = ...@@ -400,6 +400,12 @@ scripts =
# Use SlapOS patched zc.buildout # Use SlapOS patched zc.buildout
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-005
# XXX: Very dirty workaround
# XXX: Pin slapos.libnetworkcache, as version higher than 0.3 depends on
# XXX: M2Crypto which installation is not defined yet
# XXX: Note, that slapos.libnetworkcache is buildout "internal" dependency
slapos.libnetworkcache = 0.3
# pin Acquisition and Products.DCWorkflow to Nexedi flavour of eggs # pin Acquisition and Products.DCWorkflow to Nexedi flavour of eggs
Acquisition = 2.13.7nxd001 Acquisition = 2.13.7nxd001
Products.DCWorkflow = 2.2.3nxd002 Products.DCWorkflow = 2.2.3nxd002
......
...@@ -25,10 +25,6 @@ extends = ...@@ -25,10 +25,6 @@ extends =
# Websockify (socket <-> websocket proxy server) when it is ready. # Websockify (socket <-> websocket proxy server) when it is ready.
# May solve previous XXX depending on the implementation. # 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.
parts = parts =
template template
gnutls gnutls
...@@ -105,4 +101,4 @@ eggs = ...@@ -105,4 +101,4 @@ eggs =
numpy numpy
[versions] [versions]
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-009
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment