29d3bfdc8eaf162de2341490fbe667d7c619dc45
[plcapi.git] / PLC / Methods / UpdateSlice.py
1 # $Id$
2 import time
3
4 from PLC.Faults import *
5 from PLC.Method import Method
6 from PLC.Parameter import Parameter, Mixed
7 from PLC.Table import Row
8 from PLC.Auth import Auth
9
10 from PLC.Slices import Slice, Slices
11 from PLC.Sites import Site, Sites
12 from PLC.TagTypes import TagTypes
13 from PLC.SliceTags import SliceTags
14 from PLC.Methods.AddSliceTag import AddSliceTag
15 from PLC.Methods.UpdateSliceTag import UpdateSliceTag
16
17 can_update = ['instantiation', 'url', 'description', 'max_nodes', 'expires'] + \
18     Slice.related_fields.keys()
19
20 class UpdateSlice(Method):
21     """
22     Updates the parameters of an existing slice with the values in
23     slice_fields.
24
25     Users may only update slices of which they are members. PIs may
26     update any of the slices at their sites, or any slices of which
27     they are members. Admins may update any slice.
28
29     Only PIs and admins may update max_nodes. Slices cannot be renewed
30     (by updating the expires parameter) more than 8 weeks into the
31     future.
32
33     Returns 1 if successful, faults otherwise.
34     """
35
36     roles = ['admin', 'pi', 'user']
37
38     slice_fields = Row.accepted_fields(can_update, [Slice.fields,Slice.related_fields,Slice.tags])
39
40     accepts = [
41         Auth(),
42         Mixed(Slice.fields['slice_id'],
43               Slice.fields['name']),
44         slice_fields
45         ]
46
47     returns = Parameter(int, '1 if successful')
48
49     def call(self, auth, slice_id_or_name, slice_fields):
50
51         # split provided fields 
52         [native,related,tags,rejected] = Row.split_fields(slice_fields,[Slice.fields,Slice.related_fields,Slice.tags])
53
54         if rejected:
55             raise PLCInvalidArgument, "Cannot update Slice column(s) %r"%rejected
56
57         slices = Slices(self.api, [slice_id_or_name])
58         if not slices:
59             raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
60         slice = slices[0]
61
62         if slice['peer_id'] is not None:
63             raise PLCInvalidArgument, "Not a local slice"
64
65         # Authenticated function
66         assert self.caller is not None
67
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, "Not a member of the specified slice"
73             elif slice['site_id'] not in self.caller['site_ids']:
74                 raise PLCPermissionDenied, "Specified slice not associated with any of your sites"
75
76         # Renewing
77         renewing=False
78         if 'expires' in slice_fields and slice_fields['expires'] > slice['expires']:
79             sites = Sites(self.api, [slice['site_id']])
80             assert sites
81             site = sites[0]
82
83             if site['max_slices'] <= 0:
84                 raise PLCInvalidArgument, "Slice creation and renewal have been disabled for the site"
85
86             # Maximum expiration date is 8 weeks from now
87             # XXX Make this configurable
88             max_expires = time.time() + (8 * 7 * 24 * 60 * 60)
89
90             if 'admin' not in self.caller['roles'] and slice_fields['expires'] > max_expires:
91                 raise PLCInvalidArgument, "Cannot renew a slice beyond 8 weeks from now"
92
93             # XXX Make this a configurable policy
94             if slice['description'] is None or not slice['description'].strip():
95                 if 'description' not in slice_fields or slice_fields['description'] is None or \
96                    not slice_fields['description'].strip():
97                      raise PLCInvalidArgument, "Cannot renew a slice with an empty description or URL"  
98                
99             if slice['url'] is None or not slice['url'].strip():
100                 if 'url' not in slice_fields or slice_fields['url'] is None or \
101                    not slice_fields['url'].strip():
102                     raise PLCInvalidArgument, "Cannot renew a slice with an empty description or URL"
103             renewing=True
104             
105         if 'max_nodes' in slice_fields and slice_fields['max_nodes'] != slice['max_nodes']:
106             if 'admin' not in self.caller['roles'] and \
107                'pi' not in self.caller['roles']:
108                 raise PLCInvalidArgument, "Only admins and PIs may update max_nodes"
109
110         # Make requested associations
111         for (k,v) in related.iteritems():
112             slice.associate(auth,k,v)
113
114         slice.update(slice_fields)
115         slice.sync(commit=True)
116
117         for (tagname,value) in tags.iteritems():
118             # the tagtype instance is assumed to exist, just check that
119             if not TagTypes(self.api,{'tagname':tagname}):
120                 raise PLCInvalidArgument,"No such TagType %s"%tagname
121             slice_tags=SliceTags(self.api,{'tagname':tagname,'slice_id':slice['slice_id']})
122             if not slice_tags:
123                 AddSliceTag(self.api).__call__(auth,slice['slice_id'],tagname,value)
124             else:
125                 UpdateSliceTag(self.api).__call__(auth,slice_tags[0]['slice_tag_id'],value)
126
127         self.event_objects = {'Slice': [slice['slice_id']]}
128         if 'name' in slice:
129             self.message='Slice %s updated'%slice['name']
130         else:
131             self.message='Slice %d updated'%slice['slice_id']
132         if renewing:
133             # it appears that slice['expires'] may be either an int, or a formatted string
134             try:
135                 expire_date=time.strftime('%Y-%m-%d:%H:%M',time.localtime(float(slice['expires'])))
136             except:
137                 expire_date=slice['expires']
138             self.message += ' renewed until %s'%expire_date
139
140         return 1