Statistics
| Branch: | Tag: | Revision:

root / api / handlers.py @ 166bf271

History | View | Annotate | Download (10.8 kB)

1 00b4f1be Faidon Liambotis
# vim: ts=4 sts=4 et ai sw=4 fileencoding=utf-8
2 00b4f1be Faidon Liambotis
#
3 00b4f1be Faidon Liambotis
# Copyright © 2010 Greek Research and Technology Network
4 00b4f1be Faidon Liambotis
#
5 00b4f1be Faidon Liambotis
6 2c089b77 Faidon Liambotis
from django.conf import settings
7 00b4f1be Faidon Liambotis
from piston.handler import BaseHandler, AnonymousBaseHandler
8 635cfd6e Faidon Liambotis
from synnefo.api.faults import fault, noContent, accepted, created
9 2c089b77 Faidon Liambotis
from synnefo.api.helpers import instance_to_server, paginator
10 909f415b Faidon Liambotis
from synnefo.util.rapi import GanetiRapiClient, GanetiApiError
11 b8d16d68 Markos Gogoulos
from synnefo.db.models import VirtualMachine, Flavor, Image, User, id_from_instance_name
12 ccbd9f9b Markos Gogoulos
from util.rapi import GanetiRapiClient
13 2ed31bce Markos Gogoulos
14 253f0c82 Faidon Liambotis
15 ec06b07c Dimitris Moraitis
if settings.GANETI_CLUSTER_INFO:
16 ec06b07c Dimitris Moraitis
    rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
17 ec06b07c Dimitris Moraitis
else:
18 ec06b07c Dimitris Moraitis
    rapi = None
19 00b4f1be Faidon Liambotis
20 9dbe70f8 Markos Gogoulos
ganeti_prefix_id = settings.GANETI_PREFIX_ID
21 ccbd9f9b Markos Gogoulos
22 00b4f1be Faidon Liambotis
VERSIONS = [
23 00b4f1be Faidon Liambotis
    {
24 00b4f1be Faidon Liambotis
        "status": "CURRENT",
25 00b4f1be Faidon Liambotis
        "id": "v1.0",
26 00b4f1be Faidon Liambotis
        "docURL" : "http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20090714.pdf ",
27 00b4f1be Faidon Liambotis
        "wadl" : "http://docs.rackspacecloud.com/servers/api/v1.0/application.wadl"
28 00b4f1be Faidon Liambotis
    },
29 00b4f1be Faidon Liambotis
]
30 00b4f1be Faidon Liambotis
31 ccbd9f9b Markos Gogoulos
32 00b4f1be Faidon Liambotis
class VersionHandler(AnonymousBaseHandler):
33 00b4f1be Faidon Liambotis
    allowed_methods = ('GET',)
34 00b4f1be Faidon Liambotis
35 00b4f1be Faidon Liambotis
    def read(self, request, number=None):
36 00b4f1be Faidon Liambotis
        if number is None:
37 00b4f1be Faidon Liambotis
            versions = map(lambda v: {
38 00b4f1be Faidon Liambotis
                        "status": v["status"],
39 00b4f1be Faidon Liambotis
                        "id": v["id"],
40 00b4f1be Faidon Liambotis
                    }, VERSIONS)
41 00b4f1be Faidon Liambotis
            return { "versions": versions }
42 00b4f1be Faidon Liambotis
        else:
43 00b4f1be Faidon Liambotis
            for version in VERSIONS:
44 00b4f1be Faidon Liambotis
                if version["id"] == number:
45 00b4f1be Faidon Liambotis
                    return { "version": version }
46 635cfd6e Faidon Liambotis
            raise fault.itemNotFound
47 00b4f1be Faidon Liambotis
48 00b4f1be Faidon Liambotis
49 00b4f1be Faidon Liambotis
class ServerHandler(BaseHandler):
50 fd4828ce Faidon Liambotis
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
51 fd4828ce Faidon Liambotis
52 00b4f1be Faidon Liambotis
    def read(self, request, id=None):
53 38e2c5ed Christodoulos Psaltis
        from time import sleep
54 38e2c5ed Christodoulos Psaltis
        sleep(1)
55 ccbd9f9b Markos Gogoulos
        #TODO: delete the sleep once the mock objects are removed
56 00b4f1be Faidon Liambotis
        if id is None:
57 00b4f1be Faidon Liambotis
            return self.read_all(request)
58 253f0c82 Faidon Liambotis
        elif id == "detail":
59 00b4f1be Faidon Liambotis
            return self.read_all(request, detail=True)
60 00b4f1be Faidon Liambotis
        else:
61 00b4f1be Faidon Liambotis
            return self.read_one(request, id)
62 00b4f1be Faidon Liambotis
63 00b4f1be Faidon Liambotis
    def read_one(self, request, id):
64 ec06b07c Dimitris Moraitis
        if not rapi: # No ganeti backend. Return mock objects
65 2cc9d3a5 Markos Gogoulos
            servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
66 2cc9d3a5 Markos Gogoulos
            return { "server": servers[0] }
67 909f415b Faidon Liambotis
        try:
68 909f415b Faidon Liambotis
            instance = rapi.GetInstance(id)
69 909f415b Faidon Liambotis
            return { "server": instance_to_server(instance) }
70 909f415b Faidon Liambotis
        except GanetiApiError:
71 909f415b Faidon Liambotis
            raise fault.itemNotFound
72 00b4f1be Faidon Liambotis
73 2c089b77 Faidon Liambotis
    @paginator
74 00b4f1be Faidon Liambotis
    def read_all(self, request, detail=False):
75 ec06b07c Dimitris Moraitis
        if not rapi: # No ganeti backend. Return mock objects
76 ec06b07c Dimitris Moraitis
            if detail:
77 2ed31bce Markos Gogoulos
                virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
78 2ed31bce Markos Gogoulos
                #get the first user, since we don't have any user data yet
79 2ed31bce Markos Gogoulos
                virtual_servers_list = [{'status': server.state, 'flavorId': server.flavor, \
80 2ed31bce Markos Gogoulos
                    'name': server.name, 'id': server.id, 'imageId': server.imageid, 
81 2ed31bce Markos Gogoulos
                    'metadata': {'Server_Label': server.server_label, \
82 2ed31bce Markos Gogoulos
                    'Image_Version': server.image_version}, \
83 2ed31bce Markos Gogoulos
                    'hostId': '9e107d9d372bb6826bd81d3542a419d6',  \
84 2ed31bce Markos Gogoulos
                    'addresses': {'public': ['67.23.10.133'], 'private': ['10.176.42.17']}} \
85 2ed31bce Markos Gogoulos
                        for server in virtual_servers]
86 2ed31bce Markos Gogoulos
                #pass some fake data regarding ip, since we don't have any such data
87 2ed31bce Markos Gogoulos
                return { "servers":  virtual_servers_list }                
88 ec06b07c Dimitris Moraitis
            else:
89 2cc9d3a5 Markos Gogoulos
                virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
90 2cc9d3a5 Markos Gogoulos
                return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] }
91 ec06b07c Dimitris Moraitis
92 00b4f1be Faidon Liambotis
        if not detail:
93 253f0c82 Faidon Liambotis
            instances = rapi.GetInstances(bulk=False)
94 253f0c82 Faidon Liambotis
            servers = [ { "id": id, "name": id } for id in instances ]
95 00b4f1be Faidon Liambotis
        else:
96 253f0c82 Faidon Liambotis
            instances = rapi.GetInstances(bulk=True)
97 253f0c82 Faidon Liambotis
            servers = []
98 253f0c82 Faidon Liambotis
            for instance in instances:
99 253f0c82 Faidon Liambotis
                servers.append(instance_to_server(instance))
100 253f0c82 Faidon Liambotis
        return { "servers": servers }
101 00b4f1be Faidon Liambotis
102 00b4f1be Faidon Liambotis
    def create(self, request):
103 8400e1a5 Markos Gogoulos
        print 'create machine was called'
104 8400e1a5 Markos Gogoulos
        rapi.CreateInstance('create', 'machine-unwebXYZ', 'plain', [{"size": 5120}], [{}], os='debootstrap+default', ip_check=False, name_check=False,pnode="store68", beparams={'auto_balance': True, 'vcpus': 2, 'memory': 1024})
105 8400e1a5 Markos Gogoulos
        #TODO: replace with real data from request.POST
106 00b4f1be Faidon Liambotis
        return accepted
107 00b4f1be Faidon Liambotis
108 00b4f1be Faidon Liambotis
    def update(self, request, id):
109 00b4f1be Faidon Liambotis
        return noContent
110 00b4f1be Faidon Liambotis
111 00b4f1be Faidon Liambotis
    def delete(self, request, id):
112 8400e1a5 Markos Gogoulos
        machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id))
113 ccbd9f9b Markos Gogoulos
        print 'deleting machine %s' % machine
114 ccbd9f9b Markos Gogoulos
        rapi.DeleteInstance(machine.name)
115 00b4f1be Faidon Liambotis
        return accepted
116 00b4f1be Faidon Liambotis
117 00b4f1be Faidon Liambotis
118 00b4f1be Faidon Liambotis
class ServerAddressHandler(BaseHandler):
119 00b4f1be Faidon Liambotis
    allowed_methods = ('GET', 'PUT', 'DELETE')
120 00b4f1be Faidon Liambotis
121 00b4f1be Faidon Liambotis
    def read(self, request, id, type=None):
122 00b4f1be Faidon Liambotis
        """List IP addresses for a server"""
123 ccbd9f9b Markos Gogoulos
124 00b4f1be Faidon Liambotis
        if type is None:
125 00b4f1be Faidon Liambotis
            pass
126 00b4f1be Faidon Liambotis
        elif type == "private":
127 00b4f1be Faidon Liambotis
            pass
128 00b4f1be Faidon Liambotis
        elif type == "public":
129 00b4f1be Faidon Liambotis
            pass
130 00b4f1be Faidon Liambotis
        return {}
131 00b4f1be Faidon Liambotis
132 00b4f1be Faidon Liambotis
    def update(self, request, id, address):
133 00b4f1be Faidon Liambotis
        """Share an IP address to another in the group"""
134 00b4f1be Faidon Liambotis
        return accepted
135 00b4f1be Faidon Liambotis
136 00b4f1be Faidon Liambotis
    def delete(self, request, id, address):
137 00b4f1be Faidon Liambotis
        """Unshare an IP address"""
138 00b4f1be Faidon Liambotis
        return accepted
139 00b4f1be Faidon Liambotis
140 00b4f1be Faidon Liambotis
141 00b4f1be Faidon Liambotis
class ServerActionHandler(BaseHandler):
142 ccbd9f9b Markos Gogoulos
    allowed_methods = ('POST', 'DELETE', 'GET', 'PUT')
143 ccbd9f9b Markos Gogoulos
#TODO: remove GET/PUT
144 ccbd9f9b Markos Gogoulos
    
145 ccbd9f9b Markos Gogoulos
    def read(self, request, id):
146 ccbd9f9b Markos Gogoulos
        return accepted
147 00b4f1be Faidon Liambotis
148 00b4f1be Faidon Liambotis
    def create(self, request, id):
149 00b4f1be Faidon Liambotis
        """Reboot, rebuild, resize, confirm resized, revert resized"""
150 8400e1a5 Markos Gogoulos
        machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id))
151 d66446ce Markos Gogoulos
        reboot_request = request.POST.get('reboot', None)
152 d66446ce Markos Gogoulos
        shutdown_request = request.POST.get('shutdown', None)
153 d66446ce Markos Gogoulos
        if reboot_request:
154 d66446ce Markos Gogoulos
            print 'reboot was asked, with options: %s' % reboot_request   
155 8400e1a5 Markos Gogoulos
            rapi.RebootInstance(machine)
156 d66446ce Markos Gogoulos
        elif shutdown_request:
157 d66446ce Markos Gogoulos
            print 'shutdown was asked, with options: %s' % shutdown_request               
158 8400e1a5 Markos Gogoulos
            rapi.ShutdownInstance(machine)
159 00b4f1be Faidon Liambotis
        return accepted
160 427a9220 Faidon Liambotis
161 427a9220 Faidon Liambotis
162 ccbd9f9b Markos Gogoulos
    def delete(self, request, id):
163 ccbd9f9b Markos Gogoulos
        """Delete an Instance"""
164 ccbd9f9b Markos Gogoulos
        return accepted
165 ccbd9f9b Markos Gogoulos
166 ccbd9f9b Markos Gogoulos
    def update(self, request, id):
167 ccbd9f9b Markos Gogoulos
        return noContent
168 ccbd9f9b Markos Gogoulos
169 ccbd9f9b Markos Gogoulos
170 ccbd9f9b Markos Gogoulos
171 ccbd9f9b Markos Gogoulos
#read is called on GET requests
172 ccbd9f9b Markos Gogoulos
#create is called on POST, and creates new objects, and should return them (or rc.CREATED.)
173 ccbd9f9b Markos Gogoulos
#update is called on PUT, and should update an existing product and return them (or rc.ALL_OK.)
174 ccbd9f9b Markos Gogoulos
#delete is called on DELETE, and should delete an existing object. Should not return anything, just rc.DELETED.'''
175 ccbd9f9b Markos Gogoulos
176 ccbd9f9b Markos Gogoulos
177 c99adc90 Faidon Liambotis
class ServerBackupHandler(BaseHandler):
178 c99adc90 Faidon Liambotis
    """ Backup Schedules are not implemented yet, return notImplemented """
179 c99adc90 Faidon Liambotis
    allowed_methods = ('GET', 'POST', 'DELETE')
180 c99adc90 Faidon Liambotis
181 c99adc90 Faidon Liambotis
    def read(self, request, id):
182 635cfd6e Faidon Liambotis
        raise fault.notImplemented
183 c99adc90 Faidon Liambotis
184 c99adc90 Faidon Liambotis
    def create(self, request, id):
185 635cfd6e Faidon Liambotis
        raise fault.notImplemented
186 c99adc90 Faidon Liambotis
187 c99adc90 Faidon Liambotis
    def delete(self, request, id):
188 635cfd6e Faidon Liambotis
        raise fault.notImplemented
189 c99adc90 Faidon Liambotis
190 c99adc90 Faidon Liambotis
191 427a9220 Faidon Liambotis
class FlavorHandler(BaseHandler):
192 427a9220 Faidon Liambotis
    allowed_methods = ('GET',)
193 b8d16d68 Markos Gogoulos
    flavors = Flavor.objects.all()
194 b8d16d68 Markos Gogoulos
    flavors = [ {'id': flavor.id, 'name': flavor.name, 'ram': flavor.ram, \
195 b8d16d68 Markos Gogoulos
             'disk': flavor.disk} for flavor in flavors]
196 427a9220 Faidon Liambotis
197 427a9220 Faidon Liambotis
    def read(self, request, id=None):
198 dcc6a862 Faidon Liambotis
        """
199 dcc6a862 Faidon Liambotis
        List flavors or retrieve one
200 427a9220 Faidon Liambotis

201 427a9220 Faidon Liambotis
        Returns: OK
202 dcc6a862 Faidon Liambotis
        Faults: cloudServersFault, serviceUnavailable, unauthorized,
203 dcc6a862 Faidon Liambotis
                badRequest, itemNotFound
204 427a9220 Faidon Liambotis
        """
205 427a9220 Faidon Liambotis
        if id is None:
206 427a9220 Faidon Liambotis
            simple = map(lambda v: {
207 b8d16d68 Markos Gogoulos
                        "id": v['id'],
208 b8d16d68 Markos Gogoulos
                        "name": v['name'],
209 427a9220 Faidon Liambotis
                    }, self.flavors)
210 427a9220 Faidon Liambotis
            return { "flavors": simple }
211 427a9220 Faidon Liambotis
        elif id == "detail":
212 427a9220 Faidon Liambotis
            return { "flavors": self.flavors }
213 427a9220 Faidon Liambotis
        else:
214 427a9220 Faidon Liambotis
            for flavor in self.flavors:
215 b8d16d68 Markos Gogoulos
                if str(flavor['id']) == id:
216 427a9220 Faidon Liambotis
                    return { "flavor": flavor }
217 635cfd6e Faidon Liambotis
            raise fault.itemNotFound
218 dcc6a862 Faidon Liambotis
219 dcc6a862 Faidon Liambotis
220 dcc6a862 Faidon Liambotis
class ImageHandler(BaseHandler):
221 dcc6a862 Faidon Liambotis
    allowed_methods = ('GET', 'POST')
222 dcc6a862 Faidon Liambotis
223 dcc6a862 Faidon Liambotis
    def read(self, request, id=None):
224 dcc6a862 Faidon Liambotis
        """
225 dcc6a862 Faidon Liambotis
        List images or retrieve one
226 dcc6a862 Faidon Liambotis

227 dcc6a862 Faidon Liambotis
        Returns: OK
228 dcc6a862 Faidon Liambotis
        Faults: cloudServersFault, serviceUnavailable, unauthorized,
229 dcc6a862 Faidon Liambotis
                badRequest, itemNotFound
230 dcc6a862 Faidon Liambotis
        """
231 2cc9d3a5 Markos Gogoulos
        images = Image.objects.all()
232 166bf271 Markos Gogoulos
        images = [ {'created': image.created.isoformat(), 'id': image.id, \
233 166bf271 Markos Gogoulos
              'name': image.name, 'updated': image.updated.isoformat(), \
234 166bf271 Markos Gogoulos
               'description': image.description, 'state': image.state, 'serverid': image.serverid, \
235 166bf271 Markos Gogoulos
               'vm_id': image.vm_id} for image in images]
236 166bf271 Markos Gogoulos
237 ec06b07c Dimitris Moraitis
        if not rapi: # No ganeti backend. Return mock objects
238 ec06b07c Dimitris Moraitis
            if id == "detail":
239 2cc9d3a5 Markos Gogoulos
                return { "images": images }
240 ec06b07c Dimitris Moraitis
            elif id is None:
241 166bf271 Markos Gogoulos
                return { "images": [ { "id": s['id'], "name": s['name'] } for s in images ] }
242 ec06b07c Dimitris Moraitis
            else:
243 2cc9d3a5 Markos Gogoulos
                return { "image": images[0] }
244 dcc6a862 Faidon Liambotis
        if id is None:
245 dcc6a862 Faidon Liambotis
            return {}
246 dcc6a862 Faidon Liambotis
        elif id == "detail":
247 dcc6a862 Faidon Liambotis
            return {}
248 dcc6a862 Faidon Liambotis
        else:
249 635cfd6e Faidon Liambotis
            raise fault.itemNotFound
250 dcc6a862 Faidon Liambotis
251 dcc6a862 Faidon Liambotis
    def create(self, request):
252 dcc6a862 Faidon Liambotis
        """Create a new image"""
253 dcc6a862 Faidon Liambotis
        return accepted
254 ed66d976 Faidon Liambotis
255 ed66d976 Faidon Liambotis
256 ed66d976 Faidon Liambotis
class SharedIPGroupHandler(BaseHandler):
257 ed66d976 Faidon Liambotis
    allowed_methods = ('GET', 'POST', 'DELETE')
258 ed66d976 Faidon Liambotis
259 ed66d976 Faidon Liambotis
    def read(self, request, id=None):
260 ed66d976 Faidon Liambotis
        """List Shared IP Groups"""
261 ed66d976 Faidon Liambotis
        if id is None:
262 ed66d976 Faidon Liambotis
            return {}
263 ed66d976 Faidon Liambotis
        elif id == "detail":
264 ed66d976 Faidon Liambotis
            return {}
265 ed66d976 Faidon Liambotis
        else:
266 635cfd6e Faidon Liambotis
            raise fault.itemNotFound
267 ed66d976 Faidon Liambotis
268 ed66d976 Faidon Liambotis
    def create(self, request, id):
269 ed66d976 Faidon Liambotis
        """Creates a new Shared IP Group"""
270 ed66d976 Faidon Liambotis
        return created
271 ed66d976 Faidon Liambotis
272 ed66d976 Faidon Liambotis
    def delete(self, request, id):
273 ed66d976 Faidon Liambotis
        """Deletes a Shared IP Group"""
274 ed66d976 Faidon Liambotis
        return noContent
275 beb79c95 Faidon Liambotis
276 beb79c95 Faidon Liambotis
277 beb79c95 Faidon Liambotis
class LimitHandler(BaseHandler):
278 beb79c95 Faidon Liambotis
    allowed_methods = ('GET',)
279 beb79c95 Faidon Liambotis
280 beb79c95 Faidon Liambotis
    # XXX: hookup with @throttle
281 beb79c95 Faidon Liambotis
282 beb79c95 Faidon Liambotis
    rate = [
283 beb79c95 Faidon Liambotis
        {
284 beb79c95 Faidon Liambotis
           "verb" : "POST",
285 beb79c95 Faidon Liambotis
           "URI" : "*",
286 beb79c95 Faidon Liambotis
           "regex" : ".*",
287 beb79c95 Faidon Liambotis
           "value" : 10,
288 beb79c95 Faidon Liambotis
           "remaining" : 2,
289 beb79c95 Faidon Liambotis
           "unit" : "MINUTE",
290 beb79c95 Faidon Liambotis
           "resetTime" : 1244425439
291 beb79c95 Faidon Liambotis
        },
292 beb79c95 Faidon Liambotis
        {
293 beb79c95 Faidon Liambotis
           "verb" : "POST",
294 beb79c95 Faidon Liambotis
           "URI" : "*/servers",
295 beb79c95 Faidon Liambotis
           "regex" : "^/servers",
296 beb79c95 Faidon Liambotis
           "value" : 25,
297 beb79c95 Faidon Liambotis
           "remaining" : 24,
298 beb79c95 Faidon Liambotis
           "unit" : "DAY",
299 beb79c95 Faidon Liambotis
           "resetTime" : 1244511839
300 beb79c95 Faidon Liambotis
        },
301 beb79c95 Faidon Liambotis
        {
302 beb79c95 Faidon Liambotis
           "verb" : "PUT",
303 beb79c95 Faidon Liambotis
           "URI" : "*",
304 beb79c95 Faidon Liambotis
           "regex" : ".*",
305 beb79c95 Faidon Liambotis
           "value" : 10,
306 beb79c95 Faidon Liambotis
           "remaining" : 2,
307 beb79c95 Faidon Liambotis
           "unit" : "MINUTE",
308 beb79c95 Faidon Liambotis
           "resetTime" : 1244425439
309 beb79c95 Faidon Liambotis
        },
310 beb79c95 Faidon Liambotis
        {
311 beb79c95 Faidon Liambotis
           "verb" : "GET",
312 beb79c95 Faidon Liambotis
           "URI" : "*",
313 beb79c95 Faidon Liambotis
           "regex" : ".*",
314 beb79c95 Faidon Liambotis
           "value" : 3,
315 beb79c95 Faidon Liambotis
           "remaining" : 3,
316 beb79c95 Faidon Liambotis
           "unit" : "MINUTE",
317 beb79c95 Faidon Liambotis
           "resetTime" : 1244425439
318 beb79c95 Faidon Liambotis
        },
319 beb79c95 Faidon Liambotis
        {
320 beb79c95 Faidon Liambotis
           "verb" : "DELETE",
321 beb79c95 Faidon Liambotis
           "URI" : "*",
322 beb79c95 Faidon Liambotis
           "regex" : ".*",
323 beb79c95 Faidon Liambotis
           "value" : 100,
324 beb79c95 Faidon Liambotis
           "remaining" : 100,
325 beb79c95 Faidon Liambotis
           "unit" : "MINUTE",
326 beb79c95 Faidon Liambotis
           "resetTime" : 1244425439
327 beb79c95 Faidon Liambotis
        }
328 beb79c95 Faidon Liambotis
    ]
329 beb79c95 Faidon Liambotis
330 beb79c95 Faidon Liambotis
    absolute = {
331 beb79c95 Faidon Liambotis
        "maxTotalRAMSize" : 51200,
332 beb79c95 Faidon Liambotis
        "maxIPGroups" : 50,
333 beb79c95 Faidon Liambotis
        "maxIPGroupMembers" : 25
334 beb79c95 Faidon Liambotis
    }
335 beb79c95 Faidon Liambotis
336 beb79c95 Faidon Liambotis
    def read(self, request):
337 beb79c95 Faidon Liambotis
        return { "limits": {
338 beb79c95 Faidon Liambotis
                "rate": self.rate,
339 beb79c95 Faidon Liambotis
                "absolute": self.absolute,
340 beb79c95 Faidon Liambotis
               }
341 beb79c95 Faidon Liambotis
            }