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
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)
44 Representation of a row in the interfaces 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 = 'interfaces'
50 primary_key = 'interface_id'
51 join_tables = ['interface_tag']
53 'interface_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 'interface_tag_ids' : Parameter([int], "List of interface settings"),
71 view_tags_name = "view_interface_tags"
74 def validate_method(self, method):
75 network_methods = [row['method'] for row in NetworkMethods(self.api)]
76 if method not in network_methods:
77 raise PLCInvalidArgument, "Invalid addressing method %s"%method
80 def validate_type(self, type):
81 network_types = [row['type'] for row in NetworkTypes(self.api)]
82 if type not in network_types:
83 raise PLCInvalidArgument, "Invalid address type %s"%type
86 def validate_ip(self, ip):
87 if ip and not valid_ip(ip):
88 raise PLCInvalidArgument, "Invalid IP address %s"%ip
91 def validate_mac(self, mac):
96 bytes = mac.split(":")
99 for i, byte in enumerate(bytes):
101 if byte < 0 or byte > 255:
103 bytes[i] = "%02x" % byte
104 mac = ":".join(bytes)
106 raise PLCInvalidArgument, "Invalid MAC address %s"%mac
110 validate_gateway = validate_ip
111 validate_network = validate_ip
112 validate_broadcast = validate_ip
113 validate_netmask = validate_ip
114 validate_dns1 = validate_ip
115 validate_dns2 = validate_ip
117 def validate_bwlimit(self, bwlimit):
122 raise PLCInvalidArgument, 'Minimum bw is 500 kbs'
126 def validate_hostname(self, hostname):
131 if not PLC.Nodes.valid_hostname(hostname):
132 raise PLCInvalidArgument, "Invalid hostname %s"%hostname
136 def validate_node_id(self, node_id):
137 nodes = PLC.Nodes.Nodes(self.api, [node_id])
139 raise PLCInvalidArgument, "No such node %d"%node_id
143 def validate_is_primary(self, is_primary):
145 Set this interface to be the primary one.
149 nodes = PLC.Nodes.Nodes(self.api, [self['node_id']])
151 raise PLCInvalidArgument, "No such node %d"%node_id
154 if node['interface_ids']:
155 conflicts = Interfaces(self.api, node['interface_ids'])
156 for interface in conflicts:
157 if ('interface_id' not in self or \
158 self['interface_id'] != interface['interface_id']) and \
159 interface['is_primary']:
160 raise PLCInvalidArgument, "Can only set one primary interface per node"
166 Flush changes back to the database.
172 assert 'method' in self
173 method = self['method']
175 if method == "proxy" or method == "tap":
176 if 'mac' in self and self['mac']:
177 raise PLCInvalidArgument, "For %s method, mac should not be specified" % method
178 if 'ip' not in self or not self['ip']:
179 raise PLCInvalidArgument, "For %s method, ip is required" % method
180 if method == "tap" and ('gateway' not in self or not self['gateway']):
181 raise PLCInvalidArgument, "For tap method, gateway is required and should be " \
182 "the IP address of the node that proxies for this address"
183 # Should check that the proxy address is reachable, but
184 # there's no way to tell if the only primary interface is
187 elif method == "static":
188 if 'is_primary' in self and self['is_primary'] is True:
189 for key in ['gateway', 'dns1']:
190 if key not in self or not self[key]:
191 raise PLCInvalidArgument, "For static method primary network, %s is required" % key
192 globals()[key] = self[key]
193 for key in ['ip', 'network', 'broadcast', 'netmask']:
194 if key not in self or not self[key]:
195 raise PLCInvalidArgument, "For static method, %s is required" % key
196 globals()[key] = self[key]
197 if not in_same_network(ip, network, netmask):
198 raise PLCInvalidArgument, "IP address %s is inconsistent with network %s/%s" % \
199 (ip, network, netmask)
200 if not in_same_network(broadcast, network, netmask):
201 raise PLCInvalidArgument, "Broadcast address %s is inconsistent with network %s/%s" % \
202 (broadcast, network, netmask)
203 if 'gateway' in globals() and not in_same_network(ip, gateway, netmask):
204 raise PLCInvalidArgument, "Gateway %s is not reachable from %s/%s" % \
205 (gateway, ip, netmask)
207 elif method == "ipmi":
208 if 'ip' not in self or not self['ip']:
209 raise PLCInvalidArgument, "For ipmi method, ip is required"
211 class Interfaces(Table):
213 Representation of row(s) from the interfaces table in the
217 def __init__(self, api, interface_filter = None, columns = None):
218 Table.__init__(self, api, Interface, columns)
220 # the view that we're selecting upon: start with view_nodes
221 view = "view_interfaces"
222 # as many left joins as requested tags
223 for tagname in self.tag_columns:
224 view= "%s left join %s using (%s)"%(view,Interface.tagvalue_view_name(tagname),
225 Interface.primary_key)
227 sql = "SELECT %s FROM %s WHERE True" % \
228 (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
230 if interface_filter is not None:
231 if isinstance(interface_filter, (list, tuple, set)):
232 # Separate the list into integers and strings
233 ints = filter(lambda x: isinstance(x, (int, long)), interface_filter)
234 strs = filter(lambda x: isinstance(x, StringTypes), interface_filter)
235 interface_filter = Filter(Interface.fields, {'interface_id': ints, 'ip': strs})
236 sql += " AND (%s) %s" % interface_filter.sql(api, "OR")
237 elif isinstance(interface_filter, dict):
238 allowed_fields=dict(Interface.fields.items()+Interface.tags.items())
239 interface_filter = Filter(allowed_fields, interface_filter)
240 sql += " AND (%s) %s" % interface_filter.sql(api)
241 elif isinstance(interface_filter, int):
242 interface_filter = Filter(Interface.fields, {'interface_id': [interface_filter]})
243 sql += " AND (%s) %s" % interface_filter.sql(api)
244 elif isinstance (interface_filter, StringTypes):
245 interface_filter = Filter(Interface.fields, {'ip':[interface_filter]})
246 sql += " AND (%s) %s" % interface_filter.sql(api, "AND")
248 raise PLCInvalidArgument, "Wrong interface filter %r"%interface_filter