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