Several fixes:
[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         # canonical representation of dependencies
44         depends = ' '.join( sorted( (self.depends or "").split(' ') ) )
45         
46         # download rpms and pack into a tar archive
47         return (
48             "python mcastfwd.py %s"
49         ) % ( ' '.join([iface.address for iface in self.ifaces]), )
50     def _command_set(self, value):
51         # ignore
52         return
53     command = property(_command_get, _command_set)
54     
55         
56 class MulticastAnnouncer(application.Application):
57     """
58     This application installs a userspace daemon that
59     monitors multicast membership and announces it on all
60     multicast-capable interfaces.
61     This does not usually happen automatically on PlanetLab slivers.
62     """
63     def __init__(self, *p, **kw):
64         super(MulticastAnnouncer, self).__init__(*p, **kw)
65         
66         self.sources = ' '.join([
67             os.path.join( os.path.dirname(__file__),
68                 "scripts", "mcastfwd.py" ),
69             ipaddr2.__file__.replace('.pyc','.py').replace('.pyo','.py'),
70         ])
71         
72         self.sudo = True
73         
74         self.depends = "python"
75         
76         self.ifaces = []
77         self.router = None
78     
79     def _command_get(self):
80         # canonical representation of dependencies
81         depends = ' '.join( sorted( (self.depends or "").split(' ') ) )
82         
83         # download rpms and pack into a tar archive
84         return (
85             "python mcastfwd.py -A %s"
86         ) % ( ' '.join([iface.address for iface in self.ifaces]), )
87     def _command_set(self, value):
88         # ignore
89         return
90     command = property(_command_get, _command_set)
91
92 class MulticastRouter(application.Application):
93     """
94     This application installs a userspace daemon that
95     monitors multicast membership and announces it on all
96     multicast-capable interfaces.
97     This does not usually happen automatically on PlanetLab slivers.
98     """
99     ALGORITHM_MAP = {
100         'dvmrp' : {
101             'sources' :
102                 ' '.join([
103                     os.path.join( os.path.dirname(__file__),
104                         "scripts", "mrouted-3.9.5-pl.patch" ),
105                 ]) ,
106             'depends' : "",
107             'buildDepends' : "byacc gcc make patch",
108             'build' : 
109                 "mkdir -p mrouted && "
110                 "echo '3a1c1e72c4f6f7334d72df4c50b510d7  mrouted-3.9.5.tar.bz2' > archive_sums.txt && "
111                 "wget -q -c -O mrouted-3.9.5.tar.bz2 ftp://ftp.vmlinux.org/pub/People/jocke/mrouted/mrouted-3.9.5.tar.bz2 && "
112                 "md5sum -c archive_sums.txt && "
113                 "tar xvjf mrouted-3.9.5.tar.bz2 -C mrouted --strip-components=1 && "
114                 "cd mrouted && patch -p1 < ${SOURCES}/mrouted-3.9.5-pl.patch && make"
115                 ,
116             'install' : "cp mrouted/mrouted ${SOURCES}",
117             'command' : 
118                 "while test \\! -e /var/run/mcastrt ; do sleep 1 ; done ; "
119                 "echo 'phyint eth0 disable' > ./mrouted.conf ; "
120                 "for iface in %(nonifaces)s ; do echo \"phyint $iface disable\" >> ./mrouted.conf ; done ; "
121                 "./mrouted -f %(debugbit)s -c ./mrouted.conf"
122                 ,
123             'debugbit' : "-dpacket,igmp,routing,interface,pruning,membership,cache",
124         }
125     }
126     
127     def __init__(self, *p, **kw):
128         super(MulticastRouter, self).__init__(*p, **kw)
129         
130         self.algorithm = 'dvmrp'
131         self.sudo = True
132         self.nonifaces = []
133     
134     def _non_set(self, value):
135         # ignore
136         return
137     
138     def _gen_get(attribute, self):
139         return self.ALGORITHM_MAP[self.algorithm][attribute]
140     
141     def _command_get(self):
142         command = self.ALGORITHM_MAP[self.algorithm]['command']
143         debugbit = self.ALGORITHM_MAP[self.algorithm]['debugbit']
144         
145         # download rpms and pack into a tar archive
146         return command % {
147             'nonifaces' : ' '.join([iface.if_name for iface in self.nonifaces if iface.if_name]),
148             'debugbit' : (debugbit if self.stderr else ""),
149         }
150     command = property(_command_get, _non_set)
151
152     build = property(functools.partial(_gen_get, "build"), _non_set)
153     install = property(functools.partial(_gen_get, "install"), _non_set)
154     sources = property(functools.partial(_gen_get, "sources"), _non_set)
155     depends = property(functools.partial(_gen_get, "depends"), _non_set)
156     buildDepends = property(functools.partial(_gen_get, "buildDepends"), _non_set)
157