bc28a5519178df14a0c4c49d59e76f332b44cfe7
[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     accepted_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         accepted_fields
45         ]
46
47     returns = Parameter(int, '1 if successful')
48
49     def call(self, auth, slice_id_or_name, slice_fields):
50
51         slice_fields = Row.check_fields (slice_fields, self.accepted_fields)
52
53         # split provided fields 
54         [native,related,tags,rejected] = Row.split_fields(slice_fields,[Slice.fields,Slice.related_fields,Slice.tags])
55
56         if rejected:
57             raise PLCInvalidArgument, "Cannot update Slice column(s) %r"%rejected
58
59         slices = Slices(self.api, [slice_id_or_name])
60         if not slices:
61             raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
62         slice = slices[0]
63
64         if slice['peer_id'] is not None:
65             raise PLCInvalidArgument, "Not a local slice"
66
67         # Authenticated function
68         assert self.caller is not None
69
70         if 'admin' not in self.caller['roles']:
71             if self.caller['person_id'] in slice['person_ids']:
72                 pass
73             elif 'pi' not in self.caller['roles']:
74                 raise PLCPermissionDenied, "Not a member of the specified slice"
75             elif slice['site_id'] not in self.caller['site_ids']:
76                 raise PLCPermissionDenied, "Specified slice not associated with any of your sites"
77
78         # Renewing
79         renewing=False
80         if 'expires' in slice_fields and slice_fields['expires'] > slice['expires']:
81             sites = Sites(self.api, [slice['site_id']])
82             assert sites
83             site = sites[0]
84
85             if site['max_slices'] <= 0:
86                 raise PLCInvalidArgument, "Slice creation and renewal have been disabled for the site"
87
88             # Maximum expiration date is 8 weeks from now
89             # XXX Make this configurable
90             max_expires = time.time() + (8 * 7 * 24 * 60 * 60)
91
92             if 'admin' not in self.caller['roles'] and slice_fields['expires'] > max_expires:
93                 raise PLCInvalidArgument, "Cannot renew a slice beyond 8 weeks from now"
94
95             # XXX Make this a configurable policy
96             if slice['description'] is None or not slice['description'].strip():
97                 if 'description' not in slice_fields or slice_fields['description'] is None or \
98                    not slice_fields['description'].strip():
99                      raise PLCInvalidArgument, "Cannot renew a slice with an empty description or URL"  
100                
101             if slice['url'] is None or not slice['url'].strip():
102                 if 'url' not in slice_fields or slice_fields['url'] is None or \
103                    not slice_fields['url'].strip():
104                     raise PLCInvalidArgument, "Cannot renew a slice with an empty description or URL"
105             renewing=True
106             
107         if 'max_nodes' in slice_fields and slice_fields['max_nodes'] != slice['max_nodes']:
108             if 'admin' not in self.caller['roles'] and \
109                'pi' not in self.caller['roles']:
110                 raise PLCInvalidArgument, "Only admins and PIs may update max_nodes"
111
112         # Make requested associations
113         for (k,v) in related.iteritems():
114             slice.associate(auth,k,v)
115
116         slice.update(slice_fields)
117         slice.sync(commit=True)
118
119         for (tagname,value) in tags.iteritems():
120             # the tagtype instance is assumed to exist, just check that
121             if not TagTypes(self.api,{'tagname':tagname}):
122                 raise PLCInvalidArgument,"No such TagType %s"%tagname
123             slice_tags=SliceTags(self.api,{'tagname':tagname,'slice_id':slice['slice_id']})
124             if not slice_tags:
125                 AddSliceTag(self.api).__call__(auth,slice['slice_id'],tagname,value)
126             else:
127                 UpdateSliceTag(self.api).__call__(auth,slice_tags[0]['slice_tag_id'],value)
128
129         self.event_objects = {'Slice': [slice['slice_id']]}
130         if 'name' in slice:
131             self.message='Slice %s updated'%slice['name']
132         else:
133             self.message='Slice %d updated'%slice['slice_id']
134         if renewing:
135             # it appears that slice['expires'] may be either an int, or a formatted string
136             try:
137                 expire_date=time.strftime('%Y-%m-%d:%H:%M',time.localtime(float(slice['expires'])))
138             except:
139                 expire_date=slice['expires']
140             self.message += ' renewed until %s'%expire_date
141
142         return 1