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