diff --git a/actions.yaml b/actions.yaml index f2a4925..c1b8571 100644 --- a/actions.yaml +++ b/actions.yaml @@ -58,3 +58,28 @@ remove-devices: required: - ring - search-value +set-weight: + description: Sets the device's weight. + params: + ring: + type: string + description: | + Swift ring to set the weight for. Valid options are 'account', + 'container', 'object' or 'all'. + search-value: + type: string + description: | + The search-value can be of the form: + . + drz-:R:/ + _ + . + Where and are replication ip and port. Any part is + optional, but you must include at least one part. + weight: + type: number + description: The device's weight + required: + - ring + - search-value + - weight diff --git a/actions/actions.py b/actions/actions.py index 97c84fc..27d7906 100755 --- a/actions/actions.py +++ b/actions/actions.py @@ -51,6 +51,7 @@ from lib.swift_utils import ( balance_rings, remove_from_ring, services, + set_weight_in_ring, SWIFT_CONF_DIR, ) @@ -147,10 +148,36 @@ def remove_devices(args): balance_rings() +def set_weight(args): + """ Sets the device's weight. + + Sets the device's weight based on the search pattern. + + :raises SwiftProxyCharmException: if pattern action_get('search-value') + doesn't match any device in the ring. + """ + rings_valid = ['account', 'container', 'object', 'all'] + ring = action_get('ring') + if ring not in rings_valid: + action_fail('Invalid ring name.') + return + if ring == 'all': + rings_to_update = ['account', 'container', 'object'] + else: + rings_to_update = [ring] + for ring_to_update in rings_to_update: + ring_to_update_builder = ring_to_update + '.builder' + ring_to_update_path = os.path.join(SWIFT_CONF_DIR, + ring_to_update_builder) + set_weight_in_ring(ring_to_update_path, action_get('search-value'), + str(action_get('weight'))) + balance_rings() + + # A dictionary of all the defined actions to callables (which take # parsed arguments). ACTIONS = {"pause": pause, "resume": resume, 'diskusage': diskusage, - 'remove-devices': remove_devices} + 'remove-devices': remove_devices, 'set-weight': set_weight} def main(argv): diff --git a/actions/set-weight b/actions/set-weight new file mode 120000 index 0000000..405a394 --- /dev/null +++ b/actions/set-weight @@ -0,0 +1 @@ +actions.py \ No newline at end of file diff --git a/lib/swift_utils.py b/lib/swift_utils.py index 3cb8984..512ece5 100644 --- a/lib/swift_utils.py +++ b/lib/swift_utils.py @@ -524,6 +524,29 @@ def remove_from_ring(ring_path, search_value): "{}".format(search_value, ring_path, e)) +def set_weight_in_ring(ring_path, search_value, weight): + """ Resets the device's weight. + + Resets the device's weight based on the search pattern. + + :param ring_path: path to the ring + :type ring_path: str + :param search_value: search pattern + :type search_value: str + :param weight: the device's weight + :type weight: str + :raises: SwiftProxyCharmException + """ + cmd = ['swift-ring-builder', ring_path, 'set_weight', search_value, weight] + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError: + raise SwiftProxyCharmException( + "Failed to set {} weight for {} pattern on {}".format(weight, + search_value, + ring_path)) + + def _ring_port(ring_path, node): """Determine correct port from relation settings for a given ring file.""" for name in ['account', 'object', 'container']: diff --git a/unit_tests/test_actions.py b/unit_tests/test_actions.py index f6f0fb4..97c0db7 100644 --- a/unit_tests/test_actions.py +++ b/unit_tests/test_actions.py @@ -335,3 +335,27 @@ class RemoveDevicesTestCase(CharmTestCase): self.action_fail.assert_called() self.remove_from_ring.assert_not_called() self.balance_rings.assert_not_called() + + +class SetWeightTestCase(CharmTestCase): + + def setUp(self): + super(SetWeightTestCase, self).setUp( + actions.actions, ["action_fail", + "action_get", + "set_weight_in_ring", + "balance_rings"]) + + def test_ring_valid(self): + self.action_get.side_effect = ['account', 'd1', '0.0'] + actions.actions.set_weight([]) + self.set_weight_in_ring.assert_called_once_with( + '/etc/swift/account.builder', 'd1', '0.0') + self.balance_rings.assert_called_once() + + def test_ring_invalid(self): + self.action_get.side_effect = ['other', 'd1', '0.0'] + actions.actions.set_weight([]) + self.action_fail.assert_called() + self.set_weight_in_ring.assert_not_called() + self.balance_rings.assert_not_called() diff --git a/unit_tests/test_swift_utils.py b/unit_tests/test_swift_utils.py index 2634431..3d7880e 100644 --- a/unit_tests/test_swift_utils.py +++ b/unit_tests/test_swift_utils.py @@ -437,6 +437,15 @@ class SwiftUtilsTestCase(unittest.TestCase): 'remove', 'd1']) + @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__')