more accessors, and site-local might work now
[plcapi.git] / PLC / Accessors / Factory.py
1 # Thierry Parmentelat - INRIA
2 # $Id$
3
4 from types import NoneType
5
6 from PLC.Method import Method
7 from PLC.Auth import Auth
8 from PLC.Parameter import Parameter, Mixed
9
10 from PLC.Faults import *
11
12 from PLC.Nodes import Nodes, Node
13 from PLC.NodeTags import NodeTags, NodeTag
14 from PLC.Interfaces import Interfaces, Interface
15 from PLC.InterfaceSettings import InterfaceSettings, InterfaceSetting
16 from PLC.Slices import Slices, Slice
17 from PLC.SliceAttributes import SliceAttributes, SliceAttribute
18
19 # this is another story..
20 #from PLC.Ilinks import Ilink
21
22 from PLC.TagTypes import TagTypes, TagType
23
24 # known classes : { class -> secondary_key }
25 taggable_classes = { Node : {'table_class' : Nodes, 
26                              'joins_class' : NodeTags, 'join_class' : NodeTag,
27                              'value_key': 'tagvalue', 'secondary_key': 'hostname'},
28                      Interface : {'table_class' : Interfaces, 
29                                   'joins_class': InterfaceSettings, 'join_class': InterfaceSetting,
30                                   'value_key' : 'value' }, 
31                      Slice: {'table_class' : Slices, 
32                              'joins_class': SliceAttributes, 'join_class': SliceAttribute,
33                              'value_key' : 'value', 'secondary_key':'login_base'},
34 #                     Ilink : xxx
35                      }
36
37 # xxx probably defined someplace else
38 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
39 tech_roles = [ 'admin', 'pi', 'tech' ]
40
41 # generates 2 method classes:
42 # Get<classname><methodsuffix> (auth, id_or_name) -> tagvalue or None
43 # Set<classname><methodsuffix> (auth, id_or_name, tagvalue) -> None
44 # tagvalue is always a string, no cast nor typecheck for now
45 #
46 # note: tag_min_role_id gets attached to the tagtype instance, 
47 # while get_roles and set_roles get attached to the created methods
48
49 # returns a tuple (get_method, set_method)
50 # See Accessors* for examples
51
52 def define_accessors (module, objclass, methodsuffix, 
53                       tagname, category, description, tag_min_role_id=10,
54                       get_roles=['admin'], set_roles=['admin']):
55     
56     if objclass not in taggable_classes:
57         try:
58             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
59         except:
60             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
61     classname=objclass.__name__
62     get_name = "Get" + classname + methodsuffix
63     set_name = "Set" + classname + methodsuffix
64
65     # create method objects under PLC.Method.Method
66     get_class = type (get_name, (Method,),
67                       {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
68                            (classname,tagname)})
69     set_class = type (set_name, (Method,),
70                       {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
71                            (classname,tagname)})
72     # accepts 
73     get_accepts = [ Auth () ]
74     primary_key=objclass.primary_key
75     try:
76         secondary_key = taggable_classes[objclass]['secondary_key']
77         get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
78     except:
79         secondary_key = None
80         get_accepts += [ objclass.fields[primary_key] ]
81     # for set, idem set of arguments + one additional arg, the new value
82     set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
83
84     # returns
85     get_returns = Mixed (Parameter (str), Parameter(NoneType))
86     set_returns = Parameter(NoneType)
87
88     # store in classes
89     setattr(get_class,'roles',get_roles)
90     setattr(get_class,'accepts',get_accepts)
91     setattr(get_class,'returns', get_returns)
92     setattr(get_class,'skip_typecheck',True)
93
94     setattr(set_class,'roles',set_roles)
95     setattr(set_class,'accepts',set_accepts)
96     setattr(set_class,'returns', set_returns)
97     setattr(set_class,'skip_typecheck',True)
98     
99     table_class = taggable_classes[objclass]['table_class']
100     joins_class = taggable_classes[objclass]['joins_class']
101     join_class = taggable_classes[objclass]['join_class']
102     value_key = taggable_classes[objclass]['value_key']
103
104     # body of the get method
105     def get_call (self, auth, id_or_name):
106         # search the tagtype - xxx - might need a cache
107         tag_types = TagTypes (self.api, {'tagname': tagname})
108         if not tag_types:
109             return None
110         tag_type_id = tag_types[0]['tag_type_id']
111         filter = {'tag_type_id':tag_type_id}
112         if isinstance (id_or_name,int):
113             filter[primary_key]=id_or_name
114         else:
115             filter[secondary_key]=id_or_name
116         joins = joins_class (self.api,filter,[value_key])
117         if not joins:
118             # xxx - we return None even if id_or_name is not valid 
119             return None
120         else:
121             return joins[0][value_key]
122
123     # attach it
124     setattr (get_class,"call",get_call)
125
126     # body of the set method 
127     def set_call (self, auth, id_or_name, tagvalue):
128         # locate the object
129         if isinstance (id_or_name, int):
130             filter={primary_key:id_or_name}
131         else:
132             filter={secondary_key:id_or_name}
133         objs = table_class(self.api, filter,[primary_key])
134         if not objs:
135             raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
136         primary_id = objs[0][primary_key]
137                            
138         # search tag type & create if needed
139         tag_types = TagTypes (self.api, {'tagname':tagname})
140         if tag_types:
141             tag_type = tag_types[0]
142         else:
143             # not found: create it
144             tag_type_fields = {'tagname':tagname, 
145                                'category' :  category,
146                                'description' : description,
147                                'min_role_id': tag_min_role_id}
148             tag_type = TagType (self.api, tag_type_fields)
149             tag_type.sync()
150         tag_type_id = tag_type['tag_type_id']
151
152         # locate the join object (e.g. NodeTag, SliceAttribute or InterfaceSetting)
153         filter = {'tag_type_id':tag_type_id}
154         if isinstance (id_or_name,int):
155             filter[primary_key]=id_or_name
156         else:
157             filter[secondary_key]=id_or_name
158         joins = joins_class (self.api,filter)
159         # setting to something non void
160         if tagvalue is not None:
161             if not joins:
162                 join = join_class (self.api)
163                 join['tag_type_id']=tag_type_id
164                 join[primary_key]=primary_id
165                 join[value_key]=tagvalue
166                 join.sync()
167             else:
168                 joins[0][value_key]=tagvalue
169                 joins[0].sync()
170         # providing an empty value means clean up
171         else:
172             if joins:
173                 join=joins[0]
174                 join.delete()
175
176     # attach it
177     setattr (set_class,"call",set_call)
178
179     # define in module
180     setattr(module,get_name,get_class)
181     setattr(module,set_name,set_class)
182     # add in <module>.methods
183     try:
184         methods=getattr(module,'methods')
185     except:
186         methods=[]
187     methods += [get_name,set_name]
188     setattr(module,'methods',methods)
189