Commit 4b05a7da authored by Alain Takoudjou's avatar Alain Takoudjou

Merge branch 'master' into grid-computing

parents fb52bb07 2d7f2f73
Changes
=======
0.68.1 (2012-10-03)
-------------------
* slaprunner: fix "logfile" parameter to "log_file"
0.68 (2012-10-02)
-----------------
* request.py: Remove useless calls to master, fix "update" method. [Cedric
de Saint Martin]
* Add webrunner test recipe. [Alain Takoudjou]
* Add logfile for slaprunner. [Cedric de Saint Martin]
* Fix check_url_available promise (syntax + checks + IPv6 support). [Cedric
de Saint Martin]
0.67 (2012-09-26)
-----------------
* Add check_page_content promise generator. [Cedric Le Ninivin]
* Fix check_url_available recipe. [Cedric de Saint Martin]
* Set up timezone database in mariab's mysql table so that we can use
timezone conversion function. [Kazuhiko Shiozaki]
* Add many resiliency-based recipes [Timothée Lacroix]
* Fix and unify request and requestoptional recipes [Cedric de Saint Martin]
* Fix Dropbear. [Antoine Catton]
0.66 (2012-09-10)
-----------------
......
......@@ -170,4 +170,4 @@ make-targets = install -j1
# until gcc will be simplified by using more robust build recipe (like
# slapos.recipe.build) each time any of parts which reuses this one gets updated
# the hack-revision have to be increased
hack-revision = 1
hack-revision = 2
......@@ -29,5 +29,5 @@ environment =
[ghostscript-9]
<= ghostscript-common
url = http://downloads.ghostscript.com/public/ghostscript-9.05.tar.bz2
md5sum = 8bcef1f33ddf8a4d12b2cf8da385c191
url = http://downloads.ghostscript.com/public/ghostscript-9.06.tar.bz2
md5sum = 46f9ebe40dc52755287b30704270db11
......@@ -7,12 +7,12 @@ parts =
[groonga]
recipe = hexagonit.recipe.cmmi
url = http://packages.groonga.org/source/groonga/groonga-2.0.5.tar.gz
md5sum = 6ea6634ceee97f701add89ee39f4c1da
url = http://packages.groonga.org/source/groonga/groonga-2.0.6.tar.gz
md5sum = 64467d1ab8d5e0df8348a636972a95a7
configure-options =
--disable-static
--disable-glibtest
--disable-benchmark
--disable-document
--disable-nginx
--disable-groonga-httpd
--without-mecab
......@@ -9,8 +9,8 @@ parts = haproxy
[haproxy]
recipe = hexagonit.recipe.cmmi
url = http://haproxy.1wt.eu/download/1.4/src/haproxy-1.4.21.tar.gz
md5sum = f36d5d13fd4a44ab454eee5d06cb38a2
url = http://haproxy.1wt.eu/download/1.4/src/haproxy-1.4.22.tar.gz
md5sum = a0b007c76f6a78524f3b3dd5e704979c
configure-command = true
# If the system is running on Linux 2.6, we use "linux26" as the TARGET,
# otherwise use "generic".
......
......@@ -29,9 +29,9 @@ slapos_promisee =
directory:javaws
file:lib/rt.jar
file:bin/java
# http://java.com/en/download/manual.jsp?locale=en
x86 = http://javadl.sun.com/webapps/download/AutoDL?BundleId=59621 7466b0b86bef21e3e31ae578b2b9f472
x86-64 = http://javadl.sun.com/webapps/download/AutoDL?BundleId=59623 c5cfb8393d257b51e7a70e56b7784ac9
# http://java.com/en/download/manual_v6.jsp
x86 = http://javadl.sun.com/webapps/download/AutoDL?BundleId=68284 7eda40e790de1a907d591b62949b6e72
x86-64 = http://javadl.sun.com/webapps/download/AutoDL?BundleId=68286 839548714931443ba89719a995ece846
script =
import glob
if not self.options.get('url'): self.options['url'], self.options['md5sum'] = self.options[guessPlatform()].split(' ')
......@@ -47,48 +47,20 @@ script =
workdir = guessworkdir(extract_dir)
self.copyTree(glob.glob(os.path.join(workdir, "jre1.6.0_*"))[0], "%(location)s")
[java-sdk-1.6.0]
[java-re-1.7.0]
recipe = slapos.recipe.build
slapos_promisee =
directory:bin
directory:lib
directory:man
directory:plugin
directory:javaws
file:jre/lib/rt.jar
file:bin/java
x86 = http://download.oracle.com/otn-pub/java/jdk/6u27-b07/jdk-6u27-linux-i586.bin bdb5f05bd20c6aa9a4729726191bf6fd
x86-64 = http://download.oracle.com/otn-pub/java/jdk/6u27-b07/jdk-6u27-linux-x64.bin 94f93a3ff03f824a238ecd79ad90433e
script =
if not self.options.get('url'): self.options['url'], self.options['md5sum'] = self.options[guessPlatform()].split(' ')
download_file = self.download(self.options['url'], self.options.get('md5sum'))
extract_dir = tempfile.mkdtemp(self.name)
os.chdir(extract_dir)
(download_dir, filename) = os.path.split(download_file)
auto_extract_bin = os.path.join(extract_dir, filename)
shutil.move(download_file, auto_extract_bin)
os.chmod(auto_extract_bin, 0777)
subprocess.call(["patch", auto_extract_bin, "-i", "${jdk-6u27-no-user-interaction-patch:location}/${jdk-6u27-no-user-interaction-patch:filename}"])
subprocess.call([auto_extract_bin])
self.cleanup_dir_list.append(extract_dir)
workdir = guessworkdir(extract_dir)
self.copyTree(os.path.join(workdir, "jdk1.6.0_27"), "%(location)s")
[java-sdk-1.7.0]
recipe = slapos.recipe.build
slapos_promisee =
directory:bin
directory:lib
directory:man
directory:jre
file:jre/lib/rt.jar
file:lib/rt.jar
file:bin/java
file:bin/javac
x86 = http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-i586.tar.gz f97244a104f03731e5ff69f0dd5a9927
x86-64 = http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz b3c1ef5faea7b180469c129a49762b64
# http://java.com/en/download/manual.jsp?locale=en
x86 = http://javadl.sun.com/webapps/download/AutoDL?BundleId=68234 ea99bedd9db33e9e2970f4b70abd1e4b
x86-64 = http://javadl.sun.com/webapps/download/AutoDL?BundleId=68236 5aa9bd26cdf1fa6afd2b15826b4ba139
script =
if not self.options.get('url'): self.options['url'], self.options['md5sum'] = self.options[guessPlatform()].split(' ')
extract_dir = self.extract(self.download(self.options['url'], self.options.get('md5sum')))
workdir = guessworkdir(extract_dir)
self.copyTree(workdir, "%(location)s")
......@@ -59,9 +59,11 @@ environment =
LDFLAGS=-L${libaio:location}/lib
[mroonga-mariadb]
# mroonga - a storage engine for MySQL. It provides fast fulltext search feature to all MySQL users.
# http://mroonga.github.com/
recipe = hexagonit.recipe.cmmi
url = https://github.com/downloads/mroonga/mroonga/mroonga-2.05.tar.gz
md5sum = 6aee0e6ff948bba7c02854326afc11df
url = https://github.com/downloads/mroonga/mroonga/mroonga-2.06.tar.gz
md5sum = 89ce640a57ed8f2161b8358ff4c29c66
configure-options =
--with-mysql-source=${mariadb:location}__compile__/mariadb-${mariadb:version}
--with-mysql-config=${mariadb:location}/bin/mysql_config
......
......@@ -10,7 +10,7 @@ parts =
recipe = hexagonit.recipe.cmmi
depends =
${perl:version}
url = http://www.percona.com/redir/downloads/percona-toolkit/2.0.4/percona-toolkit-2.0.4.tar.gz
md5sum = df7dffcccb48d50f143849629228d4b4
url = http://www.percona.com/redir/downloads/percona-toolkit/2.1.3/percona-toolkit-2.1.3.tar.gz
md5sum = 6f8c4a0a0e43e467766cbdabaec9ef93
configure-command =
${perl:location}/bin/perl Makefile.PL
......@@ -13,8 +13,8 @@ extends =
[poppler]
recipe = hexagonit.recipe.cmmi
md5sum = 45dd2c16c8c4d1a39e830e45745c4e25
url = http://poppler.freedesktop.org/poppler-0.20.2.tar.gz
md5sum = 28c40266f374e1960a7bcead17d39f96
url = http://poppler.freedesktop.org/poppler-0.20.3.tar.gz
depends =
${libtiff:version}
configure-options =
......
# This file is used to install SlapOS Node itself.
[buildout]
extends =
../../stack/shacache-client.cfg
......@@ -112,26 +114,27 @@ scripts =
slapgrid-supervisord = slapos.grid.svcbackend:supervisord
slapproxy = slapos.proxy:main
slapos = slapos.entry:main
slapos-watchdog = slapos.grid.watchdog:main
[versions]
zc.buildout = 1.6.0-dev-SlapOS-006
zc.buildout = 1.6.0-dev-SlapOS-007
Jinja2 = 2.6
Werkzeug = 0.8.3
buildout-versions = 1.7
collective.recipe.template = 1.9
hexagonit.recipe.cmmi = 1.6
lxml = 3.0alpha2
meld3 = 0.6.8
lxml = 2.3.6
meld3 = 0.6.9
netaddr = 0.7.10
slapos.core = 0.28.6
slapos.core = 0.31.1
slapos.libnetworkcache = 0.13.2
xml-marshaller = 0.9.7
z3c.recipe.scripts = 1.0.1
zc.recipe.egg = 1.3.2
# Required by:
# slapos.core==0.28.6
# slapos.core==0.31.1
Flask = 0.9
# Required by:
......@@ -139,21 +142,21 @@ Flask = 0.9
hexagonit.recipe.download = 1.5.1
# Required by:
# slapos.core==0.28.6
# slapos.core==0.31.1
netifaces = 0.8
# Required by:
# slapos.core==0.28.6
# slapos.core==0.31.1
# slapos.libnetworkcache==0.13.2
# supervisor==3.0a12
# zc.buildout==1.6.0-dev-SlapOS-006
# supervisor==3.0b1
# zc.buildout==1.6.0-dev-SlapOS-007
# zope.interface==4.0.1
setuptools = 0.6c12dev-r88846
# Required by:
# slapos.core==0.28.6
supervisor = 3.0a12
# slapos.core==0.31.1
supervisor = 3.0b1
# Required by:
# slapos.core==0.28.6
# slapos.core==0.31.1
zope.interface = 4.0.1
This component is not used anymore - but might be revived within 4 months.
If not used within 6 months, feel free to delete this.
......@@ -20,8 +20,8 @@ filename = sphinx-1.10-beta-snowball.patch
recipe = hexagonit.recipe.cmmi
depends =
${mariadb:version}
url = http://sphinxsearch.com/files/sphinx-2.0.2-beta.tar.gz
md5sum = fafe0f1a71d0ded32404c067eba7d0b3
url = http://sphinxsearch.com/files/sphinx-2.0.5-release.tar.gz
md5sum = e71fdb5b0c2911247d48fb30550b9584
configure-options =
--with-mysql
--with-mysql-includes=${mariadb:location}/include/mysql
......
......@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
import glob
import os
version = '0.67-dev'
version = '0.68.2-dev'
name = 'slapos.cookbook'
long_description = open("README.txt").read() + "\n" + \
open("CHANGES.txt").read() + "\n"
......@@ -36,10 +36,12 @@ setup(name=name,
'xml_marshaller', # need to communication with slapgrid
'zc.buildout', # plays with buildout
'zc.recipe.egg', # for scripts generation
'pytz', # for timezone database
],
zip_safe=True,
entry_points={
'zc.buildout': [
'addresiliency = slapos.recipe.addresiliency:Recipe',
'agent = slapos.recipe.agent:Recipe',
'apache.frontend = slapos.recipe.apache_frontend:Recipe',
'apachephp = slapos.recipe.apachephp:Recipe',
......@@ -63,6 +65,7 @@ setup(name=name,
'dropbear.add_authorized_key = slapos.recipe.dropbear:AddAuthorizedKey',
'dropbear.client = slapos.recipe.dropbear:Client',
'dropbear = slapos.recipe.dropbear:Recipe',
'dumpmdb = slapos.recipe.dumpmdb:Recipe',
'duplicity = slapos.recipe.duplicity:Recipe',
'egg_test = slapos.recipe.erp5_test:EggTestRecipe',
'equeue = slapos.recipe.equeue:Recipe',
......@@ -86,6 +89,7 @@ setup(name=name,
'gitinit = slapos.recipe.gitinit:Recipe',
'haproxy = slapos.recipe.haproxy:Recipe',
'helloworld = slapos.recipe.helloworld:Recipe',
'importmdb = slapos.recipe.importmdb:Recipe',
'java = slapos.recipe.java:Recipe',
'kumofs = slapos.recipe.kumofs:Recipe',
'kvm.frontend = slapos.recipe.kvm_frontend:Recipe',
......@@ -116,7 +120,7 @@ setup(name=name,
'publishurl = slapos.recipe.publishurl:Recipe',
'pwgen = slapos.recipe.pwgen:Recipe',
'pwgen.stable = slapos.recipe.pwgen:StablePasswordGeneratorRecipe',
'requestoptional = slapos.recipe.requestoptional:Recipe',
'requestoptional = slapos.recipe.request:RequestOptional',
'request = slapos.recipe.request:Recipe',
'seleniumrunner = slapos.recipe.seleniumrunner:Recipe',
'sheepdogtestbed = slapos.recipe.sheepdogtestbed:SheepDogTestBed',
......@@ -130,6 +134,7 @@ setup(name=name,
'slapmonitor = slapos.recipe.slapmonitor:Recipe',
'slapreport = slapos.recipe.slapreport:Recipe',
'slaprunner = slapos.recipe.slaprunner:Recipe',
'slaprunner.test = slapos.recipe.slaprunner:Test',
'softwaretype = slapos.recipe.softwaretype:Recipe',
'sphinx= slapos.recipe.sphinx:Recipe',
'sshkeys_authority.request = slapos.recipe.sshkeys_authority:Request',
......
......@@ -24,84 +24,58 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
from slapos.recipe.librecipe import GenericSlapRecipe
from slapos import slap as slapmodule
import sys
import os
class Recipe(object):
""" Request a SlapOS instance. Won't fail if instance is not ready.
"""
def __init__(self, buildout, name, options):
self.logger = logging.getLogger(name)
slap = slapmodule.slap()
self.software_release_url = options['software-url']
self.name = options['name']
slap.initializeConnection(options['server-url'],
options.get('key-file'),
options.get('cert-file'),
)
computer_partition = slap.registerComputerPartition(
options['computer-id'], options['partition-id'])
self.request = computer_partition.request
self.isSlave = False
if 'slave' in options:
self.isSlave = options['slave'].lower() in ['y', 'yes', 'true', '1']
self.return_parameters = []
if 'return' in options:
self.return_parameters = [str(parameter).strip()
for parameter in options['return'].split()]
else:
self.logger.debug("No parameter to return to main instance."
"Be careful about that...")
software_type = 'RootSoftwareInstance'
if 'software-type' in options:
software_type = options['software-type']
filter_kw = {}
if 'sla' in options:
for sla_parameter in options['sla'].split():
filter_kw[sla_parameter] = options['sla-%s' % sla_parameter]
partition_parameter_kw = {}
if 'config' in options:
for config_parameter in options['config'].split():
partition_parameter_kw[config_parameter] = \
options['config-%s' % config_parameter]
self.instance = self.request(options['software-url'], software_type,
self.name, partition_parameter_kw=partition_parameter_kw,
filter_kw=filter_kw, shared=self.isSlave)
class Recipe(GenericSlapRecipe):
""" This class provides the installation of the resilience
script on the partition.
"""
self.failed = None
for param in self.return_parameters:
try:
options['connection-%s' % param] = str(
self.instance.getConnectionParameter(param))
except slapmodule.NotFoundError:
options['connection-%s' % param] = ''
if self.failed is None:
self.failed = param
def _install(self):
path_list = []
self_id = int(self.parameter_dict['number'])
ip = self.parameter_dict['ip-list'].split(' ')
print 'Creating bully script with ips : %s\n' % ip
slap_connection = self.buildout['slap-connection']
def install(self):
if self.failed is not None:
# Check instance status to know if instance has been deployed
try:
status = self.instance.getState()
except slapmodule.NotFoundError:
status = 'not ready yet'
except AttributeError:
status = 'unknown'
error_message = 'Connection parameter %s not found. '\
'Requested instance is currently %s. If this error persists, '\
'check status of this instance.' % (self.failed, status)
self.logger.error(error_message)
return []
path_conf = os.path.join(self.options['script'], 'conf.in')
path_bully = os.path.join(self.options['script'], self.parameter_dict['script'])
path_bully_new = os.path.join(self.options['script'], 'new.py')
path_run = os.path.join(self.options['run'], self.parameter_dict['wrapper'])
print 'paths: %s\n%s\n' % (path_run, path_bully)
bully_conf = dict(self_id=self_id,
ip_list=ip,
executable=sys.executable,
syspath=sys.path,
server_url=slap_connection['server-url'],
key_file=slap_connection.get('key-file'),
cert_file=slap_connection.get('cert-file'),
computer_id=slap_connection['computer-id'],
partition_id=slap_connection['partition-id'],
software=slap_connection['software-release-url'],
namebase=self.parameter_dict['namebase'],
confpath=path_conf)
try:
conf = self.createFile(path_conf,
self.substituteTemplate(
self.getTemplateFilename('conf.in.in'),
bully_conf))
path_list.append(conf)
script = self.createExecutable(path_bully,
self.substituteTemplate(
self.getTemplateFilename('bully.py.in'),
bully_conf))
path_list.append(script)
update = install
wrapper = self.createPythonScript(
path_run,
'slapos.recipe.librecipe.execute.execute',
[path_bully])
path_list.append(wrapper)
except IOError:
pass
return path_list
#!%(executable)s
import select
import socket
import threading
import time
import sys
sys.path[:] = %(syspath)s
from slapos import slap as slapmodule
port = 50000
size = 1024
wait = True
def loadConnectionInfos():
connectionInfos = {}
file = open('%(confpath)s', 'r')
params = file.read().split('\n')
file.close()
ip_list = [x.strip("' ") for x in params[0].strip('[],').split(',')]
connectionInfos['self_id'] = int(params[1])
connectionInfos['server_list'] = \
[(i, ip_list[i]) for i in range(len(ip_list))]
connectionInfos['self_ip'] = ip_list[connectionInfos['self_id']]
return connectionInfos
def rename_broken_and_stop():
try:
slap = slapmodule.slap()
slap.initializeConnection('%(server_url)s',
'%(key_file)s',
'%(cert_file)s')
computer_partition = slap.registerComputerPartition('%(computer_id)s',
'%(partition_id)s')
broken = computer_partition.request('%(software)s', 'frozen', '%(namebase)s0')
broken.rename('broken-%%s' %% (time.strftime("%%d-%%b_%%H:%%M:%%S", time.gmtime())))
broken.stopped()
computer_partition.rename('%(namebase)s0')
print 'renaming done\n'
except slapos.slap.slap.ServerError:
print 'Internal server error\n'
def election():
global wait
connection = loadConnectionInfos()
message = "%%s, %%s" %% (connection['self_id'], "Election")
victory = True
for (remote_id, addr) in connection['server_list']:
if remote_id > connection['self_id']:
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.connect((addr, port + remote_id))
s.send(message)
reply = s.recv(size)
if reply == "%%s, %%s" %% (remote_id, "Alive"):
victory = False
except (socket.error, socket.herror, socket.gaierror, socket.timeout):
pass
finally:
s.close()
if victory:
wait = True
for (remote_id, addr) in connection['server_list']:
if remote_id < connection['self_id']:
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.connect((addr, port + remote_id))
s.send("%%s, %%s" %% (connection['self_id'], "Victory"))
except (socket.error, socket.herror, socket.gaierror, socket.timeout):
pass
finally:
s.close()
rename_broken_and_stop()
def failure_detect():
global wait
connection = loadConnectionInfos()
while True:
time.sleep(30)
if wait:
print 'waiting 30 minutes\n'
time.sleep(30 * 60)
wait = False
if not connection['server_list'][0]:
continue
(remote_id, addr) = connection['server_list'][0]
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.connect((addr, port + remote_id))
s.close()
except (socket.error, socket.herror, socket.gaierror, socket.timeout):
s.close()
election()
failure_detect_thread = threading.Thread(target=failure_detect)
failure_detect_thread.start()
connection = loadConnectionInfos()
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.bind((connection['self_ip'], port + connection['self_id']))
s.listen(5)
#election()
while True:
force_election = False
client, _ = s.accept()
client_message = client.recv(1024)
if client_message:
client_id, message = client_message.split(', ')
client_id = eval(client_id)
if message == "Victory":
wait = True
print "%%s wins" %% client_id
elif message == "Election":
print "%%s starts an election" %% client_id
if client_id < connection['self_id']:
client.send("%%s, %%s" %% (connection['self_id'], "Alive"))
force_election = True
client.close()
if force_election:
election()
#!%(executable)s
import socket
import time
import sys
import thread
import time
import os
sys.path[:] = %(syspath)s
from slapos import slap as slapmodule
port = 50000
size = 1024
def rename_broken_and_stop():
try:
slap = slapmodule.slap()
slap.initializeConnection('%(server_url)s',
'%(key_file)s',
'%(cert_file)s')
computer_partition = slap.registerComputerPartition('%(computer_id)s',
'%(partition_id)s')
broken = computer_partition.request('%(software)s', 'frozen', '%(namebase)s0')
broken.rename('broken-%%s' %% (time.strftime("%%d-%%b_%%H:%%M:%%S", time.gmtime())))
broken.stopped()
computer_partition.rename('%(namebase)s0')
print 'renaming done\n'
except slapos.slap.slap.ServerError:
print 'Internal server error\n'
## Leader is always number 0
class ResilientInstance(object):
def __init__(self, comm):
self.comm = comm
self.id = 0
self.state = 'normal'
self.halter = 0
self.nbComp = nbComp
self.inElection = False
self.alive = True
self.lastPing = time.clock()
self.mainCanal = self.comm.canal(['ping', 'halt',
'victory'])
self.okCanal = self.comm.canal(['ok'])
self.loadConnectionInfos()
def loadConnectionInfos(self):
file = open('%(confpath)s', 'r')
params = file.read().split('\n')
file.close()
self.nbComp = len([x.strip("' ") for x in params[0].strip('[],').split(',')])
new_id = int(params[1])
if self.id != new_id:
self.halter = new_id
self.id = new_id
## Needs to be changed to use the master
def aliveManagement(self):
while self.alive:
time.sleep(30*60)
if self.id == 0:
continue
self.comm.send('ping', 0)
message, sender = self.okCanal.get()
if message:
continue
self.election()
def listen(self):
while self.alive:
self.comm.recv()
def main(self):
while self.alive:
message, sender = self.mainCanal.get()
if message == 'ping':
self.comm.send('ok', sender)
elif message == 'halt':
self.state = 'waitingConfirm'
self.halter = sender
self.comm.send('ok', sender)
elif message == 'victory':
if int(sender) == int(self.halter) and self.state == 'waitingConfirm':
print '%s thinks %s is the leader\n' % (self.id, sender)
self.comm.send('ok', sender)
self.state = 'normal'
def election(self):
self.inElection = True
self.loadConnectionInfos()
#Check if I'm the highest instance alive
for higher in range(self.id + 1, self.nbComp):
self.comm.send('ping', higher)
message, sender = self.okCanal.get()
if message:
#print '%s is alive (%s)\n' % (higher, self.id)
self.inElection = False
return False
continue
if not self.alive:
return False
#I should be the new coordinator, halt those below me
print 'Should be ME : %s \n' % self.id
self.state = 'election'
self.halter = self.id
ups = []
for lower in range(self.id):
self.comm.send('halt', lower)
message, sender = self.okCanal.get()
if message:
ups.append(lower)
#Broadcast Victory
self.state = 'reorganization'
for up in ups:
self.comm.send('victory', up)
message, sender = self.okCanal.get()
if message:
continue
print 'Something is wrong... let\'s start over\n'
return self.election()
self.state = 'normal'
self.active = True
print '%s Is THE LEADER \n' % self.id
rename_broken_and_stop()
self.inElection = False
return True
class FilteredCanal(object):