(*) the various modules have a priority; lower gets invoked first
[nodemanager.git] / net.py
1 # $Id$
2 # $URL$
3
4 """network configuration"""
5
6 # system provided modules
7 import os, string, time, socket
8
9 # PlanetLab system modules
10 import sioc, plnet
11
12 # local modules
13 import bwlimit, logger, iptables, tools
14
15 # we can't do anything without a network
16 priority=1
17
18 dev_default = tools.get_default_if()
19
20 def start(options, conf):
21     logger.log("net: plugin starting up...")
22
23 def GetSlivers(data, config, plc):
24     logger.verbose("net: GetSlivers called.")
25     if not 'interfaces' in data: 
26         logger.log_missing_data('net.GetSlivers','interfaces')
27         return
28     plnet.InitInterfaces(logger, plc, data)
29     if 'OVERRIDES' in dir(config): 
30         if config.OVERRIDES.get('net_max_rate') == '-1':
31             logger.log("net: Slice and node BW Limits disabled.")
32             if len(bwlimit.tc("class show dev %s" % dev_default)): 
33                 logger.verbose("net: *** DISABLING NODE BW LIMITS ***")
34                 bwlimit.stop()
35         else:
36             InitNodeLimit(data)
37             InitI2(plc, data)
38     else:
39         InitNodeLimit(data)
40         InitI2(plc, data)
41     InitNAT(plc, data)
42
43
44 def InitNodeLimit(data):
45
46     # query running network interfaces
47     devs = sioc.gifconf()
48     ips = dict(zip(devs.values(), devs.keys()))
49     macs = {}
50     for dev in devs:
51         macs[sioc.gifhwaddr(dev).lower()] = dev
52
53     for interface in data['interfaces']:
54         # Get interface name preferably from MAC address, falling
55         # back on IP address.
56         hwaddr=interface['mac']
57         if hwaddr <> None: hwaddr=hwaddr.lower()
58         if hwaddr in macs:
59             dev = macs[interface['mac']]
60         elif interface['ip'] in ips:
61             dev = ips[interface['ip']]
62         else:
63             logger.log('net: %s: no such interface with address %s/%s' % (interface['hostname'], interface['ip'], interface['mac']))
64             continue
65
66         # Get current node cap
67         try:
68             old_bwlimit = bwlimit.get_bwcap(dev)
69         except:
70             old_bwlimit = None
71
72         # Get desired node cap
73         if interface['bwlimit'] is None or interface['bwlimit'] < 0:
74             new_bwlimit = bwlimit.bwmax
75         else:
76             new_bwlimit = interface['bwlimit']
77
78         if old_bwlimit != new_bwlimit:
79             # Reinitialize bandwidth limits
80             bwlimit.init(dev, new_bwlimit)
81
82             # XXX This should trigger an rspec refresh in case
83             # some previously invalid sliver bwlimit is now valid
84             # again, or vice-versa.
85
86 def InitI2(plc, data):
87     if not 'groups' in data: return
88
89     if "Internet2" in data['groups']:
90         logger.log("net: This is an Internet2 node.  Setting rules.")
91         i2nodes = []
92         i2nodeids = plc.GetNodeGroups(["Internet2"])[0]['node_ids']
93         for node in plc.GetInterfaces({"node_id": i2nodeids}, ["ip"]):
94             # Get the IPs
95             i2nodes.append(node['ip'])
96         # this will create the set if it doesn't already exist
97         # and add IPs that don't exist in the set rather than
98         # just recreateing the set.
99         bwlimit.exempt_init('Internet2', i2nodes)
100         
101         # set the iptables classification rule if it doesnt exist.
102         cmd = '-A POSTROUTING -m set --set Internet2 dst -j CLASSIFY --set-class 0001:2000 --add-mark'
103         rules = []
104         ipt = os.popen("/sbin/iptables-save")
105         for line in ipt.readlines(): rules.append(line.strip(" \n"))
106         ipt.close()
107         if cmd not in rules:
108             logger.verbose("net:  Adding iptables rule for Internet2")
109             os.popen("/sbin/iptables -t mangle " + cmd)
110
111 def InitNAT(plc, data):
112     
113     # query running network interfaces
114     devs = sioc.gifconf()
115     ips = dict(zip(devs.values(), devs.keys()))
116     macs = {}
117     for dev in devs:
118         macs[sioc.gifhwaddr(dev).lower()] = dev
119
120     ipt = iptables.IPTables()
121     for interface in data['interfaces']:
122         # Get interface name preferably from MAC address, falling
123         # back on IP address.
124         hwaddr=interface['mac']
125         if hwaddr <> None: hwaddr=hwaddr.lower()
126         if hwaddr in macs:
127             dev = macs[interface['mac']]
128         elif interface['ip'] in ips:
129             dev = ips[interface['ip']]
130         else:
131             logger.log('net: %s: no such interface with address %s/%s' % (interface['hostname'], interface['ip'], interface['mac']))
132             continue
133
134         try:
135             settings = plc.GetInterfaceTags({'interface_tag_id': interface['interface_tag_ids']})
136         except:
137             continue
138
139         for setting in settings:
140             if setting['category'].upper() != 'FIREWALL':
141                 continue
142             if setting['name'].upper() == 'EXTERNAL':
143                 # Enable NAT for this interface
144                 ipt.add_ext(dev)
145             elif setting['name'].upper() == 'INTERNAL':
146                 ipt.add_int(dev)
147             elif setting['name'].upper() == 'PF': # XXX Uglier code is hard to find...
148                 for pf in setting['value'].split("\n"):
149                     fields = {}
150                     for field in pf.split(","):
151                         (key, val) = field.split("=", 2)
152                         fields[key] = val
153                     if 'new_dport' not in fields:
154                         fields['new_dport'] = fields['dport']
155                     if 'source' not in fields:
156                         fields['source'] = "0.0.0.0/0"
157                     ipt.add_pf(fields)
158     ipt.commit()
159
160 def start(options, config):
161     pass