def test_command_error(self):
     """Run a regular command returning an error"""
     Commands.run("true")
     with self.assertRaises(CommandError) as ce:
         Commands.run("false")
     self.assertEqual(ce.exception.command, "false")
     self.assertEqual(ce.exception.retcode, 1)
 def test_command_orders_with_errors(self):
     """Run commands in a specific order with one command return an error"""
     self.commands.insert(30, "false")
     with self.assertRaises(CommandError):
         Commands.run(*self.commands)
     self.assertEqual(file(self.testfile).read(),
                      "".join("%d\n" % i for i in range(1, 32)))
 def test_several_inexistant_commands(self):
     """Run several inexistant command"""
     with self.assertRaises(CommandError) as ce:
         Commands.run("i_do_not_exist", "unknown_command")
     self.assertEqual(ce.exception.command, "i_do_not_exist")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     self.assertEqual(ce.exception.index, 0)
 def test_several_inexistant_commands(self):
     """Run several inexistant command"""
     with self.assertRaises(CommandError) as ce:
         Commands.run("i_do_not_exist", "unknown_command")
     self.assertEqual(ce.exception.command, "i_do_not_exist")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     self.assertEqual(ce.exception.index, 0)
 def test_command_error(self):
     """Run a regular command returning an error"""
     Commands.run("true")
     with self.assertRaises(CommandError) as ce:
         Commands.run("false")
     self.assertEqual(ce.exception.command, "false")
     self.assertEqual(ce.exception.retcode, 1)
 def test_command_orders_with_errors(self):
     """Run commands in a specific order with one command return an error"""
     self.commands.insert(30, "false")
     with self.assertRaises(CommandError):
         Commands.run(*self.commands)
     self.assertEqual(
         file(self.testfile).read(),
         "".join("%d\n" % i for i in range(1, 32)))
 def test_inexistant_command(self):
     """Run an inexistant command"""
     with self.assertRaises(CommandError) as ce:
         Commands.run("i_do_not_exist")
     self.assertEqual(ce.exception.command, "i_do_not_exist")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     # Also check that we can build the exception description
     str(ce.exception)
 def test_inexistant_command(self):
     """Run an inexistant command"""
     with self.assertRaises(CommandError) as ce:
         Commands.run("i_do_not_exist")
     self.assertEqual(ce.exception.command, "i_do_not_exist")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     # Also check that we can build the exception description
     str(ce.exception)
 def test_mix_inexistant_commands(self):
     """Run one inexistant command and one regular command"""
     with self.assertRaises(CommandError) as ce:
         Commands.run("echo hello", "unknown_command")
     self.assertEqual(ce.exception.command, "unknown_command")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     self.assertEqual(ce.exception.index, 1)
     with self.assertRaises(CommandError) as ce:
         Commands.run("unknown_command", "echo hello")
     self.assertEqual(ce.exception.command, "unknown_command")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     self.assertEqual(ce.exception.index, 0)
Exemple #10
0
 def test_mix_inexistant_commands(self):
     """Run one inexistant command and one regular command"""
     with self.assertRaises(CommandError) as ce:
         Commands.run("echo hello", "unknown_command")
     self.assertEqual(ce.exception.command, "unknown_command")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     self.assertEqual(ce.exception.index, 1)
     with self.assertRaises(CommandError) as ce:
         Commands.run("unknown_command", "echo hello")
     self.assertEqual(ce.exception.command, "unknown_command")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
     self.assertEqual(ce.exception.index, 0)
Exemple #11
0
 def stats(self):
     """Return statistics for each interface and client."""
     if self.router is None:
         return {}           # Setup is not done yet
     stats = {}
     output = "\n".join([Commands.run("%(iptables)s -t mangle -v -S %(accounting)s",
                                      iptables=iptables,
                                      **self.config) for iptables in self.iptables])
     for line in output.split("\n"):
         mo = self.STATSRE.match(line.strip())
         if mo:
             interface = mo.group('interface')
             direction = mo.group('direction')
             client = mo.group('client')
             if interface not in stats:
                 stats[interface] = {}
                 stats[interface]['details'] = {}
             if client not in stats[interface]['details']:
                 stats[interface]['details'][client]= {}
             stats[interface]['details'][client][direction] = int(mo.group("bytes"))
     for interface in stats:
         up = down = clients = 0
         for client in stats[interface]['details']:
             clients = clients + 1
             up = up + stats[interface]['details'][client].get("up", 0)
             down = down + stats[interface]['details'][client].get("down", 0)
         stats[interface]["clients"] = clients
         stats[interface]["up"] = up
         stats[interface]["down"] = down
     return stats
Exemple #12
0
 def test_several_commands(self):
     """Test several commands using variables"""
     self.assertEqual(Commands.run("echo %(var1)s %(var2)s",
                                   "echo %(var1)s %(var3)s",
                                   "echo bye",
                                   var1="hello1", var2="hello2", var3="hello3"),
                      ["hello1 hello2\n", "hello1 hello3\n", "bye\n"])
Exemple #13
0
 def test_several_commands(self):
     """Test several commands using variables"""
     self.assertEqual(
         Commands.run("echo %(var1)s %(var2)s",
                      "echo %(var1)s %(var3)s",
                      "echo bye",
                      var1="hello1",
                      var2="hello2",
                      var3="hello3"),
         ["hello1 hello2\n", "hello1 hello3\n", "bye\n"])
Exemple #14
0
 def test_error_command_withoutput(self):
     """Test a command with non-0 result and output"""
     result = Commands.run_noerr("grep stuff /i_do_not_exist")
     self.assertEqual(result[:6], "grep: ")
Exemple #15
0
 def test_false_command(self):
     """Test a command that return non 0 result"""
     self.assertEqual(Commands.run_noerr("false"), "")
Exemple #16
0
 def test_inexistant_command(self):
     """Test an inexistant command with run_noerr"""
     with self.assertRaises(CommandError) as ce:
         Commands.run_noerr("i_do_not_exist")
     self.assertEqual(ce.exception.command, "i_do_not_exist")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
Exemple #17
0
 def test_run_one_command(self):
     """Run one command"""
     Commands.run("echo hello")
Exemple #18
0
 def test_no_commands(self):
     """Run no command"""
     self.assertEqual(Commands.run(), None)
Exemple #19
0
 def test_run_several_commands(self):
     """Run several commands"""
     Commands.run("echo hello", "echo hi", "echo good bye")
Exemple #20
0
 def test_output_several_commands(self):
     """Run several commands and check their outputs"""
     self.assertEqual(Commands.run("echo hello", "echo hi"),
                      ["hello\n", "hi\n"])
Exemple #21
0
 def test_output_several_commands(self):
     """Run several commands and check their outputs"""
     self.assertEqual(Commands.run("echo hello", "echo hi"),
                      ["hello\n", "hi\n"])
Exemple #22
0
 def test_several_variables(self):
     """Test a command using several variables"""
     self.assertEqual(Commands.run("echo %(var1)s %(var2)s",
                                   var1="hello1", var2="hello2"),
                      "hello1 hello2\n")
Exemple #23
0
 def test_command_orders(self):
     """Run commands in a specific order"""
     Commands.run(*self.commands)
     self.assertEqual(
         file(self.testfile).read(),
         "".join("%d\n" % i for i in range(1, 100)))
Exemple #24
0
 def test_run_one_command(self):
     """Run one command"""
     Commands.run("echo hello")
Exemple #25
0
 def test_command_orders(self):
     """Run commands in a specific order"""
     Commands.run(*self.commands)
     self.assertEqual(file(self.testfile).read(),
                      "".join("%d\n" % i for i in range(1, 100)))
Exemple #26
0
 def test_inexistant_command(self):
     """Test an inexistant command with run_noerr"""
     with self.assertRaises(CommandError) as ce:
         Commands.run_noerr("i_do_not_exist")
     self.assertEqual(ce.exception.command, "i_do_not_exist")
     self.assertEqual(ce.exception.retcode, errno.ENOENT)
Exemple #27
0
 def test_output_one_command(self):
     """Run one command and check its output"""
     self.assertEqual(Commands.run("echo hello"), "hello\n")
Exemple #28
0
 def test_run_several_commands(self):
     """Run several commands"""
     Commands.run("echo hello", "echo hi", "echo good bye")
Exemple #29
0
 def test_output_one_command(self):
     """Run one command and check its output"""
     self.assertEqual(Commands.run("echo hello"), "hello\n")
Exemple #30
0
 def test_no_commands(self):
     """Run no command"""
     self.assertEqual(Commands.run(), None)
Exemple #31
0
 def test_several_variables(self):
     """Test a command using several variables"""
     self.assertEqual(
         Commands.run("echo %(var1)s %(var2)s",
                      var1="hello1",
                      var2="hello2"), "hello1 hello2\n")
Exemple #32
0
 def test_one_variable(self):
     """Test a command using one variable"""
     self.assertEqual(Commands.run("echo %(var)s", var="hello"), "hello\n")
Exemple #33
0
 def test_false_command(self):
     """Test a command that return non 0 result"""
     self.assertEqual(Commands.run_noerr("false"), "")
Exemple #34
0
    def setup(self):
        """Setup the binder for the first time.

        Cleaning is also handled here since the binder has no way to
        clean on exit.
        """
        self.interfaces = self.router.interfaces.keys() # Ordered interface list
        self.interfaces.sort()
        self.mark = Mark(len(self.interfaces),                # Netfilter mark producer
                         self.config['max_users'])
        self.slots = SlotsProvider(self.config['max_users'])  # Slot producer
        self.tickets = TicketsProvider()                      # Ticket producer

        # Netfilter
        for chain in [ "prerouting", "accounting", "postrouting" ]:
            subs = dict(chain = self.config[chain],
                        chain_upper = chain.upper())
            if chain == "accounting":
                subs['chain_upper'] = "POSTROUTING"
            logger.info("setup %(chain)s chain" % subs)
            # Cleanup old iptables rules
            for iptables in self.iptables:
                Commands.run_noerr("%(iptables)s -t mangle -D %(chain_upper)s -j  %(chain)s",
                                   "%(iptables)s -t mangle -F %(chain)s",
                                   "%(iptables)s -t mangle -X %(chain)s",
                                   iptables=iptables,
                                   **subs)
                # Setup the new chains
                Commands.run("%(iptables)s -t mangle -N %(chain)s",
                             "%(iptables)s -t mangle -I %(chain_upper)s -j  %(chain)s",
                             iptables=iptables,
                             **subs)

        # Setup QoS
        for interface in self.interfaces + self.router.incoming:
            logger.info("setup QoS for interface %s" % interface)
            Commands.run_noerr("tc qdisc del dev %(interface)s root", interface=interface)
            Commands.run(
                # Flush QoS
                "tc qdisc add dev %(interface)s root handle 1: drr",
                # Default class
                "tc class add dev %(interface)s parent 1: classid 1:2 drr",
                "tc qdisc add dev %(interface)s parent 1:2 handle 12: sfq",
                # Use default class for unmatched traffic
                "tc filter add dev %(interface)s protocol arp parent 1:0"
                "  prio 1 u32 match u32 0 0 flowid 1:2", # ARP
                interface=interface, **self.config)
            for iptables in self.iptables:
                Commands.run(
                    "%(iptables)s -t mangle -A %(postrouting)s"
                    "  -o %(interface)s -j CLASSIFY --set-class 1:2", # IP
                    iptables=iptables,
                    interface=interface, **self.config)

        # Setup routing rules
        for interface in self.interfaces:
            logger.info("setup ip rules for interface %s" % interface)
            for ip in self.ipcmd:
                Commands.run_noerr("%(ip)s rule del fwmark %(mark)s table %(interface)s",
                                   mark="%s/%s" % self.mark(self.interfaces.index(interface)),
                                   ip = ip,
                                   interface=interface)
                Commands.run("%(ip)s rule add fwmark %(mark)s table %(interface)s",
                             ip = ip,
                             mark="%s/%s" % self.mark(self.interfaces.index(interface)),
                             interface=interface)
Exemple #35
0
 def test_error_command_withoutput(self):
     """Test a command with non-0 result and output"""
     result = Commands.run_noerr("grep stuff /i_do_not_exist")
     self.assertEqual(result[:6], "grep: ")
Exemple #36
0
    def bind(self, client, interface, qos, bind=True):
        """Bind or unbind a user.

        This is the method that will issue all `tc` and `iptables`
        commands to ensure the binding of the user to the chosen
        interface and QoS.

        :param client: IP of the user
        :type client: string
        :param interface: name of the outgoing interface
        :type interface: string
        :param qos: QoS name
        :type qos: string
        :param slot: QoS slot
        :type slot: integer
        :param bind: bind or unbind?
        :type bind: boolean
        """
        ticket = self.tickets.get(client)
        slot = self.slots.get(client)
        mark = self.mark(self.interfaces.index(interface), slot)
        # tc qdisc and classes for the user
        def build(interface, qos, what):
            r = self.router.interfaces[interface].qos[qos].settings.get(what, None)
            result = { 'up': r, 'down': r }
            if type(r) is dict:
                result['up'] = r.get('up', None)
                result['down'] = r.get('down', None)
            return result
        bw = build(interface, qos, "bandwidth")
        netem = build(interface, qos, "netem")
        for iface in [interface,] + self.router.incoming:
            direction = (iface in self.router.incoming) and 'down' or 'up'
            opts=dict(iface=iface,
                      mark=mark[0],
                      ticket=ticket,
                      bw=bw[direction],
                      netem=netem[direction],
                      add=(bind and "add" or "del"))
            # Create a deficit round robin scheduler
            Commands.run("tc class %(add)s dev %(iface)s parent 1: classid 1:%(ticket)s0 drr",
                         **opts)
            if bw[direction] is not None and bind:
                # TBF for bandwidth limit...
                Commands.run(
                    "tc qdisc %(add)s dev %(iface)s parent 1:%(ticket)s0 handle %(ticket)s0:"
                    "  tbf rate %(bw)s", **opts)
                if netem[direction] is not None and bind:
                    # ...and netem
                    Commands.run(
                        "tc qdisc %(add)s dev %(iface)s parent %(ticket)s0:1 "
                        "  handle %(ticket)s1:"
                        "  netem %(netem)s", **opts)
            elif netem[direction] is not None and bind:
                # Just netem
                Commands.run(
                    "tc qdisc %(add)s dev %(iface)s parent 1:%(ticket)s0 handle %(ticket)s0:"
                    "  netem %(netem)s", **opts)
            elif bind:
                # No QoS: just use SFQ
                Commands.run(
                    "tc qdisc %(add)s dev %(iface)s parent 1:%(ticket)s0"
                    "  handle %(ticket)s0: sfq",
                    **opts)
        # iptables to classify and accounting
        opts = dict(
            A=(bind and "A" or "D"),
            outgoing=interface,
            client=client,
            mark=mark[0], mask=mark[1],
            ticket=ticket,
            iptables=self.isipv6(client) and "ip6tables" or "iptables",
            **self.config)
        for incoming in self.router.incoming:
            Commands.run(
                # Mark the incoming packet from the client
                "%(iptables)s -t mangle -%(A)s %(prerouting)s -i %(incoming)s"
                "  -s %(client)s -j MARK --set-mark %(mark)s/%(mask)s",
                incoming=incoming,
                **opts)
        Commands.run(
            # Keep the mark only if we reached the output interface
            "%(iptables)s -t mangle -%(A)s %(postrouting)s "
            "  -o %(outgoing)s -s %(client)s -m mark --mark %(mark)s/%(mask)s"
            "  -j CONNMARK --save-mark --nfmask %(mask)s --ctmask %(mask)s",
            # Classify. Outgoing
            "%(iptables)s -t mangle -%(A)s %(postrouting)s"
            "  -o %(outgoing)s -m connmark --mark %(mark)s/%(mask)s"
            "  -j CLASSIFY --set-class 1:%(ticket)s0",
            **opts)
        for incoming in self.router.incoming:
            Commands.run(
                # Classify. Incoming
                "%(iptables)s -t mangle -%(A)s %(postrouting)s"
                "  -o %(incoming)s -m connmark --mark %(mark)s/%(mask)s"
                "  -j CLASSIFY --set-class 1:%(ticket)s0",
                incoming=incoming,
                **opts)
        Commands.run(
            # Accounting. Outgoing
            "%(iptables)s -t mangle -%(A)s %(accounting)s"
            "  -o %(outgoing)s -m connmark --mark %(mark)s/%(mask)s"
            "  -m comment --comment up-%(outgoing)s-%(client)s",
            **opts)
        for incoming in self.router.incoming:
            Commands.run(
                # Accouting. Incoming
                "%(iptables)s -t mangle -%(A)s %(accounting)s"
                "  -o %(incoming)s -m connmark --mark %(mark)s/%(mask)s"
                "  -m comment --comment down-%(outgoing)s-%(client)s",
                incoming=incoming,
                **opts)
Exemple #37
0
 def test_one_variable(self):
     """Test a command using one variable"""
     self.assertEqual(Commands.run("echo %(var)s", var="hello"), "hello\n")