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 # need to import so the core classes get decorated with caller_may_write_tag
25 from PLC.AuthorizeHelpers import AuthorizeHelpers
27 # known classes : { class -> details }
28 taggable_classes = { Node : {'table_class' : Nodes,
29 'joins_class' : NodeTags, 'join_class' : NodeTag,
30 'secondary_key': 'hostname'},
31 Interface : {'table_class' : Interfaces,
32 'joins_class': InterfaceTags, 'join_class': InterfaceTag,
33 'secondary_key' : 'ip'},
34 Slice: {'table_class' : Slices,
35 'joins_class': SliceTags, 'join_class': SliceTag,
36 'secondary_key':'name'},
37 Site: {'table_class' : Sites,
38 'joins_class': SiteTags, 'join_class': SiteTag,
39 'secondary_key':'login_base'},
40 Person: {'table_class' : Persons,
41 'joins_class': PersonTags, 'join_class': PersonTag,
42 'secondary_key':'email'},
45 # xxx probably defined someplace else
46 admin_roles = ['admin']
47 person_roles = [ 'admin', 'pi', 'tech', 'user' ]
48 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
49 tech_roles = [ 'admin', 'pi', 'tech' ]
52 # generates 2 method classes:
53 # Get<classname><methodsuffix> (auth, id_or_name) -> value or None
54 # Set<classname><methodsuffix> (auth, id_or_name, value) -> value
55 # value is always a string, no cast nor typecheck for now
57 # The expose_in_api flag tells whether this tag may be handled
58 # through the Add/Get/Update methods as a native field
60 # note: roles get attached to the tagtype instance,
61 # while get_roles and set_roles get attached to the created methods
62 # this might need a cleanup
64 # in addition a convenience method like e.g. LocateNodeArch is defined
65 # in the Accessor class; its purpose is to retrieve the tag, or to create it if needed
68 # prior to plcapi-5.0-19, this used to accept an additional argument
69 # named min_role_id; this was redundant and confusing, it has been
70 # removed, we now use set_roles to restrict access on the corresponding tag
72 # the convention here is that methodsuffix should be mixed case, e.g. MyStuff
73 # while tagname is expected to be lowercase
74 # you then end up with e.g. GetPersonMyStuff
75 def define_accessors (module, objclass, methodsuffix, tagname,
76 category, description,
77 get_roles=all_roles, set_roles=admin_roles,
78 expose_in_api = False):
80 if objclass not in taggable_classes:
82 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
84 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
86 # side-effect on, say, Node.tags, if required
88 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
90 classname=objclass.__name__
91 get_name = "Get" + classname + methodsuffix
92 set_name = "Set" + classname + methodsuffix
93 locator_name = "Locate" + classname + methodsuffix
95 # accessor method objects under PLC.Method.Method
96 get_class = type (get_name, (Method,),
97 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
99 set_class = type (set_name, (Method,),
100 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
101 (classname,tagname)})
104 get_accepts = [ Auth () ]
105 primary_key=objclass.primary_key
106 secondary_key = taggable_classes[objclass]['secondary_key']
107 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
108 # for set, idem set of arguments + one additional arg, the new value
109 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
112 get_returns = Mixed (Parameter (str), Parameter(NoneType))
113 set_returns = Parameter(NoneType)
116 setattr(get_class,'roles',get_roles)
117 setattr(get_class,'accepts',get_accepts)
118 setattr(get_class,'returns', get_returns)
119 # that was useful for legacy method only, but we now need type_checking
120 # setattr(get_class,'skip_type_check',True)
122 setattr(set_class,'roles',set_roles)
123 setattr(set_class,'accepts',set_accepts)
124 setattr(set_class,'returns', set_returns)
125 # that was useful for legacy method only, but we now need type_checking
126 # setattr(set_class,'skip_type_check',True)
128 table_class = taggable_classes[objclass]['table_class']
129 joins_class = taggable_classes[objclass]['joins_class']
130 join_class = taggable_classes[objclass]['join_class']
132 # locate the tag and create it if needed
133 # this method is attached to the Accessor class
134 def tag_locator (self):
135 return self.locate_or_create_tag (tagname=tagname,
137 description=description,
140 # attach it to the Accessor class
141 Accessor.register_tag_locator(locator_name,tag_locator)
143 # body of the get method
144 def get_call (self, auth, id_or_name):
145 # locate the tag, see above
146 tag_locator = Accessor.retrieve_tag_locator(locator_name)
147 tag_type = tag_locator(AccessorSingleton(self.api))
148 tag_type_id=tag_type['tag_type_id']
150 filter = {'tag_type_id':tag_type_id}
151 if isinstance (id_or_name,int):
152 filter[primary_key]=id_or_name
154 filter[secondary_key]=id_or_name
155 joins = joins_class (self.api,filter,['value'])
157 # xxx - we return None even if id_or_name is not valid
160 return joins[0]['value']
163 setattr (get_class,"call",get_call)
165 # body of the set method
166 def set_call (self, auth, id_or_name, value):
168 if isinstance (id_or_name, int):
169 filter={primary_key:id_or_name}
171 filter={secondary_key:id_or_name}
172 # we need the full monty b/c of the permission system
173 # objs = table_class(self.api, filter,[primary_key,secondary_key])
174 objs = table_class(self.api, filter)
176 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
177 # the object being tagged
179 primary_id = obj[primary_key]
181 # locate the tag, see above
182 tag_locator = Accessor.retrieve_tag_locator(locator_name)
183 tag_type = tag_locator(AccessorSingleton(self.api))
184 tag_type_id = tag_type['tag_type_id']
186 # check authorization
187 if not hasattr(objclass,'caller_may_write_tag'):
188 raise PLCAuthenticationFailure, "class %s misses method caller_may_write_tag"%objclass.__name__
189 obj.caller_may_write_tag (self.api,self.caller,tag_type)
191 # locate the join object (e.g. NodeTag or similar)
192 filter = {'tag_type_id':tag_type_id}
193 if isinstance (id_or_name,int):
194 filter[primary_key]=id_or_name
196 filter[secondary_key]=id_or_name
197 joins = joins_class (self.api,filter)
198 # setting to something non void
199 if value is not None:
201 join = join_class (self.api)
202 join['tag_type_id']=tag_type_id
203 join[primary_key]=primary_id
207 joins[0]['value']=value
209 # providing an empty value means clean up
215 self.event_objects= { objclass.__name__ : [primary_id] }
216 self.message=objclass.__name__
217 if secondary_key in objs[0]:
218 self.message += " %s "%objs[0][secondary_key]
220 self.message += " %d "%objs[0][primary_key]
221 self.message += "updated"
225 setattr (set_class,"call",set_call)
228 setattr(module,get_name,get_class)
229 setattr(module,set_name,set_class)
230 # add in <module>.methods
232 methods=getattr(module,'methods')
235 methods += [get_name,set_name]
236 setattr(module,'methods',methods)