1 # Thierry Parmentelat - INRIA
5 from types import NoneType
7 from PLC.Method import Method
8 from PLC.Auth import Auth
9 from PLC.Parameter import Parameter, Mixed
11 from PLC.Faults import *
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
20 # this is another story..
21 #from PLC.Ilinks import Ilink
23 from PLC.TagTypes import TagTypes, TagType
25 # known classes : { class -> secondary_key }
26 taggable_classes = { Node : {'table_class' : Nodes,
27 'joins_class' : NodeTags, 'join_class' : NodeTag,
28 'secondary_key': 'hostname'},
29 Interface : {'table_class' : Interfaces,
30 'joins_class': InterfaceTags, 'join_class': InterfaceTag,
31 'secondary_key' : 'ip'},
32 Slice: {'table_class' : Slices,
33 'joins_class': SliceTags, 'join_class': SliceTag,
34 'secondary_key':'name'},
38 # xxx probably defined someplace else
39 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
40 tech_roles = [ 'admin', 'pi', 'tech' ]
43 # generates 2 method classes:
44 # Get<classname><methodsuffix> (auth, id_or_name) -> value or None
45 # Set<classname><methodsuffix> (auth, id_or_name, value) -> None
46 # value is always a string, no cast nor typecheck for now
48 # The expose_in_api flag tells whether this tag may be handled
49 # through the Add/Get/Update methods as a native field
51 # note: tag_min_role_id gets attached to the tagtype instance,
52 # while get_roles and set_roles get attached to the created methods
53 # this might need a cleanup
56 def define_accessors (module, objclass, methodsuffix, tagname,
57 category, description,
58 get_roles=['admin'], set_roles=['admin'],
59 tag_min_role_id=10, expose_in_api = False):
61 if objclass not in taggable_classes:
63 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
65 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
67 # side-effect on, say, Node.tags, if required
69 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
71 classname=objclass.__name__
72 get_name = "Get" + classname + methodsuffix
73 set_name = "Set" + classname + methodsuffix
75 # create method objects under PLC.Method.Method
76 get_class = type (get_name, (Method,),
77 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
79 set_class = type (set_name, (Method,),
80 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
83 get_accepts = [ Auth () ]
84 primary_key=objclass.primary_key
85 secondary_key = taggable_classes[objclass]['secondary_key']
86 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
87 # for set, idem set of arguments + one additional arg, the new value
88 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
91 get_returns = Mixed (Parameter (str), Parameter(NoneType))
92 set_returns = Parameter(NoneType)
95 setattr(get_class,'roles',get_roles)
96 setattr(get_class,'accepts',get_accepts)
97 setattr(get_class,'returns', get_returns)
98 setattr(get_class,'skip_typecheck',True)
100 setattr(set_class,'roles',set_roles)
101 setattr(set_class,'accepts',set_accepts)
102 setattr(set_class,'returns', set_returns)
103 setattr(set_class,'skip_typecheck',True)
105 table_class = taggable_classes[objclass]['table_class']
106 joins_class = taggable_classes[objclass]['joins_class']
107 join_class = taggable_classes[objclass]['join_class']
109 # body of the get method
110 def get_call (self, auth, id_or_name):
111 # search the tagtype - xxx - might need a cache
112 tag_types = TagTypes (self.api, {'tagname': tagname})
115 tag_type_id = tag_types[0]['tag_type_id']
116 filter = {'tag_type_id':tag_type_id}
117 if isinstance (id_or_name,int):
118 filter[primary_key]=id_or_name
120 filter[secondary_key]=id_or_name
121 joins = joins_class (self.api,filter,['value'])
123 # xxx - we return None even if id_or_name is not valid
126 return joins[0]['value']
129 setattr (get_class,"call",get_call)
131 # body of the set method
132 def set_call (self, auth, id_or_name, value):
134 if isinstance (id_or_name, int):
135 filter={primary_key:id_or_name}
137 filter={secondary_key:id_or_name}
138 objs = table_class(self.api, filter,[primary_key,secondary_key])
140 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
141 primary_id = objs[0][primary_key]
143 # search tag type & create if needed
144 tag_types = TagTypes (self.api, {'tagname':tagname})
146 tag_type = tag_types[0]
148 # not found: create it
149 tag_type_fields = {'tagname':tagname,
150 'category' : category,
151 'description' : description,
152 'min_role_id': tag_min_role_id}
153 tag_type = TagType (self.api, tag_type_fields)
155 tag_type_id = tag_type['tag_type_id']
157 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
158 filter = {'tag_type_id':tag_type_id}
159 if isinstance (id_or_name,int):
160 filter[primary_key]=id_or_name
162 filter[secondary_key]=id_or_name
163 joins = joins_class (self.api,filter)
164 # setting to something non void
165 if value is not None:
167 join = join_class (self.api)
168 join['tag_type_id']=tag_type_id
169 join[primary_key]=primary_id
173 joins[0]['value']=value
175 # providing an empty value means clean up
181 self.event_objects= { objclass.__name__ : [primary_id] }
182 self.message=objclass.__name__
183 if secondary_key in objs[0]:
184 self.message += " %s "%objs[0][secondary_key]
186 self.message += " %d "%objs[0][primary_key]
187 self.message += "updated"
190 setattr (set_class,"call",set_call)
193 setattr(module,get_name,get_class)
194 setattr(module,set_name,set_class)
195 # add in <module>.methods
197 methods=getattr(module,'methods')
200 methods += [get_name,set_name]
201 setattr(module,'methods',methods)