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"))]
50 def validate_name(self, name):
51 # N.B.: Responsibility of the caller to ensure that login_base
52 # portion of the slice name corresponds to a valid site, if
56 # 2. Begins with login_base (letters or numbers).
57 # 3. Then single underscore after login_base.
58 # 4. Then letters, numbers, or underscores.
59 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
61 not re.match(good_name, name):
62 raise PLCInvalidArgument, "Invalid slice name"
64 conflicts = Slices(self.api, [name])
65 for slice in conflicts:
66 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
67 raise PLCInvalidArgument, "Slice name already in use, %s"%name
71 def validate_instantiation(self, instantiation):
72 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
73 if instantiation not in instantiations:
74 raise PLCInvalidArgument, "No such instantiation state"
78 validate_created = Row.validate_timestamp
80 def validate_expires(self, expires):
81 # N.B.: Responsibility of the caller to ensure that expires is
82 # not too far into the future.
83 check_future = not ('is_deleted' in self and self['is_deleted'])
84 return Row.validate_timestamp(self, expires, check_future = check_future)
86 add_person = Row.add_object(Person, 'slice_person')
87 remove_person = Row.remove_object(Person, 'slice_person')
89 add_node = Row.add_object(Node, 'slice_node')
90 remove_node = Row.remove_object(Node, 'slice_node')
92 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
93 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
95 def associate_persons(self, auth, field, value):
97 Adds persons found in value list to this slice (using AddPersonToSlice).
98 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
101 assert 'person_ids' in self
102 assert 'slice_id' in self
103 assert isinstance(value, list)
105 (person_ids, emails) = self.separate_types(value)[0:2]
107 # Translate emails into person_ids
109 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
110 person_ids += persons.keys()
112 # Add new ids, remove stale ids
113 if self['person_ids'] != person_ids:
114 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
115 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
116 new_persons = set(person_ids).difference(self['person_ids'])
117 stale_persons = set(self['person_ids']).difference(person_ids)
119 for new_person in new_persons:
120 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
121 for stale_person in stale_persons:
122 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
124 def associate_nodes(self, auth, field, value):
126 Adds nodes found in value list to this slice (using AddSliceToNodes).
127 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
130 from PLC.Nodes import Nodes
132 assert 'node_ids' in self
133 assert 'slice_id' in self
134 assert isinstance(value, list)
136 (node_ids, hostnames) = self.separate_types(value)[0:2]
138 # Translate hostnames into node_ids
140 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
141 node_ids += nodes.keys()
143 # Add new ids, remove stale ids
144 if self['node_ids'] != node_ids:
145 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
146 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
147 new_nodes = set(node_ids).difference(self['node_ids'])
148 stale_nodes = set(self['node_ids']).difference(node_ids)
151 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
153 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
154 def associate_slice_attributes(self, auth, fields, value):
156 Deletes slice_attribute_ids not found in value list (using DeleteSliceAttribute).
157 Adds slice_attributes if slice_fields w/o slice_id is found (using AddSliceAttribute).
158 Updates slice_attribute if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
161 assert 'slice_attribute_ids' in self
162 assert isinstance(value, list)
164 (attribute_ids, blank, attributes) = self.separate_types(value)
166 # There is no way to add attributes by id. They are
167 # associated with a slice when they are created.
168 # So we are only looking to delete here
169 if self['slice_attribute_ids'] != attribute_ids:
170 from PLC.Methods.DeleteSliceAttribute import DeleteSliceAttribute
171 stale_attributes = set(self['slice_attribute_ids']).difference(attribute_ids)
173 for stale_attribute in stale_attributes:
174 DeleteSliceAttribute.__call__(DeleteSliceAttribute(self.api), auth, stale_attribute['slice_attribute_id'])
176 # If dictionary exists, we are either adding new
177 # attributes or updating existing ones.
179 from PLC.Methods.AddSliceAttribute import AddSliceAttribute
180 from PLC.Methods.UpdateSliceAttribute import UpdateSliceAttribute
182 added_attributes = filter(lambda x: 'slice_attribute_id' not in x, attributes)
183 updated_attributes = filter(lambda x: 'slice_attribute_id' in x, attributes)
185 for added_attribute in added_attributes:
186 if 'tag_type' in added_attribute:
187 type = added_attribute['tag_type']
188 elif 'tag_type_id' in added_attribute:
189 type = added_attribute['tag_type_id']
191 raise PLCInvalidArgument, "Must specify tag_type or tag_type_id"
193 if 'value' in added_attribute:
194 value = added_attribute['value']
196 raise PLCInvalidArgument, "Must specify a value"
198 if 'node_id' in added_attribute:
199 node_id = added_attribute['node_id']
203 if 'nodegroup_id' in added_attribute:
204 nodegroup_id = added_attribute['nodegroup_id']
208 AddSliceAttribute.__call__(AddSliceAttribute(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
209 for updated_attribute in updated_attributes:
210 attribute_id = updated_attribute.pop('slice_attribute_id')
211 if attribute_id not in self['slice_attribute_ids']:
212 raise PLCInvalidArgument, "Attribute doesnt belong to this slice"
214 UpdateSliceAttribute.__call__(UpdateSliceAttribute(self.api), auth, attribute_id, updated_attribute)
216 def sync(self, commit = True):
218 Add or update a slice.
221 # Before a new slice is added, delete expired slices
222 if 'slice_id' not in self:
223 expired = Slices(self.api, expires = -int(time.time()))
224 for slice in expired:
227 Row.sync(self, commit)
229 def delete(self, commit = True):
231 Delete existing slice.
234 assert 'slice_id' in self
236 # Clean up miscellaneous join tables
237 for table in self.join_tables:
238 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
239 (table, self['slice_id']))
242 self['is_deleted'] = True
248 Representation of row(s) from the slices table in the
252 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
253 Table.__init__(self, api, Slice, columns)
255 sql = "SELECT %s FROM view_slices WHERE is_deleted IS False" % \
256 ", ".join(self.columns)
258 if expires is not None:
260 sql += " AND expires > %d" % expires
263 sql += " AND expires < %d" % expires
265 if slice_filter is not None:
266 if isinstance(slice_filter, (list, tuple, set)):
267 # Separate the list into integers and strings
268 ints = filter(lambda x: isinstance(x, (int, long)), slice_filter)
269 strs = filter(lambda x: isinstance(x, StringTypes), slice_filter)
270 slice_filter = Filter(Slice.fields, {'slice_id': ints, 'name': strs})
271 sql += " AND (%s) %s" % slice_filter.sql(api, "OR")
272 elif isinstance(slice_filter, dict):
273 slice_filter = Filter(Slice.fields, slice_filter)
274 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
275 elif isinstance (slice_filter, StringTypes):
276 slice_filter = Filter(Slice.fields, {'name':[slice_filter]})
277 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
278 elif isinstance (slice_filter, int):
279 slice_filter = Filter(Slice.fields, {'slice_id':[slice_filter]})
280 sql += " AND (%s) %s" % slice_filter.sql(api, "AND")
282 raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter