prettified
[plcapi.git] / PLC / Methods / AddLeases.py
1 # Thierry Parmentelat -- INRIA
2
3 from PLC.Faults import *
4 from PLC.Auth import Auth
5 from PLC.Method import Method
6 from PLC.Parameter import Parameter, Mixed
7 from PLC.Table import Row
8
9 from PLC.Leases import Leases, Lease
10 from PLC.Nodes import Nodes, Node
11 from PLC.Slices import Slices, Slice
12 from PLC.Timestamp import Timestamp
13
14 can_update = ['name', 'instantiation', 'url', 'description', 'max_nodes']
15
16
17 class AddLeases(Method):
18     """
19     Adds a new lease.
20     Mandatory arguments are node(s), slice, t_from and t_until
21     times can be either integers, datetime's, or human readable (see Timestamp)
22
23     PIs may only add leases associated with their own sites (i.e.,
24     to a slice that belongs to their site).
25     Users may only add leases associated with their own slices.
26
27     Returns the new lease_ids if successful, faults otherwise.
28     """
29
30     roles = ['admin', 'pi', 'user']
31
32     accepts = [
33         Auth(),
34         Mixed(Node.fields['node_id'], [Node.fields['node_id']],
35               Node.fields['hostname'], [Node.fields['hostname']],),
36         Mixed(Slice.fields['slice_id'],
37               Slice.fields['name']),
38         Mixed(Lease.fields['t_from']),
39         Mixed(Lease.fields['t_until']),
40     ]
41
42     returns = Parameter(
43         dict,
44         " 'new_ids' is the list of newly created ids,"
45         "'errors' is a list of error strings")
46
47     def call(self, auth, node_id_or_hostname_s, slice_id_or_name,
48              t_from, t_until):
49
50         # Get node information
51         nodes = Nodes(self.api, node_id_or_hostname_s)
52         if not nodes:
53             raise PLCInvalidArgument(
54                 "No such node(s) {}".format(node_id_or_hostname_s))
55         for node in nodes:
56             if node['node_type'] != 'reservable':
57                 raise PLCInvalidArgument(
58                     "Node {} is not reservable".format(node['hostname']))
59
60         # Get slice information
61         slices = Slices(self.api, [slice_id_or_name])
62         if not slices:
63             raise PLCInvalidArgument(
64                 "No such slice {}".format(slice_id_or_name))
65         slice = slices[0]
66
67         # check access
68         if 'admin' not in self.caller['roles']:
69             if self.caller['person_id'] in slice['person_ids']:
70                 pass
71             elif 'pi' not in self.caller['roles']:
72                 raise PLCPermissionDenied(
73                     "Not a member of the specified slice")
74             elif slice['site_id'] not in self.caller['site_ids']:
75                 raise PLCPermissionDenied(
76                     "Specified slice not associated with any of your sites")
77
78         # normalize timestamps - use granularity to round up limits
79         t_from = Timestamp.sql_validate_utc(t_from)
80         t_until = Timestamp.sql_validate_utc(t_until)
81
82         # create stuff
83         errors = []
84         result_ids = []
85         for node in nodes:
86             if node['peer_id'] is not None:
87                 errors.append("Cannot set lease on remote node {}"
88                               .format(node['hostname']))
89                 continue
90             # let the DB check for time consistency
91             try:
92                 lease = Lease(self.api, {'node_id': node['node_id'],
93                                          'slice_id': slice['slice_id'],
94                                          't_from': t_from, 't_until': t_until})
95                 lease.sync()
96                 result_ids.append(lease['lease_id'])
97
98             except PLCDBError as e:
99                 errors.append(
100                     "Timeslot busy - could not create overlapping lease"
101                     " on n={} s={} [{} .. {}]"
102                     .format(node['hostname'], slice['name'], t_from, t_until))
103                 nodes.remove(node)
104             except Exception as e:
105                 errors.append(
106                     "Could not create lease on n={} s={} [{} .. {}] -- {}"
107                     .format(node['hostname'], slice['name'], t_from, t_until, e))
108                 nodes.remove(node)
109
110         self.event_objects = {'Slice': [slice['slice_id']],
111                               'Node': [node['node_id'] for node in nodes]}
112         self.message = "New leases {} on n={} s={} [{} -> {}]"\
113             .format(result_ids, [node['hostname'] for node in nodes],
114                     slice['name'], t_from, t_until)
115
116         return {'new_ids': result_ids, 'errors': errors}