prettified
[plcapi.git] / PLC / Methods / UpdateLeases.py
1 from __future__ import print_function
2
3 from PLC.Faults import *
4 from PLC.Method import Method
5 from PLC.Parameter import Parameter, Mixed
6 from PLC.Auth import Auth
7
8 from PLC.Timestamp import Timestamp, Duration
9
10 from PLC.Leases import Lease, Leases
11 from PLC.Slices import Slice, Slices
12
13 can_update = lambda (field, value): field in ['t_from', 't_until', 'duration']
14
15
16 class UpdateLeases(Method):
17     """
18     Updates the parameters of a (set of) existing lease(s) with the values in
19     lease_fields; specifically this applies to the timeslot definition.
20     As a convenience you may, in addition to the t_from and t_until fields,
21     you can also set the 'duration' field.
22
23     Users may only update leases attached to their slices.
24     PIs may update any of the leases for slices at their sites, or any
25     slices of which they are members. Admins may update any lease.
26
27     Returns a dict of successfully updated lease_ids and error messages.
28     """
29
30     roles = ['admin', 'pi', 'tech', 'user']
31
32     lease_fields = dict(filter(can_update, Lease.fields.items()))
33
34     accepts = [
35         Auth(),
36         Mixed(Lease.fields['lease_id'],
37               [Lease.fields['lease_id']]),
38         lease_fields
39     ]
40
41     returns = Parameter(
42         dict,
43         " 'updated_ids' is the list ids updated,"
44         "'errors' is a list of error strings")
45
46     debug = False
47 #    debug=True
48
49     def call(self, auth, lease_ids, input_fields):
50         input_fields = dict(filter(can_update, input_fields.items()))
51
52         if 'duration' in input_fields:
53             if 't_from' in input_fields and 't_until' in input_fields:
54                 raise PLCInvalidArgument(
55                     "Cannot set t_from AND t_until AND duration")
56             # specify 'duration':0 to keep duration unchanged
57             if input_fields['duration']:
58                 input_fields['duration'] = Duration.validate(
59                     input_fields['duration'])
60
61         # Get lease information
62         leases = Leases(self.api, lease_ids)
63         if not leases:
64             raise PLCInvalidArgument("No such leases {}".format(lease_ids))
65
66         # fetch related slices
67         slices = Slices(self.api,
68                         [lease['slice_id'] for lease in leases],
69                         ['slice_id', 'person_ids'])
70         # create hash on slice_id
71         slice_map = dict([(slice['slice_id'], slice) for slice in slices])
72
73         updated_ids = []
74         errors = []
75
76         lease_ids = [lease['lease_id'] for lease in leases]
77         for lease in leases:
78
79             if 'admin' not in self.caller['roles']:
80                 slice = slice_map[lease['slice_id']]
81                 # check slices only once
82                 if 'verified' not in slice:
83                     if self.caller['person_id'] in slice['person_ids']:
84                         pass
85                     elif 'pi' not in self.caller['roles']:
86                         raise PLCPermissionDenied(
87                             "Not a member of slice {}".format(slice['name']))
88                     elif slice['site_id'] not in self.caller['site_ids']:
89                         raise PLCPermissionDenied(
90                             "Slice {} not associated with any of your sites"
91                             .format(slice['name']))
92             slice['verified'] = True
93
94             try:
95                 # we've ruled out already the case where all 3 (from, to,
96                 # duration) where specified
97                 if 'duration' not in input_fields:
98                     lease_fields = input_fields
99                 else:
100                     # all arithmetics on longs..
101                     duration = Duration.validate(input_fields['duration'])
102                     # specify 'duration':0 to keep duration unchanged
103                     if not duration:
104                         duration = Timestamp.cast_long(
105                                        lease['t_until']) \
106                                  - Timestamp.cast_long(lease['t_from'])
107                     if 't_from' in input_fields:
108                         lease_fields = {
109                             't_from': input_fields['t_from'],
110                             't_until': Timestamp.cast_long(
111                                 input_fields['from']) + duration}
112                     elif 't_until' in input_fields:
113                         lease_fields = {
114                             't_from': Timestamp.cast_long(
115                                           input_fields['t_until']) - duration,
116                             't_until': input_fields['t_until']}
117                     else:
118                         lease_fields = {'t_until': Timestamp.cast_long(
119                             lease['t_from']) + duration}
120                 if UpdateLeases.debug:
121                     for k in ['t_from', 't_until']:
122                         if k in lease_fields:
123                             print(k, 'aka', Timestamp.sql_validate_utc(
124                                 lease_fields[k]))
125
126                 lease.update(lease_fields)
127                 lease.sync()
128                 updated_ids.append(lease['lease_id'])
129             except Exception, e:
130                 errors.append(
131                     "Could not update lease {} - check new time limits ? -- {}"
132                     .format(lease['lease_id'], e))
133
134         # Logging variables
135         self.event_objects = {'Lease': updated_ids}
136         self.message = 'lease {} updated: {}'\
137                        .format(lease_ids, ", ".join(input_fields.keys()))
138
139         return {'updated_ids': updated_ids,
140                 'errors': errors}