diff options
Diffstat (limited to 'rumba/model.py')
-rw-r--r-- | rumba/model.py | 148 |
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 |