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