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 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 def define_accessors (module, objclass, methodsuffix, tagname,
73 category, description,
74 get_roles=all_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 setattr(get_class,'skip_typecheck',True)
118 setattr(set_class,'roles',set_roles)
119 setattr(set_class,'accepts',set_accepts)
120 setattr(set_class,'returns', set_returns)
121 setattr(set_class,'skip_typecheck',True)
123 table_class = taggable_classes[objclass]['table_class']
124 joins_class = taggable_classes[objclass]['joins_class']
125 join_class = taggable_classes[objclass]['join_class']
127 # locate the tag and create it if needed
128 # this method is attached to the Accessor class
129 def locate_or_create_tag (self):
130 return self.locate_or_create_tag (tagname=tagname,
132 description=description,
135 # attach it to the Accessor class
136 setattr(Accessor,locator_name,locate_or_create_tag)
138 # body of the get method
139 def get_call (self, auth, id_or_name):
140 # locate the tag, see above
141 locator = getattr(Accessor,locator_name)
142 tag_type_id = locator(AccessorSingleton(self.api))
144 filter = {'tag_type_id':tag_type_id}
145 if isinstance (id_or_name,int):
146 filter[primary_key]=id_or_name
148 filter[secondary_key]=id_or_name
149 joins = joins_class (self.api,filter,['value'])
151 # xxx - we return None even if id_or_name is not valid
154 return joins[0]['value']
157 setattr (get_class,"call",get_call)
159 # body of the set method
160 def set_call (self, auth, id_or_name, value):
162 if isinstance (id_or_name, int):
163 filter={primary_key:id_or_name}
165 filter={secondary_key:id_or_name}
166 objs = table_class(self.api, filter,[primary_key,secondary_key])
168 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
169 primary_id = objs[0][primary_key]
171 # locate the tag, see above
172 locator = getattr(Accessor,locator_name)
173 tag_type_id = locator(AccessorSingleton(self.api))
175 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
176 filter = {'tag_type_id':tag_type_id}
177 if isinstance (id_or_name,int):
178 filter[primary_key]=id_or_name
180 filter[secondary_key]=id_or_name
181 joins = joins_class (self.api,filter)
182 # setting to something non void
183 if value is not None:
185 join = join_class (self.api)
186 join['tag_type_id']=tag_type_id
187 join[primary_key]=primary_id
191 joins[0]['value']=value
193 # providing an empty value means clean up
199 self.event_objects= { objclass.__name__ : [primary_id] }
200 self.message=objclass.__name__
201 if secondary_key in objs[0]:
202 self.message += " %s "%objs[0][secondary_key]
204 self.message += " %d "%objs[0][primary_key]
205 self.message += "updated"
209 setattr (set_class,"call",set_call)
212 setattr(module,get_name,get_class)
213 setattr(module,set_name,set_class)
214 # add in <module>.methods
216 methods=getattr(module,'methods')
219 methods += [get_name,set_name]
220 setattr(module,'methods',methods)