svn keywords
[plcapi.git] / PLC / Accessors / Factory.py
1 # Thierry Parmentelat - INRIA
2 # $Id$
3 # $URL$
4
5 from types import NoneType
6
7 from PLC.Method import Method
8 from PLC.Auth import Auth
9 from PLC.Parameter import Parameter, Mixed
10
11 from PLC.Faults import *
12
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
19
20 # this is another story..
21 #from PLC.Ilinks import Ilink
22
23 from PLC.TagTypes import TagTypes, TagType
24
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'},
35 #                     Ilink : xxx
36                      }
37
38 # xxx probably defined someplace else
39 all_roles = [ 'admin', 'pi', 'tech', 'user', 'node' ]
40 tech_roles = [ 'admin', 'pi', 'tech' ]
41
42 #
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
47 #
48 # The expose_in_api flag tells whether this tag may be handled 
49 #   through the Add/Get/Update methods as a native field
50 #
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
54
55
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):
60     
61     if objclass not in taggable_classes:
62         try:
63             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
64         except:
65             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
66
67     # side-effect on, say, Node.tags, if required
68     if expose_in_api:
69         getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
70
71     classname=objclass.__name__
72     get_name = "Get" + classname + methodsuffix
73     set_name = "Set" + classname + methodsuffix
74
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"%\
78                            (classname,tagname)})
79     set_class = type (set_name, (Method,),
80                       {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
81                            (classname,tagname)})
82     # accepts 
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") ]
89
90     # returns
91     get_returns = Mixed (Parameter (str), Parameter(NoneType))
92     set_returns = Parameter(NoneType)
93
94     # store in classes
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)
99
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)
104     
105     table_class = taggable_classes[objclass]['table_class']
106     joins_class = taggable_classes[objclass]['joins_class']
107     join_class = taggable_classes[objclass]['join_class']
108
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})
113         if not tag_types:
114             return None
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
119         else:
120             filter[secondary_key]=id_or_name
121         joins = joins_class (self.api,filter,['value'])
122         if not joins:
123             # xxx - we return None even if id_or_name is not valid 
124             return None
125         else:
126             return joins[0]['value']
127
128     # attach it
129     setattr (get_class,"call",get_call)
130
131     # body of the set method 
132     def set_call (self, auth, id_or_name, value):
133         # locate the object
134         if isinstance (id_or_name, int):
135             filter={primary_key:id_or_name}
136         else:
137             filter={secondary_key:id_or_name}
138         objs = table_class(self.api, filter,[primary_key,secondary_key])
139         if not objs:
140             raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
141         primary_id = objs[0][primary_key]
142                            
143         # search tag type & create if needed
144         tag_types = TagTypes (self.api, {'tagname':tagname})
145         if tag_types:
146             tag_type = tag_types[0]
147         else:
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)
154             tag_type.sync()
155         tag_type_id = tag_type['tag_type_id']
156
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
161         else:
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:
166             if not joins:
167                 join = join_class (self.api)
168                 join['tag_type_id']=tag_type_id
169                 join[primary_key]=primary_id
170                 join['value']=value
171                 join.sync()
172             else:
173                 joins[0]['value']=value
174                 joins[0].sync()
175         # providing an empty value means clean up
176         else:
177             if joins:
178                 join=joins[0]
179                 join.delete()
180         # log it
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]
185         else:
186             self.message += " %d "%objs[0][primary_key]
187         self.message += "updated"
188
189     # attach it
190     setattr (set_class,"call",set_call)
191
192     # define in module
193     setattr(module,get_name,get_class)
194     setattr(module,set_name,set_class)
195     # add in <module>.methods
196     try:
197         methods=getattr(module,'methods')
198     except:
199         methods=[]
200     methods += [get_name,set_name]
201     setattr(module,'methods',methods)
202