3 from types import StringTypes
7 from PLC.Faults import *
8 from PLC.Parameter import Parameter, Mixed
9 from PLC.Filter import Filter
10 from PLC.Debug import profile
11 from PLC.Table import Row, Table
12 from PLC.SliceInstantiations import SliceInstantiation, SliceInstantiations
13 from PLC.Nodes import Node
14 from PLC.Persons import Person, Persons
15 from PLC.SliceTags import SliceTag
19 Representation of a row in the slices table. To use, optionally
20 instantiate with a dict of values. Update as you would a
21 dict. Commit to the database with sync().To use, instantiate
22 with a dict of values.
26 primary_key = 'slice_id'
27 join_tables = ['slice_node', 'slice_person', 'slice_tag', 'peer_slice', 'node_slice_whitelist']
29 'slice_id': Parameter(int, "Slice identifier"),
30 'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
31 'name': Parameter(str, "Slice name", max = 32),
32 'instantiation': Parameter(str, "Slice instantiation state"),
33 'url': Parameter(str, "URL further describing this slice", max = 254, nullok = True),
34 'description': Parameter(str, "Slice description", max = 2048, nullok = True),
35 'max_nodes': Parameter(int, "Maximum number of nodes that can be assigned to this slice"),
36 'creator_person_id': Parameter(int, "Identifier of the account that created this slice"),
37 'created': Parameter(int, "Date and time when slice was created, in seconds since UNIX epoch", ro = True),
38 'expires': Parameter(int, "Date and time when slice expires, in seconds since UNIX epoch"),
39 'node_ids': Parameter([int], "List of nodes in this slice", ro = True),
40 'person_ids': Parameter([int], "List of accounts that can use this slice", ro = True),
41 'slice_tag_ids': Parameter([int], "List of slice attributes", ro = True),
42 'peer_id': Parameter(int, "Peer to which this slice belongs", nullok = True),
43 'peer_slice_id': Parameter(int, "Foreign slice identifier at peer", nullok = True),
46 'persons': [Mixed(Parameter(int, "Person identifier"),
47 Parameter(str, "Email address"))],
48 'nodes': [Mixed(Parameter(int, "Node identifier"),
49 Parameter(str, "Fully qualified hostname"))]
52 view_tags_name="view_slice_tags"
55 def validate_name(self, name):
56 # N.B.: Responsibility of the caller to ensure that login_base
57 # portion of the slice name corresponds to a valid site, if
61 # 2. Begins with login_base (letters or numbers).
62 # 3. Then single underscore after login_base.
63 # 4. Then letters, numbers, or underscores.
64 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
66 not re.match(good_name, name):
67 raise PLCInvalidArgument, "Invalid slice name"
69 conflicts = Slices(self.api, [name])
70 for slice in conflicts:
71 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
72 raise PLCInvalidArgument, "Slice name already in use, %s"%name
76 def validate_instantiation(self, instantiation):
77 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
78 if instantiation not in instantiations:
79 raise PLCInvalidArgument, "No such instantiation state"
83 validate_created = Row.validate_timestamp
85 def validate_expires(self, expires):
86 # N.B.: Responsibility of the caller to ensure that expires is
87 # not too far into the future.
88 check_future = not ('is_deleted' in self and self['is_deleted'])
89 return Row.validate_timestamp(self, expires, check_future = check_future)
91 add_person = Row.add_object(Person, 'slice_person')
92 remove_person = Row.remove_object(Person, 'slice_person')
94 add_node = Row.add_object(Node, 'slice_node')
95 remove_node = Row.remove_object(Node, 'slice_node')
97 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
98 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
100 def associate_persons(self, auth, field, value):
102 Adds persons found in value list to this slice (using AddPersonToSlice).
103 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
106 assert 'person_ids' in self
107 assert 'slice_id' in self
108 assert isinstance(value, list)
110 (person_ids, emails) = self.separate_types(value)[0:2]
112 # Translate emails into person_ids
114 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
115 person_ids += persons.keys()
117 # Add new ids, remove stale ids
118 if self['person_ids'] != person_ids:
119 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
120 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
121 new_persons = set(person_ids).difference(self['person_ids'])
122 stale_persons = set(self['person_ids']).difference(person_ids)
124 for new_person in new_persons:
125 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
126 for stale_person in stale_persons:
127 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
129 def associate_nodes(self, auth, field, value):
131 Adds nodes found in value list to this slice (using AddSliceToNodes).
132 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
135 from PLC.Nodes import Nodes
137 assert 'node_ids' in self
138 assert 'slice_id' in self
139 assert isinstance(value, list)
141 (node_ids, hostnames) = self.separate_types(value)[0:2]
143 # Translate hostnames into node_ids
145 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
146 node_ids += nodes.keys()
148 # Add new ids, remove stale ids
149 if self['node_ids'] != node_ids:
150 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
151 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
152 new_nodes = set(node_ids).difference(self['node_ids'])
153 stale_nodes = set(self['node_ids']).difference(node_ids)
156 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
158 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
159 def associate_slice_tags(self, auth, fields, value):
161 Deletes slice_tag_ids not found in value list (using DeleteSliceTag).
162 Adds slice_tags if slice_fields w/o slice_id is found (using AddSliceTag).
163 Updates slice_tag if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
166 assert 'slice_tag_ids' in self
167 assert isinstance(value, list)
169 (attribute_ids, blank, attributes) = self.separate_types(value)
171 # There is no way to add attributes by id. They are
172 # associated with a slice when they are created.
173 # So we are only looking to delete here
174 if self['slice_tag_ids'] != attribute_ids:
175 from PLC.Methods.DeleteSliceTag import DeleteSliceTag
176 stale_attributes = set(self['slice_tag_ids']).difference(attribute_ids)
178 for stale_attribute in stale_attributes:
179 DeleteSliceTag.__call__(DeleteSliceTag(self.api), auth, stale_attribute['slice_tag_id'])
181 # If dictionary exists, we are either adding new
182 # attributes or updating existing ones.
184 from PLC.Methods.AddSliceTag import AddSliceTag
185 from PLC.Methods.UpdateSliceTag import UpdateSliceTag
187 added_attributes = filter(lambda x: 'slice_tag_id' not in x, attributes)
188 updated_attributes = filter(lambda x: 'slice_tag_id' in x, attributes)
190 for added_attribute in added_attributes:
191 if 'tag_type' in added_attribute:
192 type = added_attribute['tag_type']
193 elif 'tag_type_id' in added_attribute:
194 type = added_attribute['tag_type_id']
196 raise PLCInvalidArgument, "Must specify tag_type or tag_type_id"
198 if 'value' in added_attribute:
199 value = added_attribute['value']
201 raise PLCInvalidArgument, "Must specify a value"
203 if 'node_id' in added_attribute:
204 node_id = added_attribute['node_id']
208 if 'nodegroup_id' in added_attribute:
209 nodegroup_id = added_attribute['nodegroup_id']
213 AddSliceTag.__call__(AddSliceTag(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
214 for updated_attribute in updated_attributes:
215 attribute_id = updated_attribute.pop('slice_tag_id')
216 if attribute_id not in self['slice_tag_ids']:
217 raise PLCInvalidArgument, "Attribute doesnt belong to this slice"
219 UpdateSliceTag.__call__(UpdateSliceTag(self.api), auth, attribute_id, updated_attribute)
221 def sync(self, commit = True):
223 Add or update a slice.
226 # Before a new slice is added, delete expired slices
227 if 'slice_id' not in self:
228 expired = Slices(self.api, expires = -int(time.time()))
229 for slice in expired:
232 Row.sync(self, commit)
234 def delete(self, commit = True):
236 Delete existing slice.
239 assert 'slice_id' in self
241 # Clean up miscellaneous join tables
242 for table in self.join_tables:
243 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
244 (table, self['slice_id']))
247 self['is_deleted'] = True
253 Representation of row(s) from the slices table in the
257 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
258 Table.__init__(self, api, Slice, columns)
260 # the view that we're selecting upon: start with view_slices
262 # as many left joins as requested tags
263 for tagname in self.tag_columns:
264 view= "%s left join %s using (%s)"%(view,Slice.tagvalue_view_name(tagname),
267 sql = "SELECT %s FROM %s WHERE is_deleted IS False" % \
268 (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
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