summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Hayes <graham@hayes.ie>2013-09-02 16:31:36 +0100
committerGraham <graham.hayes@hp.com>2013-10-11 14:59:35 +0100
commitb982573688e9958f094d105218cc9e53ce2ef6f3 (patch)
tree4a68dcb0e3b389b634b4f9331ad6ab142571e3b5
parent361cd7af637870627ae399a98aff0201d6a3babb (diff)
downloaddesignate-b982573688e9958f094d105218cc9e53ce2ef6f3.tar.gz
Add status fields for domains and records
Added a status column in the domains and records tables. This is part of the asynchronous backends work to allow use to have record and domains in pending states Change-Id: Id30756365f8eb2427626b334c2c6119783924679 Implements: blueprint domain-status
-rw-r--r--designate/api/v2/controllers/zones.py21
-rw-r--r--designate/api/v2/views/zones.py2
-rw-r--r--designate/central/service.py8
-rw-r--r--designate/sqlalchemy/models.py3
-rw-r--r--designate/storage/impl_sqlalchemy/__init__.py4
-rw-r--r--designate/storage/impl_sqlalchemy/migrate_repo/versions/022_add_domain_status.py51
-rw-r--r--designate/storage/impl_sqlalchemy/models.py7
-rw-r--r--designate/tests/test_api/test_v2/test_zones.py1
-rw-r--r--designate/tests/test_central/test_service.py5
-rw-r--r--designate/tests/test_storage/__init__.py14
10 files changed, 110 insertions, 6 deletions
diff --git a/designate/api/v2/controllers/zones.py b/designate/api/v2/controllers/zones.py
index 92288d43..4461e439 100644
--- a/designate/api/v2/controllers/zones.py
+++ b/designate/api/v2/controllers/zones.py
@@ -83,7 +83,13 @@ class ZonesController(rest.RestController):
zone = central_api.create_domain(context, values)
# Prepare the response headers
- response.status_int = 201
+ # If the zone has been created asynchronously
+
+ if zone['status'] == 'PENDING':
+ response.status_int = 202
+ else:
+ response.status_int = 201
+
response.headers['Location'] = self._view._get_resource_href(request,
zone)
@@ -98,6 +104,7 @@ class ZonesController(rest.RestController):
request = pecan.request
context = request.environ['context']
body = request.body_dict
+ response = pecan.response
# TODO(kiall): Validate we have a sane UUID for zone_id
@@ -130,6 +137,11 @@ class ZonesController(rest.RestController):
values = self._view.load(context, request, body)
zone = central_api.update_domain(context, zone_id, values)
+ if zone['status'] == 'PENDING':
+ response.status_int = 202
+ else:
+ response.status_int = 200
+
return self._view.detail(context, request, zone)
@pecan.expose(template=None, content_type='application/json')
@@ -141,9 +153,12 @@ class ZonesController(rest.RestController):
# TODO(kiall): Validate we have a sane UUID for zone_id
- central_api.delete_domain(context, zone_id)
+ zone = central_api.delete_domain(context, zone_id)
- response.status_int = 204
+ if zone['status'] == 'DELETING':
+ response.status_int = 202
+ else:
+ response.status_int = 204
# NOTE: This is a hack and a half.. But Pecan needs it.
return ''
diff --git a/designate/api/v2/views/zones.py b/designate/api/v2/views/zones.py
index 71af59c8..5a492f2f 100644
--- a/designate/api/v2/views/zones.py
+++ b/designate/api/v2/views/zones.py
@@ -39,7 +39,7 @@ class ZonesView(base_view.BaseView):
"description": zone['description'],
"ttl": zone['ttl'],
"serial": zone['serial'],
- "status": "ACTIVE",
+ "status": zone['status'],
"version": zone['version'],
"created_at": zone['created_at'],
"updated_at": zone['updated_at'],
diff --git a/designate/central/service.py b/designate/central/service.py
index 205d8c57..c147da41 100644
--- a/designate/central/service.py
+++ b/designate/central/service.py
@@ -541,12 +541,14 @@ class Service(rpc_service.Service):
raise exceptions.DomainHasSubdomain('Please delete any subdomains '
'before deleting this domain')
- with self.storage_api.delete_domain(context, domain_id):
+ with self.storage_api.delete_domain(context, domain_id) as domain:
with wrap_backend_call():
self.backend.delete_domain(context, domain)
utils.notify(context, 'central', 'domain.delete', domain)
+ return domain
+
def count_domains(self, context, criterion=None):
if criterion is None:
criterion = {}
@@ -718,7 +720,7 @@ class Service(rpc_service.Service):
policy.check('delete_record', context, target)
- with self.storage_api.delete_record(context, record_id):
+ with self.storage_api.delete_record(context, record_id) as record:
with wrap_backend_call():
self.backend.delete_record(context, domain, record)
@@ -728,6 +730,8 @@ class Service(rpc_service.Service):
# Send Record deletion notification
utils.notify(context, 'central', 'record.delete', record)
+ return record
+
def count_records(self, context, criterion=None):
if criterion is None:
criterion = {}
diff --git a/designate/sqlalchemy/models.py b/designate/sqlalchemy/models.py
index 3848b909..6279280b 100644
--- a/designate/sqlalchemy/models.py
+++ b/designate/sqlalchemy/models.py
@@ -96,4 +96,7 @@ class SoftDeleteMixin(object):
self.deleted = self.id.replace('-', '')
self.deleted_at = timeutils.utcnow()
+ if hasattr(self, 'status'):
+ self.status = "DELETED"
+
self.save(session=session)
diff --git a/designate/storage/impl_sqlalchemy/__init__.py b/designate/storage/impl_sqlalchemy/__init__.py
index ced74c8e..cf32dda8 100644
--- a/designate/storage/impl_sqlalchemy/__init__.py
+++ b/designate/storage/impl_sqlalchemy/__init__.py
@@ -334,6 +334,8 @@ class SQLAlchemyStorage(base.Storage):
domain.soft_delete(self.session)
+ return dict(domain)
+
def count_domains(self, context, criterion=None):
query = self.session.query(models.Domain)
query = self._apply_criterion(models.Domain, query, criterion)
@@ -403,6 +405,8 @@ class SQLAlchemyStorage(base.Storage):
record.delete(self.session)
+ return dict(record)
+
def count_records(self, context, criterion=None):
query = self.session.query(models.Record)
query = self._apply_criterion(models.Record, query, criterion)
diff --git a/designate/storage/impl_sqlalchemy/migrate_repo/versions/022_add_domain_status.py b/designate/storage/impl_sqlalchemy/migrate_repo/versions/022_add_domain_status.py
new file mode 100644
index 00000000..ac534ac6
--- /dev/null
+++ b/designate/storage/impl_sqlalchemy/migrate_repo/versions/022_add_domain_status.py
@@ -0,0 +1,51 @@
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+#
+# Author: Graham Hayes <graham.hayes@hp.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 designate.openstack.common import log as logging
+from sqlalchemy import MetaData, Table, Column, Enum
+
+LOG = logging.getLogger(__name__)
+RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED']
+meta = MetaData()
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ domains_table = Table('domains', meta, autoload=True)
+ records_table = Table('records', meta, autoload=True)
+
+ # Add a domain & record creation status for async backends
+
+ domain_status = Column('status', Enum(name='domain_statuses',
+ *RESOURCE_STATUSES), nullable=False,
+ server_default='ACTIVE', default='ACTIVE')
+
+ record_status = Column('status', Enum(name='record_statuses',
+ *RESOURCE_STATUSES), nullable=False,
+ server_default='ACTIVE', default='ACTIVE')
+
+ domain_status.create(domains_table, populate_default=True)
+ record_status.create(records_table, populate_default=True)
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ domains_table = Table('domains', meta, autoload=True)
+ records_table = Table('records', meta, autoload=True)
+
+ domains_table.c.status.drop()
+ records_table.c.status.drop()
diff --git a/designate/storage/impl_sqlalchemy/models.py b/designate/storage/impl_sqlalchemy/models.py
index cf538597..fc2aacd8 100644
--- a/designate/storage/impl_sqlalchemy/models.py
+++ b/designate/storage/impl_sqlalchemy/models.py
@@ -32,6 +32,7 @@ from sqlalchemy.ext.declarative import declarative_base
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
+RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED']
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS', 'PTR',
'SSHFP']
TSIG_ALGORITHMS = ['hmac-md5', 'hmac-sha1', 'hmac-sha224', 'hmac-sha256',
@@ -91,6 +92,9 @@ class Domain(SoftDeleteMixin, Base):
retry = Column(Integer, default=CONF.default_soa_retry, nullable=False)
expire = Column(Integer, default=CONF.default_soa_expire, nullable=False)
minimum = Column(Integer, default=CONF.default_soa_minimum, nullable=False)
+ status = Column(Enum(name='resource_statuses', *RESOURCE_STATUSES),
+ nullable=False, server_default='ACTIVE',
+ default='ACTIVE')
records = relationship('Record', backref=backref('domain', uselist=False),
lazy='dynamic', cascade="all, delete-orphan",
@@ -121,6 +125,9 @@ class Record(Base):
managed_plugin_name = Column(Unicode(50), default=None, nullable=True)
managed_resource_type = Column(Unicode(50), default=None, nullable=True)
managed_resource_id = Column(UUID, default=None, nullable=True)
+ status = Column(Enum(name='resource_statuses', *RESOURCE_STATUSES),
+ nullable=False, server_default='ACTIVE',
+ default='ACTIVE')
@hybrid_property
def tenant_id(self):
diff --git a/designate/tests/test_api/test_v2/test_zones.py b/designate/tests/test_api/test_v2/test_zones.py
index c9561891..241e32fd 100644
--- a/designate/tests/test_api/test_v2/test_zones.py
+++ b/designate/tests/test_api/test_v2/test_zones.py
@@ -190,6 +190,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
+ self.assertIn('status', response.json['zone'])
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
diff --git a/designate/tests/test_central/test_service.py b/designate/tests/test_central/test_service.py
index 4152b636..34d84dee 100644
--- a/designate/tests/test_central/test_service.py
+++ b/designate/tests/test_central/test_service.py
@@ -336,6 +336,7 @@ class CentralServiceTest(CentralTestCase):
self.assertIsNotNone(domain['id'])
self.assertEqual(domain['name'], values['name'])
self.assertEqual(domain['email'], values['email'])
+ self.assertIn('status', domain)
# Ensure we sent exactly 1 notification
notifications = self.get_notifications()
@@ -575,6 +576,7 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(domain['id'], expected_domain['id'])
self.assertEqual(domain['name'], expected_domain['name'])
self.assertEqual(domain['email'], expected_domain['email'])
+ self.assertIn('status', domain)
def test_update_domain(self):
context = self.get_admin_context()
@@ -755,6 +757,7 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(record['name'], values['name'])
self.assertEqual(record['type'], values['type'])
self.assertEqual(record['data'], values['data'])
+ self.assertIn('status', record)
def test_create_record_over_quota(self):
self.config(quota_domain_records=1)
@@ -967,6 +970,7 @@ class CentralServiceTest(CentralTestCase):
expected_record['id'])
self.assertEqual(record['id'], expected_record['id'])
self.assertEqual(record['name'], expected_record['name'])
+ self.assertIn('status', record)
def test_find_record(self):
context = self.get_admin_context()
@@ -982,6 +986,7 @@ class CentralServiceTest(CentralTestCase):
criterion)
self.assertEqual(record['id'], expected_record['id'])
self.assertEqual(record['name'], expected_record['name'])
+ self.assertIn('status', record)
def test_get_record_incorrect_domain_id(self):
context = self.get_admin_context()
diff --git a/designate/tests/test_storage/__init__.py b/designate/tests/test_storage/__init__.py
index d73874bc..f99388a4 100644
--- a/designate/tests/test_storage/__init__.py
+++ b/designate/tests/test_storage/__init__.py
@@ -530,6 +530,7 @@ class StorageTestCase(TestCase):
self.assertEqual(result['name'], values['name'])
self.assertEqual(result['email'], values['email'])
+ self.assertIn('status', result)
def test_create_domain_duplicate(self):
# Create the Initial Domain
@@ -571,6 +572,7 @@ class StorageTestCase(TestCase):
self.assertEqual(results[0]['name'], domain_one['name'])
self.assertEqual(results[0]['email'], domain_one['email'])
+ self.assertIn('status', domain_one)
criterion = dict(
name=domain_two['name']
@@ -582,6 +584,7 @@ class StorageTestCase(TestCase):
self.assertEqual(results[0]['name'], domain_two['name'])
self.assertEqual(results[0]['email'], domain_two['email'])
+ self.assertIn('status', domain_two)
def test_get_domain(self):
# Create a domain
@@ -590,6 +593,7 @@ class StorageTestCase(TestCase):
self.assertEqual(actual['name'], expected['name'])
self.assertEqual(actual['email'], expected['email'])
+ self.assertIn('status', actual)
def test_get_domain_missing(self):
with self.assertRaises(exceptions.DomainNotFound):
@@ -617,6 +621,7 @@ class StorageTestCase(TestCase):
self.assertEqual(result['name'], domain_one['name'])
self.assertEqual(result['email'], domain_one['email'])
+ self.assertIn('status', domain_one)
criterion = dict(
name=domain_two['name']
@@ -626,6 +631,8 @@ class StorageTestCase(TestCase):
self.assertEqual(result['name'], domain_two['name'])
self.assertEqual(result['email'], domain_two['email'])
+ self.assertIn('status', domain_one)
+ self.assertIn('status', domain_two)
def test_find_domain_criterion_missing(self):
_, expected = self.create_domain(0)
@@ -646,6 +653,7 @@ class StorageTestCase(TestCase):
self.assertEqual(updated['name'], fixture['name'])
self.assertEqual(updated['email'], fixture['email'])
+ self.assertIn('status', updated)
def test_update_domain_duplicate(self):
# Create two domains
@@ -708,6 +716,7 @@ class StorageTestCase(TestCase):
self.assertEqual(result['name'], values['name'])
self.assertEqual(result['type'], values['type'])
self.assertEqual(result['data'], values['data'])
+ self.assertIn('status', result)
def test_create_record_duplicate(self):
_, domain = self.create_domain()
@@ -733,6 +742,7 @@ class StorageTestCase(TestCase):
self.assertEqual(actual[0]['name'], record_one['name'])
self.assertEqual(actual[0]['type'], record_one['type'])
self.assertEqual(actual[0]['data'], record_one['data'])
+ self.assertIn('status', record_one)
# Create a second record
_, record_two = self.create_record(domain, fixture=1)
@@ -743,6 +753,7 @@ class StorageTestCase(TestCase):
self.assertEqual(actual[1]['name'], record_two['name'])
self.assertEqual(actual[1]['type'], record_two['type'])
self.assertEqual(actual[1]['data'], record_two['data'])
+ self.assertIn('status', record_two)
def test_find_records_criterion(self):
_, domain = self.create_domain()
@@ -793,6 +804,7 @@ class StorageTestCase(TestCase):
self.assertEqual(actual['name'], expected['name'])
self.assertEqual(actual['type'], expected['type'])
self.assertEqual(actual['data'], expected['data'])
+ self.assertIn('status', actual)
def test_get_record_missing(self):
with self.assertRaises(exceptions.RecordNotFound):
@@ -813,6 +825,7 @@ class StorageTestCase(TestCase):
self.assertEqual(actual['name'], expected['name'])
self.assertEqual(actual['type'], expected['type'])
self.assertEqual(actual['data'], expected['data'])
+ self.assertIn('status', actual)
def test_find_record_criterion_missing(self):
_, domain = self.create_domain(0)
@@ -845,6 +858,7 @@ class StorageTestCase(TestCase):
self.assertEqual(updated['type'], record_fixture['type'])
self.assertEqual(updated['data'], record_fixture['data'])
self.assertNotEqual(updated['hash'], record['hash'])
+ self.assertIn('status', updated)
def test_update_record_duplicate(self):
_, domain = self.create_domain()