1 from types import StringTypes
5 from PLC.Faults import *
6 from PLC.Parameter import Parameter, Mixed
7 from PLC.Filter import Filter
8 from PLC.Debug import profile
9 from PLC.Table import Row, Table
10 from PLC.SliceInstantiations import SliceInstantiation, SliceInstantiations
11 from PLC.Nodes import Node
12 from PLC.Persons import Person, Persons
13 from PLC.SliceAttributes import SliceAttribute
17 Representation of a row in the slices table. To use, optionally
18 instantiate with a dict of values. Update as you would a
19 dict. Commit to the database with sync().To use, instantiate
20 with a dict of values.
24 primary_key = 'slice_id'
25 join_tables = ['slice_node', 'slice_person', 'slice_attribute', 'peer_slice', 'node_slice_whitelist']
27 'slice_id': Parameter(int, "Slice identifier"),
28 'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
29 'name': Parameter(str, "Slice name", max = 32),
30 'instantiation': Parameter(str, "Slice instantiation state"),
31 'url': Parameter(str, "URL further describing this slice", max = 254, nullok = True),
32 'description': Parameter(str, "Slice description", max = 2048, nullok = True),
33 'max_nodes': Parameter(int, "Maximum number of nodes that can be assigned to this slice"),
34 'creator_person_id': Parameter(int, "Identifier of the account that created this slice"),
35 'created': Parameter(int, "Date and time when slice was created, in seconds since UNIX epoch", ro = True),
36 'expires': Parameter(int, "Date and time when slice expires, in seconds since UNIX epoch"),
37 'uuid': Parameter(str, "Universal Unique Identifier"),
38 'node_ids': Parameter([int], "List of nodes in this slice", ro = True),
39 'person_ids': Parameter([int], "List of accounts that can use this slice", ro = True),
40 'slice_attribute_ids': Parameter([int], "List of slice attributes", ro = True),
41 'peer_id': Parameter(int, "Peer to which this slice belongs", nullok = True),
42 'peer_slice_id': Parameter(int, "Foreign slice identifier at peer", nullok = True),
45 'persons': [Mixed(Parameter(int, "Person identifier"),
46 Parameter(str, "Email address"))],
47 'nodes': [Mixed(Parameter(int, "Node identifier"),
48 Parameter(str, "Fully qualified hostname"))]
52 foreign_fields = ['instantiation', 'url', 'description', 'max_nodes', 'expires', 'uuid']
54 {'field': 'node_ids' , 'class': 'Node', 'table': 'slice_node' },
55 {'field': 'person_ids', 'class': 'Person', 'table': 'slice_person'},
56 {'field': 'creator_person_id', 'class': 'Person', 'table': 'unused-on-direct-refs'},
57 {'field': 'site_id', 'class': 'Site', 'table': 'unused-on-direct-refs'},
59 # forget about this one, it is read-only anyway
60 # handling it causes Cache to re-sync all over again
63 def validate_name(self, name):
64 # N.B.: Responsibility of the caller to ensure that login_base
65 # portion of the slice name corresponds to a valid site, if
69 # 2. Begins with login_base (letters or numbers).
70 # 3. Then single underscore after login_base.
71 # 4. Then letters, numbers, or underscores.
72 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
74 not re.match(good_name, name):
75 raise PLCInvalidArgument, "Invalid slice name"
77 conflicts = Slices(self.api, [name])
78 for slice in conflicts:
79 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
80 raise PLCInvalidArgument, "Slice name already in use, %s"%name
84 def validate_instantiation(self, instantiation):
85 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
86 if instantiation not in instantiations:
87 raise PLCInvalidArgument, "No such instantiation state"
91 validate_created = Row.validate_timestamp
93 def validate_expires(self, expires):
94 # N.B.: Responsibility of the caller to ensure that expires is
95 # not too far into the future.
96 check_future = not ('is_deleted' in self and self['is_deleted'])
97 return Row.validate_timestamp(self, expires, check_future = check_future)
99 add_person = Row.add_object(Person, 'slice_person')
100 remove_person = Row.remove_object(Person, 'slice_person')
102 add_node = Row.add_object(Node, 'slice_node')
103 remove_node = Row.remove_object(Node, 'slice_node')
105 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
106 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
108 def associate_persons(self, auth, field, value):
110 Adds persons found in value list to this slice (using AddPersonToSlice).
111 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
114 assert 'person_ids' in self
115 assert 'slice_id' in self
116 assert isinstance(value, list)
118 (person_ids, emails) = self.separate_types(value)[0:2]
120 # Translate emails into person_ids
122 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
123 person_ids += persons.keys()
125 # Add new ids, remove stale ids
126 if self['person_ids'] != person_ids:
127 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
128 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
129 new_persons = set(person_ids).difference(self['person_ids'])
130 stale_persons = set(self['person_ids']).difference(person_ids)
132 for new_person in new_persons:
133 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
134 for stale_person in stale_persons:
135 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
137 def associate_nodes(self, auth, field, value):
139 Adds nodes found in value list to this slice (using AddSliceToNodes).
140 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
143 from PLC.Nodes import Nodes
145 assert 'node_ids' in self
146 assert 'slice_id' in self
147 assert isinstance(value, list)
149 (node_ids, hostnames) = self.separate_types(value)[0:2]
151 # Translate hostnames into node_ids
153 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
154 node_ids += nodes.keys()
156 # Add new ids, remove stale ids
157 if self['node_ids'] != node_ids:
158 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
159 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
160 new_nodes = set(node_ids).difference(self['node_ids'])
161 stale_nodes = set(self['node_ids']).difference(node_ids)
164 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
166 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
167 def associate_slice_attributes(self, auth, fields, value):
169 Deletes slice_attribute_ids not found in value list (using DeleteSliceAttribute).
170 Adds slice_attributes if slice_fields w/o slice_id is found (using AddSliceAttribute).
171 Updates slice_attribute if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
174 assert 'slice_attribute_ids' in self
175 assert isinstance(value, list)
177 (attribute_ids, blank, attributes) = self.separate_types(value)
179 # There is no way to add attributes by id. They are
180 # associated with a slice when they are created.
181 # So we are only looking to delete here
182 if self['slice_attribute_ids'] != attribute_ids:
183 from PLC.Methods.DeleteSliceAttribute import DeleteSliceAttribute
184 stale_attributes = set(self['slice_attribute_ids']).difference(attribute_ids)
186 for stale_attribute in stale_attributes:
187 DeleteSliceAttribute.__call__(DeleteSliceAttribute(self.api), auth, stale_attribute['slice_attribute_id'])
189 # If dictionary exists, we are either adding new
190 # attributes or updating existing ones.
192 from PLC.Methods.AddSliceAttribute import AddSliceAttribute
193 from PLC.Methods.UpdateSliceAttribute import UpdateSliceAttribute
195 added_attributes = filter(lambda x: 'slice_attribute_id' not in x, attributes)
196 updated_attributes = filter(lambda x: 'slice_attribute_id' in x, attributes)
198 for added_attribute in added_attributes:
199 if 'attribute_type' in added_attribute:
200 type = added_attribute['attribute_type']
201 elif 'attribute_type_id' in added_attribute:
202 type = added_attribute['attribute_type_id']
204 raise PLCInvalidArgument, "Must specify attribute_type or attribute_type_id"
206 if 'value' in added_attribute:
207 value = added_attribute['value']
209 raise PLCInvalidArgument, "Must specify a value"
211 if 'node_id' in added_attribute:
212 node_id = added_attribute['node_id']
216 if 'nodegroup_id' in added_attribute:
217 nodegroup_id = added_attribute['nodegroup_id']
221 AddSliceAttribute.__call__(AddSliceAttribute(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
222 for updated_attribute in updated_attributes:
223 attribute_id = updated_attribute.pop('slice_attribute_id')
224 if attribute_id not in self['slice_attribute_ids']:
225 raise PLCInvalidArgument, "Attribute doesnt belong to this slice"
227 UpdateSliceAttribute.__call__(UpdateSliceAttribute(self.api), auth, attribute_id, updated_attribute)
229 def sync(self, commit = True):
231 Add or update a slice.
234 # Before a new slice is added, delete expired slices
235 if 'slice_id' not in self:
236 expired = Slices(self.api, expires = -int(time.time()))
237 for slice in expired:
240 Row.sync(self, commit)
242 def delete(self, commit = True):
244 Delete existing slice.
247 assert 'slice_id' in self
249 # Clean up miscellaneous join tables
250 for table in self.join_tables:
251 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
252 (table, self['slice_id']))
255 self['is_deleted'] = True
261 Representation of row(s) from the slices table in the
265 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
266 Table.__init__(self, api, Slice, columns)
268 sql = "SELECT %s FROM view_slices WHERE is_deleted IS False" % \
269 ", ".join(self.columns)
271 if expires is not None:
273 sql += " AND expires > %d" % expires
276 sql += " AND expires < %d" % expires
278 if slice_filter is not None:
279 if isinstance(slice_filter, (list, tuple, set)):
280 # Separate the list into integers and strings
281 ints = filter(lambda x: isinstance(x, (int, long)), slice_filter)
282 strs = filter(lambda x: isinstance(x, StringTypes), slice_filter)
283 slice_filter = Filter(Slice.fields, {'slice_id': ints, 'name': strs})
284 sql += " AND (%s) %s" % slice_filter.sql(api, "OR")
285 elif isinstance(slice_filter, dict):
286 slice_filter = Filter(Slice.fields, slice_filter)
287 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
288 elif isinstance (slice_filter, StringTypes):
289 slice_filter = Filter(Slice.fields, {'name':[slice_filter]})
290 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
291 elif isinstance (slice_filter, int):
292 slice_filter = Filter(Slice.fields, {'slice_id':[slice_filter]})
293 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
295 raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter