from peak.events.trellis import *
##
## Example #2: Observing and managing server instances in a cluster
##
class ServerInstance(Component):
started = attr(False)
cpu_load = attr(0.0) # 100% is 1.0
class ServerCluster(Component):
servers = make(Set)
@compute
def avg_load(self):
if not self.servers:
return 1.0
return sum(s.cpu_load for s in self.servers) / len(self.servers)
max_load = attr(0.9)
@compute
def need_more_servers(self):
# we use `not servers.added` to avoid CircularityError due to avg_load recalculation
# after pending server has started
return (self.avg_load > self.max_load) and not self.servers.added
@maintain
def autostart_servers(self):
if self.need_more_servers and self.pending_server is None:
self.start_new_server()
pending_server = attr()
@modifier
def start_new_server(self):
assert not self.pending_server
self.pending_server = ServerInstance()
@maintain
def on_started(self):
if self.pending_server and self.pending_server.started:
self.servers.add(self.pending_server)
self.pending_server = None
class ClusterReporter(Component):
cluster = attr()
def __init__(self, cluster):
self.cluster = cluster
@perform
def watch_server_count(self):
print 'Servers: %d; Average load: %d%%; Pending: %s' % (
len(self.cluster.servers),
100 * self.cluster.avg_load,
'YES' if self.cluster.pending_server else 'NO')
##########################################
##########################################
cl = ServerCluster()
rep = ClusterReporter(cl)
# Servers: 0; Average load: 100%; Pending: YES
s1 = cl.pending_server
s1.started = True
# Servers: 1; Average load: 0%; Pending: NO
s1.cpu_load = 1.0
# Servers: 1; Average load: 100%; Pending: YES
s2 = cl.pending_server
s2.started = True
# Servers: 2; Average load: 50%; Pending: NO
s2.cpu_load = 0.5
# Servers: 2; Average load: 75%; Pending: NO
s2.cpu_load = 0.9
# Servers: 2; Average load: 95%; Pending: YES