2 # Marta Carbone - UniPi
5 # This script allows to configure a dummynet box from the central site.
7 # The authentication is done by a dummynet box key
8 # located on the Central Site /usr/share/dummynet/dbox_key
9 # The public key will be propagated to the dummynet boxes
10 # by the same script used for users.
12 from PLC.Faults import * # faults library
13 from PLC.Method import Method # base class used for methods
14 from PLC.Parameter import Parameter, Mixed # define input parameters
15 from PLC.DummyBoxes import DummyBox, DummyBoxes # main class for a DummyBox
16 from PLC.Sites import Site, Sites # manage authentication
17 from PLC.Auth import Auth # import Auth parameter
18 from PLC.Nodes import Node, Nodes # main class for Nodes
19 from PLC.NodeNetworks import * # main class for Nodes
20 from PLC.Persons import Person, Persons # main class for Persons
21 from PLC.Slices import Slice, Slices # main class for Slices
24 # basename for the Dummynet box public key
25 # used to send configuration requests
26 # to the dummynet box.
27 DBOX_KNOWN_HOSTS="/var/tmp/.dbox_known_hosts"
28 DBOX_KEY="/usr/share/dummynet/dbox_key"
32 # return 1 if the value is present in the array
33 def match(list, value):
39 # if the field key is set in the fields_list dict,
40 # return the cmd_keyword string and the dict value
41 def pipe_build(fields_list, field, cmd_keyword):
43 a = fields_list[field]
47 return cmd_keyword + str(a)
49 class ConfigureDummynetBox(Method):
51 This script will configure an emulated link.
54 - takes as input the node_id, the slicename,
55 and other dummynet configuration parameters;
56 - make some permission checks;
57 - retrive other useful information from the database;
58 - send the emulated link configuration to the DummynetBox;
59 - wait for the remote execution and return the reply to the user.
62 roles = ['admin', 'pi', 'tech', 'user']
64 'node_id': Parameter(int, "Node identifier"),
65 'slicename': Parameter(str, "Name of the slice"),
66 'port': Parameter(int, "Port number"),
67 'bw': Parameter(str, "dummynet, bandwidth limit"),
68 'delay': Parameter(str, "dummynet, delay"),
69 'plr': Parameter(str, "dummynet, packet loss rate"),
70 'upload_extra-delay': Parameter(str, "dummynet, extra delay file"),
71 'extra-delay': Parameter(str, "dummynet, extra delay, if above unset"),
72 'timeout': Parameter(str, "Rules timeout"),
73 'noerror': Parameter(str, "dummynet, if '0' do not propagate errors to the upper layer"),
81 returns = Parameter(str, 'Remote command execution output');
83 def call(self, auth, link_config):
85 # Only a person istance can send configuration commands
86 if not isinstance(self.caller, Person):
87 return "1 Authentication method not allowed to perform this operation";
90 email = str(self.caller['email']);
92 # Get mandatory arguments
93 node_id = link_config['node_id']
94 slicename = link_config['slicename']
95 port = link_config['port']
97 # Check not mandatory, they will be done again on the dummynet box
99 return "1 Port should be > 1024"
101 # Get the dummynet box connected to this node
102 nodes = Nodes(self.api, {'node_id': node_id}, ['dummybox_id','nodenetwork_ids'])
104 return "1 Node not present"
106 dummybox_id = nodes[0]['dummybox_id']
108 if (dummybox_id == 0): # dummybox_id == 0 means empty
109 return "1 This node has no dummynet box connected"
111 dummyboxes = DummyBoxes(self.api, {'dummybox_id': dummybox_id}, ['ip'])
112 dbox_ip = str(dummyboxes[0]['ip'])
114 # Get the node ip address, we need to cross with the NodeNetworks table
115 nodenetwork_id = NodeNetworks(self.api, {'node_id': node_id, 'is_primary':'t'}, ['ip']);
116 if not nodenetwork_id or not nodenetwork_id[0]['ip']:
117 return "1 Network not configured on this node"
119 node_ip = nodenetwork_id[0]['ip']
121 # Search the person_id
122 person_id = Persons(self.api, {'email': email}, ['person_id'])
124 return "1 User not found"
126 # Search slice information
127 slices = Slices(self.api, {'name': slicename}, ['node_ids', 'person_ids'])
129 return "1 No slices found"
131 # Check for permissions:
132 # - the person_id should own the slice
133 # - the slice should be istantiated on the node
134 if not match(slices[0]['node_ids'], node_id) or \
135 not match(slices[0]['person_ids'], person_id[0]['person_id']):
136 return "1 The slice %s and the user %s should be istantiated on the node %s" % \
137 (slicename, person_id[0]['person_id'], node_id)
139 # Manage the profile upload
140 # if upload_extra-delay is present, we upload the file and use it as profile
142 file_to_upload = pipe_build(link_config, "upload_extra-delay", "")
144 cmd_line += "cat " + file_to_upload + " | "
146 # start to build the command line
147 # The ssh commands need to use a known hosts file.
148 # Since it is called by different programs, each one
149 # with a different home location, we use some option
150 # to force the known hosts file to be always the same.
151 # We make the command not interactive too.
152 SSH = "ssh -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=yes ";
153 SSH += "-o UserKnownHostsFile=" + "'"+DBOX_KNOWN_HOSTS+"'" + " ";
155 # the ssh command takes as input
156 # the node_ip, the slicename, the port number
157 # a timeout and a filename (0 don't upload)
158 cmd_line += SSH + " -i "+DBOX_KEY+" user@"+dbox_ip;
159 cmd_line += " "+str(node_ip)+" "+slicename+" "+str(port);
162 cmd = pipe_build(link_config, "timeout", "")
165 cmd_line += DEFAULT_TIMEOUT
169 # add the filename to upload, "0" if not defined
172 cmd_line += os.path.basename(file_to_upload)
176 # add the extra-delay parameter, only the basename
177 # note that the upload_extra-delay, if present win
179 link_config['upload_extra-delay'] = os.path.basename(link_config['upload_extra-delay']);
180 cmd_line += pipe_build(link_config, "upload_extra-delay", " extra-delay ")
182 link_config['extra-delay'] = os.path.basename(link_config['extra-delay']);
183 cmd_line += pipe_build(link_config, "extra-delay", " extra-delay ")
185 # add plr, bw, delay, noerror
186 cmd_line += pipe_build(link_config, "plr", " plr ")
187 cmd_line += pipe_build(link_config, "bw", " bw ")
188 cmd_line += pipe_build(link_config, "delay", " delay ")
190 # noerror should be malipulated in a different way
191 noerror = pipe_build(link_config, "noerror", "")
193 cmd_line += " noerror"
195 # send the command to the dummynetbox
196 # get output and return code
197 command = os.popen(cmd_line);
198 output = command.read();
199 ret = str(command.close());
206 ret += "The command line used to configure the link follows:\n"
208 ret += " \n" + output