From: Juan Miguel Olmo Martínez Date: Wed, 24 Mar 2021 14:30:38 +0000 (+0100) Subject: mgr/cephadm: Fix dashboard gateway configuration when using IPV6 X-Git-Tag: v17.1.0~2482^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=1b18f4f9cb28708b544c62b3d07f9e1b4c701e41;p=ceph.git mgr/cephadm: Fix dashboard gateway configuration when using IPV6 Fixes: https://tracker.ceph.com/issues/49957 Signed-off-by: Juan Miguel Olmo Martínez --- diff --git a/src/pybind/mgr/cephadm/services/iscsi.py b/src/pybind/mgr/cephadm/services/iscsi.py index c4e0762d2744..7737a637422f 100644 --- a/src/pybind/mgr/cephadm/services/iscsi.py +++ b/src/pybind/mgr/cephadm/services/iscsi.py @@ -2,6 +2,7 @@ import errno import json import logging from typing import List, cast, Optional +from ipaddress import ip_address, IPv6Address from mgr_module import HandleCommandResult from ceph.deployment.service_spec import IscsiServiceSpec @@ -95,6 +96,9 @@ class IscsiService(CephService): logger.warning('No ServiceSpec found for %s', dd) continue ip = utils.resolve_ip(dd.hostname) + # IPv6 URL encoding requires square brackets enclosing the ip + if type(ip_address(ip)) is IPv6Address: + ip = f'[{ip}]' protocol = "http" if spec.api_secure and spec.ssl_cert and spec.ssl_key: protocol = "https" diff --git a/src/pybind/mgr/cephadm/tests/test_services.py b/src/pybind/mgr/cephadm/tests/test_services.py index e52a26729962..46dfbab4d973 100644 --- a/src/pybind/mgr/cephadm/tests/test_services.py +++ b/src/pybind/mgr/cephadm/tests/test_services.py @@ -1,6 +1,6 @@ import pytest -from unittest.mock import MagicMock, call +from unittest.mock import MagicMock, call, patch from cephadm.services.cephadmservice import MonService, MgrService, MdsService, RgwService, \ RbdMirrorService, CrashService, CephadmDaemonDeploySpec @@ -13,6 +13,7 @@ from cephadm.services.exporter import CephadmExporter from ceph.deployment.service_spec import IscsiServiceSpec from orchestrator import OrchestratorError +from orchestrator._interface import DaemonDescription class FakeMgr: @@ -84,40 +85,6 @@ class TestCephadmService: } return cephadm_services - def test_iscsi_client_caps(self): - mgr = FakeMgr() - iscsi_service = self._get_services(mgr)['iscsi'] - - iscsi_spec = IscsiServiceSpec(service_type='iscsi', service_id="a") - iscsi_spec.daemon_type = "iscsi" - iscsi_spec.daemon_id = "a" - iscsi_spec.spec = MagicMock() - iscsi_spec.spec.daemon_type = "iscsi" - iscsi_spec.spec.ssl_cert = '' - - mgr.spec_store = MagicMock() - mgr.spec_store.__getitem__.return_value = iscsi_spec - - iscsi_daemon_spec = CephadmDaemonDeploySpec( - host='host', daemon_id='a', service_name=iscsi_spec.service_name()) - - iscsi_service.prepare_create(iscsi_daemon_spec) - - expected_caps = ['mon', - 'profile rbd, allow command "osd blocklist", allow command "config-key get" with "key" prefix "iscsi/"', - 'mgr', 'allow command "service status"', - 'osd', 'allow rwx'] - - expected_call = call({'prefix': 'auth get-or-create', - 'entity': 'client.iscsi.a', - 'caps': expected_caps}) - expected_call2 = call({'prefix': 'auth caps', - 'entity': 'client.iscsi.a', - 'caps': expected_caps}) - - assert expected_call in mgr.mon_command.mock_calls - assert expected_call2 in mgr.mon_command.mock_calls - def test_get_auth_entity(self): mgr = FakeMgr() cephadm_services = self._get_services(mgr) @@ -158,3 +125,95 @@ class TestCephadmService: cephadm_services[daemon_type].get_auth_entity("id1", "host") cephadm_services[daemon_type].get_auth_entity("id1", "") cephadm_services[daemon_type].get_auth_entity("id1") + + +class TestISCSIService: + + mgr = FakeMgr() + iscsi_service = IscsiService(mgr) + + iscsi_spec = IscsiServiceSpec(service_type='iscsi', service_id="a") + iscsi_spec.daemon_type = "iscsi" + iscsi_spec.daemon_id = "a" + iscsi_spec.spec = MagicMock() + iscsi_spec.spec.daemon_type = "iscsi" + iscsi_spec.spec.ssl_cert = '' + iscsi_spec.api_user = "user" + iscsi_spec.api_password = "password" + iscsi_spec.api_port = 5000 + iscsi_spec.api_secure = False + iscsi_spec.ssl_cert = "cert" + iscsi_spec.ssl_key = "key" + + mgr.spec_store = MagicMock() + mgr.spec_store.all_specs.get.return_value = iscsi_spec + + def test_iscsi_client_caps(self): + + iscsi_daemon_spec = CephadmDaemonDeploySpec( + host='host', daemon_id='a', service_name=self.iscsi_spec.service_name()) + + self.iscsi_service.prepare_create(iscsi_daemon_spec) + + expected_caps = ['mon', + 'profile rbd, allow command "osd blocklist", allow command "config-key get" with "key" prefix "iscsi/"', + 'mgr', 'allow command "service status"', + 'osd', 'allow rwx'] + + expected_call = call({'prefix': 'auth get-or-create', + 'entity': 'client.iscsi.a', + 'caps': expected_caps}) + expected_call2 = call({'prefix': 'auth caps', + 'entity': 'client.iscsi.a', + 'caps': expected_caps}) + + assert expected_call in self.mgr.mon_command.mock_calls + assert expected_call2 in self.mgr.mon_command.mock_calls + + @patch('cephadm.utils.resolve_ip') + def test_iscsi_dashboard_config(self, mock_resolve_ip): + + self.mgr.check_mon_command = MagicMock() + self.mgr.check_mon_command.return_value = ('', '{"gateways": {}}', '') + + # Case 1: use IPV4 address + id1 = DaemonDescription(daemon_type='iscsi', hostname="testhost1", + daemon_id="a", ip='192.168.1.1') + daemon_list = [id1] + mock_resolve_ip.return_value = '192.168.1.1' + + self.iscsi_service.config_dashboard(daemon_list) + + dashboard_expected_call = call({'prefix': 'dashboard iscsi-gateway-add', + 'name': 'testhost1'}, + 'http://user:password@192.168.1.1:5000') + + assert dashboard_expected_call in self.mgr.check_mon_command.mock_calls + + # Case 2: use IPV6 address + self.mgr.check_mon_command.reset_mock() + + id1 = DaemonDescription(daemon_type='iscsi', hostname="testhost1", + daemon_id="a", ip='FEDC:BA98:7654:3210:FEDC:BA98:7654:3210') + mock_resolve_ip.return_value = 'FEDC:BA98:7654:3210:FEDC:BA98:7654:3210' + + self.iscsi_service.config_dashboard(daemon_list) + + dashboard_expected_call = call({'prefix': 'dashboard iscsi-gateway-add', + 'name': 'testhost1'}, + 'http://user:password@[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:5000') + + assert dashboard_expected_call in self.mgr.check_mon_command.mock_calls + + # Case 3: IPV6 Address . Secure protocol + self.mgr.check_mon_command.reset_mock() + + self.iscsi_spec.api_secure = True + + self.iscsi_service.config_dashboard(daemon_list) + + dashboard_expected_call = call({'prefix': 'dashboard iscsi-gateway-add', + 'name': 'testhost1'}, + 'https://user:password@[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:5000') + + assert dashboard_expected_call in self.mgr.check_mon_command.mock_calls