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