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.SliceTags import SliceTag
14 from PLC.Timestamp import Timestamp
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_tag', 'peer_slice', 'node_slice_whitelist', 'leases', ]
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 = 64),
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_tag_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 view_tags_name="view_slice_tags"
54 def validate_name(self, name):
55 # N.B.: Responsibility of the caller to ensure that login_base
56 # portion of the slice name corresponds to a valid site, if
60 # 2. Begins with login_base (letters or numbers).
61 # 3. Then single underscore after login_base.
62 # 4. Then letters, numbers, or underscores.
63 good_name = r'^[a-z0-9\.]+_[a-zA-Z0-9_\.]+$'
65 not re.match(good_name, name):
66 raise PLCInvalidArgument("Invalid slice name")
68 conflicts = Slices(self.api, [name])
69 for slice in conflicts:
70 if 'slice_id' not in self or self['slice_id'] != slice['slice_id']:
71 raise PLCInvalidArgument("Slice name already in use, %s"%name)
75 def validate_instantiation(self, instantiation):
76 instantiations = [row['instantiation'] for row in SliceInstantiations(self.api)]
77 if instantiation not in instantiations:
78 raise PLCInvalidArgument("No such instantiation state")
82 validate_created = Row.validate_timestamp
84 def validate_expires(self, expires):
85 # N.B.: Responsibility of the caller to ensure that expires is
86 # not too far into the future.
87 check_future = not ('is_deleted' in self and self['is_deleted'])
88 return Timestamp.sql_validate( expires, check_future = check_future)
90 add_person = Row.add_object(Person, 'slice_person')
91 remove_person = Row.remove_object(Person, 'slice_person')
93 add_node = Row.add_object(Node, 'slice_node')
94 remove_node = Row.remove_object(Node, 'slice_node')
96 add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
97 delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
99 def associate_persons(self, auth, field, value):
101 Adds persons found in value list to this slice (using AddPersonToSlice).
102 Deletes persons not found in value list from this slice (using DeletePersonFromSlice).
105 assert 'person_ids' in self
106 assert 'slice_id' in self
107 assert isinstance(value, list)
109 (person_ids, emails) = self.separate_types(value)[0:2]
111 # Translate emails into person_ids
113 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
114 person_ids += list(persons.keys())
116 # Add new ids, remove stale ids
117 if self['person_ids'] != person_ids:
118 from PLC.Methods.AddPersonToSlice import AddPersonToSlice
119 from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice
120 new_persons = set(person_ids).difference(self['person_ids'])
121 stale_persons = set(self['person_ids']).difference(person_ids)
123 for new_person in new_persons:
124 AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, new_person, self['slice_id'])
125 for stale_person in stale_persons:
126 DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, stale_person, self['slice_id'])
128 def associate_nodes(self, auth, field, value):
130 Adds nodes found in value list to this slice (using AddSliceToNodes).
131 Deletes nodes not found in value list from this slice (using DeleteSliceFromNodes).
134 from PLC.Nodes import Nodes
136 assert 'node_ids' in self
137 assert 'slice_id' in self
138 assert isinstance(value, list)
140 (node_ids, hostnames) = self.separate_types(value)[0:2]
142 # Translate hostnames into node_ids
144 nodes = Nodes(self.api, hostnames, ['node_id']).dict('node_id')
145 node_ids += list(nodes.keys())
147 # Add new ids, remove stale ids
148 if self['node_ids'] != node_ids:
149 from PLC.Methods.AddSliceToNodes import AddSliceToNodes
150 from PLC.Methods.DeleteSliceFromNodes import DeleteSliceFromNodes
151 new_nodes = set(node_ids).difference(self['node_ids'])
152 stale_nodes = set(self['node_ids']).difference(node_ids)
155 AddSliceToNodes.__call__(AddSliceToNodes(self.api), auth, self['slice_id'], list(new_nodes))
157 DeleteSliceFromNodes.__call__(DeleteSliceFromNodes(self.api), auth, self['slice_id'], list(stale_nodes))
158 def associate_slice_tags(self, auth, fields, value):
160 Deletes slice_tag_ids not found in value list (using DeleteSliceTag).
161 Adds slice_tags if slice_fields w/o slice_id is found (using AddSliceTag).
162 Updates slice_tag if slice_fields w/ slice_id is found (using UpdateSlceiAttribute).
165 assert 'slice_tag_ids' in self
166 assert isinstance(value, list)
168 (attribute_ids, blank, attributes) = self.separate_types(value)
170 # There is no way to add attributes by id. They are
171 # associated with a slice when they are created.
172 # So we are only looking to delete here
173 if self['slice_tag_ids'] != attribute_ids:
174 from PLC.Methods.DeleteSliceTag import DeleteSliceTag
175 stale_attributes = set(self['slice_tag_ids']).difference(attribute_ids)
177 for stale_attribute in stale_attributes:
178 DeleteSliceTag.__call__(DeleteSliceTag(self.api), auth, stale_attribute['slice_tag_id'])
180 # If dictionary exists, we are either adding new
181 # attributes or updating existing ones.
183 from PLC.Methods.AddSliceTag import AddSliceTag
184 from PLC.Methods.UpdateSliceTag import UpdateSliceTag
186 added_attributes = [x for x in attributes if 'slice_tag_id' not in x]
187 updated_attributes = [x for x in attributes if 'slice_tag_id' in x]
189 for added_attribute in added_attributes:
190 if 'tag_type' in added_attribute:
191 type = added_attribute['tag_type']
192 elif 'tag_type_id' in added_attribute:
193 type = added_attribute['tag_type_id']
195 raise PLCInvalidArgument("Must specify tag_type or tag_type_id")
197 if 'value' in added_attribute:
198 value = added_attribute['value']
200 raise PLCInvalidArgument("Must specify a value")
202 if 'node_id' in added_attribute:
203 node_id = added_attribute['node_id']
207 if 'nodegroup_id' in added_attribute:
208 nodegroup_id = added_attribute['nodegroup_id']
212 AddSliceTag.__call__(AddSliceTag(self.api), auth, self['slice_id'], type, value, node_id, nodegroup_id)
213 for updated_attribute in updated_attributes:
214 attribute_id = updated_attribute.pop('slice_tag_id')
215 if attribute_id not in self['slice_tag_ids']:
216 raise PLCInvalidArgument("Attribute doesnt belong to this slice")
218 UpdateSliceTag.__call__(UpdateSliceTag(self.api), auth, attribute_id, updated_attribute)
220 def sync(self, commit = True):
222 Add or update a slice.
225 # Before a new slice is added, delete expired slices
226 if 'slice_id' not in self:
227 expired = Slices(self.api, expires = -int(time.time()))
228 for slice in expired:
231 Row.sync(self, commit)
233 def delete(self, commit = True):
235 Delete existing slice.
238 assert 'slice_id' in self
240 # Clean up miscellaneous join tables
241 for table in self.join_tables:
242 self.api.db.do("DELETE FROM %s WHERE slice_id = %d" % \
243 (table, self['slice_id']))
246 self['is_deleted'] = True
252 Representation of row(s) from the slices table in the
256 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
257 Table.__init__(self, api, Slice, columns)
259 # the view that we're selecting upon: start with view_slices
261 # as many left joins as requested tags
262 for tagname in self.tag_columns:
263 view= "%s left join %s using (%s)"%(view,Slice.tagvalue_view_name(tagname),
266 sql = "SELECT %s FROM %s WHERE is_deleted IS False" % \
267 (", ".join(list(self.columns.keys())+list(self.tag_columns.keys())),view)
269 if expires is not None:
271 sql += " AND expires > %d" % expires
274 sql += " AND expires < %d" % expires
276 if slice_filter is not None:
277 if isinstance(slice_filter, (list, tuple, set)):
278 # Separate the list into integers and strings
279 ints = [x for x in slice_filter if isinstance(x, int)]
280 strs = [x for x in slice_filter if isinstance(x, StringTypes)]
281 slice_filter = Filter(Slice.fields, {'slice_id': ints, 'name': strs})
282 sql += " AND (%s) %s" % slice_filter.sql(api, "OR")
283 elif isinstance(slice_filter, dict):
284 allowed_fields=dict(list(Slice.fields.items())+list(Slice.tags.items()))
285 slice_filter = Filter(allowed_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)