Pool Manager - Central/Storage Changes

domains table changes:
- Added 'ERROR' to status column
- Set status column default to 'PENDING'
- Added action column ('CREATE', 'DELETE', 'UPDATE', 'NONE')
  with a default of 'CREATE'

records table changes:
- Added 'ERROR' to status column
- Set status column default to 'PENDING'
- Added serial column with a default of 1
- Added action column ('CREATE', 'DELETE', 'UPDATE', 'NONE')
  with a default of 'CREATE'

Other modifications:
- Modified domain object to reflect new attribute (action)
- Modified record object to reflect new attributes (serial, action)
- Ensured central service continues to work by setting status of
  domain and record to 'ACTIVE'
- Added update_status method to central service

Change-Id: I893f8e7834de4c6ccf3ad587af65a7eee24415b3
Partially-implements: blueprint server-pools-service
This commit is contained in:
rjrjr 2014-10-29 17:09:32 -07:00
parent 2f15b33128
commit f5f743e8af
6 changed files with 189 additions and 12 deletions

View File

@ -42,14 +42,15 @@ class CentralAPI(object):
3.3 - Add methods for blacklisted domains
4.0 - Create methods now accept designate objects
4.1 - Add methods for server pools
4.2 - Add methods for pool manager integration
"""
RPC_API_VERSION = '4.1'
RPC_API_VERSION = '4.2'
def __init__(self, topic=None):
topic = topic if topic else cfg.CONF.central_topic
target = messaging.Target(topic=topic, version=self.RPC_API_VERSION)
self.client = rpc.get_client(target, version_cap='4.1')
self.client = rpc.get_client(target, version_cap='4.2')
# Misc Methods
def get_absolute_limits(self, context):
@ -376,8 +377,7 @@ class CentralAPI(object):
def create_pool(self, context, pool):
LOG.info(_LI("create_pool: Calling central's create_pool."))
cctxt = self.client.prepare(version='4.1')
return cctxt.call(context, 'create_pool',
pool=pool)
return cctxt.call(context, 'create_pool', pool=pool)
def find_pools(self, context, criterion=None, marker=None, limit=None,
sort_key=None, sort_dir=None):
@ -405,4 +405,11 @@ class CentralAPI(object):
def delete_pool(self, context, pool_id):
LOG.info(_LI("delete_pool: Calling central's delete_pool."))
cctxt = self.client.prepare(version='4.1')
return cctxt.call(context, 'delete_pool', pool_id=pool_id)
return cctxt.call(context, 'delete_pool', pool_id=pool_id)
# Pool Manager Integration Methods
def update_status(self, context, domain_id, status, serial):
LOG.info(_LI("update_status: Calling central's update_status."))
cctxt = self.client.prepare(version='4.2')
return cctxt.call(context, 'update_status', domain_id=domain_id,
status=status, serial=serial)

View File

@ -70,7 +70,7 @@ def transaction(f):
class Service(service.RPCService):
RPC_API_VERSION = '4.1'
RPC_API_VERSION = '4.2'
target = messaging.Target(version=RPC_API_VERSION)
@ -391,6 +391,7 @@ class Service(service.RPCService):
'type': "NS"})
# Add new record to recordset
ns_record = objects.Record(data=server.name)
new_record = self.create_record(context, zone['id'],
ns['id'], ns_record,
increment_serial=False)
@ -741,6 +742,12 @@ class Service(service.RPCService):
'Please create at least one server'))
raise exceptions.NoServersConfigured()
# TODO(Ron): remove this when integrated with pool manager.
# The default status is 'PENDING' for pool manager.
# Setting status to 'ACTIVE' for backward compatibility.
if cfg.CONF['service:central'].backend_driver != 'pool_manager_proxy':
domain.status = 'ACTIVE'
# Set the serial number
domain.serial = utils.increment_serial()
@ -1146,6 +1153,12 @@ class Service(service.RPCService):
# Ensure the tenant has enough quota to continue
self._enforce_record_quota(context, domain, recordset)
# TODO(Ron): remove this when integrated with pool_manager.
# The default status is 'PENDING' for pool manager.
# Setting status to 'ACTIVE' for backward compatibility.
if cfg.CONF['service:central'].backend_driver != 'pool_manager_proxy':
record.status = 'ACTIVE'
created_record = self.storage.create_record(context, domain_id,
recordset_id, record)
@ -1796,3 +1809,51 @@ class Service(service.RPCService):
pool = self.storage.delete_pool(context, pool_id)
self.notifier.info(context, 'dns.pool.delete', pool)
# Pool Manager Integration
def update_status(self, context, domain_id, status, serial):
"""
:param context: Security context information.
:param domain_id: The ID of the designate domain.
:param status: The status, 'SUCCESS' or 'ERROR'.
:param serial: The consensus serial number for the domain.
:return: None
"""
domain = self.storage.get_domain(context, domain_id)
criterion = {
'domain_id': domain_id
}
records = self.storage.find_records(context, criterion=criterion)
if status == 'SUCCESS':
if domain.action in ['CREATE', 'UPDATE'] \
and domain.status in ['PENDING', 'ERROR']:
domain.action = 'NONE'
domain.status = 'ACTIVE'
elif domain.action == 'DELETE' \
and domain.status in ['PENDING', 'ERROR']:
domain.action = 'NONE'
domain.status = 'DELETED'
for record in records:
if record.action in ['CREATE', 'UPDATE'] \
and record.status in ['PENDING', 'ERROR'] \
and serial >= record.serial:
record.action = 'NONE'
record.status = 'ACTIVE'
elif record.action == 'DELETE' \
and record.status in ['PENDING', 'ERROR'] \
and serial >= record.serial:
record.action = 'NONE'
record.status = 'DELETED'
self.storage.update_record(context, record)
elif status == 'ERROR':
if domain.status == 'PENDING':
domain.status = 'ERROR'
for record in records:
if record.status == 'PENDING' \
and serial >= record.serial:
record.status = 'ERROR'
self.storage.update_record(context, record)
self.storage.update_domain(context, domain)

View File

@ -29,7 +29,8 @@ class Domain(base.DictObjectMixin, base.SoftDeleteObjectMixin,
'parent_domain_id': {},
'serial': {},
'description': {},
'status': {}
'status': {},
'action': {}
}

View File

@ -34,7 +34,9 @@ class Record(base.DictObjectMixin, base.PersistentObjectMixin,
'recordset_id': {},
'managed_tenant_id': {},
'managed_resource_region': {},
'managed_extra': {}
'managed_extra': {},
'action': {},
'serial': {}
}

View File

@ -0,0 +1,100 @@
# Copyright (c) 2014 eBay Inc.
#
# Author: Ron Rickard <rrickard@ebaysf.com>
#
# 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 sqlalchemy import MetaData, Table, Enum, Column, Integer
from migrate.changeset.constraint import UniqueConstraint
meta = MetaData()
def upgrade(migrate_engine):
meta.bind = migrate_engine
RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR']
ACTIONS = ['CREATE', 'DELETE', 'UPDATE', 'NONE']
# Get associated database tables
domains_table = Table('domains', meta, autoload=True)
records_table = Table('records', meta, autoload=True)
# Upgrade the domains table.
domains_table.c.status.alter(
type=Enum(name='resource_statuses', *RESOURCE_STATUSES),
default='PENDING', server_default='PENDING')
action_column = Column('action', Enum(name='actions', *ACTIONS),
default='CREATE', server_default='CREATE',
nullable=False)
action_column.create(domains_table)
# Re-add constraint for sqlite.
dialect = migrate_engine.url.get_dialect().name
if dialect.startswith('sqlite'):
constraint = UniqueConstraint(
'name', 'deleted', name='unique_domain_name', table=domains_table)
constraint.create()
# Upgrade the records table.
records_table.c.status.alter(
type=Enum(name='resource_statuses', *RESOURCE_STATUSES),
default='PENDING', server_default='PENDING')
action_column = Column('action', Enum(name='actions', *ACTIONS),
default='CREATE', server_default='CREATE',
nullable=False)
action_column.create(records_table)
serial_column = Column('serial', Integer(), server_default='1',
nullable=False)
serial_column.create(records_table)
# Re-add constraint for sqlite.
if dialect.startswith('sqlite'):
constraint = UniqueConstraint(
'hash', name='unique_record', table=records_table)
constraint.create()
def downgrade(migrate_engine):
meta.bind = migrate_engine
RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED']
# Get associated database tables
domains_table = Table('domains', meta, autoload=True)
records_table = Table('records', meta, autoload=True)
# Downgrade the domains table.
domains_table.c.status.alter(
type=Enum(name='resource_statuses', *RESOURCE_STATUSES),
default='ACTIVE', server_default='ACTIVE')
domains_table.c.action.drop()
# Re-add constraint for sqlite.
dialect = migrate_engine.url.get_dialect().name
if dialect.startswith('sqlite'):
constraint = UniqueConstraint(
'name', 'deleted', name='unique_domain_name', table=domains_table)
constraint.create()
# Downgrade the records table.
records_table.c.status.alter(
type=Enum(name='resource_statuses', *RESOURCE_STATUSES),
default='ACTIVE', server_default='ACTIVE')
records_table.c.action.drop()
records_table.c.serial.drop()
# Re-add constraint for sqlite.
if dialect.startswith('sqlite'):
constraint = UniqueConstraint(
'hash', name='unique_record', table=records_table)
constraint.create()

View File

@ -26,13 +26,13 @@ from designate.sqlalchemy.types import UUID
CONF = cfg.CONF
RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED']
RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR']
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS', 'PTR',
'SSHFP']
TSIG_ALGORITHMS = ['hmac-md5', 'hmac-sha1', 'hmac-sha224', 'hmac-sha256',
'hmac-sha384', 'hmac-sha512']
POOL_PROVISIONERS = ['UNMANAGED']
ACTIONS = ['ADD', 'DELETE', 'UPDATE', 'NONE']
metadata = MetaData()
@ -98,8 +98,10 @@ domains = Table('domains', metadata,
Column('minimum', Integer, default=CONF.default_soa_minimum,
nullable=False),
Column('status', Enum(name='resource_statuses', *RESOURCE_STATUSES),
nullable=False, server_default='ACTIVE', default='ACTIVE'),
nullable=False, server_default='PENDING', default='PENDING'),
Column('parent_domain_id', UUID, default=None, nullable=True),
Column('action', Enum(name='actions', *ACTIONS),
default='CREATE', server_default='CREATE', nullable=False),
UniqueConstraint('name', 'deleted', name='unique_domain_name'),
ForeignKeyConstraint(['parent_domain_id'],
@ -152,8 +154,12 @@ records = Table('records', metadata,
Column('managed_resource_id', UUID, default=None, nullable=True),
Column('managed_tenant_id', Unicode(36), default=None, nullable=True),
Column('status', Enum(name='resource_statuses', *RESOURCE_STATUSES),
nullable=False, server_default='ACTIVE', default='ACTIVE'),
server_default='PENDING', default='PENDING', nullable=False),
Column('action', Enum(name='actions', *ACTIONS),
default='CREATE', server_default='CREATE', nullable=False),
Column('serial', Integer(), server_default='1', nullable=False),
UniqueConstraint('hash', name='unique_record'),
ForeignKeyConstraint(['domain_id'], ['domains.id'], ondelete='CASCADE'),
ForeignKeyConstraint(['recordset_id'], ['recordsets.id'],
ondelete='CASCADE'),