Source code for designate.storage.sqlalchemy

# Copyright 2012 Managed I.T.
#
# Author: Kiall Mac Innes <kiall@managedit.ie>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from oslo_utils.secretutils import md5
from oslo_utils import timeutils
from sqlalchemy import case, select, distinct, func
from sqlalchemy.sql.expression import or_, literal_column

from designate import exceptions
from designate import objects
from designate.storage import sql
from designate.storage.sqlalchemy import base
from designate.storage.sqlalchemy import tables


LOG = logging.getLogger(__name__)

MAXIMUM_SUBZONE_DEPTH = 128


[docs] class SQLAlchemyStorage(base.SQLAlchemy): """SQLAlchemy connection""" __plugin_name__ = 'sqlalchemy' def __init__(self): super().__init__()
[docs] def get_inspector(self): return sql.get_inspector()
# CRUD for our resources (quota, server, tsigkey, tenant, zone & record) # R - get_*, find_*s # # Standard Arguments # self - python object for the class # context - a dictionary of details about the request (http etc), # provided by flask. # criterion - dictionary of filters to be applied # # Quota Methods def _find_quotas(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.quotas, objects.Quota, objects.QuotaList, exceptions.QuotaNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_quota(self, context, quota): """ Create a Quota. :param context: RPC Context. :param quota: Quota object with the values to be created. """ if not isinstance(quota, objects.Quota): # TODO(kiall): Quotas should always use Objects quota = objects.Quota(**quota) return self._create( tables.quotas, quota, exceptions.DuplicateQuota)
[docs] def get_quota(self, context, quota_id): """ Get a Quota via ID. :param context: RPC Context. :param quota_id: Quota ID to get. """ return self._find_quotas(context, {'id': quota_id}, one=True)
[docs] def find_quotas(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find Quotas :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_quotas(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_quota(self, context, criterion): """ Find a single Quota. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_quotas(context, criterion, one=True)
[docs] def update_quota(self, context, quota): """ Update a Quota :param context: RPC Context. :param quota: Quota to update. """ return self._update( context, tables.quotas, quota, exceptions.DuplicateQuota, exceptions.QuotaNotFound)
[docs] def delete_quota(self, context, quota_id): """ Delete a Quota via ID. :param context: RPC Context. :param quota_id: Delete a Quota via ID """ # Fetch the existing quota, we'll need to return it. quota = self._find_quotas(context, {'id': quota_id}, one=True) return self._delete(context, tables.quotas, quota, exceptions.QuotaNotFound)
# TLD Methods def _find_tlds(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.tlds, objects.Tld, objects.TldList, exceptions.TldNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_tld(self, context, tld): """ Create a TLD. :param context: RPC Context. :param tld: Tld object with the values to be created. """ return self._create( tables.tlds, tld, exceptions.DuplicateTld)
[docs] def get_tld(self, context, tld_id): """ Get a TLD via ID. :param context: RPC Context. :param tld_id: TLD ID to get. """ return self._find_tlds(context, {'id': tld_id}, one=True)
[docs] def find_tlds(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find TLDs :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_tlds(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_tld(self, context, criterion): """ Find a single TLD. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_tlds(context, criterion, one=True)
[docs] def update_tld(self, context, tld): """ Update a TLD :param context: RPC Context. :param tld: TLD to update. """ return self._update( context, tables.tlds, tld, exceptions.DuplicateTld, exceptions.TldNotFound)
[docs] def delete_tld(self, context, tld_id): """ Delete a TLD via ID. :param context: RPC Context. :param tld_id: Delete a TLD via ID """ # Fetch the existing tld, we'll need to return it. tld = self._find_tlds(context, {'id': tld_id}, one=True) return self._delete(context, tables.tlds, tld, exceptions.TldNotFound)
# TSIG Key Methods def _find_tsigkeys(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.tsigkeys, objects.TsigKey, objects.TsigKeyList, exceptions.TsigKeyNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_tsigkey(self, context, tsigkey): """ Create a TSIG Key. :param context: RPC Context. :param tsigkey: TsigKey object with the values to be created. """ return self._create( tables.tsigkeys, tsigkey, exceptions.DuplicateTsigKey)
[docs] def get_tsigkey(self, context, tsigkey_id): """ Get a TSIG Key via ID. :param context: RPC Context. :param tsigkey_id: Server ID to get. """ return self._find_tsigkeys(context, {'id': tsigkey_id}, one=True)
[docs] def find_tsigkeys(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find TSIG Keys. :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_tsigkeys(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_tsigkey(self, context, criterion): """ Find TSIG Key. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_tsigkeys(context, criterion, one=True)
[docs] def update_tsigkey(self, context, tsigkey): """ Update a TSIG Key :param context: RPC Context. :param tsigkey: TSIG Keyto update. """ return self._update( context, tables.tsigkeys, tsigkey, exceptions.DuplicateTsigKey, exceptions.TsigKeyNotFound)
[docs] def delete_tsigkey(self, context, tsigkey_id): """ Delete a TSIG Key via ID. :param context: RPC Context. :param tsigkey_id: Delete a TSIG Key via ID """ # Fetch the existing tsigkey, we'll need to return it. tsigkey = self._find_tsigkeys(context, {'id': tsigkey_id}, one=True) return self._delete(context, tables.tsigkeys, tsigkey, exceptions.TsigKeyNotFound)
## # Tenant Methods ##
[docs] def find_tenants(self, context): """ Find all Tenants. :param context: RPC Context. """ # returns an array of tenant_id & count of their zones query = select(tables.zones.c.tenant_id, func.count(tables.zones.c.id)) query = self._apply_tenant_criteria(context, tables.zones, query) query = self._apply_deleted_criteria(context, tables.zones, query) query = query.group_by(tables.zones.c.tenant_id) with sql.get_read_session() as session: resultproxy = session.execute(query) results = resultproxy.fetchall() tenant_list = objects.TenantList( objects=[objects.Tenant(id=t[0], zone_count=t[1]) for t in results]) tenant_list.obj_reset_changes() return tenant_list
[docs] def get_tenant(self, context, tenant_id): """ Get Tenant. :param context: RPC Context. :param tenant_id: ID of the Tenant. """ # get list & count of all zones owned by given tenant_id query = select(tables.zones.c.name) query = self._apply_tenant_criteria(context, tables.zones, query) query = self._apply_deleted_criteria(context, tables.zones, query) query = query.where(tables.zones.c.tenant_id == tenant_id) with sql.get_read_session() as session: resultproxy = session.execute(query) results = resultproxy.fetchall() return objects.Tenant( id=tenant_id, zone_count=len(results), zones=[r[0] for r in results])
[docs] def count_tenants(self, context): """ Count tenants :param context: RPC Context. """ # tenants are the owner of zones, count the number of unique tenants # select count(distinct tenant_id) from zones query = select(func.count(distinct(tables.zones.c.tenant_id))) query = self._apply_tenant_criteria(context, tables.zones, query) query = self._apply_deleted_criteria(context, tables.zones, query) with sql.get_read_session() as session: resultproxy = session.execute(query) result = resultproxy.fetchone() if result is None: return 0 return result[0]
## # Zone Methods ## def _find_zones(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None, apply_tenant_criteria=True, include_shared=False): # Check to see if the criterion can use the reverse_name column criterion = self._rname_check(criterion) # Create a virtual column showing if the zone is shared or not. shared_case = case((tables.shared_zones.c.target_project_id.is_(None), literal_column('False')), else_=literal_column('True')).label('shared') query = select( tables.zones, shared_case).outerjoin(tables.shared_zones).distinct() zones = self._find( context, tables.zones, objects.Zone, objects.ZoneList, exceptions.ZoneNotFound, criterion, one, marker, limit, sort_key, sort_dir, query=query, apply_tenant_criteria=apply_tenant_criteria, include_shared=include_shared) def _load_relations(zone): if zone.type == 'SECONDARY': zone.masters = self._find_zone_masters( context, {'zone_id': zone.id}) else: # This avoids an extra DB call per primary zone. This will # always have 0 results for a PRIMARY zone. zone.masters = objects.ZoneMasterList() zone.attributes = self._find_zone_attributes( context, {'zone_id': zone.id, 'key': '!master'}) zone.obj_reset_changes(['masters', 'attributes']) # TODO(Federico) refactor part of _find_zones into _find_zone, move # _load_relations out if one: _load_relations(zones) else: zones.total_count = self.count_zones(context, criterion) for d in zones: _load_relations(d) if one: LOG.debug('Fetched zone %s', zones) return zones
[docs] def create_zone(self, context, zone): """ Create a new Zone. :param context: RPC Context. :param zone: Zone object with the values to be created. """ # Patch in the reverse_name column extra_values = {'reverse_name': zone.name[::-1]} # Don't handle recordsets for now zone = self._create( tables.zones, zone, exceptions.DuplicateZone, ['attributes', 'recordsets', 'masters'], extra_values=extra_values) if zone.obj_attr_is_set('attributes'): for attrib in zone.attributes: self.create_zone_attribute(context, zone.id, attrib) else: zone.attributes = objects.ZoneAttributeList() if zone.obj_attr_is_set('masters'): for master in zone.masters: self.create_zone_master(context, zone.id, master) else: zone.masters = objects.ZoneMasterList() zone.obj_reset_changes(['masters', 'attributes']) return zone
[docs] def get_zone(self, context, zone_id, apply_tenant_criteria=True): """ Get a Zone via its ID. :param context: RPC Context. :param zone_id: ID of the Zone. :param apply_tenant_criteria: Whether to filter results by project_id. """ zone = self._find_zones(context, {'id': zone_id}, one=True, apply_tenant_criteria=apply_tenant_criteria, include_shared=True) return zone
[docs] def find_zones(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find zones :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ zones = self._find_zones(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir, include_shared=True) return zones
[docs] def find_zone(self, context, criterion): """ Find a single Zone. :param context: RPC Context. :param criterion: Criteria to filter by. """ zone = self._find_zones(context, criterion, one=True, include_shared=True) return zone
[docs] def update_zone(self, context, zone): """ Update a Zone :param context: RPC Context. :param zone: Zone object. """ tenant_id_changed = False if 'tenant_id' in zone.obj_what_changed(): tenant_id_changed = True # Don't handle recordsets for now LOG.debug('Updating zone %s', zone) updated_zone = self._update( context, tables.zones, zone, exceptions.DuplicateZone, exceptions.ZoneNotFound, ['attributes', 'recordsets', 'masters']) if zone.obj_attr_is_set('attributes'): # Gather the Attribute ID's we have have = {r.id for r in self._find_zone_attributes( context, {'zone_id': zone.id})} # Prep some lists of changes keep = set() create = [] update = [] # Determine what to change for i in zone.attributes: keep.add(i.id) try: i.obj_get_original_value('id') except KeyError: create.append(i) else: update.append(i) # NOTE: Since we're dealing with mutable objects, the return value # of create/update/delete attribute is not needed. # The original item will be mutated in place on the input # "zone.attributes" list. # Delete Attributes for i_id in have - keep: attr = self._find_zone_attributes( context, {'id': i_id}, one=True) self.delete_zone_attribute(context, attr.id) # Update Attributes for i in update: self.update_zone_attribute(context, i) # Create Attributes for attr in create: attr.zone_id = zone.id self.create_zone_attribute(context, zone.id, attr) if zone.obj_attr_is_set('masters'): # Gather the Attribute ID's we have have = {r.id for r in self._find_zone_masters( context, {'zone_id': zone.id})} # Prep some lists of changes keep = set() create = [] update = [] # Determine what to change for i in zone.masters: keep.add(i.id) try: i.obj_get_original_value('id') except KeyError: create.append(i) else: update.append(i) # NOTE: Since we're dealing with mutable objects, the return value # of create/update/delete attribute is not needed. # The original item will be mutated in place on the input # "zone.attributes" list. # Delete Attributes for i_id in have - keep: attr = self._find_zone_masters( context, {'id': i_id}, one=True) self.delete_zone_master(context, attr.id) # Update Attributes for i in update: self.update_zone_master(context, i) # Create Attributes for attr in create: attr.zone_id = zone.id self.create_zone_master(context, zone.id, attr) if zone.obj_attr_is_set('recordsets'): existing = {} for rrset in self.find_recordsets(context, {'zone_id': zone.id}): existing[rrset.name, rrset.type] = rrset keep = set() for rrset in zone.recordsets: existing_recordset = existing.get((rrset.name, rrset.type)) if existing_recordset: existing_recordset.update(rrset) existing_recordset.records = rrset.records existing_recordset.obj_reset_changes(['zone_name']) self.update_recordset(context, existing_recordset) keep.add(existing_recordset.id) else: self.create_recordset(context, zone.id, rrset) keep.add(rrset.id) if zone.type == 'SECONDARY': # Purge anything that shouldn't be there :P for i in {i.id for i in existing.values()} - keep: self.delete_recordset(context, i) if tenant_id_changed: with sql.get_write_session() as session: session.execute( tables.recordsets.update(). where(tables.recordsets.c.zone_id == zone.id). values({'tenant_id': zone.tenant_id}) ) session.execute( tables.records.update(). where(tables.records.c.zone_id == zone.id). values({'tenant_id': zone.tenant_id}) ) return updated_zone
[docs] def increment_serial(self, context, zone_id): """Increment the zone's serial number. """ new_serial = timeutils.utcnow_ts() query = tables.zones.update().where( tables.zones.c.id == zone_id).values( {'serial': new_serial, 'increment_serial': False} ) with sql.get_write_session() as session: session.execute(query) LOG.debug('Incremented zone serial for %s to %d', zone_id, new_serial) return new_serial
[docs] def delete_zone(self, context, zone_id): """ Delete a Zone :param context: RPC Context. :param zone_id: Zone ID to delete. """ # Fetch the existing zone, we'll need to return it. zone = self._find_zones(context, {'id': zone_id}, one=True) return self._delete(context, tables.zones, zone, exceptions.ZoneNotFound)
[docs] def purge_zone(self, context, zone): """ Purge a Zone, effectively removing the zone database record. :param context: RPC Context. :param zone: Zone to delete. """ return self._delete(context, tables.zones, zone, exceptions.ZoneNotFound, hard_delete=True)
def _walk_up_zones(self, current, zones_by_id): """Walk upwards in a zone hierarchy until we find a parent zone that does not belong to "zones_by_id" :returns: parent zone ID or None """ max_steps = MAXIMUM_SUBZONE_DEPTH while current.parent_zone_id in zones_by_id: current = zones_by_id[current.parent_zone_id] max_steps -= 1 if max_steps == 0: raise exceptions.IllegalParentZone('Loop detected in the' ' zone hierarchy') return current.parent_zone_id
[docs] def purge_zones(self, context, criterion, limit): """ Purge Zones, effectively removing the zones database records. Reparent orphan childrens, if any. Transactions/locks are not needed. :param context: RPC Context. :param criterion: Criteria to filter by. :param limit: Integer limit of objects of the page size after the marker """ if 'deleted' in criterion: context.show_deleted = True zones = self.find_zones( context=context, criterion=criterion, limit=limit, ) if not zones: LOG.info('No zones to be purged') return LOG.debug('Purging %d zones', len(zones)) zones_by_id = {z.id: z for z in zones} for zone in zones: # Reparent child zones, if any. surviving_parent_id = self._walk_up_zones(zone, zones_by_id) query = ( tables.zones.update(). where(tables.zones.c.parent_zone_id == zone.id). values(parent_zone_id=surviving_parent_id) ) with sql.get_write_session() as session: resultproxy = session.execute(query) LOG.debug('%d child zones updated', resultproxy.rowcount) self.purge_zone(context, zone) LOG.info('Purged %d zones', len(zones)) return len(zones)
[docs] def count_zones(self, context, criterion=None): """ Count zones :param context: RPC Context. :param criterion: Criteria to filter by. """ query = select(func.count(tables.zones.c.id)) query = self._apply_criterion(tables.zones, query, criterion) query = self._apply_tenant_criteria(context, tables.zones, query) query = self._apply_deleted_criteria(context, tables.zones, query) with sql.get_read_session() as session: resultproxy = session.execute(query) result = resultproxy.fetchone() if result is None: return 0 return result[0]
# Shared zones methods def _find_shared_zones(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): table = tables.shared_zones query = select(table) if not context.all_tenants: query = query.where(or_( table.c.project_id == context.project_id, table.c.target_project_id == context.project_id)) return self._find( context, tables.shared_zones, objects.SharedZone, objects.SharedZoneList, exceptions.SharedZoneNotFound, criterion, one, marker, limit, sort_key, sort_dir, query=query, apply_tenant_criteria=False)
[docs] def share_zone(self, context, shared_zone): """ Share zone :param context: RPC Context. :param shared_zone: Shared Zone dict """ return self._create(tables.shared_zones, shared_zone, exceptions.DuplicateSharedZone)
[docs] def unshare_zone(self, context, zone_id, shared_zone_id): """ Unshare zone :param context: RPC Context. :param shared_zone_id: Shared Zone Id """ shared_zone = self._find_shared_zones( context, {'id': shared_zone_id, 'zone_id': zone_id}, one=True ) return self._delete(context, tables.shared_zones, shared_zone, exceptions.SharedZoneNotFound)
[docs] def find_shared_zones(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find shared zones :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_shared_zones( context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir )
[docs] def get_shared_zone(self, context, zone_id, shared_zone_id): """ Get a shared zone via ID :param context: RPC Context. :param shared_zone_id: Shared Zone Id """ return self._find_shared_zones( context, {'id': shared_zone_id, 'zone_id': zone_id}, one=True )
[docs] def is_zone_shared_with_project(self, zone_id, project_id): """ Checks if a zone is shared with a project. :param zone_id: The zone ID to check. :param project_id: The project ID to check. :returns: Boolean True/False if the zone is shared with the project. """ query = select(literal_column('true')) query = query.where(tables.shared_zones.c.zone_id == zone_id) query = query.where( tables.shared_zones.c.target_project_id == project_id) with sql.get_read_session() as session: return session.scalar(query) is not None
[docs] def delete_zone_shares(self, zone_id): """ Delete all of the zone shares for a specific zone. :param zone_id: The zone ID to check. """ query = tables.shared_zones.delete().where( tables.shared_zones.c.zone_id == zone_id) with sql.get_write_session() as session: session.execute(query)
# Zone attribute methods def _find_zone_attributes(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find(context, tables.zone_attributes, objects.ZoneAttribute, objects.ZoneAttributeList, exceptions.ZoneAttributeNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_zone_attribute(self, context, zone_id, zone_attribute): zone_attribute.zone_id = zone_id return self._create(tables.zone_attributes, zone_attribute, exceptions.DuplicateZoneAttribute)
[docs] def get_zone_attributes(self, context, zone_attribute_id): return self._find_zone_attributes( context, {'id': zone_attribute_id}, one=True)
[docs] def find_zone_attributes(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find_zone_attributes(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def update_zone_attribute(self, context, zone_attribute): return self._update(context, tables.zone_attributes, zone_attribute, exceptions.DuplicateZoneAttribute, exceptions.ZoneAttributeNotFound)
[docs] def delete_zone_attribute(self, context, zone_attribute_id): zone_attribute = self._find_zone_attributes( context, {'id': zone_attribute_id}, one=True) deleted_zone_attribute = self._delete( context, tables.zone_attributes, zone_attribute, exceptions.ZoneAttributeNotFound) return deleted_zone_attribute
# Zone master methods def _find_zone_masters(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find(context, tables.zone_masters, objects.ZoneMaster, objects.ZoneMasterList, exceptions.ZoneMasterNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_zone_master(self, context, zone_id, zone_master): zone_master.zone_id = zone_id return self._create(tables.zone_masters, zone_master, exceptions.DuplicateZoneMaster)
[docs] def update_zone_master(self, context, zone_master): return self._update(context, tables.zone_masters, zone_master, exceptions.DuplicateZoneMaster, exceptions.ZoneMasterNotFound)
[docs] def delete_zone_master(self, context, zone_master_id): zone_master = self._find_zone_masters( context, {'id': zone_master_id}, one=True) deleted_zone_master = self._delete( context, tables.zone_masters, zone_master, exceptions.ZoneMasterNotFound) return deleted_zone_master
# RecordSet Methods def _find_recordsets(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None, force_index=False, apply_tenant_criteria=True): # Check to see if the criterion can use the reverse_name column criterion = self._rname_check(criterion) if criterion is not None and not criterion.get('zones_deleted', True): # remove 'zones_deleted' from the criterion, as _apply_criterion # assumes each key in criterion to be a column name. del criterion['zones_deleted'] if one: rjoin = tables.recordsets.join( tables.zones, tables.recordsets.c.zone_id == tables.zones.c.id) query = ( select(tables.recordsets).select_from(rjoin). where(tables.zones.c.deleted == '0') ) recordsets = self._find( context, tables.recordsets, objects.RecordSet, objects.RecordSetList, exceptions.RecordSetNotFound, criterion, one, marker, limit, sort_key, sort_dir, query, apply_tenant_criteria=apply_tenant_criteria, ) recordsets.records = self._find_records( context, {'recordset_id': recordsets.id}, apply_tenant_criteria=apply_tenant_criteria, ) recordsets.obj_reset_changes(['records']) else: tc, recordsets = self._find_recordsets_with_records( context, criterion, limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, force_index=force_index, apply_tenant_criteria=apply_tenant_criteria, ) recordsets.total_count = tc return recordsets
[docs] def find_recordsets_axfr(self, context, criterion=None): """ Find RecordSets. :param context: RPC Context. :param criterion: Criteria to filter by. """ # Check to see if the criterion can use the reverse_name column criterion = self._rname_check(criterion) rjoin = tables.records.join( tables.recordsets, tables.records.c.recordset_id == tables.recordsets.c.id) query = ( select(tables.recordsets.c.id, tables.recordsets.c.type, tables.recordsets.c.ttl, tables.recordsets.c.name, tables.records.c.data, tables.records.c.action). select_from(rjoin).where(tables.records.c.action != 'DELETE') ) query = query.order_by(tables.recordsets.c.id) raw_rows = self._select_raw( context, tables.recordsets, criterion, query) return raw_rows
[docs] def create_recordset(self, context, zone_id, recordset): """ Create a recordset on a given Zone ID :param context: RPC Context. :param zone_id: Zone ID to create the recordset in. :param recordset: RecordSet object with the values to be created. """ recordset.tenant_id = context.project_id recordset.zone_id = zone_id # Patch in the reverse_name column extra_values = {'reverse_name': recordset.name[::-1]} recordset = self._create( tables.recordsets, recordset, exceptions.DuplicateRecordSet, ['records'], extra_values=extra_values) if recordset.obj_attr_is_set('records'): for record in recordset.records: # NOTE: Since we're dealing with a mutable object, the return # value is not needed. The original item will be mutated # in place on the input "recordset.records" list. self.create_record(context, zone_id, recordset.id, record) else: recordset.records = objects.RecordList() recordset.obj_reset_changes(['records']) return recordset
[docs] def find_recordsets_export(self, context, criterion=None): query = None rjoin = tables.records.join( tables.recordsets, tables.records.c.recordset_id == tables.recordsets.c.id) query = ( select(tables.recordsets.c.name, tables.recordsets.c.ttl, tables.recordsets.c.type, tables.records.c.data). select_from(rjoin) ) query = query.order_by(tables.recordsets.c.created_at) raw_rows = self._select_raw( context, tables.recordsets, criterion, query) return raw_rows
[docs] def find_recordsets(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None, force_index=False, apply_tenant_criteria=True): """ Find RecordSets. :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. :param apply_tenant_criteria: Whether to filter results by project_id. """ return self._find_recordsets( context, criterion, marker=marker, sort_dir=sort_dir, sort_key=sort_key, limit=limit, force_index=force_index, apply_tenant_criteria=apply_tenant_criteria)
[docs] def find_recordset(self, context, criterion, apply_tenant_criteria=True): """ Find a single RecordSet. :param context: RPC Context. :param criterion: Criteria to filter by. :param apply_tenant_criteria: Whether to filter results by project_id. """ return self._find_recordsets( context, criterion, one=True, apply_tenant_criteria=apply_tenant_criteria)
[docs] def update_recordset(self, context, recordset): """ Update a recordset :param context: RPC Context. :param recordset: RecordSet to update """ recordset = self._update( context, tables.recordsets, recordset, exceptions.DuplicateRecordSet, exceptions.RecordSetNotFound, ['records']) if recordset.obj_attr_is_set('records'): # Gather the Record ID's we have have_records = {r.id for r in self._find_records( context, {'recordset_id': recordset.id})} # Prep some lists of changes keep_records = set() create_records = [] update_records = [] # Determine what to change for record in recordset.records: keep_records.add(record.id) try: record.obj_get_original_value('id') except KeyError: create_records.append(record) else: update_records.append(record) # NOTE: Since we're dealing with mutable objects, the return value # of create/update/delete record is not needed. The original # item will be mutated in place on the input # "recordset.records" list. # Delete Records for record_id in have_records - keep_records: self.delete_record(context, record_id) # Update Records for record in update_records: self.update_record(context, record) # Create Records for record in create_records: self.create_record( context, recordset.zone_id, recordset.id, record) return recordset
[docs] def delete_recordset(self, context, recordset_id): """ Delete a recordset :param context: RPC Context. :param recordset_id: RecordSet ID to delete """ # Fetch the existing recordset, we'll need to return it. recordset = self._find_recordsets( context, {'id': recordset_id}, one=True) return self._delete(context, tables.recordsets, recordset, exceptions.RecordSetNotFound)
[docs] def count_recordsets(self, context, criterion=None): """ Count recordsets :param context: RPC Context. :param criterion: Criteria to filter by. """ # Ensure that we return only active recordsets rjoin = tables.recordsets.join( tables.zones, tables.recordsets.c.zone_id == tables.zones.c.id) query = ( select(func.count(tables.recordsets.c.id)). select_from(rjoin). where(tables.zones.c.deleted == '0') ) query = self._apply_criterion(tables.recordsets, query, criterion) query = self._apply_tenant_criteria(context, tables.recordsets, query) query = self._apply_deleted_criteria(context, tables.recordsets, query) with sql.get_read_session() as session: resultproxy = session.execute(query) result = resultproxy.fetchone() if result is None: return 0 return result[0]
# Record Methods def _find_records(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None, apply_tenant_criteria=True): return self._find( context, tables.records, objects.Record, objects.RecordList, exceptions.RecordNotFound, criterion, one, marker, limit, sort_key, sort_dir, apply_tenant_criteria=apply_tenant_criteria, ) def _recalculate_record_hash(self, record): """ Calculates the hash of the record, used to ensure record uniqueness. """ md5sum = md5(usedforsecurity=False) md5sum.update(('{}:{}'.format(record.recordset_id, record.data)).encode('utf-8')) return md5sum.hexdigest()
[docs] def create_record(self, context, zone_id, recordset_id, record): """ Create a record on a given Zone ID :param context: RPC Context. :param zone_id: Zone ID to create the record in. :param recordset_id: RecordSet ID to create the record in. :param record: Record object with the values to be created. """ record.tenant_id = context.project_id record.zone_id = zone_id record.recordset_id = recordset_id record.hash = self._recalculate_record_hash(record) return self._create( tables.records, record, exceptions.DuplicateRecord)
[docs] def get_record(self, context, record_id): """ Get a record via ID :param context: RPC Context. :param record_id: Record ID to get """ return self._find_records(context, {'id': record_id}, one=True)
[docs] def find_records(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find Records. :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_records(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_record(self, context, criterion): """ Find a single Record. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_records(context, criterion, one=True)
[docs] def update_record(self, context, record): """ Update a record :param context: RPC Context. :param record: Record to update """ if record.obj_what_changed(): record.hash = self._recalculate_record_hash(record) return self._update( context, tables.records, record, exceptions.DuplicateRecord, exceptions.RecordNotFound)
[docs] def delete_record(self, context, record_id): """ Delete a record :param context: RPC Context. :param record_id: Record ID to delete """ # Fetch the existing record, we'll need to return it. record = self._find_records(context, {'id': record_id}, one=True) return self._delete(context, tables.records, record, exceptions.RecordNotFound)
[docs] def count_records(self, context, criterion=None): """ Count records :param context: RPC Context. :param criterion: Criteria to filter by. """ # Ensure that we return only active records rjoin = tables.records.join( tables.zones, tables.records.c.zone_id == tables.zones.c.id) query = ( select(func.count(tables.records.c.id)). select_from(rjoin). where(tables.zones.c.deleted == '0') ) query = self._apply_criterion(tables.records, query, criterion) query = self._apply_tenant_criteria(context, tables.records, query) query = self._apply_deleted_criteria(context, tables.records, query) with sql.get_read_session() as session: resultproxy = session.execute(query) result = resultproxy.fetchone() if result is None: return 0 return result[0]
# Blacklist Methods def _find_blacklists(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.blacklists, objects.Blacklist, objects.BlacklistList, exceptions.BlacklistNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_blacklist(self, context, blacklist): """ Create a Blacklist. :param context: RPC Context. :param blacklist: Blacklist object with the values to be created. """ return self._create( tables.blacklists, blacklist, exceptions.DuplicateBlacklist)
[docs] def get_blacklist(self, context, blacklist_id): """ Get a Blacklist via ID. :param context: RPC Context. :param blacklist_id: Blacklist ID to get. """ return self._find_blacklists(context, {'id': blacklist_id}, one=True)
[docs] def find_blacklists(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find Blacklists :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_blacklists(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_blacklist(self, context, criterion): """ Find a single Blacklist. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_blacklists(context, criterion, one=True)
[docs] def update_blacklist(self, context, blacklist): """ Update a Blacklist :param context: RPC Context. :param blacklist: Blacklist to update. """ return self._update( context, tables.blacklists, blacklist, exceptions.DuplicateBlacklist, exceptions.BlacklistNotFound)
[docs] def delete_blacklist(self, context, blacklist_id): """ Delete a Blacklist via ID. :param context: RPC Context. :param blacklist_id: Delete a Blacklist via ID """ # Fetch the existing blacklist, we'll need to return it. blacklist = self._find_blacklists( context, {'id': blacklist_id}, one=True) return self._delete(context, tables.blacklists, blacklist, exceptions.BlacklistNotFound)
# Pool methods def _find_pools(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): pools = self._find(context, tables.pools, objects.Pool, objects.PoolList, exceptions.PoolNotFound, criterion, one, marker, limit, sort_key, sort_dir) # Load Relations def _load_relations(pool): pool.attributes = self._find_pool_attributes( context, {'pool_id': pool.id}) pool.ns_records = self._find_pool_ns_records( context, {'pool_id': pool.id}) pool.nameservers = self._find_pool_nameservers( context, {'pool_id': pool.id}) pool.targets = self._find_pool_targets( context, {'pool_id': pool.id}) pool.also_notifies = self._find_pool_also_notifies( context, {'pool_id': pool.id}) pool.obj_reset_changes(['attributes', 'ns_records', 'nameservers', 'targets', 'also_notifies']) if one: _load_relations(pools) else: for pool in pools: _load_relations(pool) return pools
[docs] def create_pool(self, context, pool): """ Create a Pool. :param context: RPC Context. :param pool: Pool object with the values to be created. """ pool = self._create( tables.pools, pool, exceptions.DuplicatePool, ['attributes', 'ns_records', 'nameservers', 'targets', 'also_notifies']) if pool.obj_attr_is_set('attributes'): for pool_attribute in pool.attributes: self.create_pool_attribute(context, pool.id, pool_attribute) else: pool.attributes = objects.PoolAttributeList() if pool.obj_attr_is_set('ns_records'): for ns_record in pool.ns_records: self.create_pool_ns_record(context, pool.id, ns_record) else: pool.ns_records = objects.PoolNsRecordList() if pool.obj_attr_is_set('nameservers'): for nameserver in pool.nameservers: self.create_pool_nameserver(context, pool.id, nameserver) else: pool.nameservers = objects.PoolNameserverList() if pool.obj_attr_is_set('targets'): for target in pool.targets: self.create_pool_target(context, pool.id, target) else: pool.targets = objects.PoolTargetList() if pool.obj_attr_is_set('also_notifies'): for also_notify in pool.also_notifies: self.create_pool_also_notify(context, pool.id, also_notify) else: pool.also_notifies = objects.PoolAlsoNotifyList() pool.obj_reset_changes(['attributes', 'ns_records', 'nameservers', 'targets', 'also_notifies']) return pool
[docs] def get_pool(self, context, pool_id): """ Get a Pool via the id :param context: RPC Context. :param pool_id: The ID of the pool to get """ return self._find_pools(context, {'id': pool_id}, one=True)
[docs] def find_pools(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find all Pools :param context: RPC Context. :param criterion: Criteria by which to filter :param marker: Resource ID used by paging. The next page will start at the next resource after the marker :param limit: Integer limit of objects on the page :param sort_key: Key used to sort the returned list :param sort_dir: Directions to sort after using sort_key """ return self._find_pools(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_pool(self, context, criterion): """ Find a single Pool. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_pools(context, criterion, one=True)
[docs] def update_pool(self, context, pool): """ Update the specified pool :param context: RPC Context. :param pool: Pool to update. """ pool = self._update(context, tables.pools, pool, exceptions.DuplicatePool, exceptions.PoolNotFound, ['attributes', 'ns_records', 'nameservers', 'targets', 'also_notifies']) for attribute_name in ('attributes', 'ns_records', 'nameservers', 'targets', 'also_notifies'): if pool.obj_attr_is_set(attribute_name): self._update_pool_items(context, pool, attribute_name) # Call get_pool to get the ids of all the attributes/ns_records # refreshed in the pool object updated_pool = self.get_pool(context, pool.id) return updated_pool
[docs] def delete_pool(self, context, pool_id): """ Delete the pool with the matching id :param context: RPC Context. :param pool_id: The ID of the pool to be deleted """ pool = self._find_pools(context, {'id': pool_id}, one=True) return self._delete(context, tables.pools, pool, exceptions.PoolNotFound)
# Pool attribute methods def _find_pool_attributes(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find(context, tables.pool_attributes, objects.PoolAttribute, objects.PoolAttributeList, exceptions.PoolAttributeNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_pool_attribute(self, context, pool_id, pool_attribute): """ Create a PoolAttribute. :param context: RPC Context. :param pool_id: The ID of the pool to which the attribute belongs. :param pool_attribute: PoolAttribute object with the values created. """ pool_attribute.pool_id = pool_id return self._create(tables.pool_attributes, pool_attribute, exceptions.DuplicatePoolAttribute)
[docs] def get_pool_attribute(self, context, pool_attribute_id): """ Get a PoolAttribute via the ID :param context: RPC Context. :param pool_attribute_id: The ID of the PoolAttribute to get """ return self._find_pool_attributes( context, {'id': pool_attribute_id}, one=True)
[docs] def find_pool_attributes(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find all PoolAttributes :param context: RPC Context :param criterion: Criteria by which to filer :param marker: Resource ID used by paging. The next page will start at the next resource after the marker :param limit: Integer limit of objects on the page :param sort_key: Key used to sort the returned list :param sort_dir: Directions to sort after using sort_key """ return self._find_pool_attributes(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_pool_attribute(self, context, criterion): """ Find a single PoolAttribute :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_pool_attributes(context, criterion, one=True)
[docs] def update_pool_attribute(self, context, pool_attribute): """ Update the specified pool :param context: RPC Context. :param pool_attribute: PoolAttribute to update """ return self._update(context, tables.pool_attributes, pool_attribute, exceptions.DuplicatePoolAttribute, exceptions.PoolAttributeNotFound)
[docs] def delete_pool_attribute(self, context, pool_attribute_id): """ Delete the pool with the matching id :param context: RPC Context. :param pool_attribute_id: The ID of the PoolAttribute to be deleted """ pool_attribute = self._find_pool_attributes( context, {'id': pool_attribute_id}, one=True) deleted_pool_attribute = self._delete( context, tables.pool_attributes, pool_attribute, exceptions.PoolAttributeNotFound) return deleted_pool_attribute
# Pool ns_record methods def _find_pool_ns_records(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find(context, tables.pool_ns_records, objects.PoolNsRecord, objects.PoolNsRecordList, exceptions.PoolNsRecordNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_pool_ns_record(self, context, pool_id, pool_ns_record): pool_ns_record.pool_id = pool_id return self._create(tables.pool_ns_records, pool_ns_record, exceptions.DuplicatePoolNsRecord)
[docs] def update_pool_ns_record(self, context, pool_ns_record): return self._update(context, tables.pool_ns_records, pool_ns_record, exceptions.DuplicatePoolNsRecord, exceptions.PoolNsRecordNotFound)
[docs] def delete_pool_ns_record(self, context, pool_ns_record_id): pool_ns_record = self._find_pool_ns_records( context, {'id': pool_ns_record_id}, one=True) deleted_pool_ns_record = self._delete( context, tables.pool_ns_records, pool_ns_record, exceptions.PoolNsRecordNotFound) return deleted_pool_ns_record
# PoolNameserver methods def _find_pool_nameservers(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find(context, tables.pool_nameservers, objects.PoolNameserver, objects.PoolNameserverList, exceptions.PoolNameserverNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_pool_nameserver(self, context, pool_id, pool_nameserver): pool_nameserver.pool_id = pool_id return self._create(tables.pool_nameservers, pool_nameserver, exceptions.DuplicatePoolNameserver)
[docs] def get_pool_nameserver(self, context, pool_nameserver_id): return self._find_pool_nameservers( context, {'id': pool_nameserver_id}, one=True)
[docs] def find_pool_nameservers(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find_pool_nameservers(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_pool_nameserver(self, context, criterion): return self._find_pool_nameservers(context, criterion, one=True)
[docs] def update_pool_nameserver(self, context, pool_nameserver): return self._update(context, tables.pool_nameservers, pool_nameserver, exceptions.DuplicatePoolNameserver, exceptions.PoolNameserverNotFound)
[docs] def delete_pool_nameserver(self, context, pool_nameserver_id): pool_nameserver = self._find_pool_nameservers( context, {'id': pool_nameserver_id}, one=True) deleted_pool_nameserver = self._delete( context, tables.pool_nameservers, pool_nameserver, exceptions.PoolNameserverNotFound) return deleted_pool_nameserver
# PoolTarget methods def _find_pool_targets(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): pool_targets = self._find( context, tables.pool_targets, objects.PoolTarget, objects.PoolTargetList, exceptions.PoolTargetNotFound, criterion, one, marker, limit, sort_key, sort_dir) # Load Relations def _load_relations(pool_target): pool_target.options = self._find_pool_target_options( context, {'pool_target_id': pool_target.id}) pool_target.masters = self._find_pool_target_masters( context, {'pool_target_id': pool_target.id}) pool_target.obj_reset_changes(['options', 'masters']) if one: _load_relations(pool_targets) else: for pool_target in pool_targets: _load_relations(pool_target) return pool_targets
[docs] def create_pool_target(self, context, pool_id, pool_target): pool_target.pool_id = pool_id pool_target = self._create( tables.pool_targets, pool_target, exceptions.DuplicatePoolTarget, ['options', 'masters']) if pool_target.obj_attr_is_set('options'): for pool_target_option in pool_target.options: self.create_pool_target_option( context, pool_target.id, pool_target_option) else: pool_target.options = objects.PoolTargetOptionList() if pool_target.obj_attr_is_set('masters'): for pool_target_master in pool_target.masters: self.create_pool_target_master( context, pool_target.id, pool_target_master) else: pool_target.masters = objects.PoolTargetMasterList() pool_target.obj_reset_changes(['options', 'masters']) return pool_target
[docs] def get_pool_target(self, context, pool_target_id): return self._find_pool_targets( context, {'id': pool_target_id}, one=True)
[docs] def find_pool_targets(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find_pool_targets(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_pool_target(self, context, criterion): return self._find_pool_targets(context, criterion, one=True)
def _update_pool_items(self, context, pool, attribute_name): """Update attributes beloging to a pool """ assert attribute_name in ('attributes', 'ns_records', 'nameservers', 'targets', 'also_notifies') # Gather the pool ID's we have finder = getattr(self, '_find_pool_%s' % attribute_name) have_items = set() for r in finder(context, {'pool_id': pool.id}): have_items.add(r.id) # Prep some lists of changes keep_items = set() create_items = [] update_items = [] items = [] if pool.obj_attr_is_set(attribute_name): for r in getattr(pool, attribute_name).objects: items.append(r) # Determine what to change for item in items: keep_items.add(item.id) try: item.obj_get_original_value('id') except KeyError: create_items.append(item) else: update_items.append(item) # NOTE: Since we're dealing with mutable objects, the return value # of create/update/delete option is not needed. The # original item will be mutated in place on the input # "pool.options" list. # singular: attributes -> attribute, 'notify' is as a corner case if attribute_name == 'also_notifies': singular = 'also_notify' else: singular = attribute_name[:-1] # Delete items fn = getattr(self, 'delete_pool_%s' % singular) for item_id in have_items - keep_items: fn(context, item_id) # Update items fn = getattr(self, 'update_pool_%s' % singular) for item in update_items: fn(context, item) # Create items fn = getattr(self, 'create_pool_%s' % singular) for item in create_items: fn(context, pool.id, item) def _update_pool_target_items(self, context, pool_target, attribute_name): """Update attributes beloging to a pool target """ assert attribute_name in ('options', 'masters') # Gather the pool ID's we have finder = getattr(self, '_find_pool_target_%s' % attribute_name) have_items = set() for r in finder(context, {'pool_target_id': pool_target.id}): have_items.add(r.id) # Prep some lists of changes keep_items = set() create_items = [] update_items = [] items = [] if pool_target.obj_attr_is_set(attribute_name): for r in getattr(pool_target, attribute_name).objects: items.append(r) # Determine what to change for item in items: keep_items.add(item.id) try: item.obj_get_original_value('id') except KeyError: create_items.append(item) else: update_items.append(item) # NOTE: Since we're dealing with mutable objects, the return value # of create/update/delete option is not needed. The # original item will be mutated in place on the input # "pool.options" list. # singular: options -> option singular = attribute_name[:-1] # Delete items fn = getattr(self, 'delete_pool_target_%s' % singular) for item_id in have_items - keep_items: fn(context, item_id) # Update items fn = getattr(self, 'update_pool_target_%s' % singular) for item in update_items: fn(context, item) # Create items fn = getattr(self, 'create_pool_target_%s' % singular) for item in create_items: fn(context, pool_target.id, item)
[docs] def update_pool_target(self, context, pool_target): pool_target = self._update( context, tables.pool_targets, pool_target, exceptions.DuplicatePoolTarget, exceptions.PoolTargetNotFound, ['options', 'masters']) for attribute_name in ('options', 'masters'): if pool_target.obj_attr_is_set(attribute_name): self._update_pool_target_items(context, pool_target, attribute_name) # Call get_pool to get the ids of all the attributes/ns_records # refreshed in the pool object updated_pool_target = self.get_pool_target(context, pool_target.id) return updated_pool_target
[docs] def delete_pool_target(self, context, pool_target_id): pool_target = self._find_pool_targets( context, {'id': pool_target_id}, one=True) return self._delete(context, tables.pool_targets, pool_target, exceptions.PoolTargetNotFound)
# PoolTargetOption methods def _find_pool_target_options(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.pool_target_options, objects.PoolTargetOption, objects.PoolTargetOptionList, exceptions.PoolTargetOptionNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_pool_target_option(self, context, pool_target_id, pool_target_option): pool_target_option.pool_target_id = pool_target_id return self._create(tables.pool_target_options, pool_target_option, exceptions.DuplicatePoolTargetOption)
[docs] def update_pool_target_option(self, context, pool_target_option): return self._update( context, tables.pool_target_options, pool_target_option, exceptions.DuplicatePoolTargetOption, exceptions.PoolTargetOptionNotFound)
[docs] def delete_pool_target_option(self, context, pool_target_option_id): pool_target_option = self._find_pool_target_options( context, {'id': pool_target_option_id}, one=True) deleted_pool_target_option = self._delete( context, tables.pool_target_options, pool_target_option, exceptions.PoolTargetOptionNotFound) return deleted_pool_target_option
# PoolTargetMaster methods def _find_pool_target_masters(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.pool_target_masters, objects.PoolTargetMaster, objects.PoolTargetMasterList, exceptions.PoolTargetMasterNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_pool_target_master(self, context, pool_target_id, pool_target_master): pool_target_master.pool_target_id = pool_target_id return self._create(tables.pool_target_masters, pool_target_master, exceptions.DuplicatePoolTargetMaster)
[docs] def update_pool_target_master(self, context, pool_target_master): return self._update( context, tables.pool_target_masters, pool_target_master, exceptions.DuplicatePoolTargetMaster, exceptions.PoolTargetMasterNotFound)
[docs] def delete_pool_target_master(self, context, pool_target_master_id): pool_target_master = self._find_pool_target_masters( context, {'id': pool_target_master_id}, one=True) deleted_pool_target_master = self._delete( context, tables.pool_target_masters, pool_target_master, exceptions.PoolTargetMasterNotFound) return deleted_pool_target_master
# PoolAlsoNotify methods def _find_pool_also_notifies(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find(context, tables.pool_also_notifies, objects.PoolAlsoNotify, objects.PoolAlsoNotifyList, exceptions.PoolAlsoNotifyNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def create_pool_also_notify(self, context, pool_id, pool_also_notify): pool_also_notify.pool_id = pool_id return self._create(tables.pool_also_notifies, pool_also_notify, exceptions.DuplicatePoolAlsoNotify)
[docs] def get_pool_also_notify(self, context, pool_also_notify_id): return self._find_pool_also_notifies( context, {'id': pool_also_notify_id}, one=True)
[docs] def find_pool_also_notifies(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find_pool_also_notifies(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_pool_also_notify(self, context, criterion): return self._find_pool_also_notifies(context, criterion, one=True)
[docs] def update_pool_also_notify(self, context, pool_also_notify): return self._update( context, tables.pool_also_notifies, pool_also_notify, exceptions.DuplicatePoolAlsoNotify, exceptions.PoolAlsoNotifyNotFound)
[docs] def delete_pool_also_notify(self, context, pool_also_notify_id): pool_also_notify = self._find_pool_also_notifies( context, {'id': pool_also_notify_id}, one=True) deleted_pool_also_notify = self._delete( context, tables.pool_also_notifies, pool_also_notify, exceptions.PoolAlsoNotifyNotFound) return deleted_pool_also_notify
# Zone Transfer Methods def _find_zone_transfer_requests(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): table = tables.zone_transfer_requests ljoin = tables.zone_transfer_requests.join( tables.zones, tables.zone_transfer_requests.c.zone_id == tables.zones.c.id) query = select( table, tables.zones.c.name.label('zone_name') ).select_from(ljoin) if not context.all_tenants: # If we have a system scoped token with no project_id and # all_tenants was not used, we don't know what records to return, # so return an empty list. if not context.project_id: if one: return objects.ZoneTransferRequest() return objects.ZoneTransferRequestList() query = query.where(or_( table.c.tenant_id == context.project_id, table.c.target_tenant_id == context.project_id)) return self._find( context, table, objects.ZoneTransferRequest, objects.ZoneTransferRequestList, exceptions.ZoneTransferRequestNotFound, criterion, one=one, marker=marker, limit=limit, sort_dir=sort_dir, sort_key=sort_key, query=query, apply_tenant_criteria=False )
[docs] def create_zone_transfer_request(self, context, zone_transfer_request): try: criterion = {'zone_id': zone_transfer_request.zone_id, 'status': 'ACTIVE'} self.find_zone_transfer_request( context, criterion) except exceptions.ZoneTransferRequestNotFound: return self._create( tables.zone_transfer_requests, zone_transfer_request, exceptions.DuplicateZoneTransferRequest) else: raise exceptions.DuplicateZoneTransferRequest()
[docs] def find_zone_transfer_requests(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find_zone_transfer_requests( context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def get_zone_transfer_request(self, context, zone_transfer_request_id): request = self._find_zone_transfer_requests( context, {'id': zone_transfer_request_id}, one=True) return request
[docs] def find_zone_transfer_request(self, context, criterion): return self._find_zone_transfer_requests(context, criterion, one=True)
[docs] def update_zone_transfer_request(self, context, zone_transfer_request): zone_transfer_request.obj_reset_changes('zone_name') updated_zt_request = self._update( context, tables.zone_transfer_requests, zone_transfer_request, exceptions.DuplicateZoneTransferRequest, exceptions.ZoneTransferRequestNotFound, skip_values=['zone_name']) return updated_zt_request
[docs] def delete_zone_transfer_request(self, context, zone_transfer_request_id): zone_transfer_request = self._find_zone_transfer_requests( context, {'id': zone_transfer_request_id}, one=True) return self._delete( context, tables.zone_transfer_requests, zone_transfer_request, exceptions.ZoneTransferRequestNotFound)
[docs] def count_zone_transfer_accept(self, context, criterion=None): query = select(func.count(tables.zone_transfer_accepts.c.id)) query = self._apply_criterion( tables.zone_transfer_accepts, query, criterion ) query = self._apply_deleted_criteria( context, tables.zone_transfer_accepts, query ) with sql.get_read_session() as session: resultproxy = session.execute(query) result = resultproxy.fetchone() if result is None: return 0 return result[0]
def _find_zone_transfer_accept(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): zone_transfer_accept = self._find( context, tables.zone_transfer_accepts, objects.ZoneTransferAccept, objects.ZoneTransferAcceptList, exceptions.ZoneTransferAcceptNotFound, criterion, one, marker, limit, sort_key, sort_dir) if not one: zone_transfer_accept.total_count = self.count_zone_transfer_accept( context, criterion) return zone_transfer_accept
[docs] def create_zone_transfer_accept(self, context, zone_transfer_accept): return self._create( tables.zone_transfer_accepts, zone_transfer_accept, exceptions.DuplicateZoneTransferAccept)
[docs] def find_zone_transfer_accepts(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find_zone_transfer_accept( context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def get_zone_transfer_accept(self, context, zone_transfer_accept_id): return self._find_zone_transfer_accept( context, {'id': zone_transfer_accept_id}, one=True)
[docs] def find_zone_transfer_accept(self, context, criterion): return self._find_zone_transfer_accept( context, criterion, one=True)
[docs] def update_zone_transfer_accept(self, context, zone_transfer_accept): return self._update( context, tables.zone_transfer_accepts, zone_transfer_accept, exceptions.DuplicateZoneTransferAccept, exceptions.ZoneTransferAcceptNotFound)
[docs] def delete_zone_transfer_accept(self, context, zone_transfer_accept_id): zone_transfer_accept = self._find_zone_transfer_accept( context, {'id': zone_transfer_accept_id}, one=True) return self._delete( context, tables.zone_transfer_accepts, zone_transfer_accept, exceptions.ZoneTransferAcceptNotFound)
# Zone Import Methods def _find_zone_imports(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): if not criterion: criterion = {} criterion['task_type'] = 'IMPORT' zone_imports = self._find( context, tables.zone_tasks, objects.ZoneImport, objects.ZoneImportList, exceptions.ZoneImportNotFound, criterion, one, marker, limit, sort_key, sort_dir) if not one: zone_imports.total_count = self.count_zone_tasks( context, criterion) return zone_imports
[docs] def create_zone_import(self, context, zone_import): """ Create a Zone Import. :param context: RPC Context. :param zone_import: Zone Import object with the values to be created. """ return self._create( tables.zone_tasks, zone_import, exceptions.DuplicateZoneImport)
[docs] def get_zone_import(self, context, zone_import_id): """ Get a Zone Import via ID. :param context: RPC Context. :param zone_import_id: Zone Import ID to get. """ return self._find_zone_imports(context, {'id': zone_import_id}, one=True)
[docs] def find_zone_imports(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find Zone Imports :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_zone_imports(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_zone_import(self, context, criterion): """ Find a single Zone Import. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_zone_imports(context, criterion, one=True)
[docs] def update_zone_import(self, context, zone_import): """ Update a Zone Import :param context: RPC Context. :param zone_import: Zone Import to update. """ return self._update( context, tables.zone_tasks, zone_import, exceptions.DuplicateZoneImport, exceptions.ZoneImportNotFound)
[docs] def delete_zone_import(self, context, zone_import_id): """ Delete a Zone Import via ID. :param context: RPC Context. :param zone_import_id: Delete a Zone Import via ID """ # Fetch the existing zone_import, we'll need to return it. zone_import = self._find_zone_imports(context, {'id': zone_import_id}, one=True) return self._delete(context, tables.zone_tasks, zone_import, exceptions.ZoneImportNotFound)
# Zone Export Methods def _find_zone_exports(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): if not criterion: criterion = {} criterion['task_type'] = 'EXPORT' zone_exports = self._find( context, tables.zone_tasks, objects.ZoneExport, objects.ZoneExportList, exceptions.ZoneExportNotFound, criterion, one, marker, limit, sort_key, sort_dir) if not one: zone_exports.total_count = self.count_zone_tasks( context, criterion) return zone_exports
[docs] def create_zone_export(self, context, zone_export): """ Create a Zone Export. :param context: RPC Context. :param zone_export: Zone Export object with the values to be created. """ return self._create( tables.zone_tasks, zone_export, exceptions.DuplicateZoneExport)
[docs] def get_zone_export(self, context, zone_export_id): """ Get a Zone Export via ID. :param context: RPC Context. :param zone_export_id: Zone Export ID to get. """ return self._find_zone_exports(context, {'id': zone_export_id}, one=True)
[docs] def find_zone_exports(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Find Zone Exports :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_zone_exports(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def find_zone_export(self, context, criterion): """ Find a single Zone Export. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_zone_exports(context, criterion, one=True)
[docs] def update_zone_export(self, context, zone_export): """ Update a Zone Export :param context: RPC Context. :param zone_export: Zone Export to update. """ return self._update( context, tables.zone_tasks, zone_export, exceptions.DuplicateZoneExport, exceptions.ZoneExportNotFound)
[docs] def delete_zone_export(self, context, zone_export_id): """ Delete a Zone Export via ID. :param context: RPC Context. :param zone_export_id: Delete a Zone Export via ID """ # Fetch the existing zone_export, we'll need to return it. zone_export = self._find_zone_exports(context, {'id': zone_export_id}, one=True) return self._delete(context, tables.zone_tasks, zone_export, exceptions.ZoneExportNotFound)
[docs] def count_zone_tasks(self, context, criterion=None): query = select(func.count(tables.zone_tasks.c.id)) query = self._apply_criterion(tables.zone_tasks, query, criterion) query = self._apply_tenant_criteria(context, tables.zone_tasks, query) query = self._apply_deleted_criteria(context, tables.zone_tasks, query) with sql.get_read_session() as session: resultproxy = session.execute(query) result = resultproxy.fetchone() if result is None: return 0 return result[0]
# Service Status Methods def _find_service_statuses(self, context, criterion, one=False, marker=None, limit=None, sort_key=None, sort_dir=None): return self._find( context, tables.service_status, objects.ServiceStatus, objects.ServiceStatusList, exceptions.ServiceStatusNotFound, criterion, one, marker, limit, sort_key, sort_dir)
[docs] def find_service_status(self, context, criterion): """ Find a single Service Status. :param context: RPC Context. :param criterion: Criteria to filter by. """ return self._find_service_statuses(context, criterion, one=True)
[docs] def find_service_statuses(self, context, criterion=None, marker=None, limit=None, sort_key=None, sort_dir=None): """ Retrieve status for services :param context: RPC Context. :param criterion: Criteria to filter by. :param marker: Resource ID from which after the requested page will start after :param limit: Integer limit of objects of the page size after the marker :param sort_key: Key from which to sort after. :param sort_dir: Direction to sort after using sort_key. """ return self._find_service_statuses(context, criterion, marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir)
[docs] def create_service_status(self, context, service_status): """ Create a Service status for a service. :param context: RPC Context. :param service_status: The status of a service. """ return self._create( tables.service_status, service_status, exceptions.DuplicateServiceStatus)
[docs] def update_service_status(self, context, service_status): """ Update the Service status for a service. :param context: RPC Context. :param service_status: Set the status for a service. """ return self._update( context, tables.service_status, service_status, exceptions.DuplicateServiceStatus, exceptions.ServiceStatusNotFound)
# Reverse Name utils def _rname_check(self, criterion): # If the criterion has 'name' in it, switch it out for reverse_name if criterion is not None and criterion.get('name', '').startswith('*'): criterion['reverse_name'] = criterion.pop('name')[::-1] return criterion