2 # Functions for interacting with the interfaces table in the database
4 # Mark Huang <mlhuang@cs.princeton.edu>
5 # Copyright (C) 2006 The Trustees of Princeton University
11 from types import StringTypes
15 from PLC.Faults import *
16 from PLC.Parameter import Parameter
17 from PLC.Filter import Filter
18 from PLC.Debug import profile
19 from PLC.Table import Row, Table
20 from PLC.NetworkTypes import NetworkType, NetworkTypes
21 from PLC.NetworkMethods import NetworkMethod, NetworkMethods
26 ip = socket.inet_ntoa(socket.inet_aton(ip))
31 def in_same_network(address1, address2, netmask):
33 Returns True if two IPv4 addresses are in the same network. Faults
34 if an address is invalid.
37 address1 = struct.unpack('>L', socket.inet_aton(address1))[0]
38 address2 = struct.unpack('>L', socket.inet_aton(address2))[0]
39 netmask = struct.unpack('>L', socket.inet_aton(netmask))[0]
41 return (address1 & netmask) == (address2 & netmask)
45 Representation of a row in the interfaces table. To use, optionally
46 instantiate with a dict of values. Update as you would a
47 dict. Commit to the database with sync().
50 table_name = 'interfaces'
51 primary_key = 'interface_id'
52 join_tables = ['interface_tag']
54 'interface_id': Parameter(int, "Node interface identifier"),
55 'method': Parameter(str, "Addressing method (e.g., 'static' or 'dhcp')"),
56 'type': Parameter(str, "Address type (e.g., 'ipv4')"),
57 'ip': Parameter(str, "IP address", nullok = True),
58 'mac': Parameter(str, "MAC address", nullok = True),
59 'gateway': Parameter(str, "IP address of primary gateway", nullok = True),
60 'network': Parameter(str, "Subnet address", nullok = True),
61 'broadcast': Parameter(str, "Network broadcast address", nullok = True),
62 'netmask': Parameter(str, "Subnet mask", nullok = True),
63 'dns1': Parameter(str, "IP address of primary DNS server", nullok = True),
64 'dns2': Parameter(str, "IP address of secondary DNS server", nullok = True),
65 'bwlimit': Parameter(int, "Bandwidth limit", min = 0, nullok = True),
66 'hostname': Parameter(str, "(Optional) Hostname", nullok = True),
67 'node_id': Parameter(int, "Node associated with this interface"),
68 'is_primary': Parameter(bool, "Is the primary interface for this node"),
69 'interface_tag_ids' : Parameter([int], "List of interface settings"),
72 view_tags_name = "view_interface_tags"
75 def validate_method(self, method):
76 network_methods = [row['method'] for row in NetworkMethods(self.api)]
77 if method not in network_methods:
78 raise PLCInvalidArgument, "Invalid addressing method %s"%method
81 def validate_type(self, type):
82 network_types = [row['type'] for row in NetworkTypes(self.api)]
83 if type not in network_types:
84 raise PLCInvalidArgument, "Invalid address type %s"%type
87 def validate_ip(self, ip):
88 if ip and not valid_ip(ip):
89 raise PLCInvalidArgument, "Invalid IP address %s"%ip
92 def validate_mac(self, mac):
97 bytes = mac.split(":")
100 for i, byte in enumerate(bytes):
102 if byte < 0 or byte > 255:
104 bytes[i] = "%02x" % byte
105 mac = ":".join(bytes)
107 raise PLCInvalidArgument, "Invalid MAC address %s"%mac
111 validate_gateway = validate_ip
112 validate_network = validate_ip
113 validate_broadcast = validate_ip
114 validate_netmask = validate_ip
115 validate_dns1 = validate_ip
116 validate_dns2 = validate_ip
118 def validate_bwlimit(self, bwlimit):
123 raise PLCInvalidArgument, 'Minimum bw is 500 kbs'
127 def validate_hostname(self, hostname):
132 if not PLC.Nodes.valid_hostname(hostname):
133 raise PLCInvalidArgument, "Invalid hostname %s"%hostname
137 def validate_node_id(self, node_id):
138 nodes = PLC.Nodes.Nodes(self.api, [node_id])
140 raise PLCInvalidArgument, "No such node %d"%node_id
144 def validate_is_primary(self, is_primary):
146 Set this interface to be the primary one.
150 nodes = PLC.Nodes.Nodes(self.api, [self['node_id']])
152 raise PLCInvalidArgument, "No such node %d"%node_id
155 if node['interface_ids']:
156 conflicts = Interfaces(self.api, node['interface_ids'])
157 for interface in conflicts:
158 if ('interface_id' not in self or \
159 self['interface_id'] != interface['interface_id']) and \
160 interface['is_primary']:
161 raise PLCInvalidArgument, "Can only set one primary interface per node"
167 Flush changes back to the database.
173 assert 'method' in self
174 method = self['method']
176 if method == "proxy" or method == "tap":
177 if 'mac' in self and self['mac']:
178 raise PLCInvalidArgument, "For %s method, mac should not be specified" % method
179 if 'ip' not in self or not self['ip']:
180 raise PLCInvalidArgument, "For %s method, ip is required" % method
181 if method == "tap" and ('gateway' not in self or not self['gateway']):
182 raise PLCInvalidArgument, "For tap method, gateway is required and should be " \
183 "the IP address of the node that proxies for this address"
184 # Should check that the proxy address is reachable, but
185 # there's no way to tell if the only primary interface is
188 elif method == "static":
189 if 'is_primary' in self and self['is_primary'] is True:
190 for key in ['gateway', 'dns1']:
191 if key not in self or not self[key]:
192 raise PLCInvalidArgument, "For static method primary network, %s is required" % key
193 globals()[key] = self[key]
194 for key in ['ip', 'network', 'broadcast', 'netmask']:
195 if key not in self or not self[key]:
196 raise PLCInvalidArgument, "For static method, %s is required" % key
197 globals()[key] = self[key]
198 if not in_same_network(ip, network, netmask):
199 raise PLCInvalidArgument, "IP address %s is inconsistent with network %s/%s" % \
200 (ip, network, netmask)
201 if not in_same_network(broadcast, network, netmask):
202 raise PLCInvalidArgument, "Broadcast address %s is inconsistent with network %s/%s" % \
203 (broadcast, network, netmask)
204 if 'gateway' in globals() and not in_same_network(ip, gateway, netmask):
205 raise PLCInvalidArgument, "Gateway %s is not reachable from %s/%s" % \
206 (gateway, ip, netmask)
208 elif method == "ipmi":
209 if 'ip' not in self or not self['ip']:
210 raise PLCInvalidArgument, "For ipmi method, ip is required"
212 class Interfaces(Table):
214 Representation of row(s) from the interfaces table in the
218 def __init__(self, api, interface_filter = None, columns = None):
219 Table.__init__(self, api, Interface, columns)
221 # the view that we're selecting upon: start with view_nodes
222 view = "view_interfaces"
223 # as many left joins as requested tags
224 for tagname in self.tag_columns:
225 view= "%s left join %s using (%s)"%(view,Interface.tagvalue_view_name(tagname),
226 Interface.primary_key)
228 sql = "SELECT %s FROM %s WHERE True" % \
229 (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
231 if interface_filter is not None:
232 if isinstance(interface_filter, (list, tuple, set)):
233 # Separate the list into integers and strings
234 ints = filter(lambda x: isinstance(x, (int, long)), interface_filter)
235 strs = filter(lambda x: isinstance(x, StringTypes), interface_filter)
236 interface_filter = Filter(Interface.fields, {'interface_id': ints, 'ip': strs})
237 sql += " AND (%s) %s" % interface_filter.sql(api, "OR")
238 elif isinstance(interface_filter, dict):
239 allowed_fields=dict(Interface.fields.items()+Interface.tags.items())
240 interface_filter = Filter(allowed_fields, interface_filter)
241 sql += " AND (%s) %s" % interface_filter.sql(api)
242 elif isinstance(interface_filter, int):
243 interface_filter = Filter(Interface.fields, {'interface_id': [interface_filter]})
244 sql += " AND (%s) %s" % interface_filter.sql(api)
245 elif isinstance (interface_filter, StringTypes):
246 interface_filter = Filter(Interface.fields, {'ip':[interface_filter]})
247 sql += " AND (%s) %s" % interface_filter.sql(api, "AND")
249 raise PLCInvalidArgument, "Wrong interface filter %r"%interface_filter