get/set accessors should now work, e.g.:
[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
40 # generates 2 method classes:
41 # Get<classname><methodsuffix> (auth, id_or_name) -> tagvalue or None
42 # Set<classname><methodsuffix> (auth, id_or_name, tagvalue) -> None
43 # tagvalue is always a string, no cast nor typecheck for now
44 #
45 # note: tag_min_role_id gets attached to the tagtype instance, 
46 # while get_roles and set_roles get attached to the created methods
47
48 # returns a tuple (get_method, set_method)
49 # See Accessors* for examples
50
51 def get_set_factory (objclass, methodsuffix, 
52                      tagname, category, description, tag_min_role_id=10,
53                      get_roles=['admin'], set_roles=['admin']):
54     
55     if objclass not in taggable_classes:
56         try:
57             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
58         except:
59             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
60     classname=objclass.__name__
61     get_name = "Get" + classname + methodsuffix
62     set_name = "Set" + classname + methodsuffix
63
64     # create method objects under PLC.Method.Method
65     get_class = type (get_name, (Method,),
66                       {"__doc__":"Accessor 'get' method designed for %s objects using tag %s"%\
67                            (classname,tagname)})
68     set_class = type (set_name, (Method,),
69                       {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
70                            (classname,tagname)})
71     # accepts 
72     get_accepts = [ Auth () ]
73     primary_key=objclass.primary_key
74     try:
75         secondary_key = taggable_classes[objclass]['secondary_key']
76         get_accepts += [ Mixed (objclass.fields[primary_key], objclass.fields[secondary_key]) ]
77     except:
78         secondary_key = None
79         get_accepts += [ objclass.fields[primary_key] ]
80     # for set, idem set of arguments + one additional arg, the new value
81     set_accepts = get_accepts + [ Parameter (str,"New tag value") ]
82
83     # returns
84     get_returns = Mixed (Parameter (str), Parameter(NoneType))
85     set_returns = Parameter(NoneType)
86
87     # store in classes
88     setattr(get_class,'roles',get_roles)
89     setattr(get_class,'accepts',get_accepts)
90     setattr(get_class,'returns', get_returns)
91     setattr(get_class,'skip_typecheck',True)
92
93     setattr(set_class,'roles',set_roles)
94     setattr(set_class,'accepts',set_accepts)
95     setattr(set_class,'returns', set_returns)
96     setattr(set_class,'skip_typecheck',True)
97     
98     table_class = taggable_classes[objclass]['table_class']
99     joins_class = taggable_classes[objclass]['joins_class']
100     join_class = taggable_classes[objclass]['join_class']
101     value_key = taggable_classes[objclass]['value_key']
102
103     # body of the get method
104     def get_call (self, auth, id_or_name):
105         # search the tagtype - xxx - might need a cache
106         tag_types = TagTypes (self.api, {'tagname': tagname})
107         if not tag_types:
108             return None
109         tag_type_id = tag_types[0]['tag_type_id']
110         filter = {'tag_type_id':tag_type_id}
111         if isinstance (id_or_name,int):
112             filter[primary_key]=id_or_name
113         else:
114             filter[secondary_key]=id_or_name
115         joins = joins_class (self.api,filter,[value_key])
116         if not joins:
117             # xxx - we return None even if id_or_name is not valid 
118             return None
119         else:
120             return joins[0][value_key]
121
122     # attach it
123     setattr (get_class,"call",get_call)
124
125     # body of the set method 
126     def set_call (self, auth, id_or_name, tagvalue):
127         # locate the object
128         if isinstance (id_or_name, int):
129             filter={primary_key:id_or_name}
130         else:
131             filter={secondary_key:id_or_name}
132         objs = table_class(self.api, filter,[primary_key])
133         if not objs:
134             raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
135         primary_id = objs[0][primary_key]
136                            
137         # search tag type & create if needed
138         tag_types = TagTypes (self.api, {'tagname':tagname})
139         if tag_types:
140             tag_type = tag_types[0]
141         else:
142             # not found: create it
143             tag_type_fields = {'tagname':tagname, 
144                                'category' :  category,
145                                'description' : description,
146                                'min_role_id': tag_min_role_id}
147             tag_type = TagType (self.api, tag_type_fields)
148             tag_type.sync()
149         # proceed
150         tag_type_id = tag_type['tag_type_id']
151         filter = {'tag_type_id':tag_type_id}
152         if isinstance (id_or_name,int):
153             filter[primary_key]=id_or_name
154         else:
155             filter[secondary_key]=id_or_name
156         joins = joins_class (self.api,filter)
157         if not joins:
158             join = join_class (self.api)
159             join['tag_type_id']=tag_type_id
160             join[primary_key]=primary_id
161             join[value_key]=tagvalue
162             join.sync()
163         else:
164             joins[0][value_key]=tagvalue
165             joins[0].sync()
166
167     # attach it
168     setattr (set_class,"call",set_call)
169
170     return ( get_class, set_class )
171