# # Functions for interacting with the interfaces table in the database # # Mark Huang # Copyright (C) 2006 The Trustees of Princeton University # from types import StringTypes import socket import struct from PLC.Faults import * from PLC.Parameter import Parameter from PLC.Filter import Filter from PLC.Debug import profile from PLC.Table import Row, Table from PLC.NetworkTypes import NetworkType, NetworkTypes from PLC.NetworkMethods import NetworkMethod, NetworkMethods import PLC.Nodes def valid_ip(ip): try: ip = socket.inet_ntoa(socket.inet_aton(ip)) return True except socket.error: return False def in_same_network(address1, address2, netmask): """ Returns True if two IPv4 addresses are in the same network. Faults if an address is invalid. """ address1 = struct.unpack('>L', socket.inet_aton(address1))[0] address2 = struct.unpack('>L', socket.inet_aton(address2))[0] netmask = struct.unpack('>L', socket.inet_aton(netmask))[0] return (address1 & netmask) == (address2 & netmask) class Interface(Row): """ Representation of a row in the interfaces table. To use, optionally instantiate with a dict of values. Update as you would a dict. Commit to the database with sync(). """ table_name = 'interfaces' primary_key = 'interface_id' join_tables = ['interface_tag', 'ip_addresses', 'routes'] fields = { 'interface_id': Parameter(int, "Node interface identifier"), 'method': Parameter(str, "Addressing method (e.g., 'static' or 'dhcp')"), 'mac': Parameter(str, "MAC address", nullok = True), 'bwlimit': Parameter(int, "Bandwidth limit", min = 0, nullok = True), 'hostname': Parameter(str, "(Optional) Hostname", nullok = True), 'node_id': Parameter(int, "Node associated with this interface"), 'is_primary': Parameter(bool, "Is the primary interface for this node"), 'if_name': Parameter(str, "Interface name", nullok = True), 'interface_tag_ids' : Parameter([int], "List of interface settings"), 'ip_address_ids': Parameter([int], "List of addresses"), 'last_updated': Parameter(int, "Date and time when node entry was created", ro = True), } view_tags_name = "view_interface_tags" tags = {} def validate_method(self, method): network_methods = [row['method'] for row in NetworkMethods(self.api)] if method not in network_methods: raise PLCInvalidArgument, "Invalid addressing method %s"%method return method def validate_mac(self, mac): if not mac: return mac try: bytes = mac.split(":") if len(bytes) < 6: raise Exception for i, byte in enumerate(bytes): byte = int(byte, 16) if byte < 0 or byte > 255: raise Exception bytes[i] = "%02x" % byte mac = ":".join(bytes) except: raise PLCInvalidArgument, "Invalid MAC address %s"%mac return mac def validate_bwlimit(self, bwlimit): if not bwlimit: return bwlimit if bwlimit < 1: raise PLCInvalidArgument, 'Minimum bw is 1 Mbps' if bwlimit >= 1000000: raise PLCInvalidArgument, 'Maximum bw must be less than 1000000' return bwlimit def validate_hostname(self, hostname): # Optional if not hostname: return hostname if not PLC.Nodes.valid_hostname(hostname): raise PLCInvalidArgument, "Invalid hostname %s"%hostname return hostname def validate_node_id(self, node_id): nodes = PLC.Nodes.Nodes(self.api, [node_id]) if not nodes: raise PLCInvalidArgument, "No such node %d"%node_id return node_id def validate_is_primary(self, is_primary): """ Set this interface to be the primary one. """ if is_primary: nodes = PLC.Nodes.Nodes(self.api, [self['node_id']]) if not nodes: raise PLCInvalidArgument, "No such node %d"%node_id node = nodes[0] if node['interface_ids']: conflicts = Interfaces(self.api, node['interface_ids']) for interface in conflicts: if ('interface_id' not in self or \ self['interface_id'] != interface['interface_id']) and \ interface['is_primary']: raise PLCInvalidArgument, "Can only set one primary interface per node" return is_primary def validate(self): """ Flush changes back to the database. """ # Basic validation Row.validate(self) assert 'method' in self method = self['method'] validate_last_updated = Row.validate_timestamp def update_timestamp(self, col_name, commit = True): """ Update col_name field with current time """ assert 'interface_id' in self assert self.table_name self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \ " where interface_id = %d" % (self['interface_id']) ) self.sync(commit) def update_last_updated(self, commit = True): self.update_timestamp('last_updated', commit) def delete(self,commit=True): ### need to cleanup ilinks self.api.db.do("DELETE FROM ilink WHERE src_interface_id=%d OR dst_interface_id=%d" % \ (self['interface_id'],self['interface_id'])) Row.delete(self) class Interfaces(Table): """ Representation of row(s) from the interfaces table in the database. """ def __init__(self, api, interface_filter = None, columns = None): Table.__init__(self, api, Interface, columns) # the view that we're selecting upon: start with view_nodes view = "view_interfaces" # as many left joins as requested tags for tagname in self.tag_columns: view= "%s left join %s using (%s)"%(view,Interface.tagvalue_view_name(tagname), Interface.primary_key) sql = "SELECT %s FROM %s WHERE True" % \ (", ".join(self.columns.keys()+self.tag_columns.keys()),view) if interface_filter is not None: # TODO: Deleted the ability here to filter by ipaddress; Need to make # sure that wasn't used anywhere. if isinstance(interface_filter, (list, tuple, set)): # Separate the list into integers and strings ints = filter(lambda x: isinstance(x, (int, long)), interface_filter) interface_filter = Filter(Interface.fields, {'interface_id': ints}) sql += " AND (%s) %s" % interface_filter.sql(api, "OR") elif isinstance(interface_filter, dict): allowed_fields=dict(Interface.fields.items()+Interface.tags.items()) interface_filter = Filter(allowed_fields, interface_filter) sql += " AND (%s) %s" % interface_filter.sql(api) elif isinstance(interface_filter, int): interface_filter = Filter(Interface.fields, {'interface_id': [interface_filter]}) sql += " AND (%s) %s" % interface_filter.sql(api) else: raise PLCInvalidArgument, "Wrong interface filter %r"%interface_filter self.selectall(sql)