46d5242444709ad04f904093f3d3b1204d48ddbc
[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 class AddLeases(Method):
17     """
18     Adds a new lease.
19     Mandatory arguments are node(s), slice, t_from and t_until
20     times can be either integers, datetime's, or human readable (see Timestamp)
21
22     PIs may only add leases associated with their own sites (i.e.,
23     to a slice that belongs to their site).
24     Users may only add leases associated with their own slices.
25
26     Returns the new lease_ids if successful, faults otherwise.
27     """
28
29     roles = ['admin', 'pi', 'user']
30
31     accepts = [
32         Auth(),
33         Mixed(Node.fields['node_id'],[Node.fields['node_id']],
34               Node.fields['hostname'],[Node.fields['hostname']],),
35         Mixed(Slice.fields['slice_id'],
36               Slice.fields['name']),
37         Mixed(Lease.fields['t_from']),
38         Mixed(Lease.fields['t_until']),
39         ]
40
41     returns = Parameter(dict, " 'new_ids' is the list of newly created ids, 'errors' is a list of error strings")
42
43     def call(self, auth, node_id_or_hostname_s, slice_id_or_name, t_from, t_until):
44
45         # xxx - round to plain hours somewhere
46
47         # Get node information
48         nodes = Nodes(self.api, node_id_or_hostname_s)
49         if not nodes:
50             raise PLCInvalidArgument, "No such node(s) %r"%node_id_or_hostname_s
51         for node in nodes:
52             if node['node_type'] != 'reservable':
53                 raise PLCInvalidArgument, "Node %s is not reservable"%node['hostname']
54
55         # Get slice information
56         slices = Slices(self.api, [slice_id_or_name])
57         if not slices:
58             raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
59         slice = slices[0]
60
61         # check access
62         if 'admin' not in self.caller['roles']:
63             if self.caller['person_id'] in slice['person_ids']:
64                 pass
65             elif 'pi' not in self.caller['roles']:
66                 raise PLCPermissionDenied, "Not a member of the specified slice"
67             elif slice['site_id'] not in self.caller['site_ids']:
68                 raise PLCPermissionDenied, "Specified slice not associated with any of your sites"
69
70         # normalize timestamps
71         t_from = Timestamp.sql_validate_utc(t_from)
72         t_until = Timestamp.sql_validate_utc(t_until)
73
74         ########## create stuff
75         errors=[]
76         result_ids=[]
77         for node in nodes:
78             if node['peer_id'] is not None:
79                 errors.append("Cannot set lease on remote node %r"%node['hostname'])
80                 continue
81             # let the DB check for time consistency
82             try:
83                 lease = Lease (self.api, {'node_id':node['node_id'], 'slice_id': slice['slice_id'],
84                                           't_from':t_from, 't_until':t_until})
85                 lease.sync()
86                 result_ids.append(lease['lease_id'])
87             except Exception,e:
88                 errors.append("Could not create lease on n=%s s=%s [%s .. %s] -- %r" % \
89                                   (node['hostname'],slice['name'],t_from,t_until,e))
90                 nodes.remove(node)
91
92         self.event_objects = {'Slice': [slice['slice_id']],
93                               'Node': [node['node_id'] for node in nodes]}
94         self.message = "New leases %r on n=%r s=%s [%s -> %s]" % \
95             (result_ids,[node['hostname'] for node in nodes],slice['name'],t_from,t_until)
96
97         return {'new_ids': result_ids,
98                 'errors': errors}