Forward all packets to the default multicast egress when a MulticastForwarder is...
[nepi.git] / src / nepi / testbeds / planetlab / multicast.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from constants import TESTBED_ID
5
6 import os
7 import os.path
8 import sys
9 import functools
10
11 import nepi.util.server as server
12 import nepi.util.ipaddr2 as ipaddr2
13
14 import logging
15
16 import application
17
18 class MulticastForwarder(application.Application):
19     """
20     This application installs a userspace packet forwarder
21     that, when connected to a node, filters all packets
22     flowing through multicast-capable virtual interfaces
23     and applies custom-specified routing policies
24     """
25     def __init__(self, *p, **kw):
26         super(MulticastForwarder, self).__init__(*p, **kw)
27         
28         self.sources = ' '.join([
29             os.path.join( os.path.dirname(__file__),
30                 "scripts", "mcastfwd.py" ),
31             ipaddr2.__file__.replace('.pyc','.py').replace('.pyo','.py'),
32         ])
33         
34         self.sudo = True
35         
36         self.depends = "python"
37         
38         # Initialized when connected
39         self.ifaces = []
40         self.router = None
41     
42     def _command_get(self):
43         cmd = "python mcastfwd.py "
44         if not self.router:
45             cmd += "-R "
46         cmd += ' '.join([iface.address for iface in self.ifaces])
47     def _command_set(self, value):
48         # ignore
49         return
50     command = property(_command_get, _command_set)
51     
52         
53 class MulticastAnnouncer(application.Application):
54     """
55     This application installs a userspace daemon that
56     monitors multicast membership and announces it on all
57     multicast-capable interfaces.
58     This does not usually happen automatically on PlanetLab slivers.
59     """
60     def __init__(self, *p, **kw):
61         super(MulticastAnnouncer, self).__init__(*p, **kw)
62         
63         self.sources = ' '.join([
64             os.path.join( os.path.dirname(__file__),
65                 "scripts", "mcastfwd.py" ),
66             ipaddr2.__file__.replace('.pyc','.py').replace('.pyo','.py'),
67         ])
68         
69         self.sudo = True
70         
71         self.depends = "python"
72         
73         self.ifaces = []
74         self.router = None
75     
76     def _command_get(self):
77         return (
78             "python mcastfwd.py -A %s"
79         ) % ( ' '.join([iface.address for iface in self.ifaces]), )
80     def _command_set(self, value):
81         # ignore
82         return
83     command = property(_command_get, _command_set)
84
85 class MulticastRouter(application.Application):
86     """
87     This application installs a userspace daemon that
88     monitors multicast membership and announces it on all
89     multicast-capable interfaces.
90     This does not usually happen automatically on PlanetLab slivers.
91     """
92     ALGORITHM_MAP = {
93         'dvmrp' : {
94             'sources' :
95                 ' '.join([
96                     os.path.join( os.path.dirname(__file__),
97                         "scripts", "mrouted-3.9.5-pl.patch" ),
98                 ]) ,
99             'depends' : "",
100             'buildDepends' : "byacc gcc make patch",
101             'build' : 
102                 "mkdir -p mrouted && "
103                 "echo '3a1c1e72c4f6f7334d72df4c50b510d7  mrouted-3.9.5.tar.bz2' > archive_sums.txt && "
104                 "wget -q -c -O mrouted-3.9.5.tar.bz2 ftp://ftp.vmlinux.org/pub/People/jocke/mrouted/mrouted-3.9.5.tar.bz2 && "
105                 "md5sum -c archive_sums.txt && "
106                 "tar xvjf mrouted-3.9.5.tar.bz2 -C mrouted --strip-components=1 && "
107                 "cd mrouted && patch -p1 < ${SOURCES}/mrouted-3.9.5-pl.patch && make"
108                 ,
109             'install' : "cp mrouted/mrouted ${SOURCES}",
110             'command' : 
111                 "while test \\! -e /var/run/mcastrt ; do sleep 1 ; done ; "
112                 "echo 'phyint eth0 disable' > ./mrouted.conf ; "
113                 "for iface in %(nonifaces)s ; do echo \"phyint $iface disable\" >> ./mrouted.conf ; done ; "
114                 "./mrouted -f %(debugbit)s -c ./mrouted.conf"
115                 ,
116             'debugbit' : "-dpacket,igmp,routing,interface,pruning,membership,cache",
117         }
118     }
119     
120     def __init__(self, *p, **kw):
121         super(MulticastRouter, self).__init__(*p, **kw)
122         
123         self.algorithm = 'dvmrp'
124         self.sudo = True
125         self.nonifaces = []
126     
127     def _non_set(self, value):
128         # ignore
129         return
130     
131     def _gen_get(attribute, self):
132         return self.ALGORITHM_MAP[self.algorithm][attribute]
133     
134     def _command_get(self):
135         command = self.ALGORITHM_MAP[self.algorithm]['command']
136         debugbit = self.ALGORITHM_MAP[self.algorithm]['debugbit']
137         
138         # download rpms and pack into a tar archive
139         return command % {
140             'nonifaces' : ' '.join([iface.if_name for iface in self.nonifaces if iface.if_name]),
141             'debugbit' : (debugbit if self.stderr else ""),
142         }
143     command = property(_command_get, _non_set)
144
145     build = property(functools.partial(_gen_get, "build"), _non_set)
146     install = property(functools.partial(_gen_get, "install"), _non_set)
147     sources = property(functools.partial(_gen_get, "sources"), _non_set)
148     depends = property(functools.partial(_gen_get, "depends"), _non_set)
149     buildDepends = property(functools.partial(_gen_get, "buildDepends"), _non_set)
150