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
16 from PLC.Timestamp import Timestamp
20 Representation of a row in the slices table. To use, optionally
21 instantiate with a dict of values. Update as you would a
22 dict. Commit to the database with sync().To use, instantiate
23 with a dict of values.
27 primary_key = 'slice_id'
28 join_tables = ['slice_node', 'slice_person', 'slice_tag', 'peer_slice', 'node_slice_whitelist', 'leases', ]
30 'slice_id': Parameter(int, "Slice identifier"),
31 'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
32 'name': Parameter(str, "Slice name", max = 32),
33 'instantiation': Parameter(str, "Slice instantiation state"),
34 'url': Parameter(str, "URL further describing this slice", max = 254, nullok = True),
35 'description': Parameter(str, "Slice description", max = 2048, nullok = True),
36 'max_nodes': Parameter(int, "Maximum number of nodes that can be assigned to this slice"),
37 'creator_person_id': Parameter(int, "Identifier of the account that created this slice"),
38 'created': Parameter(int, "Date and time when slice was created, in seconds since UNIX epoch", ro = True),
39 'expires': Parameter(int, "Date and time when slice expires, in seconds since UNIX epoch"),
40 'node_ids': Parameter([int], "List of nodes in this slice", ro = True),
41 'person_ids': Parameter([int], "List of accounts that can use this slice", ro = True),
42 'slice_tag_ids': Parameter([int], "List of slice attributes", ro = True),
43 'peer_id': Parameter(int, "Peer to which this slice belongs", nullok = True),
44 'peer_slice_id': Parameter(int, "Foreign slice identifier at peer", nullok = True),
47 'persons': [Mixed(Parameter(int, "Person identifier"),
48 Parameter(str, "Email address"))],
49 'nodes': [Mixed(Parameter(int, "Node identifier"),
50 Parameter(str, "Fully qualified hostname"))]
53 view_tags_name="view_slice_tags"
56 def validate_name(self, name):
57 # N.B.: Responsibility of the caller to ensure that login_base
58 # portion of the slice name corresponds to a valid site, if
62 # 2. Begins with login_base (letters or numbers).
63 # 3. Then single underscore after login_base.
64 # 4. Then letters, numbers, or underscores.
65 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
67 not re.match(good_name, name):
68 raise PLCInvalidArgument, "Invalid slice name"
70 conflicts = Slices(self.api, [name])
71 for slice in conflicts:
72 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
73 raise PLCInvalidArgument, "Slice name already in use, %s"%name
77 def validate_instantiation(self, instantiation):
78 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
79 if instantiation not in instantiations:
80 raise PLCInvalidArgument, "No such instantiation state"
84 validate_created = Row.validate_timestamp
86 def validate_expires(self, expires):
87 # N.B.: Responsibility of the caller to ensure that expires is
88 # not too far into the future.
89 check_future = not ('is_deleted' in self and self['is_deleted'])
90 return Timestamp.sql_validate( expires, check_future = check_future)
92 add_person = Row.add_object(Person, 'slice_person')
93 remove_person = Row.remove_object(Person, 'slice_person')
95 add_node = Row.add_object(Node, 'slice_node')
96 remove_node = Row.remove_object(Node, 'slice_node')
98 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
99 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
101 def associate_persons(self, auth, field, value):
103 Adds persons found in value list to this slice (using AddPersonToSlice).
104 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
107 assert 'person_ids' in self
108 assert 'slice_id' in self
109 assert isinstance(value, list)
111 (person_ids, emails) = self.separate_types(value)[0:2]
113 # Translate emails into person_ids
115 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
116 person_ids += persons.keys()
118 # Add new ids, remove stale ids
119 if self['person_ids'] != person_ids:
120 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
121 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
122 new_persons = set(person_ids).difference(self['person_ids'])
123 stale_persons = set(self['person_ids']).difference(person_ids)
125 for new_person in new_persons:
126 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
127 for stale_person in stale_persons:
128 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
130 def associate_nodes(self, auth, field, value):
132 Adds nodes found in value list to this slice (using AddSliceToNodes).
133 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
136 from PLC.Nodes import Nodes
138 assert 'node_ids' in self
139 assert 'slice_id' in self
140 assert isinstance(value, list)
142 (node_ids, hostnames) = self.separate_types(value)[0:2]
144 # Translate hostnames into node_ids
146 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
147 node_ids += nodes.keys()
149 # Add new ids, remove stale ids
150 if self['node_ids'] != node_ids:
151 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
152 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
153 new_nodes = set(node_ids).difference(self['node_ids'])
154 stale_nodes = set(self['node_ids']).difference(node_ids)
157 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
159 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
160 def associate_slice_tags(self, auth, fields, value):
162 Deletes slice_tag_ids not found in value list (using DeleteSliceTag).
163 Adds slice_tags if slice_fields w/o slice_id is found (using AddSliceTag).
164 Updates slice_tag if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
167 assert 'slice_tag_ids' in self
168 assert isinstance(value, list)
170 (attribute_ids, blank, attributes) = self.separate_types(value)
172 # There is no way to add attributes by id. They are
173 # associated with a slice when they are created.
174 # So we are only looking to delete here
175 if self['slice_tag_ids'] != attribute_ids:
176 from PLC.Methods.DeleteSliceTag import DeleteSliceTag
177 stale_attributes = set(self['slice_tag_ids']).difference(attribute_ids)
179 for stale_attribute in stale_attributes:
180 DeleteSliceTag.__call__(DeleteSliceTag(self.api), auth, stale_attribute['slice_tag_id'])
182 # If dictionary exists, we are either adding new
183 # attributes or updating existing ones.
185 from PLC.Methods.AddSliceTag import AddSliceTag
186 from PLC.Methods.UpdateSliceTag import UpdateSliceTag
188 added_attributes = filter(lambda x: 'slice_tag_id' not in x, attributes)
189 updated_attributes = filter(lambda x: 'slice_tag_id' in x, attributes)
191 for added_attribute in added_attributes:
192 if 'tag_type' in added_attribute:
193 type = added_attribute['tag_type']
194 elif 'tag_type_id' in added_attribute:
195 type = added_attribute['tag_type_id']
197 raise PLCInvalidArgument, "Must specify tag_type or tag_type_id"
199 if 'value' in added_attribute:
200 value = added_attribute['value']
202 raise PLCInvalidArgument, "Must specify a value"
204 if 'node_id' in added_attribute:
205 node_id = added_attribute['node_id']
209 if 'nodegroup_id' in added_attribute:
210 nodegroup_id = added_attribute['nodegroup_id']
214 AddSliceTag.__call__(AddSliceTag(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
215 for updated_attribute in updated_attributes:
216 attribute_id = updated_attribute.pop('slice_tag_id')
217 if attribute_id not in self['slice_tag_ids']:
218 raise PLCInvalidArgument, "Attribute doesnt belong to this slice"
220 UpdateSliceTag.__call__(UpdateSliceTag(self.api), auth, attribute_id, updated_attribute)
222 def sync(self, commit = True):
224 Add or update a slice.
227 # Before a new slice is added, delete expired slices
228 if 'slice_id' not in self:
229 expired = Slices(self.api, expires = -int(time.time()))
230 for slice in expired:
233 Row.sync(self, commit)
235 def delete(self, commit = True):
237 Delete existing slice.
240 assert 'slice_id' in self
242 # Clean up miscellaneous join tables
243 for table in self.join_tables:
244 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
245 (table, self['slice_id']))
248 self['is_deleted'] = True
254 Representation of row(s) from the slices table in the
258 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
259 Table.__init__(self, api, Slice, columns)
261 # the view that we're selecting upon: start with view_slices
263 # as many left joins as requested tags
264 for tagname in self.tag_columns:
265 view= "%s left join %s using (%s)"%(view,Slice.tagvalue_view_name(tagname),
268 sql = "SELECT %s FROM %s WHERE is_deleted IS False" % \
269 (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
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