1 from types import StringTypes
4 from datetime import datetime, timedelta
6 from PLC.Faults import *
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Debug import profile
9 from PLC.Nodes import Node
10 from PLC.Persons import Person, Persons
11 from PLC.SlicePersons import SlicePerson, SlicePersons
12 from PLC.SliceNodes import SliceNode, SliceNodes
13 from PLC.SliceTags import SliceTag, SliceTags
14 from PLC.Timestamp import Timestamp
15 from PLC.Storage.AlchemyObject import AlchemyObj
17 class Slice(AlchemyObj):
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.
28 'slice_id': Parameter(int, "Slice identifier", primary_key=True),
29 'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
30 'tenant_id': Parameter(int, "Keystone tenant identifier"),
31 'name': Parameter(str, "Slice name", max = 32),
32 'instantiation': Parameter(str, "Slice instantiation state", nullok=True),
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", default=100),
36 'creator_person_id': Parameter(str, "Identifier of the account that created this slice"),
37 'created': Parameter(datetime, "Date and time when slice was created, in seconds since UNIX epoch", ro = True),
38 'expires': Parameter(datetime, "Date and time when slice expires, in seconds since UNIX epoch"),
39 'node_ids': Parameter([str], "List of nodes in this slice", joined = True),
40 'person_ids': Parameter([str], "List of accounts that can use this slice", joined = True),
41 'slice_tag_ids': Parameter([int], "List of slice attributes", joined = 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),
47 def validate_name(self, name):
48 # N.B.: Responsibility of the caller to ensure that login_base
49 # portion of the slice name corresponds to a valid site, if
53 # 2. Begins with login_base (letters or numbers).
54 # 3. Then single underscore after login_base.
55 # 4. Then letters, numbers, or underscores.
56 good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
58 not re.match(good_name, name):
59 raise PLCInvalidArgument, "Invalid slice name"
61 conflicts = Slices(self.api, [name])
62 for slice in conflicts:
63 if 'slice_id' not in self or self['slice_id'] != slice.slice_id:
64 raise PLCInvalidArgument, "Slice name already in use, %s"%name
68 def validate_expires(self, expires):
69 # N.B.: Responsibility of the caller to ensure that expires is
70 # not too far into the future.
71 check_future = not ('is_deleted' in self and self['is_deleted'])
72 return Timestamp.sql_validate( expires, check_future = check_future)
74 def add_person(self, person_filter, role_name=None):
75 assert 'slice_id' in self
76 assert 'tenant_id' in self
79 roles = Roles(self.api, role_name)
81 raise PLCInvalidArgument, "No such role %s" % role_name
83 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
84 persons = Persons(self.api, person_filter)
85 for person in persons:
86 keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
87 tenant.add_user(keystone_user, role.object)
88 slice_person = SlicePerson(self.api, {'slice_id': self['slice_id'],
89 'person_id': person['person_id']})
92 def remove_person(self, person_filter, role=None):
93 assert 'slice_id' in self
94 assert 'tenant_id' in self
97 roles = Roles(self.api, role_name)
99 raise PLCInvalidArgument, "No such role %s" % role_name
101 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
102 persons = Persons(self.api, person_filter)
103 for person in persons:
104 keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
105 tenant.remove_user(keystone_user, role.object)
106 slice_person = SlicePerson(self.api, {'slice_id': self['slice_id'],
107 'person_id': person['person_id']})
108 slice_person.delete()
111 def add_node(self, node_filter):
112 assert 'slice_id' in self
113 nodes = Nodes(self.api, node_filter)
115 slice_node = SliceNode(self.api, {'slice_id': self['slice_id'],
116 'node_id': node['node_id']})
119 def remove_node(self, node_filter):
120 assert 'slice_id' in self
121 nodes = Nodes(self.api, node_filter)
123 slice_node = SliceNode(self.api, {'slice_id': self['slice_id'],
124 'node_id': node['node_id']})
127 #add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
128 #delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
130 def sync(self, commit = True, validate=True):
132 Add or update a slice.
134 # sync the nova record and the plc record
135 AlchemyObj.sync(self, commit=commit, validate=validate)
136 # create the nova record
137 nova_fields = ['enabled', 'description']
138 nova_can_update = lambda (field, value): field in nova_fields
139 nova_slice = dict(filter(nova_can_update, self.items()))
140 nova_slice['tenant_name'] = self['name']
141 if 'slice_id' not in self:
143 # Before a new slice is added, delete expired slices
144 #expired = Slices(self.api, expires = -int(time.time()))
145 #for slice in expired:
146 # slice.delete(commit)
147 self.object = self.api.client_shell.keystone.tenants.create(**nova_slice)
148 self['tenant_id'] = self.object.id
149 self['created'] = now
150 self['expires'] = now + timedelta(days=14)
151 AlchemyObj.insert(self, dict(self))
152 slice = AlchemyObj.select(self, filter={'tenant_id': self['tenant_id']})[0]
153 self['slice_id'] = slice.slice_id
155 self.object = self.api.client_shell.keystone.tenants.update(self['tenant_id'], **nova_slice)
156 AlchemyObj.update(self, {'slice_id': self['slice_id']}, dict(self))
158 def delete(self, commit = True):
160 Delete existing slice.
162 assert 'slice_id' in self
163 assert 'tenant_id' in self
165 # delete the nova object
166 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
167 self.api.client_shell.keystone.tenants.delete(tenant)
169 # delete relationships
170 for slice_person in SlicePerson().select(filter={'slice_id': self['slice_id']}):
171 slice_person.delete()
172 for slice_node in SliceNode().select(filter={'slice_id': self['slice_id']}):
174 for slice_tag in SliceTag().select(filter={'slice_id': self['slice_id']}):
178 AlchemyObj.delete(self, filter={'slice_id': self['slice_id']})
182 Representation of row(s) from the slices table in the
186 def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
188 # the view that we're selecting upon: start with view_slices
190 slices = Slice().select()
191 elif isinstance (slice_filter, StringTypes):
192 slices = Slice().select(filter={'name': slice_filter})
193 elif isinstance(slice_filter, dict):
194 slices = Slice().select(filter=slice_filter)
195 elif isinstance(slice_filter, (list, tuple, set)):
196 slices = Slice().select()
197 slices = [slice for slice in slices if slice.slice_id in slice_filter or slice.name in slice_filter]
199 raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter
202 slice = Slice(api, object=slice)
203 if not columns or 'person_ids' in columns:
204 slice_persons = SlicePerson().select(filter={'slice_id': slice['slice_id']})
205 slice['person_ids'] = [rec.person_id for rec in slice_persons]
207 if not columns or 'node_ids' in columns:
208 slice_nodes = SliceNode().select(filter={'slice_id': slice['slice_id']})
209 slice['node_ids'] = [rec.node_id for rec in slice_nodes]
211 if not columns or 'slice_tag_ids' in columns:
212 slice_tags = SliceTag().select(filter={'slice_id': slice['slice_id']})
213 slice['slice_tag_ids'] = [rec.slice_tag_id for rec in slice_tags]