2 # Thierry Parmentelat - INRIA
4 from types import NoneType
6 from PLC.Faults import *
8 from PLC.Auth import Auth
9 from PLC.Parameter import Parameter, Mixed
10 from PLC.Method import Method
11 from PLC.Accessor import Accessor, AccessorSingleton
13 from PLC.Nodes import Nodes, Node
14 from PLC.NodeTags import NodeTags, NodeTag
15 from PLC.Interfaces import Interfaces, Interface
16 from PLC.InterfaceTags import InterfaceTags, InterfaceTag
17 from PLC.Slices import Slices, Slice
18 from PLC.SliceTags import SliceTags, SliceTag
19 from PLC.Sites import Sites, Site
20 from PLC.SiteTags import SiteTags, SiteTag
21 from PLC.Persons import Persons, Person
22 from PLC.PersonTags import PersonTags, PersonTag
24 from PLC.AuthorizeHelpers import AuthorizeHelpers
26 # known classes : { class -> details }
27 taggable_classes = { Node : {'table_class' : Nodes,
28 'joins_class' : NodeTags, 'join_class' : NodeTag,
29 'secondary_key': 'hostname'},
30 Interface : {'table_class' : Interfaces,
31 'joins_class': InterfaceTags, 'join_class': InterfaceTag,
32 'secondary_key' : 'ip'},
33 Slice: {'table_class' : Slices,
34 'joins_class': SliceTags, 'join_class': SliceTag,
35 'secondary_key':'name'},
36 Site: {'table_class' : Sites,
37 'joins_class': SiteTags, 'join_class': SiteTag,
38 'secondary_key':'login_base'},
39 Person: {'table_class' : Persons,
40 'joins_class': PersonTags, 'join_class': PersonTag,
41 'secondary_key':'email'},
44 # xxx probably defined someplace else
45 admin_roles = ['admin']
46 person_roles = [ 'admin', 'pi', 'tech', 'user' ]
47 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
48 tech_roles = [ 'admin', 'pi', 'tech' ]
51 # generates 2 method classes:
52 # Get<classname><methodsuffix> (auth, id_or_name) -> value or None
53 # Set<classname><methodsuffix> (auth, id_or_name, value) -> value
54 # value is always a string, no cast nor typecheck for now
56 # The expose_in_api flag tells whether this tag may be handled
57 # through the Add/Get/Update methods as a native field
59 # note: roles get attached to the tagtype instance,
60 # while get_roles and set_roles get attached to the created methods
61 # this might need a cleanup
63 # in addition a convenience method like e.g. LocateNodeArch is defined
64 # in the Accessor class; its purpose is to retrieve the tag, or to create it if needed
67 # prior to plcapi-5.0-19, this used to accept an additional argument
68 # named min_role_id; this was redundant and confusing, it has been
69 # removed, we now use set_roles to restrict access on the corresponding tag
71 # the convention here is that methodsuffix should be mixed case, e.g. MyStuff
72 # while tagname is expected to be lowercase
73 # you then end up with e.g. GetPersonMyStuff
74 def define_accessors (module, objclass, methodsuffix, tagname,
75 category, description,
76 get_roles=all_roles, set_roles=admin_roles,
77 expose_in_api = False):
79 if objclass not in taggable_classes:
81 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
83 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
85 # side-effect on, say, Node.tags, if required
87 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
89 classname=objclass.__name__
90 get_name = "Get" + classname + methodsuffix
91 set_name = "Set" + classname + methodsuffix
92 locator_name = "Locate" + classname + methodsuffix
94 # accessor method objects under PLC.Method.Method
95 get_class = type (get_name, (Method,),
96 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
98 set_class = type (set_name, (Method,),
99 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
100 (classname,tagname)})
103 get_accepts = [ Auth () ]
104 primary_key=objclass.primary_key
105 secondary_key = taggable_classes[objclass]['secondary_key']
106 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
107 # for set, idem set of arguments + one additional arg, the new value
108 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
111 get_returns = Mixed (Parameter (str), Parameter(NoneType))
112 set_returns = Parameter(NoneType)
115 setattr(get_class,'roles',get_roles)
116 setattr(get_class,'accepts',get_accepts)
117 setattr(get_class,'returns', get_returns)
118 # that was useful for legacy method only, but we now need type_checking
119 # setattr(get_class,'skip_type_check',True)
121 setattr(set_class,'roles',set_roles)
122 setattr(set_class,'accepts',set_accepts)
123 setattr(set_class,'returns', set_returns)
124 # that was useful for legacy method only, but we now need type_checking
125 # setattr(set_class,'skip_type_check',True)
127 table_class = taggable_classes[objclass]['table_class']
128 joins_class = taggable_classes[objclass]['joins_class']
129 join_class = taggable_classes[objclass]['join_class']
131 # locate the tag and create it if needed
132 # this method is attached to the Accessor class
133 def locate_or_create_tag (self):
134 return self.locate_or_create_tag (tagname=tagname,
136 description=description,
139 # attach it to the Accessor class
140 setattr(Accessor,locator_name,locate_or_create_tag)
142 # body of the get method
143 def get_call (self, auth, id_or_name):
144 # locate the tag, see above
145 locator = getattr(Accessor,locator_name)
146 tag_type = locator(AccessorSingleton(self.api))
147 tag_type_id=tag_type['tag_type_id']
149 filter = {'tag_type_id':tag_type_id}
150 if isinstance (id_or_name,int):
151 filter[primary_key]=id_or_name
153 filter[secondary_key]=id_or_name
154 joins = joins_class (self.api,filter,['value'])
156 # xxx - we return None even if id_or_name is not valid
159 return joins[0]['value']
162 setattr (get_class,"call",get_call)
164 # body of the set method
165 def set_call (self, auth, id_or_name, value):
167 if isinstance (id_or_name, int):
168 filter={primary_key:id_or_name}
170 filter={secondary_key:id_or_name}
171 # we need the full monty b/c of the permission system
172 # objs = table_class(self.api, filter,[primary_key,secondary_key])
173 objs = table_class(self.api, filter)
175 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
176 # the object being tagged
178 primary_id = obj[primary_key]
180 # locate the tag, see above
181 locator = getattr(Accessor,locator_name)
182 tag_type = locator(AccessorSingleton(self.api))
183 tag_type_id = tag_type['tag_type_id']
185 # check authorization
186 if hasattr(objclass,'caller_may_write_tag'):
187 obj.caller_may_write_tag (self.api,self.caller,tag_type)
189 # locate the join object (e.g. NodeTag or similar)
190 filter = {'tag_type_id':tag_type_id}
191 if isinstance (id_or_name,int):
192 filter[primary_key]=id_or_name
194 filter[secondary_key]=id_or_name
195 joins = joins_class (self.api,filter)
196 # setting to something non void
197 if value is not None:
199 join = join_class (self.api)
200 join['tag_type_id']=tag_type_id
201 join[primary_key]=primary_id
205 joins[0]['value']=value
207 # providing an empty value means clean up
213 self.event_objects= { objclass.__name__ : [primary_id] }
214 self.message=objclass.__name__
215 if secondary_key in objs[0]:
216 self.message += " %s "%objs[0][secondary_key]
218 self.message += " %d "%objs[0][primary_key]
219 self.message += "updated"
223 setattr (set_class,"call",set_call)
226 setattr(module,get_name,get_class)
227 setattr(module,set_name,set_class)
228 # add in <module>.methods
230 methods=getattr(module,'methods')
233 methods += [get_name,set_name]
234 setattr(module,'methods',methods)