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