merge from trunk
authorTony Mack <tmack@cs.princeton.edu>
Tue, 13 Nov 2007 22:56:38 +0000 (22:56 +0000)
committerTony Mack <tmack@cs.princeton.edu>
Tue, 13 Nov 2007 22:56:38 +0000 (22:56 +0000)
44 files changed:
PLC/API.py
PLC/AddressTypes.py
PLC/Addresses.py
PLC/Auth.py
PLC/Boot.py
PLC/BootStates.py
PLC/ConfFiles.py
PLC/Config.py
PLC/EventObjects.py
PLC/Events.py
PLC/Faults.py
PLC/Filter.py
PLC/GPG.py
PLC/InitScripts.py
PLC/KeyTypes.py
PLC/Keys.py
PLC/Messages.py
PLC/Method.py
PLC/NetworkMethods.py
PLC/NetworkTypes.py
PLC/NodeGroups.py
PLC/NodeNetworkSettingTypes.py
PLC/NodeNetworkSettings.py
PLC/NodeNetworks.py
PLC/Nodes.py
PLC/PCUProtocolTypes.py [new file with mode: 0644]
PLC/PCUTypes.py [new file with mode: 0644]
PLC/PCUs.py
PLC/Parameter.py
PLC/Peers.py
PLC/Persons.py
PLC/PostgreSQL.py
PLC/PyCurl.py
PLC/Roles.py
PLC/Sessions.py
PLC/Shell.py
PLC/Sites.py
PLC/SliceAttributeTypes.py
PLC/SliceAttributes.py
PLC/SliceInstantiations.py
PLC/Slices.py
PLC/Table.py
PLC/Test.py
PLC/__init__.py

index 5c06848..5c4cb9d 100644 (file)
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 #
 # Copyright (C) 2004-2006 The Trustees of Princeton University
-# $Id: API.py,v 1.8 2007/01/05 16:09:09 tmack Exp $
+# $Id: API.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import sys
index 19f9b16..7156c00 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: AddressTypes.py,v 1.8 2006/11/09 03:07:42 mlhuang Exp $
+# $Id: AddressTypes.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from types import StringTypes
@@ -58,9 +58,9 @@ class AddressTypes(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), address_type_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), address_type_filter)
                 address_type_filter = Filter(AddressType.fields, {'address_type_id': ints, 'name': strs})
-                sql += " AND (%s)" % address_type_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % address_type_filter.sql(api, "OR")
             elif isinstance(address_type_filter, dict):
                 address_type_filter = Filter(AddressType.fields, address_type_filter)
-                sql += " AND (%s)" % address_type_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % address_type_filter.sql(api, "AND")
 
         self.selectall(sql)
index 62f9e3b..36c0d1d 100644 (file)
@@ -94,6 +94,6 @@ class Addresses(Table):
                 address_filter = Filter(Address.fields, {'address_id': address_filter})
             elif isinstance(address_filter, dict):
                 address_filter = Filter(Address.fields, address_filter)
-            sql += " AND (%s)" % address_filter.sql(api)
+            sql += " AND (%s) %s" % address_filter.sql(api)
 
         self.selectall(sql)
index 83bd211..498acc5 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Auth.py,v 1.18 2007/03/08 22:22:21 tmack Exp $
+# $Id: Auth.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import crypt
index cd3a738..00a33ca 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2007 The Trustees of Princeton University
 #
-# $Id$
+# $Id: Boot.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
index 42a5214..014b61a 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: BootStates.py,v 1.6 2006/10/24 20:02:22 mlhuang Exp $
+# $Id: BootStates.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
index 78853df..f15a574 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: ConfFiles.py,v 1.9 2006/11/09 03:07:42 mlhuang Exp $
+# $Id: ConfFiles.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
@@ -150,6 +150,6 @@ class ConfFiles(Table):
                 conf_file_filter = Filter(ConfFile.fields, {'conf_file_id': conf_file_filter})
             elif isinstance(conf_file_filter, dict):
                 conf_file_filter = Filter(ConfFile.fields, conf_file_filter)
-            sql += " AND (%s)" % conf_file_filter.sql(api)
+            sql += " AND (%s) %s" % conf_file_filter.sql(api)
 
         self.selectall(sql)
index 97233f0..693ba22 100644 (file)
@@ -6,7 +6,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2004-2006 The Trustees of Princeton University
 #
-# $Id: Config.py,v 1.2 2006/09/06 19:15:59 mlhuang Exp $
+# $Id: Config.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import os
index 9ba241a..52b44c7 100644 (file)
@@ -4,7 +4,7 @@
 # Tony Mack <tmack@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: EventObjects.py,v 1.4 2007/09/12 17:52:27 tmack Exp $
+# $Id: EventObjects.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
@@ -48,15 +48,16 @@ class EventObjects(Table):
        if event_filter is not None:
             if isinstance(event_filter, (list, tuple, set)):
                 event_filter = Filter(EventObject.fields, {'event_id': event_filter})
-                sql += " AND (%s)" % event_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % event_filter.sql(api, "OR")
             elif isinstance(event_filter, dict):
                 event_filter = Filter(EventObject.fields, event_filter)
-                sql += " AND (%s)" % event_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % event_filter.sql(api, "AND")
             elif isinstance (event_filter, int):
                 event_filter = Filter(EventObject.fields, {'event_id':[event_filter]})
-                sql += " AND (%s)" % event_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % event_filter.sql(api, "AND")
             else:
                 raise PLCInvalidArgument, "Wrong event object filter %r"%event_filter
-       sql += " ORDER BY %s" % EventObject.primary_key
+# with new filtering, caller needs to set this explicitly
+#      sql += " ORDER BY %s" % EventObject.primary_key
         
        self.selectall(sql)
index 7eaaed7..0d319cf 100644 (file)
@@ -4,7 +4,7 @@
 # Tony Mack <tmack@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Events.py,v 1.13 2007/04/11 20:29:10 tmack Exp $
+# $Id: Events.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
@@ -73,6 +73,7 @@ class Events(Table):
                 event_filter = Filter(Event.fields, {'event_id': event_filter})
             elif isinstance(event_filter, dict):
                 event_filter = Filter(Event.fields, event_filter)
-            sql += " AND (%s)" % event_filter.sql(api)
-       sql += " ORDER BY %s" % Event.primary_key
+            sql += " AND (%s) %s" % event_filter.sql(api)
+# with new filtering, caller needs to set this explicitly
+#      sql += " ORDER BY %s" % Event.primary_key
         self.selectall(sql)
index 1b8bd0d..e3d46e5 100644 (file)
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 #
 # Copyright (C) 2004-2006 The Trustees of Princeton University
-# $Id: Faults.py,v 1.2 2006/12/07 09:13:55 thierry Exp $
+# $Id: Faults.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import xmlrpclib
index c20fd9c..4063496 100644 (file)
@@ -14,34 +14,56 @@ class Filter(Parameter, dict):
     """
     A type of parameter that represents a filter on one or more
     columns of a database table.
+    Special features provide support for negation, upper and lower bounds, 
+    as well as sorting and clipping.
 
-    field should be a dictionary of field names and types, e.g.
-
-    {'node_id': Parameter(int, "Node identifier"),
-     'hostname': Parameter(int, "Fully qualified hostname", max = 255),
-     ...}
 
+    fields should be a dictionary of field names and types
     Only filters on non-sequence type fields are supported.
+    example : fields = {'node_id': Parameter(int, "Node identifier"),
+                        'hostname': Parameter(int, "Fully qualified hostname", max = 255),
+                        ...}
+
 
     filter should be a dictionary of field names and values
-    representing an intersection (if join_with is AND) or union (if
-    join_with is OR) filter. If a value is a sequence type, then it
-    should represent a list of possible values for that field.
+    representing  the criteria for filtering. 
+    example : filter = { 'hostname' : '*.edu' , site_id : [34,54] }
+    Whether the filter represents an intersection (AND) or a union (OR) 
+    of these criteria is determined by the join_with argument 
+    provided to the sql method below
+
+    Special features:
 
-    Special forms:
     * a field starting with the ~ character means negation.
-    example :  { '~peer_id' : None }
+    example :  filter = { '~peer_id' : None }
+
     * a field starting with < [  ] or > means lower than or greater than
       < > uses strict comparison
       [ ] is for using <= or >= instead
-    example :  { '>time' : 1178531418 }
-    example :  { ']event_id' : 2305 }
-    * a field starting with [ or ] means older than or more recent than
-      the associated value should be a given unix timestamp
+    example :  filter = { ']event_id' : 2305 }
+    example :  filter = { '>time' : 1178531418 }
+      in this example the integer value denotes a unix timestamp
+
+    * if a value is a sequence type, then it should represent 
+      a list of possible values for that field
+    example : filter = { 'node_id' : [12,34,56] }
+
     * a (string) value containing either a * or a % character is
       treated as a (sql) pattern; * are replaced with % that is the
       SQL wildcard character.
-    example :  { 'hostname' : '*.jp' } 
+    example :  filter = { 'hostname' : '*.jp' } 
+
+    * fields starting with - are special and relate to row selection, i.e. sorting and clipping
+    * '-SORT' : a field name, or an ordered list of field names that are used for sorting
+    * these fields may start with + (default) or - for denoting increasing or decreasing order
+    example : filter = { '-SORT' : [ '+node_id', '-hostname' ] }
+    * '-OFFSET' : the number of first rows to be ommitted
+    * '-LIMIT' : the amount of rows to be returned 
+    example : filter = { '-OFFSET' : 100, '-PAGE':25}
+
+    A realistic example would read
+    GetNodes ( { 'hostname' : '*.edu' , '-SORT' : 'hostname' , '-OFFSET' : 30 , '-PAGE' : 25 } )
+    and that would return nodes matching '*.edu' in alphabetical order from 31th to 55th
     """
 
     def __init__(self, fields = {}, filter = {}, doc = "Attribute filter"):
@@ -86,6 +108,10 @@ class Filter(Parameter, dict):
         else:
             assert join_with in ("AND", "OR")
 
+        # init 
+        sorts = []
+        clips = []
+
         for field, value in self.iteritems():
            # handle negation, numeric comparisons
            # simple, 1-depth only mechanism
@@ -93,6 +119,7 @@ class Filter(Parameter, dict):
            modifiers={'~' : False, 
                       '<' : False, '>' : False,
                       '[' : False, ']' : False,
+                       '-' : False,
                       }
 
            for char in modifiers.keys():
@@ -101,45 +128,76 @@ class Filter(Parameter, dict):
                    field = field[1:]
                    break
 
-            if field not in self.fields:
-#              print 'current fields',self.fields
-                raise PLCInvalidArgument, "Invalid filter field '%s'" % field
-
-            if isinstance(value, (list, tuple, set)):
-                # Turn empty list into (NULL) instead of invalid ()
-                if not value:
-                    value = [None]
-
-                operator = "IN"
-                value = map(str, map(api.db.quote, value))
-                value = "(%s)" % ", ".join(value)
+            # filter on fields
+            if not modifiers['-']:
+                if field not in self.fields:
+                    raise PLCInvalidArgument, "Invalid filter field '%s'" % field
+
+                if isinstance(value, (list, tuple, set)):
+                    # Turn empty list into (NULL) instead of invalid ()
+                    if not value:
+                        value = [None]
+
+                    operator = "IN"
+                    value = map(str, map(api.db.quote, value))
+                    value = "(%s)" % ", ".join(value)
+                else:
+                    if value is None:
+                        operator = "IS"
+                        value = "NULL"
+                    elif isinstance(value, StringTypes) and \
+                            (value.find("*") > -1 or value.find("%") > -1):
+                        operator = "LIKE"
+                        value = str(api.db.quote(value.replace("*", "%")))
+                    else:
+                        operator = "="
+                        if modifiers['<']:
+                            operator='<'
+                        if modifiers['>']:
+                            operator='>'
+                        if modifiers['[']:
+                            operator='<='
+                        if modifiers[']']:
+                            operator='>='
+                        else:
+                            value = str(api.db.quote(value))
+                clause = "%s %s %s" % (field, operator, value)
+
+                if modifiers['~']:
+                    clause = " ( NOT %s ) " % (clause)
+
+                conditionals.append(clause)
+            # sorting and clipping
             else:
-                if value is None:
-                    operator = "IS"
-                    value = "NULL"
-                elif isinstance(value, StringTypes) and \
-                     (value.find("*") > -1 or value.find("%") > -1):
-                   operator = "LIKE"
-                    value = str(api.db.quote(value.replace("*", "%")))
-               else:
-                    operator = "="
-                   if modifiers['<']:
-                       operator='<'
-                   if modifiers['>']:
-                       operator='>'
-                   if modifiers['[']:
-                       operator='<='
-                   if modifiers[']']:
-                       operator='>='
-                   else:
-                       value = str(api.db.quote(value))
-
-            clause = "%s %s %s" % (field, operator, value)
-
-           if modifiers['~']:
-               clause = " ( NOT %s ) " % (clause)
-
-            conditionals.append(clause)
-
-#      print 'sql=',(" %s " % join_with).join(conditionals)
-        return (" %s " % join_with).join(conditionals)
+                if field not in ('SORT','OFFSET','LIMIT'):
+                    raise PLCInvalidArgument, "Invalid filter, unknown sort and clip field %r"%field
+                # sorting
+                if field == 'SORT':
+                    if not isinstance(value,(list,tuple,set)):
+                        value=[value]
+                    for field in value:
+                        order = 'ASC'
+                        if field[0] == '+':
+                            field = field[1:]
+                        elif field[0] == '-':
+                            field = field[1:]
+                            order = 'DESC'
+                        if field not in self.fields:
+                            raise PLCInvalidArgument, "Invalid field %r in SORT filter"%field
+                        sorts.append("%s %s"%(field,order))
+                # clipping
+                elif field == 'OFFSET':
+                    clips.append("OFFSET %d"%value)
+                # clipping continued
+                elif field == 'LIMIT' :
+                    clips.append("LIMIT %d"%value)
+
+        where_part = (" %s " % join_with).join(conditionals)
+        clip_part = ""
+        if sorts:
+            clip_part += " ORDER BY " + ",".join(sorts)
+        if clips:
+            clip_part += " " + " ".join(clips)
+#      print 'where_part=',where_part,'clip_part',clip_part
+        return (where_part,clip_part)
index 13e5585..457c32b 100644 (file)
@@ -7,7 +7,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: GPG.py,v 1.6 2007/02/07 04:35:19 mlhuang Exp $
+# $Id: GPG.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import os
index f425698..9f864d2 100644 (file)
@@ -58,9 +58,9 @@ class InitScripts(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), initscript_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), initscript_filter)
                 initscript_filter = Filter(InitScript.fields, {'initscript_id': ints, 'name': strs })
-               sql += " AND (%s)" % initscript_filter.sql(api, "OR")
+               sql += " AND (%s) %s" % initscript_filter.sql(api, "OR")
             elif isinstance(initscript_filter, dict):
                 initscript_filter = Filter(InitScript.fields, initscript_filter)
-               sql += " AND (%s)" % initscript_filter.sql(api, "AND")
+               sql += " AND (%s) %s" % initscript_filter.sql(api, "AND")
 
         self.selectall(sql)
index 0092bf7..920662b 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: KeyTypes.py,v 1.3 2006/10/24 20:02:22 mlhuang Exp $
+# $Id: KeyTypes.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
index b81442d..8d22dc2 100644 (file)
@@ -117,6 +117,6 @@ class Keys(Table):
                 key_filter = Filter(Key.fields, {'key_id': key_filter})
             elif isinstance(key_filter, dict):
                 key_filter = Filter(Key.fields, key_filter)
-            sql += " AND (%s)" % key_filter.sql(api)
+            sql += " AND (%s) %s" % key_filter.sql(api)
 
        self.selectall(sql)
index 250ff12..0620ac0 100644 (file)
@@ -4,7 +4,7 @@
 # Tony Mack <tmack@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Messages.py,v 1.4 2006/11/16 17:03:36 mlhuang Exp $
+# $Id: Messages.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Parameter import Parameter
@@ -42,9 +42,9 @@ class Messages(Table):
         if message_filter is not None:
             if isinstance(message_filter, (list, tuple, set)):
                 message_filter = Filter(Message.fields, {'message_id': message_filter})
-                sql += " AND (%s)" % message_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % message_filter.sql(api, "OR")
             elif isinstance(message_filter, dict):
                 message_filter = Filter(Message.fields, message_filter)
-                sql += " AND (%s)" % message_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % message_filter.sql(api, "AND")
 
         self.selectall(sql)
index 1ba4eb1..5e7d09a 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Method.py,v 1.30 2007/09/12 17:52:27 tmack Exp $
+# $Id: Method.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import xmlrpclib
index a3d306b..d6b6a63 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: NetworkMethods.py,v 1.3 2006/10/24 20:02:22 mlhuang Exp $
+# $Id: NetworkMethods.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
index bab7af8..b42b42e 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: NetworkTypes.py,v 1.3 2006/10/24 20:02:22 mlhuang Exp $
+# $Id: NetworkTypes.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
index 90bd5f9..65b4a41 100644 (file)
@@ -4,13 +4,13 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: NodeGroups.py,v 1.18 2006/11/09 03:07:42 mlhuang Exp $
+# $Id: NodeGroups.py 5666 2007-11-06 21:52:21Z tmack $
 #
 
 from types import StringTypes
 
 from PLC.Faults import *
-from PLC.Parameter import Parameter
+from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
@@ -33,6 +33,11 @@ class NodeGroup(Row):
         'node_ids': Parameter([int], "List of nodes in this node group"),
         'conf_file_ids': Parameter([int], "List of configuration files specific to this node group"),
         }
+    related_fields = {
+       'conf_files': [Parameter(int, "ConfFile identifier")],
+       'nodes': [Mixed(Parameter(int, "Node identifier"),
+                        Parameter(str, "Fully qualified hostname"))]
+       }
 
     def validate_name(self, name):
        # Make sure name is not blank
@@ -41,8 +46,8 @@ class NodeGroup(Row):
        
        # Make sure node group does not alredy exist
        conflicts = NodeGroups(self.api, [name])
-       for nodegroup_id in conflicts:
-            if 'nodegroup_id' not in self or self['nodegroup_id'] != nodegroup_id:
+       for nodegroup in conflicts:
+            if 'nodegroup_id' not in self or self['nodegroup_id'] != nodegroup['nodegroup_id']:
                raise PLCInvalidArgument, "Node group name already in use"
 
        return name
@@ -98,6 +103,59 @@ class NodeGroup(Row):
             self['node_ids'].remove(node_id)
             node['nodegroup_ids'].remove(nodegroup_id)
 
+    def associate_nodes(self, auth, field, value):
+        """
+        Adds nodes found in value list to this nodegroup (using AddNodeToNodeGroup).
+        Deletes nodes not found in value list from this slice (using DeleteNodeFromNodeGroup).
+        """
+
+        assert 'node_ids' in self
+        assert 'nodegroup_id' in self
+        assert isinstance(value, list)
+
+        (node_ids, hostnames) = self.separate_types(value)[0:2]
+
+        # Translate hostnames into node_ids
+        if hostnames:
+            nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
+            node_ids += nodes.keys()
+
+        # Add new ids, remove stale ids
+        if self['node_ids'] != node_ids:
+            from PLC.Methods.AddNodeToNodeGroup import AddNodeToNodeGroup
+            from PLC.Methods.DeleteNodeFromNodeGroup import DeleteNodeFromNodeGroup
+            new_nodes = set(node_ids).difference(self['node_ids'])
+            stale_nodes = set(self['node_ids']).difference(node_ids)
+
+            for new_node in new_nodes:
+                AddNodeToNodeGroup.__call__(AddNodeToNodeGroup(self.api), auth, new_node, self['nodegroup_id'])
+            for stale_node in stale_nodes:
+                DeleteNodeFromNodeGroup.__call__(DeleteNodeFromNodeGroup(self.api), auth, stale_node, self['nodegroup_id'])
+
+    def associate_conf_files(self, auth, field, value):
+        """
+        Add conf_files found in value list (AddConfFileToNodeGroup)
+        Delets conf_files not found in value list (DeleteConfFileFromNodeGroup)
+        """
+
+        assert 'conf_file_ids' in self
+        assert 'nodegroup_id' in self
+        assert isinstance(value, list)
+
+        conf_file_ids = self.separate_types(value)[0]
+
+        if self['conf_file_ids'] != conf_file_ids:
+            from PLC.Methods.AddConfFileToNodeGroup import AddConfFileToNodeGroup
+            from PLC.Methods.DeleteConfFileFromNodeGroup import DeleteConfFileFromNodeGroup
+            new_conf_files = set(conf_file_ids).difference(self['conf_file_ids'])
+            stale_conf_files = set(self['conf_file_ids']).difference(conf_file_ids)
+
+            for new_conf_file in new_conf_files:
+                AddConfFileToNodeGroup.__call__(AddConfFileToNodeGroup(self.api), auth, new_conf_file, self['nodegroup_id'])
+            for stale_conf_file in stale_conf_files:
+                DeleteConfFileFromNodeGroup.__call__(DeleteConfFileFromNodeGroup(self.api), auth, stale_conf_file, self['nodegroup_id'])
+
+
 class NodeGroups(Table):
     """
     Representation of row(s) from the nodegroups table in the
@@ -116,9 +174,9 @@ class NodeGroups(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), nodegroup_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), nodegroup_filter)
                 nodegroup_filter = Filter(NodeGroup.fields, {'nodegroup_id': ints, 'name': strs})
-                sql += " AND (%s)" % nodegroup_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % nodegroup_filter.sql(api, "OR")
             elif isinstance(nodegroup_filter, dict):
                 nodegroup_filter = Filter(NodeGroup.fields, nodegroup_filter)
-                sql += " AND (%s)" % nodegroup_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % nodegroup_filter.sql(api, "AND")
 
         self.selectall(sql)
index f2ff214..69f36b7 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 1.1 $
+# $Revision: 5574 $
 #
 from types import StringTypes
 
@@ -70,13 +70,13 @@ class NodeNetworkSettingTypes(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), nodenetwork_setting_type_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), nodenetwork_setting_type_filter)
                 nodenetwork_setting_type_filter = Filter(NodeNetworkSettingType.fields, {'nodenetwork_setting_type_id': ints, 'name': strs})
-                sql += " AND (%s)" % nodenetwork_setting_type_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % nodenetwork_setting_type_filter.sql(api, "OR")
             elif isinstance(nodenetwork_setting_type_filter, dict):
                 nodenetwork_setting_type_filter = Filter(NodeNetworkSettingType.fields, nodenetwork_setting_type_filter)
-                sql += " AND (%s)" % nodenetwork_setting_type_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % nodenetwork_setting_type_filter.sql(api, "AND")
             elif isinstance (nodenetwork_setting_type_filter, StringTypes):
                 nodenetwork_setting_type_filter = Filter(NodeNetworkSettingType.fields, {'name':[nodenetwork_setting_type_filter]})
-                sql += " AND (%s)" % nodenetwork_setting_type_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % nodenetwork_setting_type_filter.sql(api, "AND")
             else:
                 raise PLCInvalidArgument, "Wrong nodenetwork setting type filter %r"%nodenetwork_setting_type_filter
 
index eddd1e2..bcf2506 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Thierry Parmentelat - INRIA
 #
-# $Revision: 1.1 $
+# $Revision: 5574 $
 #
 from PLC.Faults import *
 from PLC.Parameter import Parameter
@@ -51,7 +51,7 @@ class NodeNetworkSettings(Table):
                 nodenetwork_setting_filter = Filter(NodeNetworkSetting.fields, {'nodenetwork_setting_id': [nodenetwork_setting_filter]})
             else:
                 raise PLCInvalidArgument, "Wrong nodenetwork setting filter %r"%nodenetwork_setting_filter
-            sql += " AND (%s)" % nodenetwork_setting_filter.sql(api)
+            sql += " AND (%s) %s" % nodenetwork_setting_filter.sql(api)
 
 
         self.selectall(sql)
index 7681169..19229e6 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: NodeNetworks.py,v 1.18 2007/09/12 17:52:27 tmack Exp $
+# $Id: NodeNetworks.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from types import StringTypes
@@ -221,6 +221,6 @@ class NodeNetworks(Table):
                 nodenetwork_filter = Filter(NodeNetwork.fields, {'nodenetwork_id': [nodenetwork_filter]})
             else:
                 raise PLCInvalidArgument, "Wrong node network filter %r"%nodenetwork_filter
-            sql += " AND (%s)" % nodenetwork_filter.sql(api)
+            sql += " AND (%s) %s" % nodenetwork_filter.sql(api)
 
         self.selectall(sql)
index 1475ca2..ac7b126 100644 (file)
@@ -4,14 +4,14 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Nodes.py 800 2007-08-30 03:49:35Z thierry $
+# $Id: Nodes.py 5654 2007-11-06 03:43:55Z tmack $
 #
 
 from types import StringTypes
 import re
 
 from PLC.Faults import *
-from PLC.Parameter import Parameter
+from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
@@ -65,7 +65,17 @@ class Node(Row):
         'peer_id': Parameter(int, "Peer to which this node belongs", nullok = True),
         'peer_node_id': Parameter(int, "Foreign node identifier at peer", nullok = True),
         }
-
+    related_fields = {
+       'nodenetworks': [Mixed(Parameter(int, "NodeNetwork identifier"),
+                                      Filter(NodeNetwork.fields))],
+       'nodegroups': [Mixed(Parameter(int, "NodeGroup identifier"),
+                             Parameter(str, "NodeGroup name"))],
+       'conf_files': [Parameter(int, "ConfFile identifier")],
+       'slices': [Mixed(Parameter(int, "Slice identifier"),
+                         Parameter(str, "Slice name"))],
+       'slices_whitelist': [Mixed(Parameter(int, "Slice identifier"),
+                                   Parameter(str, "Slice name"))]
+       }
     # for Cache
     class_key = 'hostname'
     foreign_fields = ['boot_state','model','version']
@@ -124,6 +134,142 @@ class Node(Row):
                        " where node_id = %d" % (self['node_id']) )
         self.sync(commit)
 
+    def associate_nodenetworks(self, auth, field, value):
+       """
+       Delete nodenetworks not found in value list (using DeleteNodeNetwor)k   
+       Add nodenetworks found in value list (using AddNodeNetwork)
+       Updates nodenetworks found w/ nodenetwork_id in value list (using UpdateNodeNetwork) 
+       """
+
+       assert 'nodenetworkp_ids' in self
+        assert 'node_id' in self
+        assert isinstance(value, list)
+
+        (nodenetwork_ids, blank, nodenetworks) = self.separate_types(value)
+
+        if self['nodenetwork_ids'] != nodenetwork_ids:
+            from PLC.Methods.DeleteNodeNetwork import DeleteNodeNetwork
+
+            stale_nodenetworks = set(self['nodenetwork_ids']).difference(nodenetwork_ids)
+
+            for stale_nodenetwork in stale_nodenetworks:
+                DeleteNodeNetwork.__call__(DeleteNodeNetwork(self.api), auth, stale_nodenetwork['nodenetwork_id'])
+
+    def associate_nodegroups(self, auth, field, value):
+       """
+       Add node to nodegroups found in value list (AddNodeToNodegroup)
+       Delete node from nodegroup not found in value list (DeleteNodeFromNodegroup)
+       """
+       
+       from PLC.NodeGroups import NodeGroups
+       
+       assert 'nodegroup_ids' in self
+       assert 'node_id' in self
+       assert isinstance(value, list)
+
+       (nodegroup_ids, nodegroup_names) = self.separate_types(value)[0:2]
+       
+       if nodegroup_names:
+           nodegroups = NodeGroups(self.api, nodegroup_names, ['nodegroup_id']).dict('nodegroup_id')
+           nodegroup_ids += nodegroups.keys()
+
+       if self['nodegroup_ids'] != nodegroup_ids:
+           from PLC.Methods.AddNodeToNodeGroup import AddNodeToNodeGroup
+           from PLC.Methods.DeleteNodeFromNodeGroup import DeleteNodeFromNodeGroup
+       
+           new_nodegroups = set(nodegroup_ids).difference(self['nodegroup_ids'])
+           stale_nodegroups = set(self['nodegroup_ids']).difference(nodegroup_ids)
+       
+           for new_nodegroup in new_nodegroups:
+               AddNodeToNodeGroup.__call__(AddNodeToNodeGroup(self.api), auth, self['node_id'], new_nodegroup)
+           for stale_nodegroup in stale_nodegroups:
+               DeleteNodeFromNodeGroup.__call__(DeleteNodeFromNodeGroup(self.api), auth, self['node_id'], stale_nodegroup)
+         
+
+    def associate_conf_files(self, auth, field, value):
+       """
+       Add conf_files found in value list (AddConfFileToNode)
+       Delets conf_files not found in value list (DeleteConfFileFromNode)
+       """
+       
+       assert 'conf_file_ids' in self
+       assert 'node_id' in self
+       assert isinstance(value, list)
+       
+       conf_file_ids = self.separate_types(value)[0]
+       
+       if self['conf_file_ids'] != conf_file_ids:
+           from PLC.Methods.AddConfFileToNode import AddConfFileToNode
+           from PLC.Methods.DeleteConfFileFromNode import DeleteConfFileFromNode
+           new_conf_files = set(conf_file_ids).difference(self['conf_file_ids'])
+           stale_conf_files = set(self['conf_file_ids']).difference(conf_file_ids)
+       
+           for new_conf_file in new_conf_files:
+               AddConfFileToNode.__call__(AddConfFileToNode(self.api), auth, new_conf_file, self['node_id'])
+           for stale_conf_file in stale_conf_files:
+               DeleteConfFileFromNode.__call__(DeleteConfFileFromNode(self.api), auth, stale_conf_file, self['node_id'])
+
+    def associate_slices(self, auth, field, value):
+       """
+       Add slices found in value list to (AddSliceToNode)
+       Delete slices not found in value list (DeleteSliceFromNode)
+       """
+       
+       from PLC.Slices import Slices
+       
+       assert 'slice_ids' in self
+       assert 'node_id' in self
+       assert isinstance(value, list)
+       
+       (slice_ids, slice_names) = self.separate_types(value)[0:2]
+
+       if slice_names:
+           slices = Slices(self.api, slice_names, ['slice_id']).dict('slice_id')
+           slice_ids += slices.keys()
+
+       if self['slice_ids'] != slice_ids:
+           from PLC.Methods.AddSliceToNodes import AddSliceToNodes
+           from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
+           new_slices = set(slice_ids).difference(self['slice_ids'])
+           stale_slices = set(self['slice_ids']).difference(slice_ids)
+       
+       for new_slice in new_slices:
+           AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, new_slice, [self['node_id']])
+       for stale_slice in stale_slices:
+           DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, stale_slice, [self['node_id']])                         
+
+    def associate_slices_whitelist(self, auth, field, value):
+       """
+       Add slices found in value list to whitelist (AddSliceToNodesWhitelist)
+       Delete slices not found in value list from whitelist (DeleteSliceFromNodesWhitelist)
+       """
+
+       from PLC.Slices import Slices
+
+       assert 'slice_ids_whitelist' in self
+        assert 'node_id' in self
+        assert isinstance(value, list)
+
+       (slice_ids, slice_names) = self.separate_types(value)[0:2]
+
+        if slice_names:
+            slices = Slices(self.api, slice_names, ['slice_id']).dict('slice_id')
+            slice_ids += slices.keys()
+
+        if self['slice_ids_whitelist'] != slice_ids:
+            from PLC.Methods.AddSliceToNodesWhitelist import AddSliceToNodesWhitelist
+            from PLC.Methods.DeleteSliceFromNodesWhitelist import DeleteSliceFromNodesWhitelist
+            new_slices = set(slice_ids).difference(self['slice_ids_whitelist'])
+            stale_slices = set(self['slice_ids_whitelist']).difference(slice_ids)
+
+        for new_slice in new_slices:
+            AddSliceToNodesWhitelist.__call__(AddSliceToNodesWhitelist(self.api), auth, new_slice, [self['node_id']])
+        for stale_slice in stale_slices:
+            DeleteSliceFromNodesWhitelist.__call__(DeleteSliceFromNodesWhitelist(self.api), auth, stale_slice, [self['node_id']]) 
+               
+
     def delete(self, commit = True):
         """
         Delete existing node.
@@ -164,16 +310,16 @@ class Nodes(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), node_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), node_filter)
                 node_filter = Filter(Node.fields, {'node_id': ints, 'hostname': strs})
-                sql += " AND (%s)" % node_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % node_filter.sql(api, "OR")
             elif isinstance(node_filter, dict):
                 node_filter = Filter(Node.fields, node_filter)
-                sql += " AND (%s)" % node_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % node_filter.sql(api, "AND")
             elif isinstance (node_filter, StringTypes):
                 node_filter = Filter(Node.fields, {'hostname':[node_filter]})
-                sql += " AND (%s)" % node_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % node_filter.sql(api, "AND")
             elif isinstance (node_filter, int):
                 node_filter = Filter(Node.fields, {'node_id':[node_filter]})
-                sql += " AND (%s)" % node_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % node_filter.sql(api, "AND")
             else:
                 raise PLCInvalidArgument, "Wrong node filter %r"%node_filter
 
diff --git a/PLC/PCUProtocolTypes.py b/PLC/PCUProtocolTypes.py
new file mode 100644 (file)
index 0000000..30ef459
--- /dev/null
@@ -0,0 +1,75 @@
+#
+# Functions for interacting with the pcu_type_port table in the database
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id: 
+#
+
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Table import Row, Table
+from PLC.Filter import Filter
+
+class PCUProtocolType(Row):
+    """
+    Representation of a row in the pcu_protocol_type table. To use,
+    instantiate with a dict of values.
+    """
+
+    table_name = 'pcu_protocol_type'
+    primary_key = 'pcu_protocol_type_id'
+    join_tables = []
+    fields = {
+       'pcu_protocol_type_id': Parameter(int, "PCU protocol type identifier"),
+       'pcu_type_id': Parameter(int, "PCU type identifier"), 
+       'port': Parameter(int, "PCU port", max = 254),
+       'protocol': Parameter(str, "Protocol", max = 254),
+       'supported': Parameter(bool, "Is the port/protocol supported by PLC") 
+        }
+
+    def validate_port(self, port):
+       # make sure port is not blank
+       
+       if not port:
+            raise PLCInvalidArgument, "Port must be specified"
+       
+       return port
+
+    def validate_protocol(self, protocol):
+        # make sure port is not blank
+        if not len(protocol):
+            raise PLCInvalidArgument, "protocol must be specified"
+
+       return protocol
+
+class PCUProtocolTypes(Table):
+    """
+    Representation of the pcu_protocol_types table in the database.
+    """
+
+    def __init__(self, api, protocol_type_filter = None, columns = None):
+        Table.__init__(self, api, PCUProtocolType, columns)
+
+        sql = "SELECT %s FROM pcu_protocol_type WHERE True" % \
+              ", ".join(self.columns)
+        
+       if protocol_type_filter is not None:
+            if isinstance(protocol_type_filter, (list, tuple, set)):
+                # Separate the list into integers and strings
+                ints = filter(lambda x: isinstance(x, (int, long)), protocol_type_filter)
+                protocol_type_filter = Filter(PCUProtocolType.fields, {'pcu_protocol_type_id': ints})
+                sql += " AND (%s) %s" % protocol_type_filter.sql(api, "OR")
+            elif isinstance(protocol_type_filter, dict):
+                protocol_type_filter = Filter(PCUProtocolType.fields, protocol_type_filter)
+                sql += " AND (%s) %s" % protocol_type_filter.sql(api, "AND")
+            elif isinstance (protocol_type_filter, int):
+                protocol_type_filter = Filter(PCUProtocolType.fields, {'pcu_protocol_type_id':[protocol_type_filter]})
+
+                sql += " AND (%s) %s" % protocol_type_filter.sql(api, "AND")
+            else:
+                raise PLCInvalidArgument, "Wrong pcu_protocol_type filter %r"%protocol_type_filter     
+
+
+        self.selectall(sql)
diff --git a/PLC/PCUTypes.py b/PLC/PCUTypes.py
new file mode 100644 (file)
index 0000000..d1f650c
--- /dev/null
@@ -0,0 +1,104 @@
+#
+# Functions for interacting with the pcu_types table in the database
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id: 
+#
+from types import StringTypes
+
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Table import Row, Table
+from PLC.Filter import Filter
+
+class PCUType(Row):
+    """
+    Representation of a row in the pcu_types table. To use,
+    instantiate with a dict of values.
+    """
+
+    table_name = 'pcu_types'
+    primary_key = 'pcu_type_id'
+    join_tables = ['pcu_port_type']
+    fields = {
+        'pcu_type_id': Parameter(int, "PCU Type Identifier"),
+       'model': Parameter(str, "PCU model", max = 254),
+       'name': Parameter(str, "PCU full name", max = 254),
+        'pcu_protocol_type_ids': Parameter([int], "PCU Protocol Type Identifiers"),
+       'pcu_protocol_types': Parameter([dict], "PCU Protocol Type List")
+        }
+
+    def validate_model(self, model):
+       # Make sure name is not blank
+        if not len(model):
+            raise PLCInvalidArgument, "Model must be specified"
+       
+       # Make sure boot state does not alredy exist
+       conflicts = PCUTypes(self.api, [model])
+        for pcu_type in conflicts:
+           if 'pcu_type_id' not in self or self['pcu_type_id'] != pcu_type['pcu_type_id']: 
+               raise PLCInvalidArgument, "Model already in use"
+
+       return model
+
+class PCUTypes(Table):
+    """
+    Representation of the pcu_types table in the database.
+    """
+
+    def __init__(self, api, pcu_type_filter = None, columns = None):
+
+       # Remove pcu_protocol_types from query since its not really a field
+       # in the db. We will add it later
+       if columns == None:
+           columns = PCUType.fields.keys()
+       if 'pcu_protocol_types' in columns:
+           removed_fields = ['pcu_protocol_types']
+           columns.remove('pcu_protocol_types')
+       else:
+           removed_fields = []
+
+        Table.__init__(self, api, PCUType, columns)
+
+        sql = "SELECT %s FROM view_pcu_types WHERE True" % \
+              ", ".join(self.columns)
+        
+       if pcu_type_filter is not None:
+            if isinstance(pcu_type_filter, (list, tuple, set)):
+                # Separate the list into integers and strings
+                ints = filter(lambda x: isinstance(x, (int, long)), pcu_type_filter)
+                strs = filter(lambda x: isinstance(x, StringTypes), pcu_type_filter)
+                pcu_type_filter = Filter(PCUType.fields, {'pcu_type_id': ints, 'model': strs})
+                sql += " AND (%s) %s" % pcu_type_filter.sql(api, "OR")
+            elif isinstance(pcu_type_filter, dict):
+                pcu_type_filter = Filter(PCUType.fields, pcu_type_filter)
+                sql += " AND (%s) %s" % pcu_type_filter.sql(api, "AND")
+            elif isinstance (pcu_type_filter, StringTypes):
+                pcu_type_filter = Filter(PCUType.fields, {'model':[pcu_type_filter]})
+                sql += " AND (%s) %s" % pcu_type_filter.sql(api, "AND")
+            elif isinstance (pcu_type_filter, int):
+                pcu_type_filter = Filter(PCUType.fields, {'pcu_type_id':[pcu_type_filter]})
+                sql += " AND (%s) %s" % pcu_type_filter.sql(api, "AND")
+            else:
+                raise PLCInvalidArgument, "Wrong pcu_type filter %r"%pcu_type_filter   
+
+
+        self.selectall(sql)
+
+        # return a list of protocol type objects for each port type
+        if 'pcu_protocol_types' in removed_fields:
+           from PLC.PCUProtocolTypes import PCUProtocolTypes
+            protocol_type_ids = set()
+            for pcu_type in self:
+                protocol_type_ids.update(pcu_type['pcu_protocol_type_ids'])
+
+            protocol_return_fields = ['pcu_protocol_type_id', 'port', 'protocol', 'supported']
+            all_protocol_types = PCUProtocolTypes(self.api, list(protocol_type_ids), \
+                                                 protocol_return_fields).dict('pcu_protocol_type_id')
+
+            for pcu_type in self:
+                pcu_type['pcu_protocol_types'] = []
+                for protocol_type_id in pcu_type['pcu_protocol_type_ids']:
+                    pcu_type['pcu_protocol_types'].append(all_protocol_types[protocol_type_id])
index 5b69c0a..0ab56cc 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: PCUs.py,v 1.9 2006/11/09 03:07:42 mlhuang Exp $
+# $Id: PCUs.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
@@ -111,6 +111,6 @@ class PCUs(Table):
                 pcu_filter = Filter(PCU.fields, {'pcu_id': pcu_filter})
             elif isinstance(pcu_filter, dict):
                 pcu_filter = Filter(PCU.fields, pcu_filter)
-            sql += " AND (%s)" % pcu_filter.sql(api)
+            sql += " AND (%s) %s" % pcu_filter.sql(api)
 
         self.selectall(sql)
index 2307483..474ad78 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Parameter.py,v 1.6 2006/11/08 22:10:00 mlhuang Exp $
+# $Id: Parameter.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from types import *
index b1de5e7..0973c3d 100644 (file)
@@ -227,9 +227,9 @@ class Peers (Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
                 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
-                sql += " AND (%s)" % peer_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % peer_filter.sql(api, "OR")
             elif isinstance(peer_filter, dict):
                 peer_filter = Filter(Peer.fields, peer_filter)
-                sql += " AND (%s)" % peer_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % peer_filter.sql(api, "AND")
 
        self.selectall(sql)
index f035955..d2bb510 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Persons.py,v 1.39 2007/10/01 20:51:46 tmack Exp $
+# $Id: Persons.py 5652 2007-11-06 03:42:57Z tmack $
 #
 
 from types import StringTypes
@@ -17,7 +17,7 @@ import crypt
 
 from PLC.Faults import *
 from PLC.Debug import log
-from PLC.Parameter import Parameter
+from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
 from PLC.Table import Row, Table
 from PLC.Roles import Role, Roles
@@ -57,6 +57,18 @@ class Person(Row):
         'peer_id': Parameter(int, "Peer to which this user belongs", nullok = True),
         'peer_person_id': Parameter(int, "Foreign user identifier at peer", nullok = True),
         }
+    related_fields = {
+       'roles': [Mixed(Parameter(int, "Role identifier"),
+                       Parameter(str, "Role name"))],
+       'sites': [Mixed(Parameter(int, "Site identifier"),
+                       Parameter(str, "Site name"))],
+       'keys': [Mixed(Parameter(int, "Key identifier"),
+                      Filter(Key.fields))],
+       'slices': [Mixed(Parameter(int, "Slice identifier"),
+                        Parameter(str, "Slice name"))]
+       }       
+
+       
 
     # for Cache
     class_key = 'email'
@@ -229,6 +241,132 @@ class Person(Row):
                        " where person_id = %d" % (self['person_id']) )
         self.sync(commit)
 
+    def associate_roles(self, auth, field, value):
+       """
+       Adds roles found in value list to this person (using AddRoleToPerson).
+       Deletes roles not found in value list from this person (using DeleteRoleFromPerson).
+       """
+       
+       assert 'role_ids' in self
+       assert 'person_id' in self
+       assert isinstance(value, list)
+       
+       (role_ids, roles_names) = self.separate_types(value)[0:2]
+       
+       # Translate roles into role_ids
+       if roles_names:
+           roles = Roles(self.api, role_names, ['role_id']).dict('role_id')
+           role_ids += roles.keys()
+       
+       # Add new ids, remove stale ids
+       if self['role_ids'] != role_ids:
+           from PLC.Methods.AddRoleToPerson import AddRoleToPerson
+           from PLC.Methods.DeleteRoleFromPerson import DeleteRoleFromPerson
+           new_roles = set(role_ids).difference(self['role_ids'])
+           stale_roles = set(self['role_ids']).difference(role_ids)
+
+           for new_role in new_roles:
+               AddRoleToPerson.__call__(AddRoleToPerson(self.api), auth, new_role, self['person_id'])
+           for stale_role in stale_roles:
+               DeleteRoleFromPerson.__call__(DeleteRoleFromPerson(self.api), auth, stale_role, self['person_id'])
+
+
+    def associate_sites(self, auth, field, value):
+        """
+        Adds person to sites found in value list (using AddPersonToSite).
+        Deletes person from site not found in value list (using DeletePersonFromSite).
+        """
+
+       from PLC.Sites import Sites
+
+        assert 'site_ids' in self
+        assert 'person_id' in self
+        assert isinstance(value, list)
+
+        (site_ids, site_names) = self.separate_types(value)[0:2]
+
+        # Translate roles into role_ids
+        if site_names:
+            sites = Sites(self.api, site_names, ['site_id']).dict('site_id')
+            site_ids += sites.keys()
+
+        # Add new ids, remove stale ids
+        if self['site_ids'] != site_ids:
+            from PLC.Methods.AddPersonToSite import AddPersonToSite
+            from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite
+            new_sites = set(site_ids).difference(self['site_ids'])
+            stale_sites = set(self['site_ids']).difference(site_ids)
+
+            for new_site in new_sites:
+                AddPersonToSite.__call__(AddPersonToSite(self.api), auth, self['person_id'], new_site)
+            for stale_site in stale_sites:
+                DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, self['person_id'], stale_site)
+
+
+    def associate_keys(self, auth, field, value):
+       """
+        Deletes key_ids not found in value list (using DeleteKey).
+        Adds key if key_fields w/o key_id is found (using AddPersonKey).
+        Updates key if key_fields w/ key_id is found (using UpdateKey).
+        """
+       assert 'key_ids' in self
+       assert 'person_id' in self
+       assert isinstance(value, list)
+       
+       (key_ids, blank, keys) = self.separate_types(value)
+       
+       if self['key_ids'] != key_ids:
+           from PLC.Methods.DeleteKey import DeleteKey
+           stale_keys = set(self['key_ids']).difference(key_ids)
+       
+           for stale_key in stale_keys:
+               DeleteKey.__call__(DeleteKey(self.api), auth, stale_key) 
+
+       if keys:
+           from PLC.Methods.AddPersonKey import AddPersonKey
+           from PLC.Methods.UpdateKey import UpdateKey         
+           updated_keys = filter(lambda key: 'key_id' in key, keys)
+           added_keys = filter(lambda key: 'key_id' not in key, keys)
+               
+           for key in added_keys:
+               AddPersonKey.__call__(AddPersonKey(self.api), auth, self['person_id'], key)
+           for key in updated_keys:
+               key_id = key.pop('key_id')
+               UpdateKey.__call__(UpdateKey(self.api), auth, key_id, key)
+                 
+       
+    def associate_slices(self, auth, field, value):
+        """
+        Adds person to slices found in value list (using AddPersonToSlice).
+        Deletes person from slices found in value list (using DeletePersonFromSlice).
+        """
+
+       from PLC.Slices import Slices
+
+        assert 'slice_ids' in self
+        assert 'person_id' in self
+        assert isinstance(value, list)
+
+        (slice_ids, slice_names) = self.separate_types(value)[0:2]
+
+        # Translate roles into role_ids
+        if slice_names:
+            slices = Slices(self.api, slice_names, ['slice_id']).dict('slice_id')
+            slice_ids += slices.keys()
+
+        # Add new ids, remove stale ids
+        if self['slice_ids'] != slice_ids:
+            from PLC.Methods.AddPersonToSlice import AddPersonToSlice
+            from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
+            new_slices = set(slice_ids).difference(self['slice_ids'])
+            stale_slices = set(self['slice_ids']).difference(slice_ids)
+
+            for new_slice in new_slices:
+                AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, self['person_id'], new_slice)
+            for stale_slice in stale_slices:
+                DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, self['person_id'], stale_slice)
+    
+
     def delete(self, commit = True):
         """
         Delete existing user.
@@ -322,18 +460,18 @@ class Persons(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), person_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), person_filter)
                 person_filter = Filter(Person.fields, {'person_id': ints, 'email': strs})
-                sql += " AND (%s)" % person_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % person_filter.sql(api, "OR")
             elif isinstance(person_filter, dict):
                 person_filter = Filter(Person.fields, person_filter)
-                sql += " AND (%s)" % person_filter.sql(api, "AND")
-           elif isinstance (person_filter, StringTypes):
+                sql += " AND (%s) %s" % person_filter.sql(api, "AND")
+            elif isinstance (person_filter, StringTypes):
                 person_filter = Filter(Person.fields, {'email':[person_filter]})
-                sql += " AND (%s)" % person_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % person_filter.sql(api, "AND")
             elif isinstance (person_filter, int):
                 person_filter = Filter(Person.fields, {'person_id':[person_filter]})
-                sql += " AND (%s)" % person_filter.sql(api, "AND")
-           else:
-                raise PLCInvalidArgument, "Wrong person filter %r"%person_filter           
+                sql += " AND (%s) %s" % person_filter.sql(api, "AND")
+            else:
+                raise PLCInvalidArgument, "Wrong person filter %r"%person_filter
 
        # aggregate data
        all_persons = {}
index 9100ce1..c58a69f 100644 (file)
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: PostgreSQL.py,v 1.15 2007/02/12 18:41:27 mlhuang Exp $
+# $Id: PostgreSQL.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import psycopg2
index 51b08a3..032ab75 100644 (file)
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: PyCurl.py,v 1.3 2007/10/01 20:51:47 tmack Exp $
+# $Id: PyCurl.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import os
index 8647766..53d68d0 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Roles.py,v 1.7 2006/11/08 23:02:01 mlhuang Exp $
+# $Id: Roles.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from types import StringTypes
@@ -64,9 +64,9 @@ class Roles(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), role_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), role_filter)
                 role_filter = Filter(Role.fields, {'role_id': ints, 'name': strs})
-                sql += " AND (%s)" % role_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % role_filter.sql(api, "OR")
             elif isinstance(role_filter, dict):
                 role_filter = Filter(Role.fields, role_filter)
-                sql += " AND (%s)" % role_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % role_filter.sql(api, "AND")
 
         self.selectall(sql)
index b470c96..e0a57b3 100644 (file)
@@ -76,10 +76,10 @@ class Sessions(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), session_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), session_filter)
                 session_filter = Filter(Session.fields, {'person_id': ints, 'session_id': strs})
-                sql += " AND (%s)" % session_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % session_filter.sql(api, "OR")
            elif isinstance(session_filter, dict):
                session_filter = Filter(Session.fields, session_filter)
-               sql += " AND (%s)" % session_filter.sql(api, "AND")
+               sql += " AND (%s) %s" % session_filter.sql(api, "AND")
 
         if expires is not None:
             if expires >= 0:
index 6782fff..43317a0 100644 (file)
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2005 The Trustees of Princeton University
 #
-# $Id: Shell.py,v 1.5 2007/02/08 21:49:24 mlhuang Exp $
+# $Id: Shell.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 import os
index 759e1d7..6035bb7 100644 (file)
@@ -2,7 +2,7 @@ from types import StringTypes
 import string
 
 from PLC.Faults import *
-from PLC.Parameter import Parameter
+from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
@@ -45,7 +45,12 @@ class Site(Row):
         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
        'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
         }
-
+    related_fields = {
+       'persons': [Mixed(Parameter(int, "Person identifier"),
+                          Parameter(str, "Email address"))],
+       'addresses': [Mixed(Parameter(int, "Address identifer"),
+                            Filter(Address.fields))]
+       }
     # for Cache
     class_key = 'login_base'
     foreign_fields = ['abbreviated_name', 'name', 'is_public', 'latitude', 'longitude',
@@ -113,6 +118,74 @@ class Site(Row):
                        " where site_id = %d" % (self['site_id']) )
         self.sync(commit)    
 
+
+    def associate_persons(self, auth, field, value):
+       """
+       Adds persons found in value list to this site (using AddPersonToSite).
+       Deletes persons not found in value list from this site (using DeletePersonFromSite).
+       """
+       
+       assert 'person_ids' in self
+       assert 'site_id' in self
+       assert isinstance(value, list)
+
+       (person_ids, emails) = self.separate_types(value)[0:2]
+
+       # Translate emails into person_ids
+       if emails:
+           persons = Persons(self.api, emails, ['person_id']).dict('person_id')
+           person_ids += persons.keys()
+
+       # Add new ids, remove stale ids
+       if self['person_ids'] != person_ids:
+           from PLC.Methods.AddPersonToSite import AddPersonToSite
+           from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite
+           new_persons = set(person_ids).difference(self['person_ids'])
+           stale_persons = set(self['person_ids']).difference(person_ids)
+        
+           for new_person in new_persons:
+               AddPersonToSite.__call__(AddPersonToSite(self.api), auth, new_person, self['site_id'])
+           for stale_person in stale_persons:
+               DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, stale_person, self['site_id'])              
+
+    def associate_addresses(self, auth, field, value):
+       """
+       Deletes addresses_ids not found in value list (using DeleteAddress).  
+       Adds address if slice_fields w/o address_id found in value list (using AddSiteAddress).
+       Update address if slice_fields w/ address_id found in value list (using UpdateAddress).
+       """
+       
+       assert 'address_ids' in self
+       assert 'site_id' in self
+        assert isinstance(value, list)
+
+        (address_ids, blank, addresses) = self.separate_types(value)
+
+       for address in addresses:
+           if 'address_id' in address:
+               address_ids.append(address['address_id'])               
+
+        # Add new ids, remove stale ids
+        if self['address_ids'] != address_ids:
+            from PLC.Methods.DeleteAddress import DeleteAddress
+            stale_addresses = set(self['address_ids']).difference(address_ids)
+
+            for stale_address in stale_addresses:
+                DeleteAddress.__call__(DeleteAddress(self.api), auth, stale_address)   
+       
+       if addresses:
+           from PLC.Methods.AddSiteAddress import AddSiteAddress
+           from PLC.Methods.UpdateAddress import UpdateAddress
+               
+           updated_addresses = filter(lambda address: 'address_id' in address, addresses)
+           added_addresses = filter(lambda address: 'address_id' not in address, addresses)
+               
+           for address in added_addresses:
+               AddSiteAddress.__call__(AddSiteAddress(self.api), auth, self['site_id'], address)       
+           for address in updated_addresses:
+               address_id = address.pop('address_id')
+               UpdateAddress.__call__(UpdateAddress(self.api), auth, address_id, address)
+
     def delete(self, commit = True):
         """
         Delete existing site.
@@ -182,16 +255,16 @@ class Sites(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
                 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
-                sql += " AND (%s)" % site_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % site_filter.sql(api, "OR")
             elif isinstance(site_filter, dict):
                 site_filter = Filter(Site.fields, site_filter)
-                sql += " AND (%s)" % site_filter.sql(api, "AND")
-           elif isinstance (site_filter, StringTypes):
+                sql += " AND (%s) %s" % site_filter.sql(api, "AND")
+            elif isinstance (site_filter, StringTypes):
                 site_filter = Filter(Site.fields, {'login_base':[site_filter]})
-                sql += " AND (%s)" % site_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % site_filter.sql(api, "AND")
             elif isinstance (site_filter, int):
                 site_filter = Filter(Site.fields, {'site_id':[site_filter]})
-                sql += " AND (%s)" % site_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % site_filter.sql(api, "AND")
             else:
                 raise PLCInvalidArgument, "Wrong site filter %r"%site_filter
 
index 6aa4baa..5884fe0 100644 (file)
@@ -64,9 +64,9 @@ class SliceAttributeTypes(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), attribute_type_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), attribute_type_filter)
                 attribute_type_filter = Filter(SliceAttributeType.fields, {'attribute_type_id': ints, 'name': strs})
-                sql += " AND (%s)" % attribute_type_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % attribute_type_filter.sql(api, "OR")
             elif isinstance(attribute_type_filter, dict):
                 attribute_type_filter = Filter(SliceAttributeType.fields, attribute_type_filter)
-                sql += " AND (%s)" % attribute_type_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % attribute_type_filter.sql(api, "AND")
 
         self.selectall(sql)
index cfc0298..9f0b1fb 100644 (file)
@@ -41,6 +41,6 @@ class SliceAttributes(Table):
                 slice_attribute_filter = Filter(SliceAttribute.fields, {'slice_attribute_id': slice_attribute_filter})
             elif isinstance(slice_attribute_filter, dict):
                 slice_attribute_filter = Filter(SliceAttribute.fields, slice_attribute_filter)
-            sql += " AND (%s)" % slice_attribute_filter.sql(api)
+            sql += " AND (%s) %s" % slice_attribute_filter.sql(api)
 
         self.selectall(sql)
index 843b327..db11838 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: SliceInstantiations.py,v 1.2 2006/10/03 19:27:28 mlhuang Exp $
+# $Id: SliceInstantiations.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from PLC.Faults import *
index 88c0b57..1a1786c 100644 (file)
@@ -3,13 +3,14 @@ import time
 import re
 
 from PLC.Faults import *
-from PLC.Parameter import Parameter
+from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
 from PLC.SliceInstantiations import SliceInstantiation, SliceInstantiations
 from PLC.Nodes import Node
 from PLC.Persons import Person, Persons
+from PLC.SliceAttributes import SliceAttribute
 
 class Slice(Row):
     """
@@ -39,6 +40,12 @@ class Slice(Row):
         'peer_id': Parameter(int, "Peer to which this slice belongs", nullok = True),
         'peer_slice_id': Parameter(int, "Foreign slice identifier at peer", nullok = True),
         }
+    related_fields = {
+       'persons': [Mixed(Parameter(int, "Person identifier"),
+                         Parameter(str, "Email address"))],
+       'nodes': [Mixed(Parameter(int, "Node identifier"),
+                       Parameter(str, "Fully qualified hostname"))]
+       }
     # for Cache
     class_key = 'name'
     foreign_fields = ['instantiation', 'url', 'description', 'max_nodes', 'expires']
@@ -97,6 +104,127 @@ class Slice(Row):
     add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
     delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
 
+    def associate_persons(self, auth, field, value):
+        """
+        Adds persons found in value list to this slice (using AddPersonToSlice).
+       Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
+        """
+       
+       assert 'person_ids' in self
+       assert 'slice_id' in self
+        assert isinstance(value, list)
+
+       (person_ids, emails) = self.separate_types(value)[0:2]
+
+       # Translate emails into person_ids      
+       if emails:
+           persons = Persons(self.api, emails, ['person_id']).dict('person_id')
+           person_ids += persons.keys()
+       
+       # Add new ids, remove stale ids
+        if self['person_ids'] != person_ids:
+            from PLC.Methods.AddPersonToSlice import AddPersonToSlice
+            from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
+            new_persons = set(person_ids).difference(self['person_ids'])
+            stale_persons = set(self['person_ids']).difference(person_ids)
+
+            for new_person in new_persons:
+                AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
+            for stale_person in stale_persons:
+                DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
+
+    def associate_nodes(self, auth, field, value):
+       """
+       Adds nodes found in value list to this slice (using AddSliceToNodes).
+       Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
+       """
+
+        from PLC.Nodes import Nodes
+
+       assert 'node_ids' in self
+       assert 'slice_id' in self
+       assert isinstance(value, list)
+       
+       (node_ids, hostnames) = self.separate_types(value)[0:2]
+       
+       # Translate hostnames into node_ids
+       if hostnames:
+           nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
+           node_ids += nodes.keys()
+       
+       # Add new ids, remove stale ids
+       if self['node_ids'] != node_ids:
+           from PLC.Methods.AddSliceToNodes import AddSliceToNodes
+           from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
+           new_nodes = set(node_ids).difference(self['node_ids'])
+           stale_nodes = set(self['node_ids']).difference(node_ids)
+           
+           if new_nodes:
+               AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
+           if stale_nodes:
+               DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))                        
+    def associate_slice_attributes(self, auth, fields, value):
+       """
+       Deletes slice_attribute_ids not found in value list (using DeleteSliceAttribute). 
+       Adds slice_attributes if slice_fields w/o slice_id is found (using AddSliceAttribute).
+       Updates slice_attribute if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).  
+       """
+       
+       assert 'slice_attribute_ids' in self
+       assert isinstance(value, list)
+
+       (attribute_ids, blank, attributes) = self.separate_types(value)
+       
+       # There is no way to add attributes by id. They are
+       # associated with a slice when they are created.
+       # So we are only looking to delete here 
+       if self['slice_attribute_ids'] != attribute_ids:
+           from PLC.Methods.DeleteSliceAttribute import DeleteSliceAttribute
+           stale_attributes = set(self['slice_attribute_ids']).difference(attribute_ids)
+       
+           for stale_attribute in stale_attributes:
+               DeleteSliceAttribute.__call__(DeleteSliceAttribute(self.api), auth, stale_attribute['slice_attribute_id'])              
+       
+       # If dictionary exists, we are either adding new
+        # attributes or updating existing ones.
+        if attributes:
+            from PLC.Methods.AddSliceAttribute import AddSliceAttribute
+            from PLC.Methods.UpdateSliceAttribute import UpdateSliceAttribute
+       
+           added_attributes = filter(lambda x: 'slice_attribute_id' not in x, attributes)
+           updated_attributes = filter(lambda x: 'slice_attribute_id' in x, attributes)
+
+           for added_attribute in added_attributes:
+               if 'attribute_type' in added_attribute:
+                   type = added_attribute['attribute_type']
+               elif 'attribute_type_id' in added_attribute:
+                   type = added_attribute['attribute_type_id']
+               else:
+                   raise PLCInvalidArgument, "Must specify attribute_type or attribute_type_id"
+
+               if 'value' in added_attribute:
+                   value = added_attribute['value']
+               else:
+                   raise PLCInvalidArgument, "Must specify a value"
+               
+               if 'node_id' in added_attribute:
+                   node_id = added_attribute['node_id']
+               else:
+                   node_id = None
+
+               if 'nodegroup_id' in added_attribute:
+                   nodegroup_id = added_attribute['nodegroup_id']
+               else:
+                   nodegroup_id = None 
+               AddSliceAttribute.__call__(AddSliceAttribute(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
+           for updated_attribute in updated_attributes:
+               attribute_id = updated_attribute.pop('slice_attribute_id')
+               if attribute_id not in self['slice_attribute_ids']:
+                   raise PLCInvalidArgument, "Attribute doesnt belong to this slice" 
+               else:
+                   UpdateSliceAttribute.__call__(UpdateSliceAttribute(self.api), auth, attribute_id, updated_attribute)                 
+       
     def sync(self, commit = True):
         """
         Add or update a slice.
@@ -126,6 +254,7 @@ class Slice(Row):
         self['is_deleted'] = True
         self.sync(commit)
 
+
 class Slices(Table):
     """
     Representation of row(s) from the slices table in the
@@ -151,16 +280,16 @@ class Slices(Table):
                 ints = filter(lambda x: isinstance(x, (int, long)), slice_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), slice_filter)
                 slice_filter = Filter(Slice.fields, {'slice_id': ints, 'name': strs})
-                sql += " AND (%s)" % slice_filter.sql(api, "OR")
+                sql += " AND (%s) %s" % slice_filter.sql(api, "OR")
             elif isinstance(slice_filter, dict):
                 slice_filter = Filter(Slice.fields, slice_filter)
-                sql += " AND (%s)" % slice_filter.sql(api, "AND")
-           elif isinstance (slice_filter, StringTypes):
+                sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
+            elif isinstance (slice_filter, StringTypes):
                 slice_filter = Filter(Slice.fields, {'name':[slice_filter]})
-                sql += " AND (%s)" % slice_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
             elif isinstance (slice_filter, int):
                 slice_filter = Filter(Slice.fields, {'slice_id':[slice_filter]})
-                sql += " AND (%s)" % slice_filter.sql(api, "AND")
+                sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
             else:
                 raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter
 
index 4e9ddab..2362d61 100644 (file)
@@ -50,6 +50,35 @@ class Row(dict):
             if value is not None and hasattr(self, 'validate_' + key):
                 validate = getattr(self, 'validate_' + key)
                 self[key] = validate(value)
+       
+    def separate_types(self, items):
+       """
+       Separate a list of different typed objects. 
+       Return a list for each type (ints, strs and dicts)
+       """
+       
+       if isinstance(items, (list, tuple, set)):
+           ints = filter(lambda x: isinstance(x, (int, long)), items)
+           strs = filter(lambda x: isinstance(x, StringTypes), items)
+           dicts = filter(lambda x: isinstance(x, dict), items)
+           return (ints, strs, dicts)          
+       else:
+           raise PLCInvalidArgument, "Can only separate list types" 
+               
+
+    def associate(self, *args):
+       """
+       Provides a means for high lvl api calls to associate objects
+        using low lvl calls.
+       """
+
+       if len(args) < 3:
+           raise PLCInvalidArgumentCount, "auth, field, value must be specified"
+       elif hasattr(self, 'associate_' + args[1]):
+           associate = getattr(self, 'associate_'+args[1])
+           associate(*args)
+       else:
+           raise PLCInvalidArguemnt, "No such associate function associate_%s" % args[1]
 
     def validate_timestamp(self, timestamp, check_future = False):
         """
index addbafc..4867119 100644 (file)
@@ -5,7 +5,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Test.py,v 1.3 2007/09/12 17:52:27 tmack Exp $
+# $Id: Test.py 5574 2007-10-25 20:33:17Z thierry $
 #
 
 from pprint import pprint
index 5d41de2..d5ddda8 100644 (file)
@@ -1,7 +1,7 @@
 all = """
-API
-AddressTypes
 Addresses
+AddressTypes
+API
 Auth
 Boot
 BootStates
@@ -14,33 +14,35 @@ Faults
 Filter
 GPG
 InitScripts
-KeyTypes
 Keys
+KeyTypes
 Messages
 Method
 NetworkMethods
 NetworkTypes
 NodeGroups
-NodeNetworkSettingTypes
 NodeNetworkSettings
+NodeNetworkSettingTypes
 NodeNetworks
 Nodes
-PCUs
-POD
 Parameter
+PCUProtocolTypes
+PCUs
+PCUTypes
 Peers
 Persons
+POD
 PostgreSQL
 PyCurl
 Roles
+sendmail
 Sessions
 Shell
 Sites
-SliceAttributeTypes
 SliceAttributes
+SliceAttributeTypes
 SliceInstantiations
 Slices
 Table
 Test
-sendmail
 """.split()