Added a new Method that allows to configure the dummynet box from the Central Site.
authorMarta Carbone <marta@prova.iet.unipi.it>
Wed, 3 Sep 2008 14:42:09 +0000 (14:42 +0000)
committerMarta Carbone <marta@prova.iet.unipi.it>
Wed, 3 Sep 2008 14:42:09 +0000 (14:42 +0000)
At the moment works if called from plcsh.

Changed the image generation working dir, because of space problems.

PLC/Methods/ConfigureDummynetBox.py [new file with mode: 0644]
PLC/Methods/GetDummyBoxMedium.py
PLC/Methods/__init__.py

diff --git a/PLC/Methods/ConfigureDummynetBox.py b/PLC/Methods/ConfigureDummynetBox.py
new file mode 100644 (file)
index 0000000..98f4306
--- /dev/null
@@ -0,0 +1,195 @@
+#
+# Marta Carbone - UniPi
+# $Id$
+#
+# This script allows to configure a dummynet box from the central site.
+#
+# The authentication is done by a dummynet box key
+# located on the Central Site /usr/share/dummynet/dbox_key
+# The public key will be propagated to the dummynet boxes
+# by the same script used for users.
+
+from PLC.Faults import *                        # faults library
+from PLC.Method import Method                   # base class used for methods
+from PLC.Parameter import Parameter, Mixed      # define input parameters
+from PLC.DummyBoxes import DummyBox, DummyBoxes # main class for a DummyBox
+from PLC.Sites import Site, Sites               # manage authentication
+from PLC.Auth import Auth                       # import Auth parameter
+from PLC.Nodes import Node, Nodes               # main class for Nodes
+from PLC.NodeNetworks import *                  # main class for Nodes
+from PLC.Persons import Person, Persons         # main class for Persons
+from PLC.Slices import Slice, Slices            # main class for Slices
+import os                                       # ssh
+
+# basename for the Dummynet box public key
+# used to send configuration requests
+# to the dummynet box.
+DBOX_KEY="/usr/share/dummynet/dbox_key"
+
+DEFAULT_TIMEOUT= "1H"
+
+# return 1 if the value is present in the array
+def match(list, value):
+       for i in list:
+               if (value == i):
+                       return 1
+       return 0
+
+# if the field key is set in the fields_list dict,
+# return the cmd_keyword string and the dict value
+def pipe_build(fields_list, field, cmd_keyword):
+       try:
+               a = fields_list[field]
+       except:
+               return ""
+       else:
+               return cmd_keyword + str(a)
+
+class ConfigureDummynetBox(Method):
+    """
+    This script will configure an emulated link.
+    WARNING: This method is experimental, so it
+    could _work_.
+
+    To do this it:
+    - takes as input the node_id, the slicename,
+      and other dummynet configuration parameters;
+    - make some permission checks;
+    - retrive other useful information from the database;
+    - send the emulated link configuration to the DummynetBox;
+    - wait for the remote execution and return the reply to the user.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+    link_configuration_fields = {
+        'node_id': Parameter(int, "Node identifier"),
+        'slicename': Parameter(str, "Name of the slice"),
+        'port': Parameter(int, "Port number"),
+        'bw': Parameter(str, "dummynet, bandwidth limit"),
+        'delay': Parameter(str, "dummynet, delay"),
+        'plr': Parameter(str, "dummynet, packet loss rate"),
+        'upload_extra-delay': Parameter(str, "dummynet, extra delay file"),
+        'extra-delay': Parameter(str, "dummynet, extra delay, if above unset"),
+        'timeout': Parameter(str, "Rules timeout"),
+        'noerror': Parameter(str, "dummynet, if '0' do not propagate errors to the upper layer"),
+    }
+
+    accepts = [
+        Auth(),
+       link_configuration_fields,
+    ]
+
+    returns = Parameter(str, 'Remote command execution output');
+
+    def call(self, auth, link_configuration_fields):
+
+       # Only a person istance can send configuration commands
+       if not isinstance(self.caller, Person):
+               return "Authentication method not allowed to perform this operation";
+
+       # Get identity
+       email = str(self.caller['email']);
+
+       # Get mandatory arguments
+       node_id = link_configuration_fields['node_id']
+       slicename = link_configuration_fields['slicename']
+       port = link_configuration_fields['port']
+
+       # Check not mandatory, they will be done again on the dummynet box
+       if port <= 1024:
+               return "Port should be > 1024"
+
+       # Get the dummynet box connected to this node
+       nodes = Nodes(self.api, {'node_id': node_id}, ['dummybox_id','nodenetwork_ids'])
+       if not nodes:
+               return "Node not present"
+
+       dummybox_id = nodes[0]['dummybox_id']
+
+       if (dummybox_id == 0):          # dummybox_id == 0 means empty
+               return "This node has no dummynet box connected"
+
+       dummyboxes = DummyBoxes(self.api, {'dummybox_id': dummybox_id}, ['ip'])
+       dbox_ip = str(dummyboxes[0]['ip'])
+
+       # Get the node ip address, we need to cross with the NodeNetworks table
+       nodenetwork_id = NodeNetworks(self.api, {'node_id': node_id, 'is_primary':'t'}, ['ip']);
+       if not nodenetwork_id or not nodenetwork_id[0]['ip']:
+               return "Network not configured on this node"
+               
+       node_ip = nodenetwork_id[0]['ip']
+
+       # Search the person_id
+       person_id = Persons(self.api, {'email': email}, ['person_id'])
+       if not person_id:
+               return "User not found"
+       
+       # Search slice information
+       slices = Slices(self.api, {'name': slicename}, ['node_ids', 'person_ids'])
+       if not slices:
+               return "No slices found"
+
+       # Check for permissions:
+       # - the person_id should own the slice
+       # - the slice should be istantiated on the node
+       if not match(slices[0]['node_ids'], node_id) or \
+                       not match(slices[0]['person_ids'], person_id[0]['person_id']):
+               return "The slice %s and the user %s should be istantiated on the node %s" % \
+                               (slicename,  person_id[0]['person_id'], node_id)
+
+       # Manage the profile upload
+       # if upload_extra-delay is present, we upload the file and use it as profile
+       cmd_line = ""
+       file_to_upload = pipe_build(link_configuration_fields, "upload_extra-delay", "")
+       if file_to_upload:
+               cmd_line += "cat " + file_to_upload + " >> "
+
+       # start to build the command line
+       # the ssh command take as input
+       # the node_ip, the slicename, the port number
+       # a timeout and a filename (0 don't upload)
+
+       cmd_line += "ssh -i "+DBOX_KEY+" user@"+dbox_ip;
+       cmd_line += " "+str(node_ip)+" "+slicename+" "+str(port);
+
+       # add the timeout
+       cmd = pipe_build(link_configuration_fields, "timeout", "")
+       cmd_line += " "
+       if not cmd:
+               cmd_line += DEFAULT_TIMEOUT
+       else:
+               cmd_line += cmd
+
+       # add the filename to upload, "0" if not defined
+       cmd_line += " "
+       if file_to_upload:
+               cmd_line += file_to_upload
+       else:
+               cmd_line += "0"
+
+       # add the extra-delay parameter
+       # note that the upload_extra-delay, if present win
+       if file_to_upload:
+               cmd_line += pipe_build(link_configuration_fields, "upload_extra-delay", " extra-delay ")
+       else:
+               cmd_line += pipe_build(link_configuration_fields, "extra-delay", " extra-delay ")
+
+       # add plr, bw, delay, noerror
+       cmd_line += pipe_build(link_configuration_fields, "plr", " plr ")
+       cmd_line += pipe_build(link_configuration_fields, "bw", " bw ")
+       cmd_line += pipe_build(link_configuration_fields, "delay", " delay ")
+
+       # noerror should be malipulated in a different way
+       noerror = pipe_build(link_configuration_fields, "noerror", "")
+       if noerror == '0':
+               cmd_line += " noerror"
+
+       # send the command to the dummynetbox
+       # suppose that the key exist with right permissions
+
+       ret = os.system(cmd_line);
+       if ret == 0:
+               return "link configured"
+       else:
+               return "an error occurred link not configured"
+
index 1a4252a..c33d136 100644 (file)
@@ -18,7 +18,7 @@ from PLC.Auth import Auth                         # import the Auth parameter
 from PLC.DummyBoxes import DummyBox, DummyBoxes   # main class for a DummyBox
 from PLC.Methods.GetBootMedium import compute_key # key generation function
 
-WORK_DIR = "/tmp/DummynetBoxMedium"
+WORK_DIR = "/var/tmp/DummynetBoxMedium"
 BASE_IMAGE = "/usr/share/dummynet/picobsd.bin"
 
 class GetDummyBoxMedium(Method):
index 5489b27..e6d6a8b 100644 (file)
@@ -105,6 +105,7 @@ BootCheckAuthentication
 BootGetNodeDetails
 BootNotifyOwners
 BootUpdateNode
+ConfigureDummynetBox
 DeleteAddress
 DeleteAddressTypeFromAddress
 DeleteAddressType