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
8 from types import StringTypes
12 from PLC.Faults import *
13 from PLC.Parameter import Parameter
14 from PLC.Filter import Filter
15 from PLC.Debug import profile
16 from PLC.Table import Row, Table
17 from PLC.NetworkTypes import NetworkType, NetworkTypes
18 from PLC.NetworkMethods import NetworkMethod, NetworkMethods
21 # a class for validating IP address strings and applying netmasks
23 def __init__(self, addrStr, type=None, parts=None):
30 raise ValueError, "Unable to determine type of address: " + str(addrStr)
41 # e.g. 2001:db8:85a3:0:0:8a2e:370:7334
45 self.fieldMask = 0xFFFF
47 raise ValueError, "Unknown type of address: " + str(type)
50 parts = addrStr.split(self.delim)
52 # deal with ipv6 groups of zeros notation
53 # :: represents a group of 0:0:0... fields
54 if ('' in parts) and (self.type=="ipv6"):
56 parts = [elem for i, elem in enumerate(parts) if i == 0 or elem!="" or parts[i-1] != elem]
57 for i,part in enumerate(parts):
59 for j in range(0, self.count-len(parts)+1):
60 expanded_parts.append("0")
62 expanded_parts.append(part)
63 parts = expanded_parts
65 parts = [int(x, self.base) for x in parts]
68 raise ValueError, "Must supply either addrStr or parts to SimpleAddress"
70 if len(parts)!=self.count:
71 raise ValueError, "Wrong number of fields in address: " + str(addrStr)
77 textParts = ["%x"%x for x in self.parts]
78 else: # self.base == 10
79 textParts = [str(x) for x in self.parts]
80 return self.delim.join(textParts)
82 def compute_network(self, netmask):
83 if (type(netmask)==str) or (type(netmask)==unicode):
84 netmask = SimpleAddress(netmask)
86 if (self.type != netmask.type):
87 raise ValueError, "Cannot apply " + netmask.type + " netmask to " + self.type + " ip address"
90 for i in range(0, self.count):
91 result.append(self.parts[i] & netmask.parts[i])
93 return SimpleAddress(addrStr=None, type=self.type, parts = result)
95 def compute_broadcast(self, netmask):
96 if (type(netmask)==str) or (type(netmask)==unicode):
97 netmask = SimpleAddress(netmask)
99 if (self.type != netmask.type):
100 raise ValueError, "Cannot apply " + netmask.type + " netmask to " + self.type + " ip address"
103 for i in range(0, self.count):
104 result.append(self.parts[i] | (~netmask.parts[i] & self.fieldMask))
106 return SimpleAddress(addrStr=None, type=self.type, parts = result)
109 def __init__(self, subnetStr):
111 (addrStr, netBitCountStr) = subnetStr.split("/")
112 self.addr = SimpleAddress(addrStr)
113 self.netBitCount = int(netBitCountStr)
115 self.addr = SimpleAddress(subnetStr)
116 self.netBitCount = None
119 if self.netBitCount is not None:
120 return self.addr.as_str() + "/" + str(self.netBitCount)
122 return self.addr.as_str()
124 class IpAddress(Row):
126 Representation of a row in the ip_addresses table. To use, optionally
127 instantiate with a dict of values. Update as you would a
128 dict. Commit to the database with sync().
131 table_name = 'ip_addresses'
132 primary_key = 'ip_address_id'
135 'ip_address_id': Parameter(int, "IP Address identifier"),
136 'interface_id': Parameter(int, "Interface associated with this address"),
137 'type': Parameter(str, "Address type (e.g., 'ipv4')"),
138 'ip_addr': Parameter(str, "IP Address", nullok = False),
139 'netmask': Parameter(str, "Subnet mask", nullok = False),
140 'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
145 def validate_ip_addr(self, ip):
146 # SimpleAddress with throw exceptions if the ip
147 SimpleAddress(ip, self["type"])
150 validate_netmask = validate_ip_addr
152 def validate_interface_id(self, interface_id):
153 interfaces = PLC.Interfaces.Interfaces(self.api, [interface_id])
155 raise PLCInvalidArgument, "No such interface %d"%interface_id
161 Flush changes back to the database.
167 validate_last_updated = Row.validate_timestamp
169 def update_timestamp(self, col_name, commit = True):
171 Update col_name field with current time
174 assert 'ip_address_id' in self
175 assert self.table_name
177 self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \
178 " where ip_address_id = %d" % (self['ip_address_id']) )
181 def update_last_updated(self, commit = True):
182 self.update_timestamp('last_updated', commit)
184 def delete(self,commit=True):
187 def get_network(self):
188 return SimpleAddress(self["ip_addr"], self["type"]).compute_network(self["netmask"]).as_str()
190 def get_broadcast(self):
191 return SimpleAddress(self["ip_addr"], self["type"]).compute_broadcast(self["netmask"]).as_str()
193 class IpAddresses(Table):
195 Representation of row(s) from the ip_addresses table in the
199 def __init__(self, api, ip_address_filter = None, columns = None):
200 Table.__init__(self, api, IpAddress, columns)
202 # the view that we're selecting upon: start with view_ip_addresses
203 view = "view_ip_addresses"
204 # as many left joins as requested tags
205 for tagname in self.tag_columns:
206 view= "%s left join %s using (%s)"%(view,IpAddress.tagvalue_view_name(tagname),
207 IpAddress.primary_key)
209 sql = "SELECT %s FROM %s WHERE True" % \
210 (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
212 if ip_address_filter is not None:
213 if isinstance(ip_address_filter, (list, tuple, set)):
214 # Separate the list into integers and strings
215 ints = filter(lambda x: isinstance(x, (int, long)), ip_address_filter)
216 strs = filter(lambda x: isinstance(x, StringTypes), ip_address_filter)
217 ip_address_filter = Filter(IpAddress.fields, {'ip_address_id': ints, 'ip_addr': strs})
218 sql += " AND (%s) %s" % ip_address_filter.sql(api, "OR")
219 elif isinstance(ip_address_filter, dict):
220 allowed_fields=dict(IpAddress.fields.items()+IpAddress.tags.items())
221 ip_address_filter = Filter(allowed_fields, ip_address_filter)
222 sql += " AND (%s) %s" % ip_address_filter.sql(api)
223 elif isinstance(ip_address_filter, int):
224 ip_address_filter = Filter(IpAddress.fields, {'ip_address_id': [ip_address_filter]})
225 sql += " AND (%s) %s" % ip_address_filter.sql(api)
226 elif isinstance (ip_address_filter, StringTypes):
227 ip_address_filter = Filter(IpAddresses.fields, {'ip':[ip_address_filter]})
228 sql += " AND (%s) %s" % ip_address_filter.sql(api, "AND")
230 raise PLCInvalidArgument, "Wrong ip_address filter %r"%ip_address_filter