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 # this is another story..
25 #from PLC.Ilinks import Ilink
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'},
46 # xxx probably defined someplace else
47 admin_roles = ['admin']
48 person_roles = [ 'admin', 'pi', 'tech', 'user' ]
49 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
50 tech_roles = [ 'admin', 'pi', 'tech' ]
53 # generates 2 method classes:
54 # Get<classname><methodsuffix> (auth, id_or_name) -> value or None
55 # Set<classname><methodsuffix> (auth, id_or_name, value) -> value
56 # value is always a string, no cast nor typecheck for now
58 # The expose_in_api flag tells whether this tag may be handled
59 # through the Add/Get/Update methods as a native field
61 # note: roles get attached to the tagtype instance,
62 # while get_roles and set_roles get attached to the created methods
63 # this might need a cleanup
65 # in addition a convenience method like e.g. LocateNodeArch is defined
66 # in the Accessor class; its purpose is to retrieve the tag, or to create it if needed
69 # prior to plcapi-5.0-19, this used to accept an additional argument
70 # named min_role_id; this was redundant and confusing, it has been
71 # removed, we now use set_roles to restrict access on the corresponding tag
73 # the convention here is that methodsuffix should be mixed case, e.g. MyStuff
74 # while tagname is expected to be lowercase
75 # you then end up with e.g. GetPersonMyStuff
76 def define_accessors (module, objclass, methodsuffix, tagname,
77 category, description,
78 get_roles=all_roles, set_roles=admin_roles,
79 expose_in_api = False):
81 if objclass not in taggable_classes:
83 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
85 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
87 # side-effect on, say, Node.tags, if required
89 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
91 classname=objclass.__name__
92 get_name = "Get" + classname + methodsuffix
93 set_name = "Set" + classname + methodsuffix
94 locator_name = "Locate" + classname + methodsuffix
96 # accessor method objects under PLC.Method.Method
97 get_class = type (get_name, (Method,),
98 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
100 set_class = type (set_name, (Method,),
101 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
102 (classname,tagname)})
105 get_accepts = [ Auth () ]
106 primary_key=objclass.primary_key
107 secondary_key = taggable_classes[objclass]['secondary_key']
108 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
109 # for set, idem set of arguments + one additional arg, the new value
110 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
113 get_returns = Mixed (Parameter (str), Parameter(NoneType))
114 set_returns = Parameter(NoneType)
117 setattr(get_class,'roles',get_roles)
118 setattr(get_class,'accepts',get_accepts)
119 setattr(get_class,'returns', get_returns)
120 setattr(get_class,'skip_typecheck',True)
122 setattr(set_class,'roles',set_roles)
123 setattr(set_class,'accepts',set_accepts)
124 setattr(set_class,'returns', set_returns)
125 setattr(set_class,'skip_typecheck',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_id = locator(AccessorSingleton(self.api))
148 filter = {'tag_type_id':tag_type_id}
149 if isinstance (id_or_name,int):
150 filter[primary_key]=id_or_name
152 filter[secondary_key]=id_or_name
153 joins = joins_class (self.api,filter,['value'])
155 # xxx - we return None even if id_or_name is not valid
158 return joins[0]['value']
161 setattr (get_class,"call",get_call)
163 # body of the set method
164 def set_call (self, auth, id_or_name, value):
166 if isinstance (id_or_name, int):
167 filter={primary_key:id_or_name}
169 filter={secondary_key:id_or_name}
170 objs = table_class(self.api, filter,[primary_key,secondary_key])
172 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
173 primary_id = objs[0][primary_key]
175 # locate the tag, see above
176 locator = getattr(Accessor,locator_name)
177 tag_type_id = locator(AccessorSingleton(self.api))
179 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
180 filter = {'tag_type_id':tag_type_id}
181 if isinstance (id_or_name,int):
182 filter[primary_key]=id_or_name
184 filter[secondary_key]=id_or_name
185 joins = joins_class (self.api,filter)
186 # setting to something non void
187 if value is not None:
189 join = join_class (self.api)
190 join['tag_type_id']=tag_type_id
191 join[primary_key]=primary_id
195 joins[0]['value']=value
197 # providing an empty value means clean up
203 self.event_objects= { objclass.__name__ : [primary_id] }
204 self.message=objclass.__name__
205 if secondary_key in objs[0]:
206 self.message += " %s "%objs[0][secondary_key]
208 self.message += " %d "%objs[0][primary_key]
209 self.message += "updated"
213 setattr (set_class,"call",set_call)
216 setattr(module,get_name,get_class)
217 setattr(module,set_name,set_class)
218 # add in <module>.methods
220 methods=getattr(module,'methods')
223 methods += [get_name,set_name]
224 setattr(module,'methods',methods)