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 def define_accessors (module, objclass, methodsuffix, tagname,
74 category, description,
75 get_roles=all_roles, set_roles=admin_roles,
76 expose_in_api = False):
78 if objclass not in taggable_classes:
80 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
82 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
84 # side-effect on, say, Node.tags, if required
86 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
88 classname=objclass.__name__
89 get_name = "Get" + classname + methodsuffix
90 set_name = "Set" + classname + methodsuffix
91 locator_name = "Locate" + classname + methodsuffix
93 # accessor method objects under PLC.Method.Method
94 get_class = type (get_name, (Method,),
95 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
97 set_class = type (set_name, (Method,),
98 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
102 get_accepts = [ Auth () ]
103 primary_key=objclass.primary_key
104 secondary_key = taggable_classes[objclass]['secondary_key']
105 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
106 # for set, idem set of arguments + one additional arg, the new value
107 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
110 get_returns = Mixed (Parameter (str), Parameter(NoneType))
111 set_returns = Parameter(NoneType)
114 setattr(get_class,'roles',get_roles)
115 setattr(get_class,'accepts',get_accepts)
116 setattr(get_class,'returns', get_returns)
117 setattr(get_class,'skip_typecheck',True)
119 setattr(set_class,'roles',set_roles)
120 setattr(set_class,'accepts',set_accepts)
121 setattr(set_class,'returns', set_returns)
122 setattr(set_class,'skip_typecheck',True)
124 table_class = taggable_classes[objclass]['table_class']
125 joins_class = taggable_classes[objclass]['joins_class']
126 join_class = taggable_classes[objclass]['join_class']
128 # locate the tag and create it if needed
129 # this method is attached to the Accessor class
130 def locate_or_create_tag (self):
131 return self.locate_or_create_tag (tagname=tagname,
133 description=description,
136 # attach it to the Accessor class
137 setattr(Accessor,locator_name,locate_or_create_tag)
139 # body of the get method
140 def get_call (self, auth, id_or_name):
141 # locate the tag, see above
142 locator = getattr(Accessor,locator_name)
143 tag_type_id = locator(AccessorSingleton(self.api))
145 filter = {'tag_type_id':tag_type_id}
146 if isinstance (id_or_name,int):
147 filter[primary_key]=id_or_name
149 filter[secondary_key]=id_or_name
150 joins = joins_class (self.api,filter,['value'])
152 # xxx - we return None even if id_or_name is not valid
155 return joins[0]['value']
158 setattr (get_class,"call",get_call)
160 # body of the set method
161 def set_call (self, auth, id_or_name, value):
163 if isinstance (id_or_name, int):
164 filter={primary_key:id_or_name}
166 filter={secondary_key:id_or_name}
167 objs = table_class(self.api, filter,[primary_key,secondary_key])
169 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
170 primary_id = objs[0][primary_key]
172 # locate the tag, see above
173 locator = getattr(Accessor,locator_name)
174 tag_type_id = locator(AccessorSingleton(self.api))
176 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
177 filter = {'tag_type_id':tag_type_id}
178 if isinstance (id_or_name,int):
179 filter[primary_key]=id_or_name
181 filter[secondary_key]=id_or_name
182 joins = joins_class (self.api,filter)
183 # setting to something non void
184 if value is not None:
186 join = join_class (self.api)
187 join['tag_type_id']=tag_type_id
188 join[primary_key]=primary_id
192 joins[0]['value']=value
194 # providing an empty value means clean up
200 self.event_objects= { objclass.__name__ : [primary_id] }
201 self.message=objclass.__name__
202 if secondary_key in objs[0]:
203 self.message += " %s "%objs[0][secondary_key]
205 self.message += " %d "%objs[0][primary_key]
206 self.message += "updated"
210 setattr (set_class,"call",set_call)
213 setattr(module,get_name,get_class)
214 setattr(module,set_name,set_class)
215 # add in <module>.methods
217 methods=getattr(module,'methods')
220 methods += [get_name,set_name]
221 setattr(module,'methods',methods)