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