From 74265717dcb72ee5e6abf54f7b67ba9d2658e0ed Mon Sep 17 00:00:00 2001 From: Sander Vrijders Date: Wed, 28 Mar 2018 17:42:03 +0200 Subject: model: Add docstrings to model This adds docstrings to the model so that Sphinx can parse them. It also adds some changes to the rst files and conf.py so that inherited members are shown and special members are not. --- doc/conf.py | 25 +---- doc/docker.rst | 2 + doc/emulab.rst | 2 + doc/irati.rst | 2 + doc/jfed.rst | 2 + doc/local.rst | 2 + doc/ouroboros.rst | 2 + doc/qemu.rst | 2 + doc/rlite.rst | 2 + rumba/model.py | 304 ++++++++++++++++++++++++++++++++++++++++-------------- 10 files changed, 250 insertions(+), 95 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 0424dc7..988dbf7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,24 +1,7 @@ from datetime import datetime -import alabaster - - -html_theme_path = [alabaster.get_path()] -extensions = ['alabaster'] -html_theme = 'alabaster' - -html_theme_options = { - 'description': "Rumba: A framework to bootstrap a " + - "recursive internet network on a testbed." -} - -html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'searchbox.html' - ] -} +html_theme = "agogo" +extensions = [] project = 'Rumba' year = datetime.now().year @@ -30,7 +13,9 @@ default_role = 'obj' language = None extensions.extend(['sphinx.ext.autodoc']) -autodoc_default_flags = ['members', 'special-members'] +autodoc_default_flags = ['members'] + +autoclass_content = 'both' _locals = {} with open('../rumba/_version.py') as fp: diff --git a/doc/docker.rst b/doc/docker.rst index 004240d..c82d40c 100644 --- a/doc/docker.rst +++ b/doc/docker.rst @@ -11,3 +11,5 @@ see `Install Docker `_ and complete .. automodule:: rumba.testbeds.dockertb :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/emulab.rst b/doc/emulab.rst index 376f07d..77ac3d0 100644 --- a/doc/emulab.rst +++ b/doc/emulab.rst @@ -14,3 +14,5 @@ a workaround is to start an ssh-agent and add the public key there. :: .. automodule:: rumba.testbeds.emulab :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/irati.rst b/doc/irati.rst index c07feb1..0ad496a 100644 --- a/doc/irati.rst +++ b/doc/irati.rst @@ -7,3 +7,5 @@ system, initially developed by the FP7-IRATI project. .. automodule:: rumba.prototypes.irati :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/jfed.rst b/doc/jfed.rst index 7ed9e88..3d06c09 100644 --- a/doc/jfed.rst +++ b/doc/jfed.rst @@ -28,3 +28,5 @@ or sudo). .. automodule:: rumba.testbeds.jfed :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/local.rst b/doc/local.rst index c55a275..6d1ee1d 100644 --- a/doc/local.rst +++ b/doc/local.rst @@ -3,3 +3,5 @@ Local .. automodule:: rumba.testbeds.local :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/ouroboros.rst b/doc/ouroboros.rst index 785bf92..2b83531 100644 --- a/doc/ouroboros.rst +++ b/doc/ouroboros.rst @@ -7,3 +7,5 @@ works on any POSIX.1-2001 enabled system. .. automodule:: rumba.prototypes.ouroboros :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/qemu.rst b/doc/qemu.rst index ff1ede3..375ab55 100644 --- a/doc/qemu.rst +++ b/doc/qemu.rst @@ -11,3 +11,5 @@ bridge-utils packages on which the testbed depends: :: .. automodule:: rumba.testbeds.qemu :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/doc/rlite.rst b/doc/rlite.rst index f58a16a..418bf3a 100644 --- a/doc/rlite.rst +++ b/doc/rlite.rst @@ -7,3 +7,5 @@ Architecture (RINA) for GNU/Linux operating systems. .. automodule:: rumba.prototypes.rlite :member-order: bysource + :show-inheritance: + :inherited-members: diff --git a/rumba/model.py b/rumba/model.py index d05e7fe..f9a0d3c 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -59,14 +59,11 @@ except OSError: pass -# Represents generic testbed info -# -# @username [string] user name -# @password [string] password -# @proj_name [string] project name -# @exp_name [string] experiment name -# + class Testbed(object): + """ + Base class for every testbed plugin. + """ def __init__(self, exp_name, username, @@ -74,6 +71,15 @@ class Testbed(object): proj_name, http_proxy=None, system_logs=None): + """ + :param exp_name: The experiment name. + :param username: The username. + :param password: The password. + :param proj_name: The project name. + :param http_proxy: HTTP proxy used by the testbed. + :param system_logs: Location of the system logs of + images of the testbed. + """ self.username = username self.password = password self.proj_name = proj_name @@ -89,7 +95,7 @@ class Testbed(object): def swap_in(self, experiment): """ - Swaps experiment in + Swaps experiment in on the testbed. :param experiment: The experiment. """ @@ -108,7 +114,7 @@ class Testbed(object): def swap_out(self, experiment): """ - Swaps experiment out + Swaps experiment out of the testbed. :param experiment: The experiment. """ @@ -118,12 +124,15 @@ class Testbed(object): def _swap_out(self, experiment): logger.info("swap_out(): nothing to do") -# Base class for DIFs -# -# @name [string] DIF name -# class DIF(object): + """ + Base class for DIFs. + """ def __init__(self, name, members=None): + """ + :param name: Name of the DIF. + :param members: List of nodes that are members of the DIF. + """ self.name = name if members is None: members = list() @@ -156,7 +165,14 @@ class DIF(object): # Shim over UDP # class ShimUDPDIF(DIF): + """ + Shim over UDP. + """ def __init__(self, name, members=None): + """ + :param name: Name of the DIF. + :param members: List of members of the DIF. + """ DIF.__init__(self, name, members) def get_ipcp_class(self): @@ -168,11 +184,18 @@ class ShimUDPDIF(DIF): # @link_speed [int] Speed of the Ethernet network, in Mbps # class ShimEthDIF(DIF): - + """ + Shim over Ethernet. + """ def get_e_id(self): return "ShimEthDIF." + self.name def __init__(self, name, members=None, link_quality=None): + """ + :param name: Name of the DIF. + :param members: List of members of the DIF. + :param link_quality: Quality of the link. + """ DIF.__init__(self, name, members) self._link_quality = link_quality if link_quality is not None else LinkQuality() @@ -209,6 +232,7 @@ class ShimEthDIF(DIF): """ Set the delay parameters of the underlying link. Parameters as in :py:meth:`.Delay.__init__` + :param delay: average delay in ms :type delay: :py:class:`int` :param jitter: jitter in ms @@ -216,7 +240,7 @@ class ShimEthDIF(DIF): :param correlation: correlation in % :type correlation: :py:class:`int` :param distribution: delay distribution, defaults to a Normal - distribution + distribution :type distribution: :py:class:`.Distribution` """ new_delay = Delay(delay, jitter, correlation, distribution) @@ -229,6 +253,7 @@ class ShimEthDIF(DIF): """ Set the loss parameter of the underlying link. Parameters as in :py:meth:`.Loss.__init__` + :param loss: loss in percentage :type loss: :py:class:`int` or :py:class:`float` :param correlation: correlation in percentage @@ -241,6 +266,7 @@ class ShimEthDIF(DIF): def set_rate(self, rate=None): """ Set the rate parameter of the underlying link. + :param rate: The desired rate in mbps :type rate: :py:class:`int` """ @@ -250,7 +276,8 @@ class ShimEthDIF(DIF): def set_quality(self, delay, loss, rate): """ Configure the basic quality parameters of the - underlying link + underlying link. + :param delay: the link delay, in ms :type delay: :py:class:`int` :param loss: the link loss, as a percentage @@ -262,27 +289,44 @@ class ShimEthDIF(DIF): self.link_quality = new_quality -# Normal DIF -# -# @policies [dict] Policies of the normal DIF. Format: -# dict( componentName: str --> comp_policy: -# dict( policy_name: str --> parameters: -# dict( name: str --> value: str ))) -# class NormalDIF(DIF): + """ + Normal DIF. + """ def __init__(self, name, members=None, policy=None): + """ + :param name: The name of the DIF. + :param members: The list of members. + :param policy: Policies of the normal DIF. + """ DIF.__init__(self, name, members) if policy is None: policy = Policy(self) self.policy = policy def add_policy(self, comp, pol, **params): + """ + Adds a policy to the DIF. + + :param comp: Component name. + :param pol: Policy name + :param params: Parameters of the policy. + """ self.policy.add_policy(comp, pol, **params) - def del_policy(self, comp=None, policy_name=None): + def del_policy(self, comp=None, pol=None): + """ + Deletes a policy from the DIF. + + :param comp: Component name. + :param pol: Policy name + """ self.policy.del_policy(comp, policy_name) def show(self): + """ + :return: A string of the policies in the DIF. + """ s = DIF.__repr__(self) for comp, pol_dict in self.policy.get_policies().items(): for pol, params in pol_dict.items(): @@ -292,16 +336,23 @@ class NormalDIF(DIF): class Distribution(Enum): + """ + An enum holding different statistical distributions. + """ NORMAL = 1 PARETO = 2 PARETONORMAL = 3 class Delay(object): + """ + A class representing delay of a link. + """ def __init__(self, delay=0, jitter=None, correlation=None, distribution=None): """ - Configure link delay + Configure link delay. + :param delay: average delay in ms :type delay: :py:class:`int` :param jitter: jitter in ms @@ -309,7 +360,7 @@ class Delay(object): :param correlation: correlation in % :type correlation: :py:class:`int` :param distribution: delay distribution, defaults to a Normal - distribution + distribution :type distribution: :py:class:`.Distribution` """ @@ -362,9 +413,13 @@ class Delay(object): class Loss(object): + """ + A class representing loss on a link. + """ def __init__(self, loss, correlation=None): """ - Configure link loss + Configure link loss. + :param loss: loss in percentage :type loss: :py:class:`int` or :py:class:`float` :param correlation: correlation in percentage @@ -397,13 +452,16 @@ class Loss(object): class LinkQuality(object): + """ + A class representing the link quality. + """ _active = set() @classmethod def clone(cls, old_quality, delay=None, loss=None, rate=None): """ Clone old_quality, updating it with the provided parameters - if present + if present. :param old_quality: A :py:class`.LinkQuality` instance to use as a base @@ -416,8 +474,8 @@ class LinkQuality(object): :type loss: :py:class:`.Loss` or :py:class:`float` :param rate: The rate of the link in mbit :type rate: :py:class:`int` - :return: a new :py:class`.LinkQuality` instance - :rtype :py:class`LinkQuality` + :return: a new :py:class`.LinkQuality` instance. + :rtype: py:class`LinkQuality` """ if delay is None: delay = old_quality.delay @@ -429,7 +487,8 @@ class LinkQuality(object): def __init__(self, delay=None, loss=None, rate=None): """ - Link quality configuration + Link quality configuration. + :param delay: Delay object holding delay configuration or number corresponding to delay in ms :type delay: :py:class:`.Delay` or :py:class:`int` @@ -509,9 +568,6 @@ class LinkQuality(object): "netem" % ipcp.ifname, as_root=True) LinkQuality._active.remove(ipcp) - -# SSH Configuration -# class SSHConfig(object): def __init__(self, hostname, port=22, proxy_server=None): self.username = None @@ -532,21 +588,22 @@ class SSHConfig(object): def set_http_proxy(self, proxy): self.http_proxy = proxy - -# A node in the experiment -# -# @difs: DIFs the node will have an IPCP in -# @dif_registrations: Which DIF is registered in which DIF -# @policies: dict of dif -> policy to apply for that dif in this node -# -# class Node(object): - + """ + A node in the experiment. + """ def get_e_id(self): return "Node." + self.name def __init__(self, name, difs=None, dif_registrations=None, policies=None, machine_type=None): + """ + :param name: Name of the node. + :param difs: A list of DIFs the node is in. + :param dif_registrations: How the DIFs are stacked. + :param policies: Policies of a DIF specific to the node. + :param machine_type: Type of machine to use, physical or virtual. + """ self.name = name if difs is None: difs = list() @@ -573,6 +630,10 @@ class Node(object): self._validate() def get_ipcp_by_dif(self, dif): + """ + :param dif: The DIF to get the IPCP of. + :return: The IPCP of the node that is in the DIF. + """ for ipcp in self.ipcps: if ipcp.dif == dif: return ipcp @@ -620,6 +681,11 @@ class Node(object): return not self == other def add_dif(self, dif): + """ + Adds a DIF to the list. + + :param dif: Name of the DIF to add. + """ self.difs.append(dif) dif.add_member(self) if hasattr(dif, 'policy'): @@ -627,6 +693,11 @@ class Node(object): self._validate() def del_dif(self, dif): + """ + Adds a DIF to the list. + + :param dif: Name of the DIF to add. + """ self.difs.remove(dif) dif.del_member(self) try: @@ -637,63 +708,126 @@ class Node(object): self._validate() def add_dif_registration(self, upper, lower): + """ + Adds a DIF registration. + + :param upper: Name of the DIF that is requesting IPC. + :param lower: Name of the DIF providing IPC. + """ self.dif_registrations[upper].append(lower) self._validate() def del_dif_registration(self, upper, lower): + """ + Removes a DIF registration. + + :param upper: Name of the DIF that is requesting IPC. + :param lower: Name of the DIF providing IPC. + """ self.dif_registrations[upper].remove(lower) self._validate() def add_policy(self, dif, component_name, policy_name, **parameters): + """ + Adds a policy. + + :param dif: The name of the DIF. + :param component_name: Name of the component. + :param policy_name: Name of the policy. + :param parameters: Parameters of the policy. + """ self.policies[dif].add_policy(component_name, policy_name, **parameters) def del_policy(self, dif, component_name=None, policy_name=None): + """ + Removes a policy. + + :param component_name: Name of the component. + :param policy_name: Name of the policy. + """ self.policies[dif].del_policy(component_name, policy_name) def get_policy(self, dif): + """ + :param dif: The DIF to get the policy of. + :return: Returns the policy. + """ return self.policies[dif] def execute_commands(self, commands, as_root=False, time_out=3, use_proxy=False): + """ + Execute a list of a commands on the node. + + :param commands: A list of commands. + :param as_root: Execute as root? + :param time_out: Seconds before timing out. + :param use_proxy: Use a proxy to execute the commands? + """ return self.executor.execute_commands(self, commands, as_root, time_out) def execute_command(self, command, as_root=False, time_out=3, use_proxy=False): - return self.executor.execute_command(self, command, as_root, time_out) + """ + Execute a single command on a node. - def write_text_to_file(self, text, file_name): - # ssh_support.write_text_to_file( - # self.ssh_config, - # self.ssh_config, - # text, - # file_name - # ) - return + :param command: A command. + :param as_root: Execute as root? + :param time_out: Seconds before timing out. + :param use_proxy: Use a proxy to execute the commands? + :return: The stdout of the command. + """ + return self.executor.execute_command(self, command, as_root, time_out) def copy_file(self, path, destination): + """ + Copy file to node. + + :param path: Local location of the file. + :param destination: Destination location of the file. + """ self.executor.copy_file(self, path, destination) def copy_files(self, paths, destination): - self.executor.copy_files(self, paths, destination) + """ + Copy files to node. - def fetch_files(self, paths, destination, sudo=False): - self.executor.fetch_files(self, paths, destination, sudo) + :param paths: Local location of the files. + :param destination: Destination location of the files. + """ + self.executor.copy_files(self, paths, destination) def fetch_file(self, path, destination, sudo=False): + """ + Fetch file from the node. + + :param paths: Location of the files on the node. + :param destination: Destination location of the files. + :param sudo: The file is owned by root on the node? + """ self.executor.fetch_file(self, path, destination, sudo) + def fetch_files(self, paths, destination, sudo=False): + """ + Fetch files from the node. + + :param paths: Location of the files on the node. + :param destination: Destination location of the files. + :param sudo: The file is owned by root on the node? + """ + self.executor.fetch_files(self, paths, destination, sudo) + def set_link_state(self, dif, state): + """ + Change the state of a link on the node. + + :param dif: The name of the shim Ethernet DIF. + :param state: Up or down. + """ ipcp = self.get_ipcp_by_dif(dif) self.execute_command('ip link set dev ' + ipcp.ifname + ' ' + state, as_root=True) - -# Base class representing an IPC Process to be created in the experiment -# -# @name [string]: IPCP name -# @node: Node where the IPCP gets created -# @dif: the DIF the IPCP belongs to -# class IPCP(object): def __init__(self, name, node, dif): self.name = name @@ -734,12 +868,6 @@ class ShimUDPIPCP(IPCP): IPCP.__init__(self, name, node, dif) # TODO: add IP and port - -# Class representing DIF and Node policies -# -# @dif: the dif this policy is applied to. -# @node: the node -# class Policy(object): def __init__(self, dif, node=None, policies=None): self.dif = dif # type: NormalDIF @@ -812,13 +940,10 @@ class Policy(object): s += "\n]\n" return s - -# Base class for ARCFIRE experiments -# -# @name [string] Name of the experiment -# @nodes: Nodes in the experiment -# class Experiment(object): + """ + Base class for experiments. + """ __metaclass__ = abc.ABCMeta def __init__(self, testbed, @@ -827,6 +952,14 @@ class Experiment(object): git_branch=None, log_dir=None, prototype_logs=None): + """ + :param testbed: The testbed of the experiment. + :param nodes: The list of nodes in the experiment. + :param git_repo: The git repository of the prototype. + :param git_branch: The git branch of the repository. + :param log_dir: Where to log output of the experiment. + :param prototype_logs: Where the prototype logs its output. + """ if nodes is None: nodes = list() self.nodes = nodes @@ -871,10 +1004,20 @@ class Experiment(object): return s def add_node(self, node): + """ + Adds a node to the experiment. + + :param node: A node. + """ self.nodes.append(node) self.generate() def del_node(self, node): + """ + Deletes a node from the experiment. + + :param node: A node. + """ self.nodes.remove(node) self.generate() @@ -1136,6 +1279,9 @@ class Experiment(object): logger.info("Layer ordering computation took %.2f seconds", end - start) def install_prototype(self): + """ + Installs the prototype on the nodes. + """ start = time.time() self._install_prototype() end = time.time() @@ -1146,6 +1292,9 @@ class Experiment(object): node.startup_command = command def bootstrap_prototype(self): + """ + Bootstraps the prototype on the nodes. + """ start = time.time() self._bootstrap_prototype() end = time.time() @@ -1168,7 +1317,9 @@ class Experiment(object): raise Exception('terminate_prototype() method not implemented') def swap_in(self): - # Realize the experiment testbed (testbed-specific) + """ + Swap the experiment in on the testbed. + """ start = time.time() self.testbed.swap_in(self) self.dump_ssh_info() @@ -1176,6 +1327,9 @@ class Experiment(object): logger.info("Swap-in took %.2f seconds", end - start) def swap_out(self): + """ + Swap the experiment out of the testbed. + """ start = time.time() # Terminate prototype gracefully self._terminate_prototype() -- cgit v1.2.3