add interface to pcus
authorMark Huang <mlhuang@cs.princeton.edu>
Wed, 11 Oct 2006 19:54:53 +0000 (19:54 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Wed, 11 Oct 2006 19:54:53 +0000 (19:54 +0000)
PLC/Methods/AddPCU.py [new file with mode: 0644]
PLC/Methods/DeletePCU.py [new file with mode: 0644]
PLC/Methods/GetPCUs.py [new file with mode: 0644]
PLC/Methods/UpdatePCU.py [new file with mode: 0644]
PLC/Methods/__init__.py
PLC/PCUs.py

diff --git a/PLC/Methods/AddPCU.py b/PLC/Methods/AddPCU.py
new file mode 100644 (file)
index 0000000..1f66dcf
--- /dev/null
@@ -0,0 +1,54 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.PCUs import PCU, PCUs
+from PLC.Auth import PasswordAuth
+from PLC.Sites import Site, Sites
+
+class AddPCU(Method):
+    """
+    Adds a new power control unit (PCU) to the specified site. Any
+    fields specified in optional_vals are used, otherwise defaults are
+    used.
+
+    PIs and technical contacts may only add PCUs to their own sites.
+
+    Returns the new pcu_id (> 0) if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech']
+
+    can_update = lambda (field, value): field in \
+                 ['hostname', 'ip', 'protocol',
+                  'username', 'password',
+                  'model', 'notes']
+    update_fields = dict(filter(can_update, PCU.fields.items()))
+
+    accepts = [
+        PasswordAuth(),
+        Mixed(Site.fields['site_id'],
+              Site.fields['login_base']),
+        update_fields
+        ]
+
+    returns = Parameter(int, 'New pcu_id (> 0) if successful')
+
+    def call(self, auth, site_id_or_login_base, optional_vals = {}):
+        if filter(lambda field: field not in self.update_fields, optional_vals):
+            raise PLCInvalidArgument, "Invalid field specified"
+
+        # Get associated site details
+        sites = Sites(self.api, [site_id_or_login_base]).values()
+        if not sites:
+            raise PLCInvalidArgument, "No such site"
+        site = sites[0]
+
+        if 'admin' not in self.caller['roles']:
+            if site['site_id'] not in self.caller['site_ids']:
+                raise PLCPermissionDenied, "Not allowed to add a PCU to that site"
+
+        pcu = PCU(self.api, optional_vals)
+        pcu['site_id'] = site['site_id']
+        pcu.sync()
+
+        return pcu['pcu_id']
diff --git a/PLC/Methods/DeletePCU.py b/PLC/Methods/DeletePCU.py
new file mode 100644 (file)
index 0000000..7099b89
--- /dev/null
@@ -0,0 +1,45 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.PCUs import PCU, PCUs
+from PLC.Auth import PasswordAuth
+
+class DeletePCU(Method):
+    """
+    Deletes a PCU.
+
+    Non-admins may only delete PCUs at their sites.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech']
+
+    accepts = [
+        PasswordAuth(),
+        PCU.fields['pcu_id'],
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, pcu_id):
+        # Get associated PCU details
+        pcus = PCUs(self.api, [pcu_id]).values()
+        if not pcus:
+            raise PLCInvalidArgument, "No such PCU"
+        pcu = pcus[0]
+
+        if 'admin' not in self.caller['roles']:
+            if not pcu_ids:
+                ok = False
+                sites = Sites(self.api, self.caller['site_ids']).values()
+                for site in sites:
+                    if pcu['pcu_id'] in site['pcu_ids']:
+                        ok = True
+                        break
+                if not ok:
+                    raise PLCPermissionDenied, "Not allowed to delete that PCU"
+
+        pcu.delete()
+
+        return 1
diff --git a/PLC/Methods/GetPCUs.py b/PLC/Methods/GetPCUs.py
new file mode 100644 (file)
index 0000000..93511b2
--- /dev/null
@@ -0,0 +1,34 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.PCUs import PCU, PCUs
+from PLC.Auth import PasswordAuth
+
+class GetPCUs(Method):
+    """
+    Return an array of structs containing details about PCUs. If
+    pcu_id_list is specified, only the specified PCUs will be queried.
+
+    Admin may query all PCUs. Non-admins may only query the PCUs at
+    their sites.
+    """
+
+    roles = ['admin', 'pi', 'tech']
+
+    accepts = [
+        PasswordAuth(),
+        [PCU.fields['pcu_id']]
+        ]
+
+    returns = [PCU.fields]
+
+    def call(self, auth, pcu_ids = None):
+       # If we are not admin, make sure to only return our own PCUs
+        if 'admin' not in self.caller['roles']:
+            if not pcu_ids:
+                pcu_ids = []
+                sites = Sites(self.api, self.caller['site_ids']).values()
+                for site in sites:
+                    pcu_ids = set(pcu_ids).union(site['pcu_ids'])
+
+        return PCUs(self.api, pcu_ids).values()
diff --git a/PLC/Methods/UpdatePCU.py b/PLC/Methods/UpdatePCU.py
new file mode 100644 (file)
index 0000000..2aa3766
--- /dev/null
@@ -0,0 +1,56 @@
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.PCUs import PCU, PCUs
+from PLC.Auth import PasswordAuth
+
+class UpdatePCU(Method):
+    """
+    Updates the parameters of an existing PCU with the values in
+    pcu_fields.
+
+    Non-admins may only update their own keys.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech']
+
+    can_update = lambda (field, value): field not in \
+                 ['pcu_id', 'site_id']
+    update_fields = dict(filter(can_update, PCU.fields.items()))
+
+    accepts = [
+        PasswordAuth(),
+        PCU.fields['pcu_id'],
+        update_fields
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, pcu_id, pcu_fields):
+       # Make sure only valid fields are specified
+       if filter(lambda field: field not in self.update_fields, pcu_fields):
+            raise PLCInvalidArgument, "Invalid field specified"
+
+        # Get associated PCU details
+        pcus = PCUs(self.api, [pcu_id]).values()
+        if not pcus:
+            raise PLCInvalidArgument, "No such PCU"
+        pcu = pcus[0]
+
+        if 'admin' not in self.caller['roles']:
+            if not pcu_ids:
+                ok = False
+                sites = Sites(self.api, self.caller['site_ids']).values()
+                for site in sites:
+                    if pcu['pcu_id'] in site['pcu_ids']:
+                        ok = True
+                        break
+                if not ok:
+                    raise PLCPermissionDenied, "Not allowed to update that PCU"
+
+        pcu.update(pcu_fields)
+        pcu.sync()
+
+        return 1
index cbf37ea..2aeb73f 100644 (file)
@@ -1 +1 @@
-methods = 'AddAddress AddAddressType AddAddressTypeToAddress AddAttribute AddBootState AddKey AddKeyType AddNetworkMethod AddNetworkType AddNodeGroup AddNodeNetwork AddNode AddNodeToNodeGroup AddPerson AddPersonToSite AddPersonToSlice AddRole AddRoleToPerson AddSite AddSliceAttribute AddSlice AddSliceToNodes AdmAddNodeGroup AdmAddNodeNetwork AdmAddNode AdmAddNodeToNodeGroup AdmAddPerson AdmAddPersonToSite AdmAddSite AdmAuthCheck AdmDeleteNodeGroup AdmDeleteNodeNetwork AdmDeleteNode AdmDeletePerson AdmDeleteSite AdmGetAllNodeNetworkBandwidthLimits AdmGetAllNodeNetworks AdmGetAllRoles AdmGetNodeGroupNodes AdmGetNodeGroups AdmGetNodes AdmGetPersonRoles AdmGetPersonSites AdmGetPersons AdmGetSiteNodes AdmGetSitePersons AdmGetSites AdmGrantRoleToPerson AdmIsPersonInRole AdmRemoveNodeFromNodeGroup AdmRemovePersonFromSite AdmRevokeRoleFromPerson AdmSetPersonEnabled AdmSetPersonPrimarySite AdmUpdateNodeGroup AdmUpdateNodeNetwork AdmUpdateNode AdmUpdatePerson AdmUpdateSite AuthCheck BlacklistKey DeleteAddress DeleteAddressTypeFromAddress DeleteAddressType DeleteAttribute DeleteBootState DeleteKey DeleteKeyType DeleteNetworkMethod DeleteNetworkType DeleteNodeFromNodeGroup DeleteNodeGroup DeleteNodeNetwork DeleteNode DeletePersonFromSite DeletePersonFromSlice DeletePerson DeleteRoleFromPerson DeleteRole DeleteSite DeleteSliceAttribute DeleteSliceFromNodes DeleteSlice GetAddresses GetAddressTypes GetAttributes GetBootStates GetKeys GetKeyTypes GetNetworkMethods GetNetworkTypes GetNodeGroups GetNodeNetworks GetNodes GetPersons GetRoles GetSites GetSliceAttributes GetSlices SetPersonPrimarySite UpdateAddress UpdateAddressType UpdateAttribute UpdateKey UpdateNodeGroup UpdateNodeNetwork UpdateNode UpdatePerson UpdateSite UpdateSliceAttribute UpdateSlice  system.listMethods  system.methodHelp  system.methodSignature  system.multicall'.split()
+methods = 'AddAddress AddAddressType AddAddressTypeToAddress AddAttribute AddBootState AddKey AddKeyType AddNetworkMethod AddNetworkType AddNodeGroup AddNodeNetwork AddNode AddNodeToNodeGroup AddPCU AddPerson AddPersonToSite AddPersonToSlice AddRole AddRoleToPerson AddSite AddSliceAttribute AddSlice AddSliceToNodes AdmAddNodeGroup AdmAddNodeNetwork AdmAddNode AdmAddNodeToNodeGroup AdmAddPerson AdmAddPersonToSite AdmAddSite AdmAuthCheck AdmDeleteNodeGroup AdmDeleteNodeNetwork AdmDeleteNode AdmDeletePerson AdmDeleteSite AdmGetAllNodeNetworkBandwidthLimits AdmGetAllNodeNetworks AdmGetAllRoles AdmGetNodeGroupNodes AdmGetNodeGroups AdmGetNodes AdmGetPersonRoles AdmGetPersonSites AdmGetPersons AdmGetSiteNodes AdmGetSitePersons AdmGetSites AdmGrantRoleToPerson AdmIsPersonInRole AdmRemoveNodeFromNodeGroup AdmRemovePersonFromSite AdmRevokeRoleFromPerson AdmSetPersonEnabled AdmSetPersonPrimarySite AdmUpdateNodeGroup AdmUpdateNodeNetwork AdmUpdateNode AdmUpdatePerson AdmUpdateSite AuthCheck BlacklistKey DeleteAddress DeleteAddressTypeFromAddress DeleteAddressType DeleteAttribute DeleteBootState DeleteKey DeleteKeyType DeleteNetworkMethod DeleteNetworkType DeleteNodeFromNodeGroup DeleteNodeGroup DeleteNodeNetwork DeleteNode DeletePCU DeletePersonFromSite DeletePersonFromSlice DeletePerson DeleteRoleFromPerson DeleteRole DeleteSite DeleteSliceAttribute DeleteSliceFromNodes DeleteSlice GetAddresses GetAddressTypes GetAttributes GetBootStates GetKeys GetKeyTypes GetNetworkMethods GetNetworkTypes GetNodeGroups GetNodeNetworks GetNodes GetPCUs GetPersons GetRoles GetSites GetSliceAttributes GetSlices SetPersonPrimarySite UpdateAddress UpdateAddressType UpdateAttribute UpdateKey UpdateNodeGroup UpdateNodeNetwork UpdateNode UpdatePCU UpdatePerson UpdateSite UpdateSliceAttribute UpdateSlice  system.listMethods  system.methodHelp  system.methodSignature  system.multicall'.split()
index bcf9255..155ac9a 100644 (file)
@@ -4,75 +4,45 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id$
+# $Id: PCUs.py,v 1.1 2006/09/06 15:36:07 mlhuang Exp $
 #
 
 from PLC.Faults import *
 from PLC.Parameter import Parameter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
+from PLC.NodeNetworks import valid_ip, NodeNetwork, NodeNetworks
 
 class PCU(Row):
     """
-    Representation of a row in the pcu table. To use,
+    Representation of a row in the pcus table. To use,
     instantiate with a dict of values.
     """
 
+    table_name = 'pcus'
+    primary_key = 'pcu_id'
     fields = {
-        'pcu_id': Parameter(int, "Node group identifier"),
-        'hostname': Parameter(str, "Fully qualified hostname"),
+        'pcu_id': Parameter(int, "PCU identifier"),
+        'site_id': Parameter(int, "Identifier of site where PCU is located"),
+        'hostname': Parameter(str, "PCU hostname", max = 254),
+        'ip': Parameter(str, "PCU IP address", max = 254),
+        'protocol': Parameter(str, "PCU protocol, e.g. ssh, https, telnet", max = 16),
+        'username': Parameter(str, "PCU username", max = 254),
+        'password': Parameter(str, "PCU username", max = 254),
+        'notes': Parameter(str, "Miscellaneous notes", max = 254),
+        'model': Parameter(str, "PCU model string", max = 32),
+        'node_ids': Parameter([int], "List of nodes that this PCU controls", ro = True),
+        'ports': Parameter([int], "List of the port numbers that each node is connected to", ro = True),
         }
 
-    # These fields are derived from join tables and are not
-    # actually in the pcu table.
-    join_fields = {
-        'node_ids': Parameter([int], "List of nodes that this PCU controls"),
-        }
-
-    def __init__(self, api, fields):
+    def __init__(self, api, fields = {}):
         Row.__init__(self, fields)
         self.api = api
 
-    def flush(self, commit = True):
-        """
-        Commit changes back to the database.
-        """
-
-        self.validate()
-
-        # Fetch a new pcu_id if necessary
-        if 'pcu_id' not in self:
-            rows = self.api.db.selectall("SELECT NEXTVAL('pcu_pcu_id_seq') AS pcu_id")
-            if not rows:
-                raise PLCDBError, "Unable to fetch new pcu_id"
-            self['pcu_id'] = rows[0]['pcu_id']
-            insert = True
-        else:
-            insert = False
-
-        # Filter out unknown fields
-        fields = dict(filter(lambda (key, value): key in self.fields,
-                             self.items()))
-
-        # Parameterize for safety
-        keys = fields.keys()
-        values = [self.api.db.param(key, value) for (key, value) in fields.items()]
-
-        if insert:
-            # Insert new row in pcu table
-            sql = "INSERT INTO pcu (%s) VALUES (%s)" % \
-                  (", ".join(keys), ", ".join(values))
-        else:
-            # Update existing row in sites table
-            columns = ["%s = %s" % (key, value) for (key, value) in zip(keys, values)]
-            sql = "UPDATE pcu SET " + \
-                  ", ".join(columns) + \
-                  " WHERE pcu_id = %(pcu_id)d"
-
-        self.api.db.do(sql, fields)
-
-        if commit:
-            self.api.db.commit()
+    def validate_ip(self, ip):
+        if not valid_ip(ip):
+            raise PLCInvalidArgument, "Invalid IP address " + ip
+        return ip
 
     def delete(self, commit = True):
         """
@@ -81,46 +51,37 @@ class PCU(Row):
 
         assert 'pcu_id' in self
 
-        # Delete ourself
-        for table in ['pcu_ports', 'pcu']:
-            self.api.db.do("DELETE FROM %s" \
-                           " WHERE nodenetwork_id = %(pcu_id)" % \
-                           table, self)
+        # Clean up various join tables
+        for table in ['pcu_node', 'pcus']:
+            self.api.db.do("DELETE FROM " + table +
+                           " WHERE pcu_id = %(pcu_id)d",
+                           self)
 
         if commit:
             self.api.db.commit()
 
 class PCUs(Table):
     """
-    Representation of row(s) from the pcu table in the
+    Representation of row(s) from the pcus table in the
     database.
     """
 
-    def __init__(self, api, pcu_id_or_hostname_list = None):
+    def __init__(self, api, pcu_ids = None):
         self.api = api
 
         # N.B.: Node IDs returned may be deleted.
-        sql = "SELECT pcu.*, pcu_ports.node_id" \
-              " FROM pcu" \
-              " LEFT JOIN pcu_ports USING (pcu_id)"
+        sql = "SELECT %s FROM view_pcus" % \
+              ", ".join(PCU.fields)
+
+        if pcu_ids:
+            sql += " WHERE pcu_id IN (%s)" % ", ".join(map(str, pcu_ids))
 
-        if pcu_id_or_hostname_list:
-            # Separate the list into integers and strings
-            pcu_ids = filter(lambda pcu_id: isinstance(pcu_id, (int, long)),
-                                   pcu_id_or_hostname_list)
-            hostnames = filter(lambda hostname: isinstance(hostname, StringTypes),
-                           pcu_id_or_hostname_list)
-            sql += " AND (False"
-            if pcu_ids:
-                sql += " OR pcu_id IN (%s)" % ", ".join(map(str, pcu_ids))
-            if hostnames:
-                sql += " OR hostname IN (%s)" % ", ".join(api.db.quote(hostnames)).lower()
-            sql += ")"
+        rows = self.api.db.selectall(sql)
 
-        rows = self.api.db.selectall(sql, locals())
         for row in rows:
-            if self.has_key(row['pcu_id']):
-                pcu = self[row['pcu_id']]
-                pcu.update(row)
-            else:
-                self[row['pcu_id']] = PCU(api, row)
+            self[row['pcu_id']] = pcu = PCU(api, row)
+            for aggregate in ['pcu_ids', 'ports']:
+                if not pcu.has_key(aggregate) or pcu[aggregate] is None:
+                    pcu[aggregate] = []
+                else:
+                    pcu[aggregate] = map(int, pcu[aggregate].split(','))