
The mock third party library was needed for mock support in py2 runtimes. Since we now only support py36 and later, we can use the standard lib unittest.mock module instead. Note that https://github.com/openstack/charms.openstack is used during tests and he need `mock`, unfortunatelly it doesn't declare `mock` in its requirements so it retrieve mock from other charm project (cross dependency). So we depend on charms.openstack first and when Ib1ed5b598a52375e29e247db9ab4786df5b6d142 will be merged then CI will pass without errors. Depends-On: Ib1ed5b598a52375e29e247db9ab4786df5b6d142 Change-Id: I8757d430ab233f4a21d1d599f1a7a9a0dc68d7ef
859 lines
36 KiB
Python
859 lines
36 KiB
Python
# Copyright 2016 Canonical Ltd
|
|
#
|
|
# 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 copy
|
|
from unittest import mock
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
import uuid
|
|
import unittest
|
|
import subprocess
|
|
|
|
with mock.patch('charmhelpers.core.hookenv.config'):
|
|
import lib.swift_utils as swift_utils
|
|
|
|
|
|
def init_ring_paths(tmpdir):
|
|
swift_utils.SWIFT_CONF_DIR = tmpdir
|
|
for ring in swift_utils.SWIFT_RINGS.keys():
|
|
path = os.path.join(tmpdir, "{}.builder".format(ring))
|
|
swift_utils.SWIFT_RINGS[ring] = path
|
|
with open(path, 'w') as fd:
|
|
fd.write("0\n")
|
|
|
|
|
|
def create_mock_load_builder_fn(mock_rings):
|
|
"""To avoid the need for swift.common.ring library, mock a basic rings
|
|
dictionary, keyed by path. Each ring has enough logic to hold a dictionary
|
|
with a single 'devs' key, which stores the list of passed dev(s) by
|
|
add_dev().
|
|
|
|
If swift (actual) ring representation diverges (see _load_builder),
|
|
this mock will need to be adapted.
|
|
|
|
:param mock_rings: a dict containing the dict form of the rings
|
|
"""
|
|
def mock_load_builder_fn(path):
|
|
class mock_ring(object):
|
|
def __init__(self, path):
|
|
self.path = path
|
|
|
|
def to_dict(self):
|
|
return mock_rings[self.path]
|
|
|
|
def add_dev(self, dev):
|
|
mock_rings[self.path]['devs'].append(dev)
|
|
|
|
return mock_ring(path)
|
|
return mock_load_builder_fn
|
|
|
|
|
|
class SwiftUtilsTestCase(unittest.TestCase):
|
|
|
|
@mock.patch.object(swift_utils, 'previously_synced')
|
|
@mock.patch('lib.swift_utils.update_www_rings')
|
|
@mock.patch('lib.swift_utils.get_builders_checksum')
|
|
@mock.patch('lib.swift_utils.get_rings_checksum')
|
|
@mock.patch('lib.swift_utils.balance_rings')
|
|
@mock.patch('lib.swift_utils.log')
|
|
@mock.patch('lib.swift_utils.os.path.exists')
|
|
@mock.patch('lib.swift_utils.is_elected_leader')
|
|
@mock.patch('lib.swift_utils.get_min_part_hours')
|
|
@mock.patch('lib.swift_utils.set_min_part_hours')
|
|
@mock.patch('lib.swift_utils.write_rings')
|
|
@mock.patch('lib.swift_utils.nodes_have_rep_data')
|
|
def test_update_rings(self,
|
|
mock_nodes_have_rep_data,
|
|
mock_write_rings, mock_set_min_hours,
|
|
mock_get_min_hours, mock_is_elected_leader,
|
|
mock_path_exists, mock_log, mock_balance_rings,
|
|
mock_get_rings_checksum, mock_get_builders_checksum,
|
|
mock_update_www_rings, mock_previously_synced):
|
|
|
|
# Make sure same is returned for both so that we don't try to sync
|
|
mock_get_rings_checksum.return_value = None
|
|
mock_get_builders_checksum.return_value = None
|
|
mock_previously_synced.return_value = True
|
|
mock_nodes_have_rep_data.return_value = True
|
|
|
|
# Test blocker 1
|
|
mock_is_elected_leader.return_value = False
|
|
swift_utils.update_rings()
|
|
self.assertFalse(mock_balance_rings.called)
|
|
|
|
# Test blocker 2
|
|
mock_path_exists.return_value = False
|
|
mock_is_elected_leader.return_value = True
|
|
swift_utils.update_rings()
|
|
self.assertFalse(mock_get_min_hours.called)
|
|
self.assertFalse(mock_balance_rings.called)
|
|
|
|
# Test blocker 3
|
|
mock_path_exists.return_value = True
|
|
mock_is_elected_leader.return_value = True
|
|
mock_get_min_hours.return_value = 10
|
|
swift_utils.update_rings(min_part_hours=10)
|
|
self.assertTrue(mock_get_min_hours.called)
|
|
self.assertFalse(mock_set_min_hours.called)
|
|
self.assertFalse(mock_balance_rings.called)
|
|
|
|
mock_get_min_hours.reset_mock()
|
|
|
|
# Test go through
|
|
mock_path_exists.return_value = True
|
|
mock_is_elected_leader.return_value = True
|
|
mock_get_min_hours.return_value = 0
|
|
swift_utils.update_rings(min_part_hours=10)
|
|
self.assertTrue(mock_get_min_hours.called)
|
|
self.assertTrue(mock_set_min_hours.called)
|
|
self.assertTrue(mock_balance_rings.called)
|
|
mock_write_rings.assert_not_called()
|
|
|
|
@mock.patch('lib.swift_utils.previously_synced')
|
|
@mock.patch('lib.swift_utils.balance_rings')
|
|
@mock.patch('lib.swift_utils.add_to_ring')
|
|
@mock.patch('lib.swift_utils.exists_in_ring')
|
|
@mock.patch('lib.swift_utils.is_elected_leader')
|
|
@mock.patch('lib.swift_utils.update_ring_node_ports')
|
|
@mock.patch('lib.swift_utils.write_rings')
|
|
@mock.patch('lib.swift_utils.nodes_have_rep_data')
|
|
def test_update_rings_multiple_devs(self,
|
|
mock_nodes_have_rep_data,
|
|
mock_write_rings,
|
|
mock_update_ring_node_ports,
|
|
mock_is_leader_elected,
|
|
mock_exists_in_ring,
|
|
mock_add_to_ring,
|
|
mock_balance_rings,
|
|
mock_previously_synced):
|
|
# note that this test does not (and neither did its predecessor) test
|
|
# the 'min_part_hours is non None' part of update_rings()
|
|
devices = ['sdb', 'sdc']
|
|
node_settings = {
|
|
'object_port': 6000,
|
|
'container_port': 6001,
|
|
'account_port': 6002,
|
|
'zone': 1,
|
|
'ip': '1.2.3.4',
|
|
}
|
|
|
|
nodes = []
|
|
for dev in devices:
|
|
node = node_settings.copy()
|
|
node['device'] = dev
|
|
nodes.append(node)
|
|
|
|
mock_is_leader_elected.return_value = True
|
|
mock_previously_synced.return_value = True
|
|
mock_exists_in_ring.side_effect = lambda *args: False
|
|
mock_nodes_have_rep_data.return_value = True
|
|
|
|
swift_utils.update_rings(nodes)
|
|
calls = [mock.call(os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'account.builder'),
|
|
{
|
|
'zone': 1,
|
|
'object_port': 6000,
|
|
'ip': '1.2.3.4',
|
|
'container_port': 6001,
|
|
'device': 'sdb',
|
|
'account_port': 6002}),
|
|
mock.call(os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'container.builder'),
|
|
{
|
|
'zone': 1,
|
|
'object_port': 6000,
|
|
'ip': '1.2.3.4',
|
|
'container_port': 6001,
|
|
'device': 'sdb',
|
|
'account_port': 6002}),
|
|
mock.call(os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'object.builder'),
|
|
{
|
|
'zone': 1,
|
|
'object_port': 6000,
|
|
'ip': '1.2.3.4',
|
|
'container_port': 6001,
|
|
'device': 'sdb',
|
|
'account_port': 6002}),
|
|
mock.call(os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'account.builder'),
|
|
{
|
|
'zone': 1,
|
|
'object_port': 6000,
|
|
'ip': '1.2.3.4',
|
|
'container_port': 6001,
|
|
'device': 'sdc',
|
|
'account_port': 6002}),
|
|
mock.call(os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'container.builder'),
|
|
{
|
|
'zone': 1,
|
|
'object_port': 6000,
|
|
'ip': '1.2.3.4',
|
|
'container_port': 6001,
|
|
'device': 'sdc',
|
|
'account_port': 6002}),
|
|
mock.call(os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'object.builder'),
|
|
{
|
|
'zone': 1,
|
|
'object_port': 6000,
|
|
'ip': '1.2.3.4',
|
|
'container_port': 6001,
|
|
'device': 'sdc',
|
|
'account_port': 6002})]
|
|
mock_exists_in_ring.assert_has_calls(calls)
|
|
mock_balance_rings.assert_called_once_with()
|
|
mock_add_to_ring.assert_called()
|
|
mock_update_ring_node_ports.assert_called()
|
|
mock_write_rings.assert_called()
|
|
|
|
# try re-adding, assert add_to_ring was not called
|
|
mock_add_to_ring.reset_mock()
|
|
mock_exists_in_ring.side_effect = lambda *args: True
|
|
swift_utils.update_rings(nodes)
|
|
mock_add_to_ring.assert_not_called()
|
|
|
|
@mock.patch('lib.swift_utils.balance_rings')
|
|
@mock.patch('lib.swift_utils.log')
|
|
@mock.patch('lib.swift_utils.is_elected_leader')
|
|
@mock.patch('lib.swift_utils.config')
|
|
@mock.patch('lib.swift_utils.update_www_rings')
|
|
@mock.patch('lib.swift_utils.cluster_sync_rings')
|
|
def test_sync_builders_and_rings_if_changed(self, mock_cluster_sync_rings,
|
|
mock_update_www_rings,
|
|
mock_config,
|
|
mock_is_elected_leader,
|
|
mock_log,
|
|
mock_balance_rings):
|
|
|
|
_SWIFT_CONF_DIR = copy.deepcopy(swift_utils.SWIFT_CONF_DIR)
|
|
_SWIFT_RINGS = copy.deepcopy(swift_utils.SWIFT_RINGS)
|
|
|
|
@swift_utils.sync_builders_and_rings_if_changed
|
|
def mock_balance():
|
|
for ring, builder in swift_utils.SWIFT_RINGS.items():
|
|
ring = os.path.join(swift_utils.SWIFT_CONF_DIR,
|
|
'{}.ring.gz'.format(ring))
|
|
with open(ring, 'w') as fd:
|
|
fd.write(str(uuid.uuid4()))
|
|
|
|
with open(builder, 'w') as fd:
|
|
fd.write(str(uuid.uuid4()))
|
|
|
|
mock_balance_rings.side_effect = mock_balance
|
|
|
|
tmp_ring_dir = tempfile.mkdtemp()
|
|
init_ring_paths(tmp_ring_dir)
|
|
try:
|
|
swift_utils.balance_rings()
|
|
finally:
|
|
shutil.rmtree(tmp_ring_dir)
|
|
|
|
self.assertTrue(mock_update_www_rings.called)
|
|
self.assertTrue(mock_cluster_sync_rings.called)
|
|
swift_utils.SWIFT_CONF_DIR = _SWIFT_CONF_DIR
|
|
swift_utils.SWIFT_RINGS = _SWIFT_RINGS
|
|
|
|
def test__ring_port_rep(self):
|
|
node = {
|
|
'region': 1,
|
|
'zone': 1,
|
|
'ip': '172.16.0.2',
|
|
'ip_rep': '172.16.0.2',
|
|
'account_port': 6000,
|
|
'account_port_rep': 6010,
|
|
'device': '/dev/sdb',
|
|
}
|
|
expected = node['account_port_rep']
|
|
actual = swift_utils._ring_port_rep('/etc/swift/account.builder', node)
|
|
self.assertEqual(actual, expected)
|
|
|
|
@mock.patch.object(swift_utils, 'get_manager')
|
|
def test_get_current_replicas(self, mock_get_manager):
|
|
swift_utils.get_current_replicas('/etc/swift/account.builder')
|
|
mock_get_manager().get_current_replicas.assert_called_once_with(
|
|
'/etc/swift/account.builder')
|
|
|
|
@mock.patch.object(subprocess, 'check_call')
|
|
def test_update_replicas(self, check_call):
|
|
swift_utils.update_replicas('/etc/swift/account.builder', 3)
|
|
check_call.assert_called_once_with(['swift-ring-builder',
|
|
'/etc/swift/account.builder',
|
|
'set_replicas',
|
|
'3'])
|
|
|
|
@mock.patch('lib.swift_utils.is_elected_leader', lambda arg: True)
|
|
@mock.patch('lib.swift_utils.previously_synced')
|
|
@mock.patch.object(subprocess, 'check_output')
|
|
def test_write_rings(self, check_call, mock_previously_synced):
|
|
swift_utils.write_rings()
|
|
expected_calls = []
|
|
for path in swift_utils.SWIFT_RINGS.values():
|
|
expected_calls.append(mock.call(['swift-ring-builder',
|
|
path, 'write_ring']))
|
|
check_call.assert_has_calls(expected_calls, any_order=True)
|
|
|
|
@mock.patch('lib.swift_utils.is_elected_leader', lambda arg: True)
|
|
@mock.patch('lib.swift_utils.previously_synced')
|
|
@mock.patch.object(subprocess, 'check_output')
|
|
def test_write_rings_exception_pass(self, check_call,
|
|
mock_previously_synced):
|
|
my_execp = subprocess.CalledProcessError(
|
|
2, "swift-ring-builder", output='Unable to write empty ring.')
|
|
check_call.side_effect = (my_execp, my_execp, my_execp)
|
|
swift_utils.write_rings()
|
|
|
|
@mock.patch('lib.swift_utils.is_elected_leader', lambda arg: True)
|
|
@mock.patch('lib.swift_utils.previously_synced')
|
|
@mock.patch.object(subprocess, 'check_output')
|
|
def test_write_rings_exception_blocks(self, mock_check_call,
|
|
mock_previously_synced):
|
|
my_execp = subprocess.CalledProcessError(2, "swift-ring-builder",
|
|
output='')
|
|
mock_check_call.side_effect = my_execp
|
|
self.assertRaises(swift_utils.SwiftProxyCharmException,
|
|
swift_utils.write_rings)
|
|
|
|
def test_nodes_have_rep_data_true(self):
|
|
nodes = [{
|
|
'ip_rep': '0.0.0.0',
|
|
'ip_cls': '1.1.1.1',
|
|
'region': '',
|
|
'object_port_rep': 10,
|
|
'container_port_rep': 20,
|
|
'account_port_rep': 30}, {
|
|
'ip_rep': '2.2.2.2',
|
|
'ip_cls': '3.3.3.3',
|
|
'region': '',
|
|
'object_port_rep': 40,
|
|
'container_port_rep': 50,
|
|
'account_port_rep': 60}]
|
|
|
|
self.assertTrue(swift_utils.nodes_have_rep_data(nodes))
|
|
|
|
def test_nodes_have_rep_data_false(self):
|
|
nodes = [{'ip_cls': '1.1.1.1', 'region': ''}, {'ip_cls': '3.3.3.3', }]
|
|
|
|
self.assertFalse(swift_utils.nodes_have_rep_data(nodes))
|
|
|
|
@mock.patch('lib.swift_utils.get_www_dir')
|
|
def test_mark_www_rings_deleted(self, mock_get_www_dir):
|
|
try:
|
|
tmpdir = tempfile.mkdtemp()
|
|
mock_get_www_dir.return_value = tmpdir
|
|
swift_utils.mark_www_rings_deleted()
|
|
finally:
|
|
shutil.rmtree(tmpdir)
|
|
|
|
@mock.patch('lib.swift_utils.is_elected_leader', lambda arg: True)
|
|
@mock.patch.object(swift_utils, 'get_hostaddr', lambda *args: '1.2.3.4')
|
|
@mock.patch('lib.swift_utils.uuid')
|
|
def test_cluster_rpc_stop_proxy_request(self, mock_uuid):
|
|
mock_uuid.uuid4.return_value = 'test-uuid'
|
|
rpc = swift_utils.SwiftProxyClusterRPC()
|
|
rq = rpc.stop_proxy_request(peers_only=True)
|
|
self.assertEqual({'trigger': 'test-uuid',
|
|
'broker-token': None,
|
|
'broker-timestamp': None,
|
|
'builder-broker': '1.2.3.4',
|
|
'peers-only': 1,
|
|
'leader-changed-notification': None,
|
|
'resync-request': None,
|
|
'stop-proxy-service': 'test-uuid',
|
|
'stop-proxy-service-ack': None,
|
|
'sync-only-builders': None}, rq)
|
|
|
|
rq = rpc.stop_proxy_request()
|
|
self.assertEqual({'trigger': 'test-uuid',
|
|
'broker-token': None,
|
|
'broker-timestamp': None,
|
|
'builder-broker': '1.2.3.4',
|
|
'peers-only': None,
|
|
'leader-changed-notification': None,
|
|
'resync-request': None,
|
|
'stop-proxy-service': 'test-uuid',
|
|
'stop-proxy-service-ack': None,
|
|
'sync-only-builders': None}, rq)
|
|
|
|
template_keys = set(rpc.template())
|
|
self.assertTrue(set(rq.keys()).issubset(template_keys))
|
|
|
|
@mock.patch('lib.swift_utils.uuid')
|
|
def test_cluster_rpc_stop_proxy_ack(self, mock_uuid):
|
|
mock_uuid.uuid4.return_value = 'token2'
|
|
rpc = swift_utils.SwiftProxyClusterRPC()
|
|
rq = rpc.stop_proxy_ack(echo_token='token1', echo_peers_only='1')
|
|
self.assertEqual({'trigger': 'token2',
|
|
'broker-token': None,
|
|
'builder-broker': None,
|
|
'broker-timestamp': None,
|
|
'peers-only': '1',
|
|
'leader-changed-notification': None,
|
|
'resync-request': None,
|
|
'stop-proxy-service': None,
|
|
'stop-proxy-service-ack': 'token1',
|
|
'sync-only-builders': None}, rq)
|
|
|
|
template_keys = set(rpc.template())
|
|
self.assertTrue(set(rq.keys()).issubset(template_keys))
|
|
|
|
@mock.patch('lib.swift_utils.is_elected_leader', lambda arg: True)
|
|
@mock.patch.object(swift_utils, 'get_hostaddr', lambda *args: '1.2.3.4')
|
|
@mock.patch('lib.swift_utils.uuid')
|
|
def test_cluster_rpc_sync_request(self, mock_uuid):
|
|
mock_uuid.uuid4.return_value = 'token2'
|
|
rpc = swift_utils.SwiftProxyClusterRPC()
|
|
rq = rpc.sync_rings_request('token1', '1.234000')
|
|
self.assertEqual({'trigger': 'token2',
|
|
'broker-token': 'token1',
|
|
'broker-timestamp': '1.234000',
|
|
'builder-broker': '1.2.3.4',
|
|
'peers-only': None,
|
|
'leader-changed-notification': None,
|
|
'resync-request': None,
|
|
'stop-proxy-service': None,
|
|
'stop-proxy-service-ack': None,
|
|
'sync-only-builders': None}, rq)
|
|
|
|
template_keys = set(rpc.template())
|
|
self.assertTrue(set(rq.keys()).issubset(template_keys))
|
|
|
|
@mock.patch('lib.swift_utils.is_elected_leader', lambda arg: True)
|
|
@mock.patch('lib.swift_utils.uuid')
|
|
def test_cluster_rpc_notify_leader_changed(self, mock_uuid):
|
|
mock_uuid.uuid4.return_value = 'e4b67426-6cc0-4aa3-829d-227999cd0a75'
|
|
rpc = swift_utils.SwiftProxyClusterRPC()
|
|
rq = rpc.notify_leader_changed('token1')
|
|
self.assertEqual({'trigger': 'e4b67426-6cc0-4aa3-829d-227999cd0a75',
|
|
'broker-token': None,
|
|
'builder-broker': None,
|
|
'broker-timestamp': None,
|
|
'peers-only': None,
|
|
'leader-changed-notification': 'token1',
|
|
'stop-proxy-service': None,
|
|
'stop-proxy-service-ack': None,
|
|
'resync-request': None,
|
|
'sync-only-builders': None}, rq)
|
|
|
|
template_keys = set(rpc.template().keys())
|
|
self.assertTrue(set(rq.keys()).issubset(template_keys))
|
|
|
|
def test_all_responses_equal(self):
|
|
responses = [{'a': 1, 'c': 3}]
|
|
self.assertTrue(swift_utils.all_responses_equal(responses, 'b',
|
|
must_exist=False))
|
|
|
|
responses = [{'a': 1, 'c': 3}]
|
|
self.assertFalse(swift_utils.all_responses_equal(responses, 'b'))
|
|
|
|
responses = [{'a': 1, 'b': 2, 'c': 3}]
|
|
self.assertTrue(swift_utils.all_responses_equal(responses, 'b'))
|
|
|
|
responses = [{'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 2, 'c': 3}]
|
|
self.assertTrue(swift_utils.all_responses_equal(responses, 'b'))
|
|
|
|
responses = [{'a': 1, 'b': 2, 'c': 3}, {'a': 2, 'b': 2, 'c': 3}]
|
|
self.assertTrue(swift_utils.all_responses_equal(responses, 'b'))
|
|
|
|
responses = [{'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 3, 'c': 3}]
|
|
self.assertFalse(swift_utils.all_responses_equal(responses, 'b'))
|
|
|
|
def test_get_first_available_value(self):
|
|
rsps = [{'key1': 'A'}, {'key1': 'B'}]
|
|
self.assertEqual('A',
|
|
swift_utils.get_first_available_value(rsps, 'key1'))
|
|
|
|
rsps = [{'key2': 'A'}, {'key1': 'B'}]
|
|
self.assertEqual('B',
|
|
swift_utils.get_first_available_value(rsps, 'key1'))
|
|
|
|
rsps = [{'key2': 'A'}, {'key1': 'B'}]
|
|
self.assertIsNone(swift_utils.get_first_available_value(rsps, 'key3'))
|
|
|
|
rsps = []
|
|
self.assertIsNone(swift_utils.get_first_available_value(rsps, 'key3'))
|
|
|
|
@mock.patch.object(swift_utils, 'relation_get')
|
|
@mock.patch.object(swift_utils, 'related_units', lambda arg: ['proxy/1'])
|
|
@mock.patch.object(swift_utils, 'relation_ids', lambda arg: ['cluster:1'])
|
|
def test_is_most_recent_timestamp(self, mock_rel_get):
|
|
mock_rel_get.return_value = {'broker-timestamp': '1111'}
|
|
self.assertTrue(swift_utils.is_most_recent_timestamp('1234'))
|
|
mock_rel_get.return_value = {'broker-timestamp': '2234'}
|
|
self.assertFalse(swift_utils.is_most_recent_timestamp('1234'))
|
|
mock_rel_get.return_value = {}
|
|
self.assertFalse(swift_utils.is_most_recent_timestamp('1234'))
|
|
mock_rel_get.return_value = {'broker-timestamp': '2234'}
|
|
self.assertFalse(swift_utils.is_most_recent_timestamp(None))
|
|
|
|
@mock.patch.object(swift_utils, 'relation_get')
|
|
@mock.patch.object(swift_utils, 'related_units', lambda arg: ['proxy/1'])
|
|
@mock.patch.object(swift_utils, 'relation_ids', lambda arg: ['cluster:1'])
|
|
def test_timestamps_available(self, mock_rel_get):
|
|
mock_rel_get.return_value = {}
|
|
self.assertFalse(swift_utils.timestamps_available('proxy/1'))
|
|
mock_rel_get.return_value = {'broker-timestamp': '1234'}
|
|
self.assertFalse(swift_utils.timestamps_available('proxy/1'))
|
|
mock_rel_get.return_value = {'broker-timestamp': '1234'}
|
|
self.assertTrue(swift_utils.timestamps_available('proxy/2'))
|
|
|
|
@mock.patch.object(swift_utils, 'get_manager')
|
|
def test_add_to_ring(self, mock_get_manager):
|
|
ring = 'account'
|
|
node = {
|
|
'region': 1,
|
|
'zone': 1,
|
|
'ip': '172.16.0.2',
|
|
'account_port': 6000,
|
|
'device': '/dev/sdb',
|
|
}
|
|
swift_utils.add_to_ring(ring, node)
|
|
mock_get_manager().add_dev.assert_called_once_with('account', {
|
|
'zone': 1,
|
|
'ip': '172.16.0.2',
|
|
'port': 6000,
|
|
'device': '/dev/sdb',
|
|
'meta': '',
|
|
'weight': 100
|
|
})
|
|
|
|
@mock.patch.object(swift_utils, 'get_manager')
|
|
def test_add_to_ring_rep(self, mock_get_manager):
|
|
ring = 'account'
|
|
node = {
|
|
'region': 1,
|
|
'zone': 1,
|
|
'ip': '172.16.0.2',
|
|
'ip_rep': '172.16.0.2',
|
|
'account_port': 6000,
|
|
'account_port_rep': 6010,
|
|
'device': '/dev/sdb',
|
|
}
|
|
swift_utils.add_to_ring(ring, node)
|
|
mock_get_manager().add_dev.assert_called_once_with('account', {
|
|
'region': 1,
|
|
'zone': 1,
|
|
'ip': '172.16.0.2',
|
|
'replication_ip': '172.16.0.2',
|
|
'port': 6000,
|
|
'replication_port': 6010,
|
|
'device': '/dev/sdb',
|
|
'meta': '',
|
|
'weight': 100
|
|
})
|
|
|
|
@mock.patch.object(subprocess, 'check_call')
|
|
def test_remove_from_ring(self, check_call):
|
|
swift_utils.remove_from_ring('/etc/swift/account.builder', 'd1')
|
|
check_call.assert_called_once_with(['swift-ring-builder',
|
|
'/etc/swift/account.builder',
|
|
'remove',
|
|
'd1',
|
|
'-y'])
|
|
|
|
@mock.patch.object(subprocess, 'check_call')
|
|
def test_set_weight_in_ring(self, check_call):
|
|
swift_utils.set_weight_in_ring('/etc/swift/account.builder', 'd1', 0.0)
|
|
check_call.assert_called_once_with(['swift-ring-builder',
|
|
'/etc/swift/account.builder',
|
|
'set_weight',
|
|
'd1',
|
|
0.0])
|
|
|
|
@mock.patch('lib.swift_utils.config')
|
|
@mock.patch('lib.swift_utils.set_os_workload_status')
|
|
@mock.patch('lib.swift_utils.SwiftRingContext.__call__')
|
|
@mock.patch('lib.swift_utils.has_minimum_zones')
|
|
@mock.patch('lib.swift_utils.relation_ids')
|
|
def customer_check_assess_status(
|
|
self, relation_ids, has_min_zones,
|
|
ctxt, workload_status, config):
|
|
config.side_effect = [3, 3, 3]
|
|
|
|
relation_ids.return_value = []
|
|
s, m = swift_utils.customer_check_assess_status(None)
|
|
assert s, m == ('blocked', 'Missing relation: storage')
|
|
|
|
relation_ids.return_value = ['swift-storage:1']
|
|
|
|
ctxt.return_value = {'allowed_hosts': ['1.2.3.4']}
|
|
s, m = swift_utils.customer_check_assess_status(None)
|
|
assert s, m == ('blocked',
|
|
'Not enough related storage nodes')
|
|
|
|
ctxt.return_value = {'allowed_hosts': ['1.2.3.4', '2.3.4.5',
|
|
'3.4.5.6']}
|
|
has_min_zones.return_value = False
|
|
s, m = swift_utils.customer_check_assess_status(None)
|
|
assert s, m == ('blocked',
|
|
'Not enough storage zones for minimum '
|
|
'replicas')
|
|
|
|
@mock.patch.object(swift_utils, 'os_application_version_set')
|
|
def test_assess_status(self, os_application_version_set):
|
|
with mock.patch.object(swift_utils, 'assess_status_func') as asf:
|
|
callee = mock.MagicMock()
|
|
asf.return_value = callee
|
|
swift_utils.assess_status('test-config')
|
|
asf.assert_called_once_with('test-config', None)
|
|
callee.assert_called_once_with()
|
|
os_application_version_set.assert_called_with(
|
|
swift_utils.VERSION_PACKAGE
|
|
)
|
|
|
|
@mock.patch.object(swift_utils, 'get_managed_services_and_ports')
|
|
@mock.patch.object(swift_utils, 'relation_ids')
|
|
@mock.patch.object(swift_utils, 'services')
|
|
@mock.patch.object(swift_utils, 'make_assess_status_func')
|
|
def test_assess_status_func(self,
|
|
make_assess_status_func,
|
|
services,
|
|
relation_ids,
|
|
get_managed_services_and_ports):
|
|
get_managed_services_and_ports.return_value = (['s1'], [])
|
|
relation_ids.return_value = True
|
|
services.return_value = 's1'
|
|
required_interfaces = {'identity': ['identity-service']}
|
|
swift_utils.assess_status_func('test-config')
|
|
relation_ids.assert_called_once_with('identity-service')
|
|
# ports=None whilst port checks are disabled.
|
|
make_assess_status_func.assert_called_once_with(
|
|
'test-config', required_interfaces,
|
|
charm_func=swift_utils.customer_check_assess_status,
|
|
services=['s1'], ports=None)
|
|
|
|
@mock.patch.object(swift_utils, 'os_release')
|
|
@mock.patch.object(swift_utils, 'leader_set')
|
|
@mock.patch.object(swift_utils, 'determine_api_port')
|
|
@mock.patch.object(swift_utils, 'is_leader')
|
|
@mock.patch.object(swift_utils, 'config')
|
|
@mock.patch.object(swift_utils, 'leader_get')
|
|
@mock.patch.object(subprocess, 'check_call')
|
|
def test_config_and_leader_get(self, check_call, leader_get, config,
|
|
is_leader, determine_api_port, leader_set,
|
|
os_release):
|
|
"""Ensure that we config_get, and then leader_get."""
|
|
config.side_effect = lambda key: {
|
|
'auth-type': 'swauth',
|
|
'swauth-admin-key': None,
|
|
'bind-port': 8080}[key]
|
|
determine_api_port.return_value = 8080
|
|
is_leader.return_value = True
|
|
leader_get.return_value = "Test"
|
|
os_release.return_value = "queens"
|
|
swift_utils.try_initialize_swauth()
|
|
check_call.assert_called_with(['swauth-prep',
|
|
'-A',
|
|
'http://localhost:8080/auth',
|
|
'-K',
|
|
'Test'])
|
|
|
|
@mock.patch.object(swift_utils.uuid, 'uuid4')
|
|
@mock.patch.object(swift_utils, 'relation_set')
|
|
@mock.patch.object(swift_utils, 'get_swift_hash')
|
|
@mock.patch.object(swift_utils, 'log')
|
|
@mock.patch.object(swift_utils, 'relation_ids')
|
|
@mock.patch.object(swift_utils, 'get_www_dir')
|
|
@mock.patch.object(swift_utils, 'format_ipv6_addr')
|
|
@mock.patch.object(swift_utils, 'get_hostaddr')
|
|
@mock.patch.object(swift_utils, 'is_elected_leader')
|
|
def test_notify_storage_and_consumers_rings_available(
|
|
self,
|
|
mock_is_leader,
|
|
mock_get_hostaddr,
|
|
mock_format_ipv6_addr,
|
|
mock_get_www_dir,
|
|
mock_relation_ids,
|
|
mock_log,
|
|
mock_get_swift_hash,
|
|
mock_relation_set,
|
|
mock_uuid):
|
|
|
|
mock_is_leader.return_value = True
|
|
mock_get_hostaddr.return_value = '10.0.0.1'
|
|
mock_format_ipv6_addr.return_value = None
|
|
mock_get_www_dir.return_value = 'some/dir'
|
|
mock_relation_ids.side_effect = [['storage:0'],
|
|
['rings-distributor:0']]
|
|
mock_get_swift_hash.return_value = 'greathash'
|
|
mock_uuid.return_value = 'uuid-1234'
|
|
calls = [mock.call(broker_timestamp='1.234',
|
|
relation_id='storage:0',
|
|
rings_url='http://10.0.0.1/dir',
|
|
swift_hash='greathash',
|
|
trigger='uuid-1234'),
|
|
mock.call(broker_timestamp='1.234',
|
|
relation_id='rings-distributor:0',
|
|
rings_url='http://10.0.0.1/dir',
|
|
swift_hash='greathash',
|
|
trigger='uuid-1234')]
|
|
swift_utils.notify_storage_and_consumers_rings_available('1.234')
|
|
mock_relation_set.assert_has_calls(calls)
|
|
|
|
@mock.patch.object(swift_utils, 'relation_set')
|
|
@mock.patch.object(swift_utils, 'relation_ids')
|
|
def test_clear_notify_storage_and_consumers_rings_available(
|
|
self,
|
|
mock_relation_ids,
|
|
mock_relation_set):
|
|
|
|
mock_relation_ids.return_value = ['storage:0']
|
|
swift_utils.clear_storage_rings_available()
|
|
mock_relation_set.assert_called_once_with(
|
|
relation_id='storage:0', rings_url=None)
|
|
|
|
def test_determine_packages(self):
|
|
self.assertEqual(
|
|
['swift',
|
|
'swift-proxy',
|
|
'memcached',
|
|
'apache2',
|
|
'python-keystone',
|
|
'python-swiftclient'],
|
|
swift_utils.determine_packages('essex')
|
|
)
|
|
|
|
self.assertEqual(
|
|
['swift',
|
|
'swift-proxy',
|
|
'memcached',
|
|
'apache2',
|
|
'python-keystone',
|
|
'python-swiftclient',
|
|
'swift-plugin-s3',
|
|
'swauth'],
|
|
swift_utils.determine_packages('folsom')
|
|
)
|
|
|
|
self.assertEqual(
|
|
['swift',
|
|
'swift-proxy',
|
|
'memcached',
|
|
'apache2',
|
|
'python-swiftclient',
|
|
'swift-plugin-s3',
|
|
'swauth',
|
|
'python-ceilometermiddleware',
|
|
'python-keystonemiddleware'],
|
|
swift_utils.determine_packages('mitaka')
|
|
)
|
|
|
|
self.assertEqual(
|
|
['swift',
|
|
'swift-proxy',
|
|
'memcached',
|
|
'apache2',
|
|
'python-swiftclient',
|
|
'swauth',
|
|
'python-ceilometermiddleware',
|
|
'python-keystonemiddleware'],
|
|
swift_utils.determine_packages('rocky')
|
|
)
|
|
|
|
self.assertEqual(
|
|
['swift',
|
|
'swift-proxy',
|
|
'memcached',
|
|
'apache2',
|
|
'python-swiftclient',
|
|
'swauth',
|
|
'python-ceilometermiddleware',
|
|
'python-keystonemiddleware'],
|
|
swift_utils.determine_packages('stein')
|
|
)
|
|
|
|
self.assertEqual(
|
|
['swift',
|
|
'swift-proxy',
|
|
'memcached',
|
|
'apache2',
|
|
'python3-ceilometermiddleware',
|
|
'python3-keystonemiddleware',
|
|
'python3-six',
|
|
'python3-swift',
|
|
'python3-swiftclient'],
|
|
swift_utils.determine_packages('train')
|
|
)
|
|
|
|
@mock.patch('lib.swift_utils.config')
|
|
def test_determine_replicas_account_set(self, config):
|
|
config.side_effect = lambda key: {
|
|
'replicas': 3,
|
|
'replicas-account': 2,
|
|
'replicas-container': None}[key]
|
|
replicas = swift_utils.determine_replicas('account')
|
|
self.assertEqual(replicas, 2)
|
|
|
|
@mock.patch('lib.swift_utils.config')
|
|
def test_determine_replicas_account_not_set(self, config):
|
|
config.side_effect = lambda key: {
|
|
'replicas': 3,
|
|
'replicas-account': None,
|
|
'replicas-container': None}[key]
|
|
replicas = swift_utils.determine_replicas('account')
|
|
self.assertEqual(replicas, 3)
|
|
|
|
@mock.patch('lib.swift_utils.config')
|
|
def test_determine_replicas_container_set(self, config):
|
|
config.side_effect = lambda key: {
|
|
'replicas': 3,
|
|
'replicas-account': None,
|
|
'replicas-container': 2}[key]
|
|
replicas = swift_utils.determine_replicas('container')
|
|
self.assertEqual(replicas, 2)
|
|
|
|
@mock.patch('lib.swift_utils.config')
|
|
def test_determine_replicas_container_not_set(self, config):
|
|
config.side_effect = lambda key: {
|
|
'replicas': 3,
|
|
'replicas-account': None,
|
|
'replicas-container': None}[key]
|
|
replicas = swift_utils.determine_replicas('container')
|
|
self.assertEqual(replicas, 3)
|
|
|
|
@mock.patch('lib.swift_utils.config')
|
|
def test_determine_replicas_object(self, config):
|
|
config.side_effect = lambda key: {
|
|
'replicas': 3,
|
|
'replicas-account': 2,
|
|
'replicas-container': 2}[key]
|
|
replicas = swift_utils.determine_replicas('object')
|
|
self.assertEqual(replicas, 3)
|
|
|
|
def test_fetch_swift_rings_and_builders(self):
|
|
"""
|
|
Based on the 'test_fetch_swift_rings' function from the swift-storage
|
|
charm.
|
|
"""
|
|
url = 'http://someproxynode/rings'
|
|
_SWIFT_CONF_DIR = copy.deepcopy(swift_utils.SWIFT_CONF_DIR)
|
|
swift_utils.SWIFT_CONF_DIR = tempfile.mkdtemp()
|
|
try:
|
|
swift_utils.fetch_swift_rings_and_builders(url)
|
|
wgets = []
|
|
for s in ['account', 'object', 'container']:
|
|
for ext in ['ring.gz', 'builder']:
|
|
_c = mock.call(['wget', '%s/%s.%s' % (url, s, ext),
|
|
'--retry-connrefused', '-t', '10',
|
|
'-O', swift_utils.SWIFT_CONF_DIR +
|
|
'/%s.%s' % (s, ext)])
|
|
wgets.append(_c)
|
|
self.assertEqual(wgets, self.check_call.call_args_list)
|
|
except Exception:
|
|
shutil.rmtree(swift_utils.SWIFT_CONF_DIR)
|
|
swift_utils.SWIFT_CONF_DIR = _SWIFT_CONF_DIR
|