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
7 # $Id: NodeNetworks.py,v 1.15 2006/11/09 19:43:55 mlhuang Exp $
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'
52 'nodenetwork_id': Parameter(int, "Node interface identifier"),
53 'method': Parameter(str, "Addressing method (e.g., 'static' or 'dhcp')"),
54 'type': Parameter(str, "Address type (e.g., 'ipv4')"),
55 'ip': Parameter(str, "IP address", nullok = True),
56 'mac': Parameter(str, "MAC address", nullok = True),
57 'gateway': Parameter(str, "IP address of primary gateway", nullok = True),
58 'network': Parameter(str, "Subnet address", nullok = True),
59 'broadcast': Parameter(str, "Network broadcast address", nullok = True),
60 'netmask': Parameter(str, "Subnet mask", nullok = True),
61 'dns1': Parameter(str, "IP address of primary DNS server", nullok = True),
62 'dns2': Parameter(str, "IP address of secondary DNS server", nullok = True),
63 'bwlimit': Parameter(int, "Bandwidth limit", min = 0, nullok = True),
64 'hostname': Parameter(str, "(Optional) Hostname", nullok = True),
65 'node_id': Parameter(int, "Node associated with this interface"),
66 'is_primary': Parameter(bool, "Is the primary interface for this node"),
69 def validate_method(self, method):
70 network_methods = [row['method'] for row in NetworkMethods(self.api)]
71 if method not in network_methods:
72 raise PLCInvalidArgument, "Invalid addressing method %s"%method
75 def validate_type(self, type):
76 network_types = [row['type'] for row in NetworkTypes(self.api)]
77 if type not in network_types:
78 raise PLCInvalidArgument, "Invalid address type %s"%type
81 def validate_ip(self, ip):
82 if ip and not valid_ip(ip):
83 raise PLCInvalidArgument, "Invalid IP address %s"%ip
86 def validate_mac(self, mac):
91 bytes = mac.split(":")
94 for i, byte in enumerate(bytes):
96 if byte < 0 or byte > 255:
98 bytes[i] = "%02x" % byte
101 raise PLCInvalidArgument, "Invalid MAC address %s"%mac
105 validate_gateway = validate_ip
106 validate_network = validate_ip
107 validate_broadcast = validate_ip
108 validate_netmask = validate_ip
109 validate_dns1 = validate_ip
110 validate_dns2 = validate_ip
112 def validate_hostname(self, hostname):
117 if not PLC.Nodes.valid_hostname(hostname):
118 raise PLCInvalidArgument, "Invalid hostname %s"%hostname
122 def validate_node_id(self, node_id):
123 nodes = PLC.Nodes.Nodes(self.api, [node_id])
125 raise PLCInvalidArgument, "No such node %d"%node_id
129 def validate_is_primary(self, is_primary):
131 Set this interface to be the primary one.
135 nodes = PLC.Nodes.Nodes(self.api, [self['node_id']])
137 raise PLCInvalidArgument, "No such node %d"%node_id
140 if node['nodenetwork_ids']:
141 conflicts = NodeNetworks(self.api, node['nodenetwork_ids'])
142 for nodenetwork in conflicts:
143 if ('nodenetwork_id' not in self or \
144 self['nodenetwork_id'] != nodenetwork['nodenetwork_id']) and \
145 nodenetwork['is_primary']:
146 raise PLCInvalidArgument, "Can only set one primary interface per node"
152 Flush changes back to the database.
158 assert 'method' in self
159 method = self['method']
161 if method == "proxy" or method == "tap":
162 if 'mac' in self and self['mac']:
163 raise PLCInvalidArgument, "For %s method, mac should not be specified" % method
164 if 'ip' not in self or not self['ip']:
165 raise PLCInvalidArgument, "For %s method, ip is required" % method
166 if method == "tap" and ('gateway' not in self or not self['gateway']):
167 raise PLCInvalidArgument, "For tap method, gateway is required and should be " \
168 "the IP address of the node that proxies for this address"
169 # Should check that the proxy address is reachable, but
170 # there's no way to tell if the only primary interface is
173 elif method == "static":
174 for key in ['ip', 'gateway', 'network', 'broadcast', 'netmask', 'dns1']:
175 if key not in self or not self[key]:
176 raise PLCInvalidArgument, "For static method, %s is required" % key
177 globals()[key] = self[key]
178 if not in_same_network(ip, network, netmask):
179 raise PLCInvalidArgument, "IP address %s is inconsistent with network %s/%s" % \
180 (ip, network, netmask)
181 if not in_same_network(broadcast, network, netmask):
182 raise PLCInvalidArgument, "Broadcast address %s is inconsistent with network %s/%s" % \
183 (broadcast, network, netmask)
184 if not in_same_network(ip, gateway, netmask):
185 raise PLCInvalidArgument, "Gateway %s is not reachable from %s/%s" % \
186 (gateway, ip, netmask)
188 elif method == "ipmi":
189 if 'ip' not in self or not self['ip']:
190 raise PLCInvalidArgument, "For ipmi method, ip is required"
192 class NodeNetworks(Table):
194 Representation of row(s) from the nodenetworks table in the
198 def __init__(self, api, nodenetwork_filter = None, columns = None):
199 Table.__init__(self, api, NodeNetwork, columns)
201 sql = "SELECT %s FROM nodenetworks WHERE True" % \
202 ", ".join(self.columns)
204 if nodenetwork_filter is not None:
205 if isinstance(nodenetwork_filter, (list, tuple, set)):
206 nodenetwork_filter = Filter(NodeNetwork.fields, {'nodenetwork_id': nodenetwork_filter})
207 elif isinstance(nodenetwork_filter, dict):
208 nodenetwork_filter = Filter(NodeNetwork.fields, nodenetwork_filter)
209 sql += " AND (%s)" % nodenetwork_filter.sql(api)