1 # Thierry Parmentelat - INRIA
4 from types import NoneType
6 from PLC.Method import Method
7 from PLC.Auth import Auth
8 from PLC.Parameter import Parameter, Mixed
10 from PLC.Faults import *
12 from PLC.Nodes import Nodes, Node
13 from PLC.NodeTags import NodeTags, NodeTag
14 from PLC.Interfaces import Interfaces, Interface
15 from PLC.InterfaceTags import InterfaceTags, InterfaceTag
16 from PLC.Slices import Slices, Slice
17 from PLC.SliceTags import SliceTags, SliceTag
19 # this is another story..
20 #from PLC.Ilinks import Ilink
22 from PLC.TagTypes import TagTypes, TagType
24 # known classes : { class -> secondary_key }
25 taggable_classes = { Node : {'table_class' : Nodes,
26 'joins_class' : NodeTags, 'join_class' : NodeTag,
27 'secondary_key': 'hostname'},
28 Interface : {'table_class' : Interfaces,
29 'joins_class': InterfaceTags, 'join_class': InterfaceTag,
30 'secondary_key' : 'ip'},
31 Slice: {'table_class' : Slices,
32 'joins_class': SliceTags, 'join_class': SliceTag,
33 'secondary_key':'name'},
37 # xxx probably defined someplace else
38 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
39 tech_roles = [ 'admin', 'pi', 'tech' ]
42 # generates 2 method classes:
43 # Get<classname><methodsuffix> (auth, id_or_name) -> value or None
44 # Set<classname><methodsuffix> (auth, id_or_name, value) -> None
45 # value is always a string, no cast nor typecheck for now
47 # The expose_in_api flag tells whether this tag may be handled
48 # through the Add/Get/Update methods as a native field
50 # note: tag_min_role_id gets attached to the tagtype instance,
51 # while get_roles and set_roles get attached to the created methods
52 # this might need a cleanup
55 def define_accessors (module, objclass, methodsuffix, tagname,
56 category, description,
57 get_roles=['admin'], set_roles=['admin'],
58 tag_min_role_id=10, expose_in_api = False):
60 if objclass not in taggable_classes:
62 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
64 raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
66 # side-effect on, say, Node.tags, if required
68 getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
70 classname=objclass.__name__
71 get_name = "Get" + classname + methodsuffix
72 set_name = "Set" + classname + methodsuffix
74 # create method objects under PLC.Method.Method
75 get_class = type (get_name, (Method,),
76 {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
78 set_class = type (set_name, (Method,),
79 {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
82 get_accepts = [ Auth () ]
83 primary_key=objclass.primary_key
84 secondary_key = taggable_classes[objclass]['secondary_key']
85 get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
86 # for set, idem set of arguments + one additional arg, the new value
87 set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
90 get_returns = Mixed (Parameter (str), Parameter(NoneType))
91 set_returns = Parameter(NoneType)
94 setattr(get_class,'roles',get_roles)
95 setattr(get_class,'accepts',get_accepts)
96 setattr(get_class,'returns', get_returns)
97 setattr(get_class,'skip_typecheck',True)
99 setattr(set_class,'roles',set_roles)
100 setattr(set_class,'accepts',set_accepts)
101 setattr(set_class,'returns', set_returns)
102 setattr(set_class,'skip_typecheck',True)
104 table_class = taggable_classes[objclass]['table_class']
105 joins_class = taggable_classes[objclass]['joins_class']
106 join_class = taggable_classes[objclass]['join_class']
108 # body of the get method
109 def get_call (self, auth, id_or_name):
110 # search the tagtype - xxx - might need a cache
111 tag_types = TagTypes (self.api, {'tagname': tagname})
114 tag_type_id = tag_types[0]['tag_type_id']
115 filter = {'tag_type_id':tag_type_id}
116 if isinstance (id_or_name,int):
117 filter[primary_key]=id_or_name
119 filter[secondary_key]=id_or_name
120 joins = joins_class (self.api,filter,['value'])
122 # xxx - we return None even if id_or_name is not valid
125 return joins[0]['value']
128 setattr (get_class,"call",get_call)
130 # body of the set method
131 def set_call (self, auth, id_or_name, value):
133 if isinstance (id_or_name, int):
134 filter={primary_key:id_or_name}
136 filter={secondary_key:id_or_name}
137 objs = table_class(self.api, filter,[primary_key,secondary_key])
139 raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
140 primary_id = objs[0][primary_key]
142 # search tag type & create if needed
143 tag_types = TagTypes (self.api, {'tagname':tagname})
145 tag_type = tag_types[0]
147 # not found: create it
148 tag_type_fields = {'tagname':tagname,
149 'category' : category,
150 'description' : description,
151 'min_role_id': tag_min_role_id}
152 tag_type = TagType (self.api, tag_type_fields)
154 tag_type_id = tag_type['tag_type_id']
156 # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
157 filter = {'tag_type_id':tag_type_id}
158 if isinstance (id_or_name,int):
159 filter[primary_key]=id_or_name
161 filter[secondary_key]=id_or_name
162 joins = joins_class (self.api,filter)
163 # setting to something non void
164 if value is not None:
166 join = join_class (self.api)
167 join['tag_type_id']=tag_type_id
168 join[primary_key]=primary_id
172 joins[0]['value']=value
174 # providing an empty value means clean up
180 self.event_objects= { objclass.__name__ : [primary_id] }
181 self.message=objclass.__name__
182 if secondary_key in objs[0]:
183 self.message += " %s "%objs[0][secondary_key]
185 self.message += " %d "%objs[0][primary_key]
186 self.message += "updated"
189 setattr (set_class,"call",set_call)
192 setattr(module,get_name,get_class)
193 setattr(module,set_name,set_class)
194 # add in <module>.methods
196 methods=getattr(module,'methods')
199 methods += [get_name,set_name]
200 setattr(module,'methods',methods)