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 from PLC.TagTypes import TagTypes, TagType
30 # known classes : { class -> secondary_key }
31 taggable_classes = { Node : {'table_class' : Nodes,
32 'joins_class' : NodeTags, 'join_class' : NodeTag,
33 'secondary_key': 'hostname'},
34 Interface : {'table_class' : Interfaces,
35 'joins_class': InterfaceTags, 'join_class': InterfaceTag,
36 'secondary_key' : 'ip'},
37 Slice: {'table_class' : Slices,
38 'joins_class': SliceTags, 'join_class': SliceTag,
39 'secondary_key':'name'},
40 Site: {'table_class' : Sites,
41 'joins_class': SiteTags, 'join_class': SiteTag,
42 'secondary_key':'login_base'},
43 Person: {'table_class' : Persons,
44 'joins_class': PersonTags, 'join_class': PersonTag,
45 'secondary_key':'email'},
49 # xxx probably defined someplace else
50 admin_roles = ['admin']
51 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
52 tech_roles = [ 'admin', 'pi', 'tech' ]
55 # generates 2 method classes:
56 # Get<classname><methodsuffix> (auth, id_or_name) -> value or None
57 # Set<classname><methodsuffix> (auth, id_or_name, value) -> value
58 # value is always a string, no cast nor typecheck for now
60 # The expose_in_api flag tells whether this tag may be handled
61 # through the Add/Get/Update methods as a native field
63 # note: tag_min_role_id gets attached to the tagtype instance,
64 # while get_roles and set_roles get attached to the created methods
65 # this might need a cleanup
67 # in addition a convenience method like e.g. LocateNodeArch is defined
68 # in the Accessor class; its purpose is to retrieve the tag, or to create it if needed
70 def define_accessors (module, objclass, methodsuffix, tagname,
71 category, description,
72 get_roles=['admin'], set_roles=['admin'],
73 tag_min_role_id=10, expose_in_api = False):
75 if objclass not in taggable_classes:
77 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
79 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
81 # side-effect on, say, Node.tags, if required
83 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
85 classname=objclass.__name__
86 get_name = "Get" + classname + methodsuffix
87 set_name = "Set" + classname + methodsuffix
88 locator_name = "Locate" + classname + methodsuffix
90 # accessor method objects under PLC.Method.Method
91 get_class = type (get_name, (Method,),
92 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
94 set_class = type (set_name, (Method,),
95 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
99 get_accepts = [ Auth () ]
100 primary_key=objclass.primary_key
101 secondary_key = taggable_classes[objclass]['secondary_key']
102 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
103 # for set, idem set of arguments + one additional arg, the new value
104 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
107 get_returns = Mixed (Parameter (str), Parameter(NoneType))
108 set_returns = Parameter(NoneType)
111 setattr(get_class,'roles',get_roles)
112 setattr(get_class,'accepts',get_accepts)
113 setattr(get_class,'returns', get_returns)
114 setattr(get_class,'skip_typecheck',True)
116 setattr(set_class,'roles',set_roles)
117 setattr(set_class,'accepts',set_accepts)
118 setattr(set_class,'returns', set_returns)
119 setattr(set_class,'skip_typecheck',True)
121 table_class = taggable_classes[objclass]['table_class']
122 joins_class = taggable_classes[objclass]['joins_class']
123 join_class = taggable_classes[objclass]['join_class']
125 # locate the tag and create it if needed
126 # this method is attached to the Accessor class
127 def locate_or_create_tag (self):
128 return self.locate_or_create_tag (tagname=tagname,
130 description=description,
131 min_role_id=tag_min_role_id)
133 # attach it to the Accessor class
134 setattr(Accessor,locator_name,locate_or_create_tag)
136 # body of the get method
137 def get_call (self, auth, id_or_name):
138 # locate the tag, see above
139 locator = getattr(Accessor,locator_name)
140 tag_type_id = locator(AccessorSingleton(self.api))
142 filter = {'tag_type_id':tag_type_id}
143 if isinstance (id_or_name,int):
144 filter[primary_key]=id_or_name
146 filter[secondary_key]=id_or_name
147 joins = joins_class (self.api,filter,['value'])
149 # xxx - we return None even if id_or_name is not valid
152 return joins[0]['value']
155 setattr (get_class,"call",get_call)
157 # body of the set method
158 def set_call (self, auth, id_or_name, value):
160 if isinstance (id_or_name, int):
161 filter={primary_key:id_or_name}
163 filter={secondary_key:id_or_name}
164 objs = table_class(self.api, filter,[primary_key,secondary_key])
166 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
167 primary_id = objs[0][primary_key]
169 # locate the tag, see above
170 locator = getattr(Accessor,locator_name)
171 tag_type_id = locator(AccessorSingleton(self.api))
173 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
174 filter = {'tag_type_id':tag_type_id}
175 if isinstance (id_or_name,int):
176 filter[primary_key]=id_or_name
178 filter[secondary_key]=id_or_name
179 joins = joins_class (self.api,filter)
180 # setting to something non void
181 if value is not None:
183 join = join_class (self.api)
184 join['tag_type_id']=tag_type_id
185 join[primary_key]=primary_id
189 joins[0]['value']=value
191 # providing an empty value means clean up
197 self.event_objects= { objclass.__name__ : [primary_id] }
198 self.message=objclass.__name__
199 if secondary_key in objs[0]:
200 self.message += " %s "%objs[0][secondary_key]
202 self.message += " %d "%objs[0][primary_key]
203 self.message += "updated"
207 setattr (set_class,"call",set_call)
210 setattr(module,get_name,get_class)
211 setattr(module,set_name,set_class)
212 # add in <module>.methods
214 methods=getattr(module,'methods')
217 methods += [get_name,set_name]
218 setattr(module,'methods',methods)