2 from types import StringTypes
6 from PLC.Faults import *
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Filter import Filter
9 from PLC.Debug import profile
10 from PLC.Table import Row, Table
11 from PLC.SliceInstantiations import SliceInstantiation, SliceInstantiations
12 from PLC.Nodes import Node
13 from PLC.Persons import Person, Persons
14 from PLC.SliceAttributes import SliceAttribute
18 Representation of a row in the slices table. To use, optionally
19 instantiate with a dict of values. Update as you would a
20 dict. Commit to the database with sync().To use, instantiate
21 with a dict of values.
25 primary_key = 'slice_id'
26 join_tables = ['slice_node', 'slice_person', 'slice_attribute', 'peer_slice', 'node_slice_whitelist']
28 'slice_id': Parameter(int, "Slice identifier"),
29 'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
30 'name': Parameter(str, "Slice name", max = 32),
31 'instantiation': Parameter(str, "Slice instantiation state"),
32 'url': Parameter(str, "URL further describing this slice", max = 254, nullok = True),
33 'description': Parameter(str, "Slice description", max = 2048, nullok = True),
34 'max_nodes': Parameter(int, "Maximum number of nodes that can be assigned to this slice"),
35 'creator_person_id': Parameter(int, "Identifier of the account that created this slice"),
36 'created': Parameter(int, "Date and time when slice was created, in seconds since UNIX epoch", ro = True),
37 'expires': Parameter(int, "Date and time when slice expires, in seconds since UNIX epoch"),
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"))]
51 def validate_name(self, name):
52 # N.B.: Responsibility of the caller to ensure that login_base
53 # portion of the slice name corresponds to a valid site, if
57 # 2. Begins with login_base (letters or numbers).
58 # 3. Then single underscore after login_base.
59 # 4. Then letters, numbers, or underscores.
60 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
62 not re.match(good_name, name):
63 raise PLCInvalidArgument, "Invalid slice name"
65 conflicts = Slices(self.api, [name])
66 for slice in conflicts:
67 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
68 raise PLCInvalidArgument, "Slice name already in use, %s"%name
72 def validate_instantiation(self, instantiation):
73 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
74 if instantiation not in instantiations:
75 raise PLCInvalidArgument, "No such instantiation state"
79 validate_created = Row.validate_timestamp
81 def validate_expires(self, expires):
82 # N.B.: Responsibility of the caller to ensure that expires is
83 # not too far into the future.
84 check_future = not ('is_deleted' in self and self['is_deleted'])
85 return Row.validate_timestamp(self, expires, check_future = check_future)
87 add_person = Row.add_object(Person, 'slice_person')
88 remove_person = Row.remove_object(Person, 'slice_person')
90 add_node = Row.add_object(Node, 'slice_node')
91 remove_node = Row.remove_object(Node, 'slice_node')
93 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
94 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
96 def associate_persons(self, auth, field, value):
98 Adds persons found in value list to this slice (using AddPersonToSlice).
99 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
102 assert 'person_ids' in self
103 assert 'slice_id' in self
104 assert isinstance(value, list)
106 (person_ids, emails) = self.separate_types(value)[0:2]
108 # Translate emails into person_ids
110 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
111 person_ids += persons.keys()
113 # Add new ids, remove stale ids
114 if self['person_ids'] != person_ids:
115 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
116 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
117 new_persons = set(person_ids).difference(self['person_ids'])
118 stale_persons = set(self['person_ids']).difference(person_ids)
120 for new_person in new_persons:
121 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
122 for stale_person in stale_persons:
123 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
125 def associate_nodes(self, auth, field, value):
127 Adds nodes found in value list to this slice (using AddSliceToNodes).
128 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
131 from PLC.Nodes import Nodes
133 assert 'node_ids' in self
134 assert 'slice_id' in self
135 assert isinstance(value, list)
137 (node_ids, hostnames) = self.separate_types(value)[0:2]
139 # Translate hostnames into node_ids
141 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
142 node_ids += nodes.keys()
144 # Add new ids, remove stale ids
145 if self['node_ids'] != node_ids:
146 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
147 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
148 new_nodes = set(node_ids).difference(self['node_ids'])
149 stale_nodes = set(self['node_ids']).difference(node_ids)
152 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
154 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
155 def associate_slice_attributes(self, auth, fields, value):
157 Deletes slice_attribute_ids not found in value list (using DeleteSliceAttribute).
158 Adds slice_attributes if slice_fields w/o slice_id is found (using AddSliceAttribute).
159 Updates slice_attribute if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
162 assert 'slice_attribute_ids' in self
163 assert isinstance(value, list)
165 (attribute_ids, blank, attributes) = self.separate_types(value)
167 # There is no way to add attributes by id. They are
168 # associated with a slice when they are created.
169 # So we are only looking to delete here
170 if self['slice_attribute_ids'] != attribute_ids:
171 from PLC.Methods.DeleteSliceAttribute import DeleteSliceAttribute
172 stale_attributes = set(self['slice_attribute_ids']).difference(attribute_ids)
174 for stale_attribute in stale_attributes:
175 DeleteSliceAttribute.__call__(DeleteSliceAttribute(self.api), auth, stale_attribute['slice_attribute_id'])
177 # If dictionary exists, we are either adding new
178 # attributes or updating existing ones.
180 from PLC.Methods.AddSliceAttribute import AddSliceAttribute
181 from PLC.Methods.UpdateSliceAttribute import UpdateSliceAttribute
183 added_attributes = filter(lambda x: 'slice_attribute_id' not in x, attributes)
184 updated_attributes = filter(lambda x: 'slice_attribute_id' in x, attributes)
186 for added_attribute in added_attributes:
187 if 'tag_type' in added_attribute:
188 type = added_attribute['tag_type']
189 elif 'tag_type_id' in added_attribute:
190 type = added_attribute['tag_type_id']
192 raise PLCInvalidArgument, "Must specify tag_type or tag_type_id"
194 if 'value' in added_attribute:
195 value = added_attribute['value']
197 raise PLCInvalidArgument, "Must specify a value"
199 if 'node_id' in added_attribute:
200 node_id = added_attribute['node_id']
204 if 'nodegroup_id' in added_attribute:
205 nodegroup_id = added_attribute['nodegroup_id']
209 AddSliceAttribute.__call__(AddSliceAttribute(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
210 for updated_attribute in updated_attributes:
211 attribute_id = updated_attribute.pop('slice_attribute_id')
212 if attribute_id not in self['slice_attribute_ids']:
213 raise PLCInvalidArgument, "Attribute doesnt belong to this slice"
215 UpdateSliceAttribute.__call__(UpdateSliceAttribute(self.api), auth, attribute_id, updated_attribute)
217 def sync(self, commit = True):
219 Add or update a slice.
222 # Before a new slice is added, delete expired slices
223 if 'slice_id' not in self:
224 expired = Slices(self.api, expires = -int(time.time()))
225 for slice in expired:
228 Row.sync(self, commit)
230 def delete(self, commit = True):
232 Delete existing slice.
235 assert 'slice_id' in self
237 # Clean up miscellaneous join tables
238 for table in self.join_tables:
239 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
240 (table, self['slice_id']))
243 self['is_deleted'] = True
249 Representation of row(s) from the slices table in the
253 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
254 Table.__init__(self, api, Slice, columns)
256 sql = "SELECT %s FROM view_slices WHERE is_deleted IS False" % \
257 ", ".join(self.columns)
259 if expires is not None:
261 sql += " AND expires > %d" % expires
264 sql += " AND expires < %d" % expires
266 if slice_filter is not None:
267 if isinstance(slice_filter, (list, tuple, set)):
268 # Separate the list into integers and strings
269 ints = filter(lambda x: isinstance(x, (int, long)), slice_filter)
270 strs = filter(lambda x: isinstance(x, StringTypes), slice_filter)
271 slice_filter = Filter(Slice.fields, {'slice_id': ints, 'name': strs})
272 sql += " AND (%s) %s" % slice_filter.sql(api, "OR")
273 elif isinstance(slice_filter, dict):
274 slice_filter = Filter(Slice.fields, slice_filter)
275 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
276 elif isinstance (slice_filter, StringTypes):
277 slice_filter = Filter(Slice.fields, {'name':[slice_filter]})
278 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
279 elif isinstance (slice_filter, int):
280 slice_filter = Filter(Slice.fields, {'slice_id':[slice_filter]})
281 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
283 raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter