aboutsummaryrefslogtreecommitdiff
path: root/rumba/model.py
diff options
context:
space:
mode:
Diffstat (limited to 'rumba/model.py')
-rw-r--r--rumba/model.py148
1 files changed, 139 insertions, 9 deletions
diff --git a/rumba/model.py b/rumba/model.py
index 9187fcb..285d937 100644
--- a/rumba/model.py
+++ b/rumba/model.py
@@ -20,6 +20,9 @@
# MA 02110-1301 USA
import abc
+import random
+
+import time
import rumba.log as log
@@ -152,7 +155,7 @@ class SSHConfig:
#
class Node:
def __init__(self, name, difs=None, dif_registrations=None,
- registrations=None, bindings=None):
+ registrations=None, bindings=None, client=False):
self.name = name
if difs is None:
difs = list()
@@ -170,6 +173,7 @@ class Node:
self.bindings = bindings
self.ssh_config = SSHConfig(name)
self.ipcps = []
+ self.client = client
self._validate()
@@ -285,7 +289,7 @@ class IPCP:
self.dif = dif
self.registrations = []
- # Is this node the first in the DIF, so that it does not need
+ # Is this IPCP the first in its DIF, so that it does not need
# to enroll to anyone ?
self.dif_bootstrapper = False
@@ -511,6 +515,7 @@ class Experiment:
if dif not in node.difs:
continue
+ # Create an instance of the required IPCP class
ipcp = dif.get_ipcp_class()(
name='%s.%s' % (dif.name, node.name),
node=node, dif=dif)
@@ -519,30 +524,34 @@ class Experiment:
for lower in node.dif_registrations[dif]:
ipcp.registrations.append(lower)
+ node.ipcps.append(ipcp)
+ dif.ipcps.append(ipcp)
+
+ def compute_bootstrappers(self):
+ for node in self.nodes:
+ for ipcp in node.ipcps:
ipcp.dif_bootstrapper = True
for el in self.enrollments:
for e in el:
- if e['dif'] != dif:
+ if e['dif'] != ipcp.dif:
# Skip this DIF
break
- if e['enrollee'] == node:
+ if e['enrollee'] == ipcp:
ipcp.dif_bootstrapper = False
# Exit the loops
break
if not ipcp.dif_bootstrapper:
break
- node.ipcps.append(ipcp)
- dif.ipcps.append(ipcp)
-
- logger.info("IPCP for node %s: %s", node.name, node.ipcps)
-
# Examine the nodes and DIFs, compute the registration and enrollment
# order, the list of IPCPs to create, registrations, ...
def generate(self):
self.compute_dif_ordering()
self.compute_ipcps()
self.compute_enrollments()
+ self.compute_bootstrappers()
+ for node in self.nodes:
+ logger.info("IPCPs for node %s: %s", node.name, node.ipcps)
@abc.abstractmethod
def install_prototype(self):
@@ -559,3 +568,124 @@ class Experiment:
def swap_out(self):
# Undo the testbed (testbed-specific)
self.testbed.swap_out(self)
+
+
+# Base class for client programs
+#
+# @ap: Application Process binary
+# @options: Options to pass to the binary
+#
+class Client(object):
+ def __init__(self, ap, options=None):
+ self.ap = ap
+ self.options = options
+
+ def start_process(self, node, duration, start_time):
+ return ClientProcess(self.ap, node, duration, start_time, self.options)
+
+
+# Base class for client processes
+#
+# @ap: Application Process binary
+# @node: The node on which this process should run
+# @duration: The time (in seconds) this process should run
+# @start_time: The time at which this process is started.
+# @options: Options to pass to the binary
+#
+class ClientProcess(Client):
+ def __init__(self, ap, node, duration, start_time, options=None):
+ super(ClientProcess, self).__init__(ap, options=options)
+ self.node = node
+ self.duration = duration
+ self.start_time = start_time
+ self.run()
+ self.running = True
+
+ def run(self):
+ pass # TODO to be implemented
+
+ def stop(self):
+ pass # TODO to be implemented
+
+ def check(self, now):
+ if not self.running:
+ return
+ if now - self.start_time >= self.duration:
+ self.stop()
+
+
+# Base class for server programs
+#
+# @ap: Application Process binary
+# @arrival_rate: Average requests/s to be received by this server
+# @mean_duration: Average duration of a client connection (in seconds)
+# @options: Options to pass to the binary
+# @max_clients: Maximum number of clients to serve
+# @clients: Client binaries that will use this server
+# @nodes: Specific nodes to start this server on
+#
+class Server:
+ def __init__(self, ap, arrival_rate, mean_duration,
+ options=None, max_clients=None,
+ clients=None, nodes=None):
+ self.ap = ap
+ self.options = options
+ self.max_clients = max_clients
+ if clients is None:
+ clients = list()
+ self.clients = clients
+ self.nodes = nodes
+ self.arrival_rate = arrival_rate # mean requests/s
+ self.mean_duration = mean_duration # in seconds
+
+ def add_client(self, client):
+ self.clients.append(client)
+
+ def del_client(self, client):
+ self.clients.remove(client)
+
+ def add_node(self, node):
+ self.nodes.append(node)
+
+ def del_node(self, node):
+ self.nodes.remove(node)
+
+ def get_new_clients(self, interval):
+ """
+ Returns a list of clients of size appropriate to the server's rate.
+
+ The list's size should be a sample from Poisson(arrival_rate) over
+ interval seconds.
+ Hence, the average size should be interval * arrival_rate.
+ """
+ pass
+
+ def make_client_process(self):
+ """Returns a client of this server"""
+ if len(self.clients) == 0:
+ raise Exception("Server %s has empty client list," % (self,))
+ pass # TODO should return a ClientProcess
+
+
+# Base class for ARCFIRE storyboards
+#
+# @experiment: Experiment to use as input
+# @duration: Duration of the whole storyboard
+# @servers: App servers available in the network
+#
+class StoryBoard:
+ def __init__(self, experiment, duration, servers=None):
+ self.experiment = experiment
+ self.duration = duration
+ if servers is None:
+ servers = list()
+ self.servers = servers
+
+ def add_server(self, server):
+ self.servers.append(server)
+
+ def del_server(self, server):
+ self.servers.remove(server)
+
+ def start(self):
+ pass