diff --git a/swift/common/storage_policy.py b/swift/common/storage_policy.py index f2bdcc1755..335b4908b4 100644 --- a/swift/common/storage_policy.py +++ b/swift/common/storage_policy.py @@ -64,7 +64,7 @@ class StoragePolicy(object): is known via the Collection's get_object_ring method, but it may be over-ridden via object_ring kwarg at create time for testing. """ - def __init__(self, idx, name='', is_default=False, + def __init__(self, idx, name='', is_default=False, is_deprecated=False, policy_type=DEFAULT_TYPE, object_ring=None): try: self.idx = int(idx) @@ -75,7 +75,10 @@ class StoragePolicy(object): if not name: raise PolicyError('Invalid name', idx) self.name = name - self.is_default = config_true_value(is_default) + self.is_deprecated = config_true_value(is_deprecated) + # deprecation takes precedence over default + self.is_default = config_true_value(is_default) and \ + not self.is_deprecated if policy_type not in VALID_TYPES: raise PolicyError('Invalid type', idx) self.policy_type = policy_type @@ -91,8 +94,10 @@ class StoragePolicy(object): return cmp(self.idx, int(other)) def __repr__(self): - return "StoragePolicy(%d, %r, is_default=%s, policy_type=%r)" % ( - self.idx, self.name, self.is_default, self.policy_type) + return ("StoragePolicy(%d, %r, is_default=%s, " + "is_deprecated=%s, policy_type=%r)") % ( + self.idx, self.name, self.is_default, self.is_deprecated, + self.policy_type) def load_ring(self, swift_dir): if self.object_ring: @@ -165,9 +170,14 @@ class StoragePolicyCollection(object): if 0 not in self.by_index: self.add_policy(StoragePolicy(0, 'Policy_0', False)) - # if needed, specify default of policy 0 + # at least one policy must be enabled + enabled_policies = [p for p in self if not p.is_deprecated] + if not enabled_policies: + raise PolicyError("Unable to find policy that's not deprecated!") + + # if needed, specify default if not self.default: - self.default = self[0] + self.default = sorted(enabled_policies)[0] self.default.is_default = True def get_by_name(self, name): @@ -220,6 +230,9 @@ class StoragePolicyCollection(object): """ policy_info = [] for pol in self: + # delete from /info if deprecated + if pol.is_deprecated: + continue policy_entry = {} policy_entry['name'] = pol.name policy_entry['type'] = pol.policy_type @@ -263,6 +276,7 @@ def parse_storage_policies(conf): config_to_policy_option_map = { 'name': 'name', 'default': 'is_default', + 'deprecated': 'is_deprecated', 'type': 'policy_type', } policy_options = {} diff --git a/swift/proxy/controllers/container.py b/swift/proxy/controllers/container.py index f8d0367bc1..cd8abb70f0 100644 --- a/swift/proxy/controllers/container.py +++ b/swift/proxy/controllers/container.py @@ -124,6 +124,12 @@ class ContainerController(Controller): if policy_index is None: # make sure all backend servers get the same default policy policy_index = POLICIES.default.idx + policy = POLICIES[policy_index] + if policy.is_deprecated: + resp = HTTPBadRequest(request=req) + resp.body = 'Storage Policy %r is deprecated' % \ + (policy.name) + return resp if not req.environ.get('swift_owner'): for key in self.app.swift_owner_headers: req.headers.pop(key, None) diff --git a/test/unit/common/test_storage_policies.py b/test/unit/common/test_storage_policies.py index 5946cecbcf..96c09ea03a 100755 --- a/test/unit/common/test_storage_policies.py +++ b/test/unit/common/test_storage_policies.py @@ -35,8 +35,10 @@ class TestStoragePolicies(unittest.TestCase): @patch_policies([StoragePolicy(0, 'zero', True), StoragePolicy(1, 'one', False), - StoragePolicy(2, 'two', False)]) + StoragePolicy(2, 'two', False), + StoragePolicy(3, 'three', False, is_deprecated=True)]) def test_swift_info(self): + # the deprecated 'three' should not exist in expect expect = [{'default': True, 'type': 'replication', 'name': 'zero'}, {'type': 'replication', 'name': 'two'}, {'type': 'replication', 'name': 'one'}] @@ -101,6 +103,25 @@ class TestStoragePolicies(unittest.TestCase): self.assertEquals(policies.default, policies[0]) self.assertEquals(policies.default.name, 'Policy_0') + def test_deprecate_policies(self): + # deprecation specified + test_policies = [StoragePolicy(0, 'zero', True), + StoragePolicy(1, 'one', False), + StoragePolicy(2, 'two', False, is_deprecated=True)] + policies = StoragePolicyCollection(test_policies) + self.assertEquals(policies.default, test_policies[0]) + self.assertEquals(policies.default.name, 'zero') + self.assertEquals(len(policies), 3) + + # deprecating old default makes policy 0 the default + test_policies = [StoragePolicy(0, 'zero', False), + StoragePolicy(1, 'one', True, is_deprecated=True), + StoragePolicy(2, 'two', False)] + policies = StoragePolicyCollection(test_policies) + self.assertEquals(policies.default, test_policies[0]) + self.assertEquals(policies.default.name, 'zero') + self.assertEquals(len(policies), 3) + def test_validate_policies_indexes(self): # duplicate indexes test_policies = [StoragePolicy(0, 'zero', True), @@ -149,7 +170,39 @@ class TestStoragePolicies(unittest.TestCase): else: self.fail('%r did not raise %s' % (message, exc_class.__name__)) + def test_deprecated_default(self): + bad_conf = self._conf(""" + [storage-policy:1] + name = one + deprecated = yes + default = yes + """) + + policies = parse_storage_policies(bad_conf) + self.assertEqual(len(policies), 2) + self.assertTrue(policies[1].is_deprecated) + self.assertFalse(policies[1].is_default) + self.assertEqual(policies.default.idx, 0) + self.assertTrue(policies[0].is_default) + self.assertFalse(policies[0].is_deprecated) + def test_parse_storage_policies(self): + # ValueError when deprecating policy 0 + bad_conf = self._conf(""" + [storage-policy:0] + name = zero + deprecated = yes + default = yes + + [storage-policy:1] + name = one + deprecated = yes + """) + + self.assertRaisesWithMessage( + ValueError, "Unable to find policy that's not deprecated", + parse_storage_policies, bad_conf) + bad_conf = self._conf(""" [storage-policy:x] name = zero diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index e0c7b93024..09c3668d2a 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -4541,7 +4541,8 @@ class TestObjectController(unittest.TestCase): @patch_policies([ StoragePolicy(0, 'zero', True, object_ring=FakeRing()), - StoragePolicy(1, 'one', False, object_ring=FakeRing()) + StoragePolicy(1, 'one', False, object_ring=FakeRing()), + StoragePolicy(2, 'two', False, True, object_ring=FakeRing()) ]) class TestContainerController(unittest.TestCase): "Test swift.proxy_server.ContainerController" @@ -4561,6 +4562,9 @@ class TestContainerController(unittest.TestCase): req = Request.blank('/a/c', headers={'Content-Length': '0', 'Content-Type': 'text/plain', POLICY: 'one'}) self.assertEqual(controller._convert_policy_to_index(req), 1) + req = Request.blank('/a/c', headers={'Content-Length': '0', + 'Content-Type': 'text/plain', POLICY: 'two'}) + self.assertEqual(controller._convert_policy_to_index(req), 2) req = Request.blank('/a/c', headers={'Content-Length': '0', 'Content-Type': 'text/plain', POLICY: 'nada'}) self.assertRaises(HTTPException, controller._convert_policy_to_index, @@ -4695,6 +4699,14 @@ class TestContainerController(unittest.TestCase): else: policy = POLICIES.default res = req.get_response(self.app) + if policy.is_deprecated: + self.assertEquals(res.status_int, 400) + self.assertEqual(0, len(backend_requests)) + expected = 'is deprecated' + self.assertTrue(expected in res.body, + '%r did not include %r' % ( + res.body, expected)) + return self.assertEquals(res.status_int, 201) self.assertEqual( policy.object_ring.replicas, @@ -6350,6 +6362,7 @@ class TestProxyObjectPerformance(unittest.TestCase): @patch_policies([StoragePolicy(0, 'migrated'), StoragePolicy(1, 'ernie', True), + StoragePolicy(2, 'deprecated', is_deprecated=True), StoragePolicy(3, 'bert')]) class TestSwiftInfo(unittest.TestCase): def setUp(self): @@ -6391,6 +6404,8 @@ class TestSwiftInfo(unittest.TestCase): self.assertTrue('policies' in si) sorted_pols = sorted(si['policies'], key=operator.itemgetter('name')) self.assertEqual(len(sorted_pols), 3) + for policy in sorted_pols: + self.assertNotEquals(policy['name'], 'deprecated') self.assertEqual(sorted_pols[0]['name'], 'bert') self.assertEqual(sorted_pols[1]['name'], 'ernie') self.assertEqual(sorted_pols[2]['name'], 'migrated')