Slice tags can be managed from AddSlice/UpdateSlice/GetSlices
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Thu, 11 Dec 2008 18:29:31 +0000 (18:29 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Thu, 11 Dec 2008 18:29:31 +0000 (18:29 +0000)
SliceTags now can access the slice name
Set accessors occurences logged in Events
Other minor improvements in some log messages (show names rather than ids as often as possible)

PLC/Accessors/Accessors_standard.py
PLC/Accessors/Factory.py
PLC/Methods/AddNode.py
PLC/Methods/AddSlice.py
PLC/Methods/UpdateInterface.py
PLC/Methods/UpdateNode.py
PLC/Methods/UpdateSlice.py
PLC/SliceTags.py
PLC/Slices.py
planetlab5.sql

index f158273..db7858f 100644 (file)
@@ -20,7 +20,7 @@ current_module = sys.modules[__name__]
 # xxx - don't expose yet in api interface and slices dont know how to use that yet
 define_accessors(current_module, Slice, "Vref", "vref", 
                  "slice/config", "vserver reference image type",
-                 get_roles=all_roles, set_roles=["admin"], expose_in_api=False)
+                 get_roles=all_roles, set_roles=["admin"], expose_in_api=True)
 
 
 # node architecture 
index cd526cc..4d0d506 100644 (file)
@@ -30,7 +30,7 @@ taggable_classes = { Node : {'table_class' : Nodes,
                                   'secondary_key' : 'ip'},
                      Slice: {'table_class' : Slices, 
                              'joins_class': SliceTags, 'join_class': SliceTag,
-                             'secondary_key':'login_base'},
+                             'secondary_key':'name'},
 #                     Ilink : xxx
                      }
 
@@ -81,12 +81,8 @@ def define_accessors (module, objclass, methodsuffix, tagname,
     # accepts 
     get_accepts = [ Auth () ]
     primary_key=objclass.primary_key
-    try:
-        secondary_key = taggable_classes[objclass]['secondary_key']
-        get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
-    except:
-        secondary_key = None
-        get_accepts += [ objclass.fields[primary_key] ]
+    secondary_key = taggable_classes[objclass]['secondary_key']
+    get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
     # for set, idem set of arguments + one additional arg, the new value
     set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
 
@@ -138,7 +134,7 @@ def define_accessors (module, objclass, methodsuffix, tagname,
             filter={primary_key:id_or_name}
         else:
             filter={secondary_key:id_or_name}
-        objs = table_class(self.api, filter,[primary_key])
+        objs = table_class(self.api, filter,[primary_key,secondary_key])
         if not objs:
             raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
         primary_id = objs[0][primary_key]
@@ -180,6 +176,14 @@ def define_accessors (module, objclass, methodsuffix, tagname,
             if joins:
                 join=joins[0]
                 join.delete()
+        # log it
+        self.event_objects= { objclass.__name__ : [primary_id] }
+        self.message=objclass.__name__
+        if secondary_key in objs[0]:
+            self.message += " %s "%objs[0][secondary_key]
+        else:
+            self.message += " %d "%objs[0][primary_key]
+        self.message += "updated"
 
     # attach it
     setattr (set_class,"call",set_call)
index 9e4a945..e8d073a 100644 (file)
@@ -68,10 +68,6 @@ class AddNode(Method):
         node['site_id'] = site['site_id']
         node.sync()
 
-       self.event_objects = {'Site': [site['site_id']],
-                            'Node': [node['node_id']]} 
-       self.message = "Node %s created" % node['node_id']
-
         for (tagname,value) in tags.iteritems():
             # the tagtype instance is assumed to exist, just check that
             if not TagTypes(self.api,{'tagname':tagname}):
@@ -82,4 +78,8 @@ class AddNode(Method):
             else:
                 UpdateNodeTag(self.api).__call__(auth,node_tags[0]['node_tag_id'],value)
 
+       self.event_objects = {'Site': [site['site_id']],
+                            'Node': [node['node_id']]} 
+       self.message = "Node %s created" % node['node_id']
+
         return node['node_id']
index 9b7e875..001e0f9 100644 (file)
@@ -2,14 +2,19 @@
 import re
 
 from PLC.Faults import *
+from PLC.Auth import Auth
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
+from PLC.Table import Row
+
 from PLC.Slices import Slice, Slices
-from PLC.Auth import Auth
 from PLC.Sites import Site, Sites
+from PLC.TagTypes import TagTypes
+from PLC.SliceTags import SliceTags
+from PLC.Methods.AddSliceTag import AddSliceTag
+from PLC.Methods.UpdateSliceTag import UpdateSliceTag
 
-can_update = lambda (field, value): field in \
-             ['name', 'instantiation', 'url', 'description', 'max_nodes']
+can_update = ['name', 'instantiation', 'url', 'description', 'max_nodes']
 
 class AddSlice(Method):
     """
@@ -30,17 +35,24 @@ class AddSlice(Method):
 
     roles = ['admin', 'pi']
 
-    slice_fields = dict(filter(can_update, Slice.fields.items()))
+    accepted_fields = Row.accepted_fields(can_update, [Slice.fields,Slice.tags])
 
     accepts = [
         Auth(),
-        slice_fields
+        accepted_fields
         ]
 
     returns = Parameter(int, 'New slice_id (> 0) if successful')
 
     def call(self, auth, slice_fields):
-        slice_fields = dict(filter(can_update, slice_fields.items()))
+
+        [native,tags,rejected]=Row.split_fields(slice_fields,[Slice.fields,Slice.tags])
+
+        if rejected:
+            raise PLCInvalidArgument, "Cannot add Slice with column(s) %r"%rejected
+
+        # Authenticated function
+        assert self.caller is not None
 
         # 1. Lowercase.
         # 2. Begins with login_base (letters or numbers).
@@ -61,21 +73,32 @@ class AddSlice(Method):
 
         if 'admin' not in self.caller['roles']:
             if site['site_id'] not in self.caller['site_ids']:
-                raise PLCPermissionDenied, "Slice prefix %s must be the same as the login_base of one of your sites"%login_base
+                raise PLCPermissionDenied, "Slice prefix %s must match one of your sites' login_base"%login_base
 
         if len(site['slice_ids']) >= site['max_slices']:
-            raise PLCInvalidArgument, "Site %s has reached (%d) its maximum allowable slice count (%d)"%(site['name'],
-                                                                                                         len(site['slice_ids']),
-                                                                                                         site['max_slices'])
-
+            raise PLCInvalidArgument, \
+                "Site %s has reached (%d) its maximum allowable slice count (%d)"%(site['name'],
+                                                                                   len(site['slice_ids']),
+                                                                                   site['max_slices'])
        if not site['enabled']:
-           raise PLCInvalidArgument, "Site %s is disabled can cannot create slices" % (site['name'])
+           raise PLCInvalidArgument, "Site %s is disabled and can cannot create slices" % (site['name'])
         
-        slice = Slice(self.api, slice_fields)
+        slice = Slice(self.api, native)
         slice['creator_person_id'] = self.caller['person_id']
         slice['site_id'] = site['site_id']
         slice.sync()
 
+        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']]}
+        self.message = "Slice %d created" % slice['slice_id']
 
         return slice['slice_id']
index b7d7f30..dad6420 100644 (file)
@@ -82,7 +82,10 @@ class UpdateInterface(Method):
                 UpdateInterfaceTag(self.api).__call__(auth,interface_tags[0]['interface_tag_id'],value)
 
        self.event_objects = {'Interface': [interface['interface_id']]}
-       self.message = "Interface %d updated: %s " % \
-           (interface['interface_id'], ", ".join(interface_fields.keys()))
+        if 'ip' in interface:
+            self.message = "Interface %s updated"%interface['ip']
+        else:
+            self.message = "Interface %d updated"%interface['interface_id']
+        self.message += "[%s]." % ", ".join(interface_fields.keys())
 
         return 1
index 46e63be..d66dd9e 100644 (file)
@@ -90,9 +90,12 @@ class UpdateNode(Method):
 
        # Logging variables
        self.event_objects = {'Node': [node['node_id']]}
-       self.message = 'Node %d updated: %s.' % \
-               (node['node_id'], ", ".join(node_fields.keys()))
+        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']
+               self.message += ' boot_state updated to %s' % node_fields['boot_state']
 
         return 1
index e29ae33..b58a106 100644 (file)
@@ -4,15 +4,18 @@ import time
 from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
-from PLC.Slices import Slice, Slices
+from PLC.Table import Row
 from PLC.Auth import Auth
-from PLC.Sites import Site, Sites
 
-related_fields = Slice.related_fields.keys() 
-can_update = lambda (field, value): field in \
-             ['instantiation', 'url', 'description', 'max_nodes', 'expires'] + \
-            related_fields
+from PLC.Slices import Slice, Slices
+from PLC.Sites import Site, Sites
+from PLC.TagTypes import TagTypes
+from PLC.SliceTags import SliceTags
+from PLC.Methods.AddSliceTag import AddSliceTag
+from PLC.Methods.UpdateSliceTag import UpdateSliceTag
 
+can_update = ['instantiation', 'url', 'description', 'max_nodes', 'expires'] + \
+    Slice.related_fields.keys()
 
 class UpdateSlice(Method):
     """
@@ -32,7 +35,7 @@ class UpdateSlice(Method):
 
     roles = ['admin', 'pi', 'user']
 
-    slice_fields = dict(filter(can_update, Slice.fields.items() + Slice.related_fields.items()))
+    slice_fields = Row.accepted_fields(can_update, [Slice.fields,Slice.related_fields,Slice.tags])
 
     accepts = [
         Auth(),
@@ -44,16 +47,24 @@ class UpdateSlice(Method):
     returns = Parameter(int, '1 if successful')
 
     def call(self, auth, slice_id_or_name, slice_fields):
-        slice_fields = dict(filter(can_update, slice_fields.items()))
-        
+
+        # split provided fields 
+        [native,related,tags,rejected] = Row.split_fields(slice_fields,[Slice.fields,Slice.related_fields,Slice.tags])
+
+        if rejected:
+            raise PLCInvalidArgument, "Cannot update Node column(s) %r"%rejected
+
        slices = Slices(self.api, [slice_id_or_name])
         if not slices:
-            raise PLCInvalidArgument, "No such slice"
+            raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
         slice = slices[0]
 
         if slice['peer_id'] is not None:
             raise PLCInvalidArgument, "Not a local slice"
 
+        # Authenticated function
+        assert self.caller is not None
+
         if 'admin' not in self.caller['roles']:
             if self.caller['person_id'] in slice['person_ids']:
                 pass
@@ -63,12 +74,13 @@ class UpdateSlice(Method):
                 raise PLCPermissionDenied, "Specified slice not associated with any of your sites"
 
         # Renewing
+        renewing=False
         if 'expires' in slice_fields and slice_fields['expires'] > slice['expires']:
             sites = Sites(self.api, [slice['site_id']])
             assert sites
             site = sites[0]
 
-            if site['max_slices'] < 0:
+            if site['max_slices'] <= 0:
                 raise PLCInvalidArgument, "Slice creation and renewal have been disabled for the site"
 
             # Maximum expiration date is 8 weeks from now
@@ -88,6 +100,7 @@ class UpdateSlice(Method):
                if 'url' not in slice_fields or slice_fields['url'] is None or \
                   not slice_fields['url'].strip():
                     raise PLCInvalidArgument, "Cannot renew a slice with an empty description or URL"
+            renewing=True
            
         if 'max_nodes' in slice_fields and slice_fields['max_nodes'] != slice['max_nodes']:
             if 'admin' not in self.caller['roles'] and \
@@ -95,14 +108,28 @@ class UpdateSlice(Method):
                 raise PLCInvalidArgument, "Only admins and PIs may update max_nodes"
 
        # Make requested associations
-       for field in related_fields:
-           if field in slice_fields:
-               slice.associate(auth, field, slice_fields[field])
-               slice_fields.pop(field)
+        for (k,v) in related.iteritems():
+            slice.associate(auth,k,v)
 
        slice.update(slice_fields)
-        slice.sync()
+        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:
+            self.message += ' renewed until %s'%time.strftime('%Y-%m-%d:%H:%M',localtime(slice['expires']))
 
         return 1
index e7c8b8a..c966beb 100644 (file)
@@ -3,6 +3,10 @@ from PLC.Faults import *
 from PLC.Parameter import Parameter
 from PLC.Filter import Filter
 from PLC.Table import Row, Table
+# seems to cause import loops
+#from PLC.Slices import Slice, Slices
+from PLC.Nodes import Node, Nodes
+from PLC.NodeGroups import NodeGroup, NodeGroups
 from PLC.TagTypes import TagType, TagTypes
 
 class SliceTag(Row):
@@ -14,10 +18,11 @@ class SliceTag(Row):
     table_name = 'slice_tag'
     primary_key = 'slice_tag_id'
     fields = {
-        'slice_tag_id': Parameter(int, "Slice attribute identifier"),
+        'slice_tag_id': Parameter(int, "Slice tag identifier"),
         'slice_id': Parameter(int, "Slice identifier"),
-        'node_id': Parameter(int, "Node identifier, if a sliver attribute"),
-       'nodegroup_id': Parameter(int, "Nodegroup identifier, if a sliver attribute"),
+        'name': Parameter(str, "Slice name"),
+        'node_id': Node.fields['node_id'],
+       'nodegroup_id': NodeGroup.fields['nodegroup_id'],
         'tag_type_id': TagType.fields['tag_type_id'],
         'tagname': TagType.fields['tagname'],
         'description': TagType.fields['description'],
index 55dc007..f2dcff9 100644 (file)
@@ -256,8 +256,15 @@ class Slices(Table):
     def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
         Table.__init__(self, api, Slice, columns)
 
-        sql = "SELECT %s FROM view_slices WHERE is_deleted IS False" % \
-              ", ".join(self.columns)
+        # the view that we're selecting upon: start with view_slices
+        view = "view_slices"
+        # as many left joins as requested tags
+        for tagname in self.tag_columns:
+            view= "%s left join %s using (%s)"%(view,Slice.tagvalue_view_name(tagname),
+                                                Slice.primary_key)
+            
+        sql = "SELECT %s FROM %s WHERE is_deleted IS False" % \
+              (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
 
         if expires is not None:
             if expires >= 0:
index c4ef7dc..f2d261a 100644 (file)
@@ -1232,9 +1232,11 @@ tag_types.tagname,
 tag_types.description,
 tag_types.category,
 tag_types.min_role_id,
-slice_tag.value
+slice_tag.value,
+slices.name
 FROM slice_tag
-INNER JOIN tag_types USING (tag_type_id);
+INNER JOIN tag_types USING (tag_type_id)
+INNER JOIN slices USING (slice_id);
 
 --------------------------------------------------------------------------------
 CREATE OR REPLACE VIEW view_sessions AS