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