bug fixes
authorTony Mack <tmack@paris.CS.Princeton.EDU>
Thu, 11 Oct 2012 18:09:06 +0000 (14:09 -0400)
committerTony Mack <tmack@paris.CS.Princeton.EDU>
Thu, 11 Oct 2012 18:09:06 +0000 (14:09 -0400)
27 files changed:
PLC/API.py
PLC/GPG.py
PLC/InterfaceTags.py
PLC/Keys.py
PLC/Methods/AddNode.py
PLC/Methods/AddNodeGroup.py
PLC/Methods/AddPCUType.py
PLC/Methods/AddRoleToPerson.py
PLC/Methods/BootNotifyOwners.py
PLC/Methods/DeleteRoleFromTagType.py
PLC/Methods/GetBootMedium.py
PLC/Methods/GetNodeFlavour.py
PLC/Methods/GetPersons.py
PLC/Methods/GetSliceFamily.py
PLC/Methods/GetSlivers.py
PLC/Methods/UpdateKey.py
PLC/Methods/UpdateNode.py
PLC/Methods/UpdatePerson.py
PLC/Methods/UpdateSite.py
PLC/Methods/UpdateSlice.py
PLC/NodeGroups.py
PLC/Nodes.py
PLC/PCUTypes.py
PLC/PersonTags.py
PLC/Shell.py
PLC/Slices.py
PLCAPI.spec

index 16d3a61..cd0520b 100644 (file)
@@ -171,7 +171,8 @@ class PLCAPI:
             module = __import__(fullpath, globals(), locals(), [classname])
             return getattr(module, classname)(self)
         except ImportError, AttributeError:
-            raise PLCInvalidAPIMethod, "import error %s for %s" % (AttributeError,fullpath)
+            raise
+            #raise PLCInvalidAPIMethod, "import error %s for %s" % (AttributeError,fullpath)
 
     def call(self, source, method, *args):
         """
index ffecff0..f239741 100644 (file)
@@ -14,7 +14,8 @@ import shutil
 from types import StringTypes
 from StringIO import StringIO
 from xml.dom import minidom
-from xml.dom.ext import Canonicalize
+import lxml
+from lxml import etree
 from subprocess import Popen, PIPE, call
 from tempfile import NamedTemporaryFile, mkdtemp
 
@@ -28,17 +29,10 @@ def canonicalize(args, methodname = None, methodresponse = False):
     """
 
     xml = xmlrpclib.dumps(args, methodname, methodresponse, encoding = 'utf-8', allow_none = 1)
-    dom = minidom.parseString(xml)
-
-    # Canonicalize(), though it claims to, does not encode unicode
-    # nodes to UTF-8 properly and throws an exception unless you write
-    # the stream to a file object, so just encode it ourselves.
-    buf = StringIO()
-    Canonicalize(dom, output = buf)
-    xml = buf.getvalue().encode('utf-8')
-
-    return xml
-
+    parser = etree.XMLParser(remove_blank_text=True)        
+    tree = etree.parse(StringIO(xml), parser)
+    return etree.tostring(tree.getroot(), method='c14n')
 def gpg_export(keyring, armor = True):
     """
     Exports the specified public keyring file.
index 4ed6e96..3c0644e 100644 (file)
@@ -56,7 +56,7 @@ class InterfaceTags(list):
         if isinstance(interface_tag_filter, (list, tuple, set, int, long)):
             interface_tags = InterfaceTag().select(filter={'interface_tag_id': interface_tag_filter})
         elif isinstance(interface_tag_filter, dict):
-            interface_tags = InterfaceTag().select(filter=interface_tag_filter})
+            interface_tags = InterfaceTag().select(filter=interface_tag_filter)
         else:
             raise PLCInvalidArgument, "Wrong interface setting filter %r"%interface_tag_filter
 
index 6f82838..c174470 100644 (file)
@@ -6,9 +6,9 @@ from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
 from PLC.KeyTypes import KeyType, KeyTypes
-from PLC.NovaTable import NovaObject, NovaTable
+from PLC.Storage.AlchemyObject import AlchemyObj
 
-class Key(NovaObject):
+class Key(AlchemyObj):
     """
     Representation of a row in the keys table. To use, instantiate with a
     dict of values. Update as you would a dict. Commit to the database
@@ -16,24 +16,30 @@ class Key(NovaObject):
     """
 
     tablename = 'keys'
-    join_tables = ['person_key', 'peer_key']
     fields = {
-        'id': Parameter(str, "Key identifier", primary_key=True),
-        #'key_type': Parameter(str, "Key type"),
-        'public_key': Parameter(str, "Key string", max = 4096),
-        'name': Parameter(str, "Key name",)
+        'key_id': Parameter(str, "Key identifier", primary_key=True),
+        'key_type': Parameter(str, "Key type"),
+        'key': Parameter(str, "Key string", max = 4096), # backwards compatibility
+        'name': Parameter(str, "Key name"),
+        'uuid': Parameter(str, "Key name", ro=True)
         }
 
-    def sync(self, insert = False, validate = True):
-        NovaObject.sync(self, insert, validate)
-        if insert == True or 'id' not in self:
-            self.object = self.api.client_shell.nova.keypairs.create(self.id,
-                                                                     self.key)
-        else:
-            self.object = self.api.client_shell.nova.keypairs.update(self.id, dict(self))
+    def sync(self, commit = True, validate = True):
+        # sync the nova record and the plc record
+        assert 'key' in self
+        assert 'name' in self
+        AlchemyObj.sync(self, commit=commit, validate=validate)
+        nova_key = {
+            'public_key': self['key'],
+            'name': self['name'] 
+        }
+        self.object = self.api.client_shell.nova.keypairs.create(**nova_key)
+        self['uuid'] = self.object.uuid
+        AlchemyObj.insert(self, dict(self))
 
     def delete(self):
-        assert 'id' in self
+        assert 'key_id' in self
+        assert 'name' in self
         self.api.client_shell.nova.keypairs.delete(self.id)
 
     def validate_public_key(self, key):
@@ -79,26 +85,9 @@ class Key(NovaObject):
         assert 'key_id' in self
         assert 'key' in self
 
-        # Get all matching keys
-        rows = self.api.db.selectall("SELECT key_id FROM keys WHERE key = %(key)s",
-                                     self)
-        key_ids = [row['key_id'] for row in rows]
-        assert key_ids
-        assert self['key_id'] in key_ids
-
-        # Keep the keys in the table
-        self.api.db.do("UPDATE keys SET is_blacklisted = True" \
-                       " WHERE key_id IN (%s)" % ", ".join(map(str, key_ids)))
-
-        # But disassociate them from all join tables
-        for table in self.join_tables:
-            self.api.db.do("DELETE FROM %s WHERE key_id IN (%s)" % \
-                           (table, ", ".join(map(str, key_ids))))
-
-        if commit:
-            self.api.db.commit()
+        pass
 
-class Keys(NovaTable):
+class Keys(list):
     """
     Representation of row(s) from the keys table in the
     database.
index 3a5ce90..830444e 100644 (file)
@@ -10,7 +10,8 @@ from PLC.NodeTags import NodeTags, NodeTag
 from PLC.Methods.AddNodeTag import AddNodeTag
 from PLC.Methods.UpdateNodeTag import UpdateNodeTag
 
-can_update = ['hostname', 'node_type', 'boot_state', 'model', 'version']
+can_update = lambda (field, value): field in \
+            ['hostname', 'node_type', 'boot_state', 'model', 'version']
 
 class AddNode(Method):
     """
@@ -25,8 +26,7 @@ class AddNode(Method):
 
     roles = ['admin', 'pi', 'tech']
 
-    accepted_fields = Row.accepted_fields(can_update,Node.fields)
-    accepted_fields.update(Node.tags)
+    accepted_fields = dict(filter(can_update, Node.fields.items()))
 
     accepts = [
         Auth(),
@@ -39,13 +39,6 @@ class AddNode(Method):
 
     def call(self, auth, site_id_or_login_base, node_fields):
 
-        [native,tags,rejected]=Row.split_fields(node_fields,[Node.fields,Node.tags])
-
-        # type checking
-        native = Row.check_fields(native, self.accepted_fields)
-        if rejected:
-            raise PLCInvalidArgument, "Cannot add Node with column(s) %r"%rejected
-
         # Get site information
         sites = Sites(self.api, [site_id_or_login_base])
         if not sites:
@@ -65,13 +58,15 @@ class AddNode(Method):
             else:
                 assert self.caller['person_id'] in site['person_ids']
 
-        node = Node(self.api, native)
+        node_fields = dict(filter(can_update, node_fields.items()))
+        node = Node(self.api, node_fields)
         node['site_id'] = site['site_id']
         node.sync()
 
         # since hostname was specified lets add the 'hrn' node tag
         root_auth = self.api.config.PLC_HRN_ROOT
         login_base = site['login_base']
+        tags={}
         tags['hrn'] = hostname_to_hrn(root_auth, login_base, node['hostname'])
 
         for (tagname,value) in tags.iteritems():
@@ -93,8 +88,5 @@ class AddNode(Method):
                 node_tag['value'] = value
                 node_tag.sync() 
 
-        self.event_objects = {'Site': [site['site_id']],
-                              'Node': [node['node_id']]}
-        self.message = "Node %d=%s created" % (node['node_id'],node['hostname'])
 
         return node['node_id']
index a24fa8e..505327f 100644 (file)
@@ -7,7 +7,7 @@ from PLC.NodeGroups import NodeGroup, NodeGroups
 from PLC.TagTypes import TagType, TagTypes
 from PLC.NodeTags import NodeTag, NodeTags
 
-can_update = lambda (field, value): field in NodeGroup.fields.keys() and field != NodeGroup.primary_field
+can_update = lambda (field, value): field in NodeGroup.fields.keys() and field not in [NodeGroup.fields['nodegroup_id']] 
 
 class AddNodeGroup(Method):
     """
index 2c8fbe5..1037883 100644 (file)
@@ -30,6 +30,5 @@ class AddPCUType(Method):
         pcu_type_fields = dict(filter(can_update, pcu_type_fields.items()))
         pcu_type = PCUType(self.api, pcu_type_fields)
         pcu_type.sync()
-        self.event_object = {'PCUType': [pcu_type['pcu_type_id']]}
 
         return pcu_type['pcu_type_id']
index 1e47033..3fc1df3 100644 (file)
@@ -19,7 +19,7 @@ class AddRoleToPerson(Method):
 
     accepts = [
         Auth(),
-        Mixed(Role.fields['role_id'],
+        Mixed(Role.fields['id'],
               Role.fields['name']),
         Mixed(Person.fields['person_id'],
               Person.fields['email']),
@@ -40,9 +40,6 @@ class AddRoleToPerson(Method):
             raise PLCInvalidArgument, "No such account"
         person = persons[0]
 
-        if person['peer_id'] is not None:
-            raise PLCInvalidArgument, "Not a local account"
-
         # Authenticated function
         assert self.caller is not None
 
@@ -58,9 +55,4 @@ class AddRoleToPerson(Method):
         if role['role_id'] not in person['role_ids']:
             person.add_role(role)
 
-        self.event_objects = {'Person': [person['person_id']],
-                              'Role': [role['role_id']]}
-        self.message = "Role %d granted to person %d" % \
-                       (role['role_id'], person['person_id'])
-
         return 1
index 7458b9d..13799f7 100644 (file)
@@ -1,6 +1,6 @@
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
-from PLC.Auth import Auth, BootAuth, SessionAuth
+from PLC.Auth import Auth
 from PLC.Nodes import Node, Nodes
 from PLC.Messages import Message, Messages
 
index cdce6fa..db009d2 100644 (file)
@@ -19,7 +19,7 @@ class DeleteRoleFromTagType(Method):
 
     accepts = [
         Auth(),
-        Mixed(Role.fields['role_id'],
+        Mixed(Role.fields['id'],
               Role.fields['name']),
         Mixed(TagType.fields['tag_type_id'],
               TagType.fields['tagname']),
index 7225b12..b1380db 100644 (file)
@@ -14,7 +14,7 @@ from PLC.Interfaces import Interface, Interfaces
 from PLC.InterfaceTags import InterfaceTag, InterfaceTags
 from PLC.NodeTags import NodeTag, NodeTags
 
-from PLC.Accessors.Accessors_standard import *                  # import node accessors
+#from PLC.Accessors.Accessors_standard import *                  # import node accessors
 
 # could not define this in the class..
 # create a dict with the allowed actions for each type of node
index 92c0fc4..9800f41 100644 (file)
@@ -4,8 +4,6 @@ from PLC.Faults import *
 from PLC.Parameter import *
 from PLC.Nodes import Node, Nodes
 
-from PLC.Accessors.Accessors_standard import *                  # import node accessors
-
 class GetNodeFlavour(Method):
     """
     Returns detailed information on a given node's flavour, i.e. its
index 07a0cd0..6725f33 100644 (file)
@@ -24,7 +24,7 @@ class GetPersons(Method):
 
     accepts = [
         Auth(),
-        Mixed(Person.fields['id'],
+        Mixed(Person.fields['person_id'],
               Parameter(str,"email"),
               Filter(Person.fields)),
         Parameter([str], "List of fields to return", nullok = True)
index 2a6e830..8167fbd 100644 (file)
@@ -3,9 +3,7 @@ from PLC.Auth import Auth
 from PLC.Faults import *
 from PLC.Parameter import *
 from PLC.Slices import Slice, Slices
-
-from PLC.Accessors.Accessors_standard import *                  # import slice accessors
-from PLC.Accessors.Accessors_sliverauth import *                # import slice accessors
+from PLC.SliceTags import SliceTag, SliceTags
 
 class GetSliceFamily(Method):
     """
@@ -30,24 +28,28 @@ class GetSliceFamily(Method):
     ### system slices - at least planetflow - still rely on 'vref'
     #
     def call(self, auth, slice_id_or_name):
+        # default values
+        arch = self.api.config.PLC_FLAVOUR_SLICE_ARCH
+        pldistro = self.api.config.PLC_FLAVOUR_SLICE_PLDISTRO
+        fcdistro = self.api.config.PLC_FLAVOUR_SLICE_FCDISTRO
+        vref = None
         # Get slice information
         slices = Slices(self.api, [slice_id_or_name])
         if not slices:
             raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
         slice = slices[0]
-        slice_id = slice['slice_id']
-
-        arch = GetSliceArch (self.api,self.caller).call(auth,slice_id)
-        if not arch: arch = self.api.config.PLC_FLAVOUR_SLICE_ARCH
-
-        pldistro = GetSlicePldistro (self.api,self.caller).call(auth, slice_id)
-        if not pldistro: pldistro = self.api.config.PLC_FLAVOUR_SLICE_PLDISTRO
-
-        fcdistro = GetSliceFcdistro (self.api,self.caller).call(auth, slice_id)
-        if not fcdistro: fcdistro = self.api.config.PLC_FLAVOUR_SLICE_FCDISTRO
-
-        # the vref tag, if set, wins over pldistro
-        vref = GetSliceVref (self.api,self.caller).call(auth,slice_id)
+        slice_tags = SliceTags(self.api, slice['slice_tag_ids'])
+       
+        for slice_tag in slice_tags:
+            if slice_tag['tagname'] == 'arch':
+                arch = slice_tag['value']
+            elif slice_tag['tagname'] == 'pldistro':
+                pldistro = slice_tag['value']
+            elif slice_tag['tagname'] == 'fcdistro':
+                fcdistro = slice_tag['value']
+            elif slice_tag['tagname'] == 'vref':
+                vref = slice_tag['value']
 
         # omf-control'ed slivers need the omf vserver reference image
         # we used to issue SetSliceVref (self.api) (auth,slice_id,'omf')
index 1f65e1e..84768e5 100644 (file)
@@ -21,8 +21,6 @@ from PLC.Timestamp import Duration
 from PLC.Methods.GetSliceFamily import GetSliceFamily
 from PLC.PersonTags import PersonTag,PersonTags
 
-from PLC.Accessors.Accessors_standard import *
-
 # XXX used to check if slice expiration time is sane
 MAXINT =  2L**31-1
 
index 870fca7..b858ddd 100644 (file)
@@ -30,26 +30,4 @@ class UpdateKey(Method):
     returns = Parameter(int, '1 if successful')
 
     def call(self, auth, key_id, key_fields):
-        key_fields = dict(filter(can_update, key_fields.items()))
-
-        # Get key information
-        keys = Keys(self.api, [key_id])
-        if not keys:
-            raise PLCInvalidArgument, "No such key"
-        key = keys[0]
-
-        if key['peer_id'] is not None:
-            raise PLCInvalidArgument, "Not a local key"
-
-        if 'admin' not in self.caller['roles']:
-            if key['key_id'] not in self.caller['key_ids']:
-                raise PLCPermissionDenied, "Key must be associated with one of your accounts"
-
-        key.update(key_fields)
-        key.sync()
-
-        # Logging variables
-        self.event_objects = {'Key': [key['key_id']]}
-        self.message = 'key %d updated: %s' % \
-                (key['key_id'], ", ".join(key_fields.keys()))
         return 1
index c09e10c..18207f8 100644 (file)
@@ -29,9 +29,6 @@ class UpdateNode(Method):
     roles = ['admin', 'pi', 'tech']
 
     accepted_fields = Row.accepted_fields(can_update,Node.fields)
-    # xxx check the related_fields feature
-    accepted_fields.update(Node.related_fields)
-    accepted_fields.update(Node.tags)
 
     accepts = [
         Auth(),
@@ -44,22 +41,14 @@ class UpdateNode(Method):
 
     def call(self, auth, node_id_or_hostname, node_fields):
 
-        # split provided fields
-        [native,related,tags,rejected] = Row.split_fields(node_fields,[Node.fields,Node.related_fields,Node.tags])
-
-        # type checking
-        native = Row.check_fields (native, self.accepted_fields)
-        if rejected:
-            raise PLCInvalidArgument, "Cannot update Node column(s) %r"%rejected
-
         # Authenticated function
         assert self.caller is not None
 
         # Remove admin only fields
         if 'admin' not in self.caller['roles']:
             for key in admin_only:
-                if native.has_key(key):
-                    del native[key]
+                if node_fields.has_key(key):
+                    del node_fields[key]
 
         # Get account information
         nodes = Nodes(self.api, [node_id_or_hostname])
@@ -67,21 +56,13 @@ class UpdateNode(Method):
             raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname
         node = nodes[0]
 
-        if node['peer_id'] is not None:
-            raise PLCInvalidArgument, "Not a local node %r"%node_id_or_hostname
-
         # If we are not an admin, make sure that the caller is a
         # member of the site at which the node is located.
         if 'admin' not in self.caller['roles']:
             if node['site_id'] not in self.caller['site_ids']:
                 raise PLCPermissionDenied, "Not allowed to delete nodes from specified site"
 
-        # Make requested associations
-        for (k,v) in related.iteritems():
-            node.associate(auth, k,v)
-
-        node.update(native)
-        node.update_last_updated(commit=False)
+        node.update(node_fields)
         node.sync(commit=True)
 
         # if hostname was modifed make sure to update the hrn
@@ -112,14 +93,4 @@ class UpdateNode(Method):
                 node_tag = node_tags[0]
                 node_tag['value'] = value
                 node_tag.sync()
-        # Logging variables
-        self.event_objects = {'Node': [node['node_id']]}
-        if 'hostname' in node:
-            self.message = 'Node %s updated'%node['hostname']
-        else:
-            self.message = 'Node %d updated'%node['node_id']
-        self.message += " [%s]." % (", ".join(node_fields.keys()),)
-        if 'boot_state' in node_fields.keys():
-            self.message += ' boot_state updated to %s' % node_fields['boot_state']
-
         return 1
index 79c1900..52462b3 100644 (file)
@@ -5,11 +5,10 @@ from PLC.Persons import Person, Persons
 from PLC.Auth import Auth
 from PLC.sendmail import sendmail
 
-related_fields = Person.related_fields.keys()
 can_update = lambda (field, value): field in \
              ['first_name', 'last_name', 'title', 'email',
               'password', 'phone', 'url', 'bio', 'accepted_aup',
-              'enabled'] + related_fields
+              'enabled'] 
 
 class UpdatePerson(Method):
     """
@@ -24,7 +23,7 @@ class UpdatePerson(Method):
 
     roles = ['admin', 'pi', 'user', 'tech']
 
-    person_fields = dict(filter(can_update, Person.fields.items() + Person.related_fields.items()))
+    person_fields = dict(filter(can_update, Person.fields.items() ))
 
     accepts = [
         Auth(),
index 936fdae..b50890a 100644 (file)
@@ -24,7 +24,7 @@ class UpdateSite(Method):
 
     accepts = [
         Auth(),
-        Site.fields['id'],
+        Site.fields['site_id'],
         site_fields
         ]
 
@@ -45,7 +45,7 @@ class UpdateSite(Method):
         # If we are not an admin, make sure that the caller is a
         # member of the site.
         if 'admin' not in self.caller['roles']:
-            if site['id'] not in self.caller['tenant_id']:
+            if site['site_id'] not in self.caller['site_id']:
                 raise PLCPermissionDenied, "Not allowed to modify specified site"
 
             # Remove admin only fields
index 9129ba0..5ebca66 100644 (file)
@@ -34,9 +34,6 @@ class UpdateSlice(Method):
     roles = ['admin', 'pi', 'user']
 
     accepted_fields = Row.accepted_fields(can_update, Slice.fields)
-    # xxx check the related_fields feature
-    accepted_fields.update(Slice.related_fields)
-    accepted_fields.update(Slice.tags)
 
     accepts = [
         Auth(),
@@ -49,14 +46,6 @@ class UpdateSlice(Method):
 
     def call(self, auth, slice_id_or_name, slice_fields):
 
-        # split provided fields
-        [native,related,tags,rejected] = Row.split_fields(slice_fields,[Slice.fields,Slice.related_fields,Slice.tags])
-
-        # type checking
-        native = Row.check_fields (native, self.accepted_fields)
-        if rejected:
-            raise PLCInvalidArgument, "Cannot update Slice column(s) %r"%rejected
-
         slices = Slices(self.api, [slice_id_or_name])
         if not slices:
             raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
@@ -110,34 +99,7 @@ class UpdateSlice(Method):
                'pi' not in self.caller['roles']:
                 raise PLCInvalidArgument, "Only admins and PIs may update max_nodes"
 
-        # Make requested associations
-        for (k,v) in related.iteritems():
-            slice.associate(auth,k,v)
-
         slice.update(slice_fields)
         slice.sync(commit=True)
 
-        for (tagname,value) in tags.iteritems():
-            # the tagtype instance is assumed to exist, just check that
-            if not TagTypes(self.api,{'tagname':tagname}):
-                raise PLCInvalidArgument,"No such TagType %s"%tagname
-            slice_tags=SliceTags(self.api,{'tagname':tagname,'slice_id':slice['slice_id']})
-            if not slice_tags:
-                AddSliceTag(self.api).__call__(auth,slice['slice_id'],tagname,value)
-            else:
-                UpdateSliceTag(self.api).__call__(auth,slice_tags[0]['slice_tag_id'],value)
-
-        self.event_objects = {'Slice': [slice['slice_id']]}
-        if 'name' in slice:
-            self.message='Slice %s updated'%slice['name']
-        else:
-            self.message='Slice %d updated'%slice['slice_id']
-        if renewing:
-            # it appears that slice['expires'] may be either an int, or a formatted string
-            try:
-                expire_date=time.strftime('%Y-%m-%d:%H:%M',time.localtime(float(slice['expires'])))
-            except:
-                expire_date=slice['expires']
-            self.message += ' renewed until %s'%expire_date
-
         return 1
index bbe20a8..01a28d3 100644 (file)
@@ -11,7 +11,7 @@ from PLC.Debug import profile
 from PLC.Storage.AlchemyObject import AlchemyObj
 from PLC.Nodes import Node, Nodes
 
-class NodeGroup(Row):
+class NodeGroup(AlchemyObj):
     """
     Representation of a row in the nodegroups table. To use, optionally
     instantiate with a dict of values. Update as you would a
@@ -21,7 +21,7 @@ class NodeGroup(Row):
     tablename = 'nodegroups'
     join_tables = ['conf_file_nodegroup']
     fields = {
-        'nodegroup_id': Parameter(int, "Node group identifier", primary_key),
+        'nodegroup_id': Parameter(int, "Node group identifier", primary_key=True),
         'groupname': Parameter(str, "Node group name", max = 50),
         'tag_type_id': Parameter (int, "Node tag type id"),
         'value' : Parameter(str, "value that the nodegroup definition is based upon"),
@@ -29,8 +29,6 @@ class NodeGroup(Row):
         'conf_file_ids': Parameter([int], "List of configuration files specific to this node group", joined=True),
         'node_ids' : Parameter([int], "List of node_ids that belong to this nodegroup", joined=True),
         }
-    related_fields = {
-        }
 
     def validate_name(self, name):
         # Make sure name is not blank
index ba93906..2932d1a 100644 (file)
@@ -121,14 +121,14 @@ class Node(AlchemyObj):
         """
         Update col_name field with current time
         """
-
         assert 'node_id' in self
-        assert self.table_name
-
-        self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \
-                       " where node_id = %d" % (self['node_id']) )
-        self.sync(commit)
-
+        self[col_name] = datetime.now()
+        fields = {
+            'node_id': self['node_id'],         
+            col_name: datetime.now()
+        } 
+        Node(self.api, fields).sync()
+        
     def update_last_boot(self, commit = True):
         self.update_timestamp('last_boot', commit)
     def update_last_download(self, commit = True):
index bd41936..554da90 100644 (file)
@@ -9,7 +9,7 @@ from types import StringTypes
 
 from PLC.Faults import *
 from PLC.Parameter import Parameter
-from PLC.Storage.AlchemyObj import AlchemyObj
+from PLC.Storage.AlchemyObject import AlchemyObj
 
 class PCUType(AlchemyObj):
     """
index 5581a69..f6beefc 100644 (file)
@@ -16,7 +16,7 @@ class PersonTag(AlchemyObj):
     tablename = 'person_tag'
     fields = {
         'person_tag_id': Parameter(int, "Person setting identifier", primary_key=True),
-        'person_id': Person.fields['id'],
+        'person_id': Person.fields['person_id'],
         'email': Person.fields['email'],
         'tag_type_id': TagType.fields['tag_type_id'],
         'tagname': TagType.fields['tagname'],
index d6c3a54..fb401d3 100644 (file)
@@ -144,10 +144,6 @@ class Shell:
             if role is not None:
                 self.auth['Role'] = role
 
-        self.load_methods()
-    def load_methods(self):
-
         for method in PLC.API.PLCAPI.all_methods:
             api_function = self.api.callable(method)
 
index a79f86f..1219c2e 100644 (file)
@@ -71,11 +71,58 @@ class Slice(AlchemyObj):
         check_future = not ('is_deleted' in self and self['is_deleted'])
         return Timestamp.sql_validate( expires, check_future = check_future)
 
-    #add_person = Row.add_object(Person, 'slice_person')
-    #remove_person = Row.remove_object(Person, 'slice_person')
+    def add_person(self, person_filter, role_name=None):
+        assert 'slice_id' in self
+        assert 'tenant_id' in self
+        if not role_name:
+            role_name = 'user'
+        roles = Roles(self.api, role_name)
+        if not roles:
+            raise PLCInvalidArgument, "No such role %s" % role_name
+        role = roles[0]
+        tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
+        persons = Persons(self.api, person_filter)
+        for person in persons:
+            keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
+            tenant.add_user(keystone_user, role.object)
+            slice_person = SlicePerson(self.api, {'slice_id': self['slice_id'],
+                                                  'person_id': person['person_id']})
+            slice_person.sync()
+
+    def remove_person(self, person_filter, role=None):
+        assert 'slice_id' in self
+        assert 'tenant_id' in self
+        if not role_name:
+            role_name = 'user'
+        roles = Roles(self.api, role_name)
+        if not roles:
+            raise PLCInvalidArgument, "No such role %s" % role_name
+        role = roles[0]
+        tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
+        persons = Persons(self.api, person_filter)
+        for person in persons:
+            keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
+            tenant.remove_user(keystone_user, role.object)
+            slice_person = SlicePerson(self.api, {'slice_id': self['slice_id'],
+                                                  'person_id': person['person_id']})
+            slice_person.delete()
 
-    #add_node = Row.add_object(Node, 'slice_node')
-    #remove_node = Row.remove_object(Node, 'slice_node')
+    def add_node(self, node_filter):
+        assert 'slice_id' in self
+        nodes = Nodes(self.api, node_filter)
+        for node in nodes:
+            slice_node = SliceNode(self.api, {'slice_id': self['slice_id'],
+                                              'node_id': node['node_id']})
+            slice_node.sync()
+    def remove_node(self, node_filter):
+        assert 'slice_id' in self
+        nodes = Nodes(self.api, node_filter)
+        for node in nodes:
+            slice_node = SliceNode(self.api, {'slice_id': self['slice_id'],
+                                              'node_id': node['node_id']})
+            slice_node.delete()
 
     #add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
     #delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
index 28c968a..8922f66 100644 (file)
@@ -25,6 +25,7 @@ Requires: python >= 2.4
 Requires: python-pycurl
 Requires: httpd
 Requires: python-gevent
+Requires: python-lxml
 # mod_wsgi will replace mod_python when we are ready
 Requires: mod_wsgi
 Requires: mod_ssl