diff options
author | Graham Hayes <graham@hayes.ie> | 2013-09-02 16:31:36 +0100 |
---|---|---|
committer | Graham <graham.hayes@hp.com> | 2013-10-11 14:59:35 +0100 |
commit | b982573688e9958f094d105218cc9e53ce2ef6f3 (patch) | |
tree | 4a68dcb0e3b389b634b4f9331ad6ab142571e3b5 | |
parent | 361cd7af637870627ae399a98aff0201d6a3babb (diff) | |
download | designate-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.py | 21 | ||||
-rw-r--r-- | designate/api/v2/views/zones.py | 2 | ||||
-rw-r--r-- | designate/central/service.py | 8 | ||||
-rw-r--r-- | designate/sqlalchemy/models.py | 3 | ||||
-rw-r--r-- | designate/storage/impl_sqlalchemy/__init__.py | 4 | ||||
-rw-r--r-- | designate/storage/impl_sqlalchemy/migrate_repo/versions/022_add_domain_status.py | 51 | ||||
-rw-r--r-- | designate/storage/impl_sqlalchemy/models.py | 7 | ||||
-rw-r--r-- | designate/tests/test_api/test_v2/test_zones.py | 1 | ||||
-rw-r--r-- | designate/tests/test_central/test_service.py | 5 | ||||
-rw-r--r-- | designate/tests/test_storage/__init__.py | 14 |
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() |