1 from types import StringTypes
2 from datetime import datetime
5 from PLC.Faults import *
6 from PLC.Logger import logger
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Storage.AlchemyObject import AlchemyObj
9 from PLC.Slices import Slice, Slices
10 from PLC.Persons import Person, Persons
11 from PLC.SitePersons import SitePerson, SitePersons
12 from PLC.SiteAddresses import SiteAddress, SiteAddress
13 from PLC.PCUs import PCU, PCUs
14 from PLC.Nodes import Node, Nodes
15 from PLC.Roles import Role, Roles
16 from PLC.SiteTags import SiteTag, SiteTags
18 class Site(AlchemyObj):
20 Representation of a row in the sites table. To use, optionally
21 instantiate with a dict of values. Update as you would a
22 dict. Commit to the database with sync().
28 'site_id': Parameter(int, "Site identifier", primary_key=True),
29 'tenant_id': Parameter(str, "Tenant identifier"),
30 'enabled': Parameter(bool, "Has been enabled"),
31 'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
32 'login_base': Parameter(str, "Site slice prefix", max = 20),
33 'is_public': Parameter(bool, "Publicly viewable site"),
34 'name': Parameter(str, "Full site name", max = 254),
35 'description': Parameter(str, "Description", max = 254),
36 'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
37 'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
38 'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
39 'date_created': Parameter(datetime, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True, default=datetime.now()),
40 'last_updated': Parameter(datetime, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True, nullok=True),
41 'max_slices': Parameter(int, "Maximum number of slices that the site is able to create", default=10),
42 'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create", default=1000),
43 'person_ids': Parameter([int], "List of account identifiers", joined=True),
44 'slice_ids': Parameter([int], "List of slice identifiers", joined=True),
45 'address_ids': Parameter([int], "List of address identifiers", joined=True),
46 'pcu_ids': Parameter([int], "List of PCU identifiers", joined=True),
47 'node_ids': Parameter([int], "List of site node identifiers", joined=True),
48 'site_tag_ids' : Parameter ([int], "List of tags attached to this site", joined=True),
49 'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
50 'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
51 'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
54 def validate_last_updated(self, last_updated):
55 # always return current timestamp
56 last_updated = datetime.now()
59 def validate_login_base(self, login_base):
60 if not len(login_base):
61 raise PLCInvalidArgument, "Login base must be specified"
63 if not set(login_base).issubset(string.lowercase + string.digits):
64 raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters or numbers"
66 conflicts = Site().select(filter={'login_base': login_base})
67 for site in conflicts:
68 if 'site_id' not in self or self['site_id'] != site.site_id:
69 raise PLCInvalidArgument, "login_base already in use"
73 def add_person(self, person_filter, role_name=None):
74 assert 'site_id' in self
75 assert 'tenant_id' in self
78 roles = Roles(self.api, role_name)
80 raise PLCInvalidArgument, "No such role %s" % role_name
82 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
83 persons = Persons(self.api, person_filter)
84 for person in persons:
85 keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
86 tenant.add_user(keystone_user, role.object)
87 site_person = SitePerson(self.api, {'site_id': self['site_id'],
88 'person_id': person['person_id']})
92 def delete_person(self, person_filter, role=None):
93 assert 'site_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 site_persons = SitePersons(self.api, {'site_id': self['id'],
107 'person_id': person['person_id']})
108 for site_person in site_persons:
112 def sync(self, commit=True, validate=True):
114 Add or update the site.
116 assert 'login_base' in self
117 AlchemyObj.sync(self, commit=commit, validate=validate)
118 # filter out fields that are not supported in keystone
119 nova_fields = ['enabled', 'description']
120 nova_can_update = lambda (field, value): field in nova_fields
121 nova_site = dict(filter(nova_can_update, self.items()))
122 nova_site['tenant_name'] = self['login_base']
123 if 'site_id' not in self:
124 # check if keystone record exsits
125 tenants = self.api.client_shell.keystone.tenants.findall(name=self['login_base'])
127 self.object = self.api.client_shell.keystone.tenants.create(**nova_site)
129 self.object = tenants[0]
130 self['tenant_id'] = self.object.id
131 # sync the plc record
132 AlchemyObj.insert(self, dict(self))
133 site = AlchemyObj.select(self, filter={'tenant_id': self['tenant_id']})[0]
134 self['site_id'] = site.site_id
137 self.object = self.api.client_shell.keystone.tenants.update(self['tenant_id'], **nova_site)
138 AlchemyObj.update(self, {'site_id': self['site_id']}, dict(self))
140 def delete(self, commit=True):
141 assert 'site_id' in self
142 assert 'tenant_id' in self
145 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
146 self.api.client_shell.keystone.tenants.delete(tenant)
148 # delete relationships
149 for site_person in SitePerson().select(filter={'site_id': self['site_id']}):
151 for slice in Slice().select(filter={'site_id': self['site_id']}):
153 for pcu in PCU().select(filter={'site_id': self['site_id']}):
155 for node in Node().select(filter={'site_id': self['site_id']}):
157 for address in SiteAddress().select(filter={'site_id': self['site_id']}):
159 for site_tag in SiteTag().select(filter={'site_id': self['site_id']}):
163 AlchemyObj.delete(self, filter={'site_id': self['site_id']})
169 Representation of row(s) from the sites table in the
173 def __init__(self, api, site_filter = None, columns = None):
177 sites = Site().select()
178 elif isinstance(site_filter, int):
179 sites = Site().select(filter={'site_id': site_filter})
180 elif isinstance(site_filter, StringTypes):
181 sites = Site().select(filter={'login_base': site_filter})
182 elif isinstance(site_filter, dict):
183 sites = Site().select(filter=site_filter)
184 elif isinstance(site_filter, (list, tuple, set)):
185 ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
186 strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
187 site_filter = {'site_id': ints, 'login_base': strs}
188 sites = Site().select(filter=site_filter)
190 raise PLCInvalidArgument, "Wrong site filter %s" % site_filter
194 site = Site(self.api, object = site, columns=columns)
195 if not columns or 'person_ids' in columns:
196 site_persons = SitePerson().select(filter={'site_id': site['site_id']})
197 site['person_ids'] = [rec.person_id for rec in site_persons]
199 if not columns or 'slice_ids' in columns:
200 site_slices = Slice().select(filter={'site_id': site['site_id']})
201 site['slice_ids'] = [rec.slice_id for rec in site_slices]
203 if not columns or 'puc_ids' in columns:
204 site_pcus = PCU().select(filter={'site_id': site['site_id']})
205 site['pcu_ids'] = [rec.pcu_id for rec in site_pcus]
206 if not columns or 'node_ids' in columns:
207 site_nodes = Node().select(filter={'site_id': site['site_id']})
208 site['node_ids'] = [rec.node_id for rec in site_nodes]
209 if not columns or 'address_ids' in columns:
210 site_addresses = SiteAddress().select(filter={'site_id': site['site_id']})
211 site['address_ids'] = [rec.address_id for rec in site_addresses]
213 if not columns or 'site_tag_ids' in columns:
214 site_tags = SiteTag().select(filter={'site_id': site['site_id']})
215 site['site_tag_ids'] = [rec.tag_id for rec in site_tags]
219 def refresh(self, api):
221 Import tenants from keystone.
224 sites = Site().select()
225 login_bases = [site.login_base for site in sites]
227 # get current tenants
228 tenants = api.client_shell.keystone.tenants.list()
230 # add tenants that dont already exist
231 for tenant in tenants:
232 # site tenants should not contain '_'
233 if '_' not in tenant.name and tenant.name not in login_bases:
234 description = tenant.description
235 if not description: description = tenant.name
236 site = Site(api, {'login_base': tenant.name,
237 'tenant_id': tenant.id,
238 'enabled': tenant.enabled,
239 'description': description
241 'abbreviated_name': tenant.name,