keep things simple, use 'value' rather than 'tagvalue' everywhere
[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.InterfaceTags import InterfaceTags, InterfaceTag
16 from PLC.Slices import Slices, Slice
17 from PLC.SliceTags import SliceTags, SliceTag
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                              'secondary_key': 'hostname'},
28                      Interface : {'table_class' : Interfaces, 
29                                   'joins_class': InterfaceTags, 'join_class': InterfaceTag,
30                                   },
31                      Slice: {'table_class' : Slices, 
32                              'joins_class': SliceTags, 'join_class': SliceTag,
33                              '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) -> value or None
43 # Set<classname><methodsuffix> (auth, id_or_name, value) -> None
44 # value 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
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'])
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']
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, value):
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         tag_type_id = tag_type['tag_type_id']
150
151         # locate the join object (e.g. NodeTag, SliceTag or InterfaceTag)
152         filter = {'tag_type_id':tag_type_id}
153         if isinstance (id_or_name,int):
154             filter[primary_key]=id_or_name
155         else:
156             filter[secondary_key]=id_or_name
157         joins = joins_class (self.api,filter)
158         # setting to something non void
159         if value is not None:
160             if not joins:
161                 join = join_class (self.api)
162                 join['tag_type_id']=tag_type_id
163                 join[primary_key]=primary_id
164                 join['value']=value
165                 join.sync()
166             else:
167                 joins[0]['value']=value
168                 joins[0].sync()
169         # providing an empty value means clean up
170         else:
171             if joins:
172                 join=joins[0]
173                 join.delete()
174
175     # attach it
176     setattr (set_class,"call",set_call)
177
178     # define in module
179     setattr(module,get_name,get_class)
180     setattr(module,set_name,set_class)
181     # add in <module>.methods
182     try:
183         methods=getattr(module,'methods')
184     except:
185         methods=[]
186     methods += [get_name,set_name]
187     setattr(module,'methods',methods)
188