From c4274cc50530a9e9e7043a5614f1432eb20659ef Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Thu, 11 Dec 2008 18:29:31 +0000 Subject: [PATCH] Slice tags can be managed from AddSlice/UpdateSlice/GetSlices 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 | 2 +- PLC/Accessors/Factory.py | 20 ++++++---- PLC/Methods/AddNode.py | 8 ++-- PLC/Methods/AddSlice.py | 49 +++++++++++++++++------- PLC/Methods/UpdateInterface.py | 7 +++- PLC/Methods/UpdateNode.py | 9 +++-- PLC/Methods/UpdateSlice.py | 59 +++++++++++++++++++++-------- PLC/SliceTags.py | 11 ++++-- PLC/Slices.py | 11 +++++- planetlab5.sql | 6 ++- 10 files changed, 128 insertions(+), 54 deletions(-) diff --git a/PLC/Accessors/Accessors_standard.py b/PLC/Accessors/Accessors_standard.py index f158273..db7858f 100644 --- a/PLC/Accessors/Accessors_standard.py +++ b/PLC/Accessors/Accessors_standard.py @@ -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 diff --git a/PLC/Accessors/Factory.py b/PLC/Accessors/Factory.py index cd526cc..4d0d506 100644 --- a/PLC/Accessors/Factory.py +++ b/PLC/Accessors/Factory.py @@ -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) diff --git a/PLC/Methods/AddNode.py b/PLC/Methods/AddNode.py index 9e4a945..e8d073a 100644 --- a/PLC/Methods/AddNode.py +++ b/PLC/Methods/AddNode.py @@ -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'] diff --git a/PLC/Methods/AddSlice.py b/PLC/Methods/AddSlice.py index 9b7e875..001e0f9 100644 --- a/PLC/Methods/AddSlice.py +++ b/PLC/Methods/AddSlice.py @@ -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'] diff --git a/PLC/Methods/UpdateInterface.py b/PLC/Methods/UpdateInterface.py index b7d7f30..dad6420 100644 --- a/PLC/Methods/UpdateInterface.py +++ b/PLC/Methods/UpdateInterface.py @@ -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 diff --git a/PLC/Methods/UpdateNode.py b/PLC/Methods/UpdateNode.py index 46e63be..d66dd9e 100644 --- a/PLC/Methods/UpdateNode.py +++ b/PLC/Methods/UpdateNode.py @@ -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 diff --git a/PLC/Methods/UpdateSlice.py b/PLC/Methods/UpdateSlice.py index e29ae33..b58a106 100644 --- a/PLC/Methods/UpdateSlice.py +++ b/PLC/Methods/UpdateSlice.py @@ -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 diff --git a/PLC/SliceTags.py b/PLC/SliceTags.py index e7c8b8a..c966beb 100644 --- a/PLC/SliceTags.py +++ b/PLC/SliceTags.py @@ -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'], diff --git a/PLC/Slices.py b/PLC/Slices.py index 55dc007..f2dcff9 100644 --- a/PLC/Slices.py +++ b/PLC/Slices.py @@ -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: diff --git a/planetlab5.sql b/planetlab5.sql index c4ef7dc..f2d261a 100644 --- a/planetlab5.sql +++ b/planetlab5.sql @@ -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 -- 2.45.2