designate/designate/backend/impl_multi.py

170 lines
6.0 KiB
Python

# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Artom Lifshitz <artom.lifshitz@enovance.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.
import logging
from oslo.config import cfg
from designate import backend
from designate.backend import base
from designate.openstack.common import excutils
LOG = logging.getLogger(__name__)
CFG_GRP = 'backend:multi'
cfg.CONF.register_group(cfg.OptGroup(
name=CFG_GRP, title="Configuration for multi-backend Backend"
))
cfg.CONF.register_opts([
cfg.StrOpt('master', default='fake', help='Master backend'),
cfg.StrOpt('slave', default='fake', help='Slave backend'),
], group=CFG_GRP)
class MultiBackend(base.Backend):
"""
Multi-backend backend
This backend dispatches calls to a master backend and a slave backend.
It enforces master/slave ordering semantics as follows:
Creates for tsigkeys, servers and domains are done on the master first,
then on the slave.
Updates for tsigkeys, servers and domains and all operations on records
are done on the master only. It's assumed masters and slaves use an
external mechanism to sync existing domains, most likely XFR.
Deletes are done on the slave first, then on the master.
If the create on the slave fails, the domain/tsigkey/server is deleted from
the master. If delete on the master fails, the domain/tdigkey/server is
recreated on the slave.
"""
__plugin_name__ = 'multi'
def __init__(self, central_service):
super(MultiBackend, self).__init__(central_service)
self.central = central_service
self.master = backend.get_backend(cfg.CONF[CFG_GRP].master,
central_service)
self.slave = backend.get_backend(cfg.CONF[CFG_GRP].slave,
central_service)
def start(self):
self.master.start()
self.slave.start()
def stop(self):
self.slave.stop()
self.master.stop()
def create_tsigkey(self, context, tsigkey):
self.master.create_tsigkey(context, tsigkey)
try:
self.slave.create_tsigkey(context, tsigkey)
except Exception:
with excutils.save_and_reraise_exception():
self.master.delete_tsigkey(context, tsigkey)
def update_tsigkey(self, context, tsigkey):
self.master.update_tsigkey(context, tsigkey)
def delete_tsigkey(self, context, tsigkey):
self.slave.delete_tsigkey(context, tsigkey)
try:
self.master.delete_tsigkey(context, tsigkey)
except Exception:
with excutils.save_and_reraise_exception():
self.slave.create_tsigkey(context, tsigkey)
def create_domain(self, context, domain):
self.master.create_domain(context, domain)
try:
self.slave.create_domain(context, domain)
except Exception:
with excutils.save_and_reraise_exception():
self.master.delete_domain(context, domain)
def update_domain(self, context, domain):
self.master.update_domain(context, domain)
def delete_domain(self, context, domain):
# Fetch the full domain from Central first, as we may
# have to recreate it on slave if delete on master fails
deleted_context = context.deepcopy()
deleted_context.show_deleted = True
full_domain = self.central.find_domain(
deleted_context, {'id': domain['id']})
self.slave.delete_domain(context, domain)
try:
self.master.delete_domain(context, domain)
except Exception:
with excutils.save_and_reraise_exception():
self.slave.create_domain(context, domain)
[self.slave.create_record(context, domain, record)
for record in self.central.find_records(
context, {'domain_id': full_domain['id']})]
def create_server(self, context, server):
self.master.create_server(context, server)
try:
self.slave.create_server(context, server)
except Exception:
with excutils.save_and_reraise_exception():
self.master.delete_server(context, server)
def update_server(self, context, server):
self.master.update_server(context, server)
def delete_server(self, context, server):
self.slave.delete_server(context, server)
try:
self.master.delete_server(context, server)
except Exception:
with excutils.save_and_reraise_exception():
self.slave.create_server(context, server)
def create_recordset(self, context, domain, recordset):
self.master.create_recordset(context, domain, recordset)
def update_recordset(self, context, domain, recordset):
self.master.update_recordset(context, domain, recordset)
def delete_recordset(self, context, domain, recordset):
self.master.delete_recordset(context, domain, recordset)
def create_record(self, context, domain, recordset, record):
self.master.create_record(context, domain, recordset, record)
def update_record(self, context, domain, recordset, record):
self.master.update_record(context, domain, recordset, record)
def delete_record(self, context, domain, recordset, record):
self.master.delete_record(context, domain, recordset, record)
def ping(self, context):
return {
'master': self.master.ping(context),
'slave': self.slave.ping(context)
}