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 'node_ids': Parameter([int], "List of nodes in this slice", ro = True),
38 'person_ids': Parameter([int], "List of accounts that can use this slice", ro = True),
39 'slice_attribute_ids': Parameter([int], "List of slice attributes", ro = True),
40 'peer_id': Parameter(int, "Peer to which this slice belongs", nullok = True),
41 'peer_slice_id': Parameter(int, "Foreign slice identifier at peer", nullok = True),
44 'persons': [Mixed(Parameter(int, "Person identifier"),
45 Parameter(str, "Email address"))],
46 'nodes': [Mixed(Parameter(int, "Node identifier"),
47 Parameter(str, "Fully qualified hostname"))]
51 foreign_fields = ['instantiation', 'url', 'description', 'max_nodes', 'expires']
53 {'field': 'node_ids' , 'class': 'Node', 'table': 'slice_node' },
54 {'field': 'person_ids', 'class': 'Person', 'table': 'slice_person'},
55 {'field': 'creator_person_id', 'class': 'Person', 'table': 'unused-on-direct-refs'},
56 {'field': 'site_id', 'class': 'Site', 'table': 'unused-on-direct-refs'},
58 # forget about this one, it is read-only anyway
59 # handling it causes Cache to re-sync all over again
62 def validate_name(self, name):
63 # N.B.: Responsibility of the caller to ensure that login_base
64 # portion of the slice name corresponds to a valid site, if
68 # 2. Begins with login_base (letters or numbers).
69 # 3. Then single underscore after login_base.
70 # 4. Then letters, numbers, or underscores.
71 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
73 not re.match(good_name, name):
74 raise PLCInvalidArgument, "Invalid slice name"
76 conflicts = Slices(self.api, [name])
77 for slice in conflicts:
78 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
79 raise PLCInvalidArgument, "Slice name already in use, %s"%name
83 def validate_instantiation(self, instantiation):
84 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
85 if instantiation not in instantiations:
86 raise PLCInvalidArgument, "No such instantiation state"
90 validate_created = Row.validate_timestamp
92 def validate_expires(self, expires):
93 # N.B.: Responsibility of the caller to ensure that expires is
94 # not too far into the future.
95 check_future = not ('is_deleted' in self and self['is_deleted'])
96 return Row.validate_timestamp(self, expires, check_future = check_future)
98 add_person = Row.add_object(Person, 'slice_person')
99 remove_person = Row.remove_object(Person, 'slice_person')
101 add_node = Row.add_object(Node, 'slice_node')
102 remove_node = Row.remove_object(Node, 'slice_node')
104 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
105 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
107 def associate_persons(self, auth, field, value):
109 Adds persons found in value list to this slice (using AddPersonToSlice).
110 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
113 assert 'person_ids' in self
114 assert 'slice_id' in self
115 assert isinstance(value, list)
117 (person_ids, emails) = self.separate_types(value)[0:2]
119 # Translate emails into person_ids
121 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
122 person_ids += persons.keys()
124 # Add new ids, remove stale ids
125 if self['person_ids'] != person_ids:
126 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
127 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
128 new_persons = set(person_ids).difference(self['person_ids'])
129 stale_persons = set(self['person_ids']).difference(person_ids)
131 for new_person in new_persons:
132 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
133 for stale_person in stale_persons:
134 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
136 def associate_nodes(self, auth, field, value):
138 Adds nodes found in value list to this slice (using AddSliceToNodes).
139 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
142 from PLC.Nodes import Nodes
144 assert 'node_ids' in self
145 assert 'slice_id' in self
146 assert isinstance(value, list)
148 (node_ids, hostnames) = self.separate_types(value)[0:2]
150 # Translate hostnames into node_ids
152 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
153 node_ids += nodes.keys()
155 # Add new ids, remove stale ids
156 if self['node_ids'] != node_ids:
157 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
158 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
159 new_nodes = set(node_ids).difference(self['node_ids'])
160 stale_nodes = set(self['node_ids']).difference(node_ids)
163 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
165 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
166 def associate_slice_attributes(self, auth, fields, value):
168 Deletes slice_attribute_ids not found in value list (using DeleteSliceAttribute).
169 Adds slice_attributes if slice_fields w/o slice_id is found (using AddSliceAttribute).
170 Updates slice_attribute if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
173 assert 'slice_attribute_ids' in self
174 assert isinstance(value, list)
176 (attribute_ids, blank, attributes) = self.separate_types(value)
178 # There is no way to add attributes by id. They are
179 # associated with a slice when they are created.
180 # So we are only looking to delete here
181 if self['slice_attribute_ids'] != attribute_ids:
182 from PLC.Methods.DeleteSliceAttribute import DeleteSliceAttribute
183 stale_attributes = set(self['slice_attribute_ids']).difference(attribute_ids)
185 for stale_attribute in stale_attributes:
186 DeleteSliceAttribute.__call__(DeleteSliceAttribute(self.api), auth, stale_attribute['slice_attribute_id'])
188 # If dictionary exists, we are either adding new
189 # attributes or updating existing ones.
191 from PLC.Methods.AddSliceAttribute import AddSliceAttribute
192 from PLC.Methods.UpdateSliceAttribute import UpdateSliceAttribute
194 added_attributes = filter(lambda x: 'slice_attribute_id' not in x, attributes)
195 updated_attributes = filter(lambda x: 'slice_attribute_id' in x, attributes)
197 for added_attribute in added_attributes:
198 if 'attribute_type' in added_attribute:
199 type = added_attribute['attribute_type']
200 elif 'attribute_type_id' in added_attribute:
201 type = added_attribute['attribute_type_id']
203 raise PLCInvalidArgument, "Must specify attribute_type or attribute_type_id"
205 if 'value' in added_attribute:
206 value = added_attribute['value']
208 raise PLCInvalidArgument, "Must specify a value"
210 if 'node_id' in added_attribute:
211 node_id = added_attribute['node_id']
215 if 'nodegroup_id' in added_attribute:
216 nodegroup_id = added_attribute['nodegroup_id']
220 AddSliceAttribute.__call__(AddSliceAttribute(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
221 for updated_attribute in updated_attributes:
222 attribute_id = updated_attribute.pop('slice_attribute_id')
223 if attribute_id not in self['slice_attribute_ids']:
224 raise PLCInvalidArgument, "Attribute doesnt belong to this slice"
226 UpdateSliceAttribute.__call__(UpdateSliceAttribute(self.api), auth, attribute_id, updated_attribute)
228 def sync(self, commit = True):
230 Add or update a slice.
233 # Before a new slice is added, delete expired slices
234 if 'slice_id' not in self:
235 expired = Slices(self.api, expires = -int(time.time()))
236 for slice in expired:
239 Row.sync(self, commit)
241 def delete(self, commit = True):
243 Delete existing slice.
246 assert 'slice_id' in self
248 # Clean up miscellaneous join tables
249 for table in self.join_tables:
250 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
251 (table, self['slice_id']))
254 self['is_deleted'] = True
260 Representation of row(s) from the slices table in the
264 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
265 Table.__init__(self, api, Slice, columns)
267 sql = "SELECT %s FROM view_slices WHERE is_deleted IS False" % \
268 ", ".join(self.columns)
270 if expires is not None:
272 sql += " AND expires > %d" % expires
275 sql += " AND expires < %d" % expires
277 if slice_filter is not None:
278 if isinstance(slice_filter, (list, tuple, set)):
279 # Separate the list into integers and strings
280 ints = filter(lambda x: isinstance(x, (int, long)), slice_filter)
281 strs = filter(lambda x: isinstance(x, StringTypes), slice_filter)
282 slice_filter = Filter(Slice.fields, {'slice_id': ints, 'name': strs})
283 sql += " AND (%s) %s" % slice_filter.sql(api, "OR")
284 elif isinstance(slice_filter, dict):
285 slice_filter = Filter(Slice.fields, slice_filter)
286 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
287 elif isinstance (slice_filter, StringTypes):
288 slice_filter = Filter(Slice.fields, {'name':[slice_filter]})
289 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
290 elif isinstance (slice_filter, int):
291 slice_filter = Filter(Slice.fields, {'slice_id':[slice_filter]})
292 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
294 raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter