1 # Thierry Parmentelat - INRIA
5 from types import NoneType
7 from PLC.Faults import *
9 from PLC.Auth import Auth
10 from PLC.Parameter import Parameter, Mixed
11 from PLC.Method import Method
12 from PLC.Accessor import Accessor, AccessorSingleton
14 from PLC.Nodes import Nodes, Node
15 from PLC.NodeTags import NodeTags, NodeTag
16 from PLC.Interfaces import Interfaces, Interface
17 from PLC.InterfaceTags import InterfaceTags, InterfaceTag
18 from PLC.Slices import Slices, Slice
19 from PLC.SliceTags import SliceTags, SliceTag
20 from PLC.Sites import Sites, Site
21 from PLC.SiteTags import SiteTags, SiteTag
22 from PLC.Persons import Persons, Person
23 from PLC.PersonTags import PersonTags, PersonTag
25 # this is another story..
26 #from PLC.Ilinks import Ilink
28 # known classes : { class -> details }
29 taggable_classes = { Node : {'table_class' : Nodes,
30 'joins_class' : NodeTags, 'join_class' : NodeTag,
31 'secondary_key': 'hostname'},
32 Interface : {'table_class' : Interfaces,
33 'joins_class': InterfaceTags, 'join_class': InterfaceTag,
34 'secondary_key' : 'ip'},
35 Slice: {'table_class' : Slices,
36 'joins_class': SliceTags, 'join_class': SliceTag,
37 'secondary_key':'name'},
38 Site: {'table_class' : Sites,
39 'joins_class': SiteTags, 'join_class': SiteTag,
40 'secondary_key':'login_base'},
41 Person: {'table_class' : Persons,
42 'joins_class': PersonTags, 'join_class': PersonTag,
43 'secondary_key':'email'},
47 # xxx probably defined someplace else
48 admin_roles = ['admin']
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: tag_min_role_id gets 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
68 def define_accessors (module, objclass, methodsuffix, tagname,
69 category, description,
70 get_roles=['admin'], set_roles=['admin'],
71 tag_min_role_id=10, expose_in_api = False):
73 if objclass not in taggable_classes:
75 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
77 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
79 # side-effect on, say, Node.tags, if required
81 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
83 classname=objclass.__name__
84 get_name = "Get" + classname + methodsuffix
85 set_name = "Set" + classname + methodsuffix
86 locator_name = "Locate" + classname + methodsuffix
88 # accessor method objects under PLC.Method.Method
89 get_class = type (get_name, (Method,),
90 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
92 set_class = type (set_name, (Method,),
93 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
97 get_accepts = [ Auth () ]
98 primary_key=objclass.primary_key
99 secondary_key = taggable_classes[objclass]['secondary_key']
100 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
101 # for set, idem set of arguments + one additional arg, the new value
102 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
105 get_returns = Mixed (Parameter (str), Parameter(NoneType))
106 set_returns = Parameter(NoneType)
109 setattr(get_class,'roles',get_roles)
110 setattr(get_class,'accepts',get_accepts)
111 setattr(get_class,'returns', get_returns)
112 setattr(get_class,'skip_typecheck',True)
114 setattr(set_class,'roles',set_roles)
115 setattr(set_class,'accepts',set_accepts)
116 setattr(set_class,'returns', set_returns)
117 setattr(set_class,'skip_typecheck',True)
119 table_class = taggable_classes[objclass]['table_class']
120 joins_class = taggable_classes[objclass]['joins_class']
121 join_class = taggable_classes[objclass]['join_class']
123 # locate the tag and create it if needed
124 # this method is attached to the Accessor class
125 def locate_or_create_tag (self):
126 return self.locate_or_create_tag (tagname=tagname,
128 description=description,
129 min_role_id=tag_min_role_id)
131 # attach it to the Accessor class
132 setattr(Accessor,locator_name,locate_or_create_tag)
134 # body of the get method
135 def get_call (self, auth, id_or_name):
136 # locate the tag, see above
137 locator = getattr(Accessor,locator_name)
138 tag_type_id = locator(AccessorSingleton(self.api))
140 filter = {'tag_type_id':tag_type_id}
141 if isinstance (id_or_name,int):
142 filter[primary_key]=id_or_name
144 filter[secondary_key]=id_or_name
145 joins = joins_class (self.api,filter,['value'])
147 # xxx - we return None even if id_or_name is not valid
150 return joins[0]['value']
153 setattr (get_class,"call",get_call)
155 # body of the set method
156 def set_call (self, auth, id_or_name, value):
158 if isinstance (id_or_name, int):
159 filter={primary_key:id_or_name}
161 filter={secondary_key:id_or_name}
162 objs = table_class(self.api, filter,[primary_key,secondary_key])
164 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
165 primary_id = objs[0][primary_key]
167 # locate the tag, see above
168 locator = getattr(Accessor,locator_name)
169 tag_type_id = locator(AccessorSingleton(self.api))
171 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
172 filter = {'tag_type_id':tag_type_id}
173 if isinstance (id_or_name,int):
174 filter[primary_key]=id_or_name
176 filter[secondary_key]=id_or_name
177 joins = joins_class (self.api,filter)
178 # setting to something non void
179 if value is not None:
181 join = join_class (self.api)
182 join['tag_type_id']=tag_type_id
183 join[primary_key]=primary_id
187 joins[0]['value']=value
189 # providing an empty value means clean up
195 self.event_objects= { objclass.__name__ : [primary_id] }
196 self.message=objclass.__name__
197 if secondary_key in objs[0]:
198 self.message += " %s "%objs[0][secondary_key]
200 self.message += " %d "%objs[0][primary_key]
201 self.message += "updated"
205 setattr (set_class,"call",set_call)
208 setattr(module,get_name,get_class)
209 setattr(module,set_name,set_class)
210 # add in <module>.methods
212 methods=getattr(module,'methods')
215 methods += [get_name,set_name]
216 setattr(module,'methods',methods)