from types import StringTypes from datetime import datetime import string from PLC.Faults import * from PLC.Logger import logger from PLC.Parameter import Parameter, Mixed from PLC.Storage.AlchemyObject import AlchemyObj from PLC.Slices import Slice, Slices from PLC.Persons import Person, Persons from PLC.SitePersons import SitePerson, SitePersons from PLC.SiteAddresses import SiteAddress, SiteAddress from PLC.PCUs import PCU, PCUs from PLC.Nodes import Node, Nodes from PLC.Roles import Role, Roles from PLC.SiteTags import SiteTag, SiteTags class Site(AlchemyObj): """ Representation of a row in the sites table. To use, optionally instantiate with a dict of values. Update as you would a dict. Commit to the database with sync(). """ tablename = 'sites' fields = { 'site_id': Parameter(int, "Site identifier", primary_key=True), 'tenant_id': Parameter(str, "Tenant identifier"), 'enabled': Parameter(bool, "Has been enabled"), 'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50), 'login_base': Parameter(str, "Site slice prefix", max = 20), 'is_public': Parameter(bool, "Publicly viewable site"), 'name': Parameter(str, "Full site name", max = 254), 'description': Parameter(str, "Description", max = 254), 'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True), 'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True), 'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True), 'date_created': Parameter(datetime, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True, default=datetime.now()), 'last_updated': Parameter(datetime, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True, nullok=True), 'max_slices': Parameter(int, "Maximum number of slices that the site is able to create", default=10), 'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create", default=1000), 'person_ids': Parameter([int], "List of account identifiers", joined=True), 'slice_ids': Parameter([int], "List of slice identifiers", joined=True), 'address_ids': Parameter([int], "List of address identifiers", joined=True), 'pcu_ids': Parameter([int], "List of PCU identifiers", joined=True), 'node_ids': Parameter([int], "List of site node identifiers", joined=True), 'site_tag_ids' : Parameter ([int], "List of tags attached to this site", joined=True), 'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True), 'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True), 'ext_consortium_id': Parameter(int, "external consortium id", nullok = True) } def validate_last_updated(self, last_updated): # always return current timestamp last_updated = datetime.now() return last_updated def validate_login_base(self, login_base): if not len(login_base): raise PLCInvalidArgument, "Login base must be specified" if not set(login_base).issubset(string.lowercase + string.digits): raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters or numbers" conflicts = Site().select(filter={'login_base': login_base}) for site in conflicts: if 'site_id' not in self or self['site_id'] != site.site_id: raise PLCInvalidArgument, "login_base already in use" return login_base def add_person(self, person_filter, role_name=None): assert 'site_id' in self assert 'tenant_id' in self if not role_name: role_name = 'user' roles = Roles(self.api, role_name) if not roles: raise PLCInvalidArgument, "No such role %s" % role_name role = roles[0] tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id']) persons = Persons(self.api, person_filter) for person in persons: keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id']) tenant.add_user(keystone_user, role.object) site_person = SitePerson(self.api, {'site_id': self['site_id'], 'person_id': person['person_id']}) site_person.sync() def delete_person(self, person_filter, role=None): assert 'site_id' in self assert 'tenant_id' in self if not role: role = 'user' roles = Roles(self.api, role_name) if not roles: raise PLCInvalidArgument, "No such role %s" % role_name role = roles[0] tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id']) persons = Persons(self.api, person_filter) for person in persons: keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id']) tenant.remove_user(keystone_user, role.object) site_persons = SitePersons(self.api, {'site_id': self['id'], 'person_id': person['person_id']}) for site_person in site_persons: site_person.delete() def sync(self, commit=True, validate=True): """ Add or update the site. """ assert 'login_base' in self AlchemyObj.sync(self, commit=commit, validate=validate) # filter out fields that are not supported in keystone nova_fields = ['enabled', 'description'] nova_can_update = lambda (field, value): field in nova_fields nova_site = dict(filter(nova_can_update, self.items())) nova_site['tenant_name'] = self['login_base'] if 'site_id' not in self: # check if keystone record exsits tenants = self.api.client_shell.keystone.tenants.findall(name=self['login_base']) if not tenants: self.object = self.api.client_shell.keystone.tenants.create(**nova_site) else: self.object = tenants[0] self['tenant_id'] = self.object.id # sync the plc record AlchemyObj.insert(self, dict(self)) site = AlchemyObj.select(self, filter={'tenant_id': self['tenant_id']})[0] self['site_id'] = site.site_id else: self.object = self.api.client_shell.keystone.tenants.update(self['tenant_id'], **nova_site) AlchemyObj.update(self, {'site_id': self['site_id']}, dict(self)) def delete(self, commit=True): assert 'site_id' in self assert 'tenant_id' in self # delete nova object tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id']) self.api.client_shell.keystone.tenants.delete(tenant) # delete relationships for site_person in SitePerson().select(filter={'site_id': self['site_id']}): site_person.delete() for slice in Slice().select(filter={'site_id': self['site_id']}): slice.delete() for pcu in PCU().select(filter={'site_id': self['site_id']}): pcu.delete() for node in Node().select(filter={'site_id': self['site_id']}): node.delete() for address in SiteAddress().select(filter={'site_id': self['site_id']}): address.delete() for site_tag in SiteTag().select(filter={'site_id': self['site_id']}): site_tag.delete() # delete site AlchemyObj.delete(self, filter={'site_id': self['site_id']}) class Sites(list): """ Representation of row(s) from the sites table in the database. """ def __init__(self, api, site_filter = None, columns = None): self.api = api self.refresh(api) if not site_filter: sites = Site().select() elif isinstance(site_filter, int): sites = Site().select(filter={'site_id': site_filter}) elif isinstance(site_filter, StringTypes): sites = Site().select(filter={'login_base': site_filter}) elif isinstance(site_filter, dict): sites = Site().select(filter=site_filter) elif isinstance(site_filter, (list, tuple, set)): ints = filter(lambda x: isinstance(x, (int, long)), site_filter) strs = filter(lambda x: isinstance(x, StringTypes), site_filter) site_filter = {'site_id': ints, 'login_base': strs} sites = Site().select(filter=site_filter) else: raise PLCInvalidArgument, "Wrong site filter %s" % site_filter for site in sites: site = Site(self.api, object = site, columns=columns) if not columns or 'person_ids' in columns: site_persons = SitePerson().select(filter={'site_id': site['site_id']}) site['person_ids'] = [rec.person_id for rec in site_persons] if not columns or 'slice_ids' in columns: site_slices = Slice().select(filter={'site_id': site['site_id']}) site['slice_ids'] = [rec.slice_id for rec in site_slices] if not columns or 'puc_ids' in columns: site_pcus = PCU().select(filter={'site_id': site['site_id']}) site['pcu_ids'] = [rec.pcu_id for rec in site_pcus] if not columns or 'node_ids' in columns: site_nodes = Node().select(filter={'site_id': site['site_id']}) site['node_ids'] = [rec.node_id for rec in site_nodes] if not columns or 'address_ids' in columns: site_addresses = SiteAddress().select(filter={'site_id': site['site_id']}) site['address_ids'] = [rec.address_id for rec in site_addresses] if not columns or 'site_tag_ids' in columns: site_tags = SiteTag().select(filter={'site_id': site['site_id']}) site['site_tag_ids'] = [rec.tag_id for rec in site_tags] self.append(site) def refresh(self, api): """ Import tenants from keystone. """ # get current sites sites = Site().select() login_bases = [site.login_base for site in sites] # get current tenants tenants = api.client_shell.keystone.tenants.list() # add tenants that dont already exist for tenant in tenants: # site tenants should not contain '_' if '_' not in tenant.name and tenant.name not in login_bases: description = tenant.description if not description: description = tenant.name site = Site(api, {'login_base': tenant.name, 'tenant_id': tenant.id, 'enabled': tenant.enabled, 'description': description 'name': tenant.name, 'abbreviated_name': tenant.name, 'is_public': True}) site.sync()