2 # Functions for interacting with the nodenetworks table in the database
4 # Mark Huang <mlhuang@cs.princeton.edu>
5 # Copyright (C) 2006 The Trustees of Princeton University
10 from types import StringTypes
14 from PLC.Faults import *
15 from PLC.Parameter import Parameter
16 from PLC.Filter import Filter
17 from PLC.Debug import profile
18 from PLC.Table import Row, Table
19 from PLC.NetworkTypes import NetworkType, NetworkTypes
20 from PLC.NetworkMethods import NetworkMethod, NetworkMethods
25 ip = socket.inet_ntoa(socket.inet_aton(ip))
30 def in_same_network(address1, address2, netmask):
32 Returns True if two IPv4 addresses are in the same network. Faults
33 if an address is invalid.
36 address1 = struct.unpack('>L', socket.inet_aton(address1))[0]
37 address2 = struct.unpack('>L', socket.inet_aton(address2))[0]
38 netmask = struct.unpack('>L', socket.inet_aton(netmask))[0]
40 return (address1 & netmask) == (address2 & netmask)
42 class NodeNetwork(Row):
44 Representation of a row in the nodenetworks table. To use, optionally
45 instantiate with a dict of values. Update as you would a
46 dict. Commit to the database with sync().
49 table_name = 'nodenetworks'
50 primary_key = 'nodenetwork_id'
51 join_tables = ['nodenetwork_setting']
53 'nodenetwork_id': Parameter(int, "Node interface identifier"),
54 'method': Parameter(str, "Addressing method (e.g., 'static' or 'dhcp')"),
55 'type': Parameter(str, "Address type (e.g., 'ipv4')"),
56 'ip': Parameter(str, "IP address", nullok = True),
57 'mac': Parameter(str, "MAC address", nullok = True),
58 'gateway': Parameter(str, "IP address of primary gateway", nullok = True),
59 'network': Parameter(str, "Subnet address", nullok = True),
60 'broadcast': Parameter(str, "Network broadcast address", nullok = True),
61 'netmask': Parameter(str, "Subnet mask", nullok = True),
62 'dns1': Parameter(str, "IP address of primary DNS server", nullok = True),
63 'dns2': Parameter(str, "IP address of secondary DNS server", nullok = True),
64 'bwlimit': Parameter(int, "Bandwidth limit", min = 0, nullok = True),
65 'hostname': Parameter(str, "(Optional) Hostname", nullok = True),
66 'node_id': Parameter(int, "Node associated with this interface"),
67 'is_primary': Parameter(bool, "Is the primary interface for this node"),
68 'nodenetwork_setting_ids' : Parameter([int], "List of nodenetwork settings"),
71 def validate_method(self, method):
72 network_methods = [row['method'] for row in NetworkMethods(self.api)]
73 if method not in network_methods:
74 raise PLCInvalidArgument, "Invalid addressing method %s"%method
77 def validate_type(self, type):
78 network_types = [row['type'] for row in NetworkTypes(self.api)]
79 if type not in network_types:
80 raise PLCInvalidArgument, "Invalid address type %s"%type
83 def validate_ip(self, ip):
84 if ip and not valid_ip(ip):
85 raise PLCInvalidArgument, "Invalid IP address %s"%ip
88 def validate_mac(self, mac):
93 bytes = mac.split(":")
96 for i, byte in enumerate(bytes):
98 if byte < 0 or byte > 255:
100 bytes[i] = "%02x" % byte
101 mac = ":".join(bytes)
103 raise PLCInvalidArgument, "Invalid MAC address %s"%mac
107 validate_gateway = validate_ip
108 validate_network = validate_ip
109 validate_broadcast = validate_ip
110 validate_netmask = validate_ip
111 validate_dns1 = validate_ip
112 validate_dns2 = validate_ip
114 def validate_bwlimit(self, bwlimit):
119 raise PLCInvalidArgument, 'Minimum bw is 500 kbs'
123 def validate_hostname(self, hostname):
128 if not PLC.Nodes.valid_hostname(hostname):
129 raise PLCInvalidArgument, "Invalid hostname %s"%hostname
133 def validate_node_id(self, node_id):
134 nodes = PLC.Nodes.Nodes(self.api, [node_id])
136 raise PLCInvalidArgument, "No such node %d"%node_id
140 def validate_is_primary(self, is_primary):
142 Set this interface to be the primary one.
146 nodes = PLC.Nodes.Nodes(self.api, [self['node_id']])
148 raise PLCInvalidArgument, "No such node %d"%node_id
151 if node['nodenetwork_ids']:
152 conflicts = NodeNetworks(self.api, node['nodenetwork_ids'])
153 for nodenetwork in conflicts:
154 if ('nodenetwork_id' not in self or \
155 self['nodenetwork_id'] != nodenetwork['nodenetwork_id']) and \
156 nodenetwork['is_primary']:
157 raise PLCInvalidArgument, "Can only set one primary interface per node"
163 Flush changes back to the database.
169 assert 'method' in self
170 method = self['method']
172 if method == "proxy" or method == "tap":
173 if 'mac' in self and self['mac']:
174 raise PLCInvalidArgument, "For %s method, mac should not be specified" % method
175 if 'ip' not in self or not self['ip']:
176 raise PLCInvalidArgument, "For %s method, ip is required" % method
177 if method == "tap" and ('gateway' not in self or not self['gateway']):
178 raise PLCInvalidArgument, "For tap method, gateway is required and should be " \
179 "the IP address of the node that proxies for this address"
180 # Should check that the proxy address is reachable, but
181 # there's no way to tell if the only primary interface is
184 elif method == "static":
185 if 'gateway' not in self or self['gateway'] is None:
186 if 'is_primary' in self and self['is_primary'] is True:
187 raise PLCInvalidArgument, "For static method primary network, gateway is required"
188 for key in ['ip', 'network', 'broadcast', 'netmask', 'dns1']:
189 if key not in self or not self[key]:
190 raise PLCInvalidArgument, "For static method, %s is required" % key
191 globals()[key] = self[key]
192 if not in_same_network(ip, network, netmask):
193 raise PLCInvalidArgument, "IP address %s is inconsistent with network %s/%s" % \
194 (ip, network, netmask)
195 if not in_same_network(broadcast, network, netmask):
196 raise PLCInvalidArgument, "Broadcast address %s is inconsistent with network %s/%s" % \
197 (broadcast, network, netmask)
198 if 'gateway' in globals() and not in_same_network(ip, gateway, netmask):
199 raise PLCInvalidArgument, "Gateway %s is not reachable from %s/%s" % \
200 (gateway, ip, netmask)
202 elif method == "ipmi":
203 if 'ip' not in self or not self['ip']:
204 raise PLCInvalidArgument, "For ipmi method, ip is required"
206 class NodeNetworks(Table):
208 Representation of row(s) from the nodenetworks table in the
212 def __init__(self, api, nodenetwork_filter = None, columns = None):
213 Table.__init__(self, api, NodeNetwork, columns)
215 sql = "SELECT %s FROM view_nodenetworks WHERE True" % \
216 ", ".join(self.columns)
218 if nodenetwork_filter is not None:
219 if isinstance(nodenetwork_filter, (list, tuple, set)):
220 nodenetwork_filter = Filter(NodeNetwork.fields, {'nodenetwork_id': nodenetwork_filter})
221 elif isinstance(nodenetwork_filter, dict):
222 nodenetwork_filter = Filter(NodeNetwork.fields, nodenetwork_filter)
223 elif isinstance(nodenetwork_filter, int):
224 nodenetwork_filter = Filter(NodeNetwork.fields, {'nodenetwork_id': [nodenetwork_filter]})
226 raise PLCInvalidArgument, "Wrong node network filter %r"%nodenetwork_filter
227 sql += " AND (%s) %s" % nodenetwork_filter.sql(api)