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