4d0d506ea5178b03d177334e45e35886024dcad1
[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                                   'secondary_key' : 'ip'},
31                      Slice: {'table_class' : Slices, 
32                              'joins_class': SliceTags, 'join_class': SliceTag,
33                              'secondary_key':'name'},
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 #
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
46 #
47 # The expose_in_api flag tells whether this tag may be handled 
48 #   through the Add/Get/Update methods as a native field
49 #
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
53
54
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):
59     
60     if objclass not in taggable_classes:
61         try:
62             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class %s"%objclass.__name__
63         except:
64             raise PLCInvalidArgument,"PLC.Accessors.Factory: unknown class ??"
65
66     # side-effect on, say, Node.tags, if required
67     if expose_in_api:
68         getattr(objclass,'tags')[tagname]=Parameter(str,"accessor")
69
70     classname=objclass.__name__
71     get_name = "Get" + classname + methodsuffix
72     set_name = "Set" + classname + methodsuffix
73
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"%\
77                            (classname,tagname)})
78     set_class = type (set_name, (Method,),
79                       {"__doc__":"Accessor 'set' method designed for %s objects using tag %s"%\
80                            (classname,tagname)})
81     # accepts 
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") ]
88
89     # returns
90     get_returns = Mixed (Parameter (str), Parameter(NoneType))
91     set_returns = Parameter(NoneType)
92
93     # store in classes
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)
98
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)
103     
104     table_class = taggable_classes[objclass]['table_class']
105     joins_class = taggable_classes[objclass]['joins_class']
106     join_class = taggable_classes[objclass]['join_class']
107
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})
112         if not tag_types:
113             return None
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
118         else:
119             filter[secondary_key]=id_or_name
120         joins = joins_class (self.api,filter,['value'])
121         if not joins:
122             # xxx - we return None even if id_or_name is not valid 
123             return None
124         else:
125             return joins[0]['value']
126
127     # attach it
128     setattr (get_class,"call",get_call)
129
130     # body of the set method 
131     def set_call (self, auth, id_or_name, value):
132         # locate the object
133         if isinstance (id_or_name, int):
134             filter={primary_key:id_or_name}
135         else:
136             filter={secondary_key:id_or_name}
137         objs = table_class(self.api, filter,[primary_key,secondary_key])
138         if not objs:
139             raise PLCInvalidArgument, "Cannot set tag on %s %r"%(objclass.__name__,id_or_name)
140         primary_id = objs[0][primary_key]
141                            
142         # search tag type & create if needed
143         tag_types = TagTypes (self.api, {'tagname':tagname})
144         if tag_types:
145             tag_type = tag_types[0]
146         else:
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)
153             tag_type.sync()
154         tag_type_id = tag_type['tag_type_id']
155
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
160         else:
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:
165             if not joins:
166                 join = join_class (self.api)
167                 join['tag_type_id']=tag_type_id
168                 join[primary_key]=primary_id
169                 join['value']=value
170                 join.sync()
171             else:
172                 joins[0]['value']=value
173                 joins[0].sync()
174         # providing an empty value means clean up
175         else:
176             if joins:
177                 join=joins[0]
178                 join.delete()
179         # log it
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]
184         else:
185             self.message += " %d "%objs[0][primary_key]
186         self.message += "updated"
187
188     # attach it
189     setattr (set_class,"call",set_call)
190
191     # define in module
192     setattr(module,get_name,get_class)
193     setattr(module,set_name,set_class)
194     # add in <module>.methods
195     try:
196         methods=getattr(module,'methods')
197     except:
198         methods=[]
199     methods += [get_name,set_name]
200     setattr(module,'methods',methods)
201