====
[plcapi.git] / PLC / Interfaces.py
index 46b6f40..0e6c728 100644 (file)
@@ -4,9 +4,6 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id$
-# $URL$
-#
 
 from types import StringTypes
 import socket
 
 from types import StringTypes
 import socket
@@ -21,25 +18,49 @@ from PLC.NetworkTypes import NetworkType, NetworkTypes
 from PLC.NetworkMethods import NetworkMethod, NetworkMethods
 import PLC.Nodes
 
 from PLC.NetworkMethods import NetworkMethod, NetworkMethods
 import PLC.Nodes
 
-def valid_ip(ip):
+def valid_ipv4(ip):
     try:
         ip = socket.inet_ntoa(socket.inet_aton(ip))
         return True
     except socket.error:
         return False
 
     try:
         ip = socket.inet_ntoa(socket.inet_aton(ip))
         return True
     except socket.error:
         return False
 
-def in_same_network(address1, address2, netmask):
+def valid_ipv6(ip):
+    try:
+        ip = socket.inet_ntop(socket.AF_INET6, socket.inet_pton(socket.AF_INET6, ip))
+        return True
+    except socket.error:
+        return False   
+
+def valid_ip(ip):
+    return valid_ipv4(ip) or valid_ipv6(ip)
+
+def in_same_network_ipv4(address1, address2, netmask):
     """
     Returns True if two IPv4 addresses are in the same network. Faults
     if an address is invalid.
     """
     """
     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)
 
     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)
 
+def in_same_network_ipv6(address1, address2, netmask):
+    """
+    Returns True if two IPv6 addresses are in the same network. Faults
+    if an address is invalid.
+    """
+    address1 = struct.unpack('>2Q', socket.inet_pton(socket.AF_INET6, address1))[0]
+    address2 = struct.unpack('>2Q', socket.inet_pton(socket.AF_INET6, address2))[0]
+    netmask = struct.unpack('>2Q', socket.inet_pton(socket.AF_INET6, netmask))[0]
+
+    return (address1 & netmask) == (address2 & netmask)
+
+def in_same_network(address1, address2, netmask):
+    return in_same_network_ipv4(address1, address2, netmask) or \
+           in_same_network_ipv6(address1, address2, netmask) 
+
 class Interface(Row):
     """
     Representation of a row in the interfaces table. To use, optionally
 class Interface(Row):
     """
     Representation of a row in the interfaces table. To use, optionally
@@ -67,6 +88,7 @@ class Interface(Row):
         'node_id': Parameter(int, "Node associated with this interface"),
         'is_primary': Parameter(bool, "Is the primary interface for this node"),
         'interface_tag_ids' : Parameter([int], "List of interface settings"),
         'node_id': Parameter(int, "Node associated with this interface"),
         'is_primary': Parameter(bool, "Is the primary interface for this node"),
         'interface_tag_ids' : Parameter([int], "List of interface settings"),
+        'last_updated': Parameter(int, "Date and time when node entry was created", ro = True),
         }
 
     view_tags_name = "view_interface_tags"
         }
 
     view_tags_name = "view_interface_tags"
@@ -186,29 +208,59 @@ class Interface(Row):
             # DHCP!
 
         elif method == "static":
             # DHCP!
 
         elif method == "static":
-            if 'is_primary' in self and self['is_primary'] is True:
+            if self['type'] == 'ipv4':
                 for key in ['gateway', 'dns1']:
                     if key not in self or not self[key]:
                 for key in ['gateway', 'dns1']:
                     if key not in self or not self[key]:
-                        raise PLCInvalidArgument, "For static method primary network, %s is required" % key
+                        if 'is_primary' in self and self['is_primary'] is True:
+                            raise PLCInvalidArgument, "For static method primary network, %s is required" % key
+                    else:
+                        globals()[key] = self[key]
+                for key in ['ip', 'network', 'broadcast', 'netmask']:
+                    if key not in self or not self[key]:
+                        raise PLCInvalidArgument, "For static method, %s is required" % key
+                    globals()[key] = self[key]
+                if not in_same_network(ip, network, netmask):
+                    raise PLCInvalidArgument, "IP address %s is inconsistent with network %s/%s" % \
+                          (ip, network, netmask)
+                if not in_same_network(broadcast, network, netmask):
+                    raise PLCInvalidArgument, "Broadcast address %s is inconsistent with network %s/%s" % \
+                          (broadcast, network, netmask)
+                if 'gateway' in globals() and not in_same_network(ip, gateway, netmask):
+                    raise PLCInvalidArgument, "Gateway %s is not reachable from %s/%s" % \
+                          (gateway, ip, netmask)
+            elif self['type'] == 'ipv6':
+                for key in ['ip', 'gateway']:
+                    if key not in self or not self[key]:
+                        raise PLCInvalidArgument, "For static ipv6 method, %s is required" % key
                     globals()[key] = self[key]
                     globals()[key] = self[key]
-            for key in ['ip', 'network', 'broadcast', 'netmask']:
-                if key not in self or not self[key]:
-                    raise PLCInvalidArgument, "For static method, %s is required" % key
-                globals()[key] = self[key]
-            if not in_same_network(ip, network, netmask):
-                raise PLCInvalidArgument, "IP address %s is inconsistent with network %s/%s" % \
-                      (ip, network, netmask)
-            if not in_same_network(broadcast, network, netmask):
-                raise PLCInvalidArgument, "Broadcast address %s is inconsistent with network %s/%s" % \
-                      (broadcast, network, netmask)
-            if 'gateway' in globals() and not in_same_network(ip, gateway, netmask):
-                raise PLCInvalidArgument, "Gateway %s is not reachable from %s/%s" % \
-                      (gateway, ip, netmask)
-
         elif method == "ipmi":
             if 'ip' not in self or not self['ip']:
                 raise PLCInvalidArgument, "For ipmi method, ip is required"
 
         elif method == "ipmi":
             if 'ip' not in self or not self['ip']:
                 raise PLCInvalidArgument, "For ipmi method, ip is required"
 
+    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
 class Interfaces(Table):
     """
     Representation of row(s) from the interfaces table in the