Skip to content

Commit 6e9ac79

Browse files
committed
mgr/dashboard: upgrading nvmeof doesn't update configuration
Happens from 19.2.0 to any of the latest upgrade During upgrade I get ``` Failed to set Dashboard config for nvmeof: dashboard nvmeof-gateway-add failed: JSON array/object not allowed {"prefix": "dashboard nvmeof-gateway-add", "name": "nvmeof.rbd", "group": null, "daemon_name": "nvmeof.rbd.ceph-node-01.irpssg"} retval: -22 ``` which is fixed by handling the group_name when its not there in spec. And the other error was ``` Failed to set Dashboard config for nvmeof: dashboard nvmeof-gateway-add failed: Traceback (most recent call last): File "/usr/share/ceph/mgr/mgr_module.py", line 1864, in _handle_command return CLICommand.COMMANDS[cmd['prefix']].call(self, cmd, inbuf) File "/usr/share/ceph/mgr/mgr_module.py", line 499, in call return self.func(mgr, **kwargs) File "/usr/share/ceph/mgr/mgr_module.py", line 535, in check return func(*args, **kwargs) File "/usr/share/ceph/mgr/dashboard/services/nvmeof_cli.py", line 28, in add_nvmeof_gateway NvmeofGatewaysConfig.add_gateway(name, service_url, group, daemon_name) File "/usr/share/ceph/mgr/dashboard/services/nvmeof_conf.py", line 61, in add_gateway gateway['daemon_name'] = daemon_name TypeError: 'str' object does not support item assignment retval: -22 ``` which is fixed by properly updating the config to the newer format that is available in newer version Fixes: https://tracker.ceph.com/issues/70629 Signed-off-by: Nizamudeen A <[email protected]>
1 parent 6fc1a6d commit 6e9ac79

File tree

4 files changed

+194
-8
lines changed

4 files changed

+194
-8
lines changed

src/pybind/mgr/cephadm/services/nvmeof.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def get_set_cmd_dicts(out: str) -> List[dict]:
171171
'prefix': 'dashboard nvmeof-gateway-add',
172172
'inbuf': service_url,
173173
'name': service_name,
174-
'group': spec.group,
174+
'group': spec.group if spec.group else '',
175175
'daemon_name': dd.name()
176176
})
177177
return cmd_dicts

src/pybind/mgr/dashboard/services/nvmeof_conf.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,19 @@ def add_gateway(cls, name, service_url, group, daemon_name):
5555
config = cls.get_gateways_config()
5656

5757
if name in config.get('gateways', {}):
58-
existing_gateways = config['gateways'][name]
59-
for gateway in existing_gateways:
60-
if 'daemon_name' not in gateway:
61-
gateway['daemon_name'] = daemon_name
62-
break
63-
if gateway['service_url'] == service_url:
64-
return
58+
# the nvmeof dashboard config used in v19.2.0 saves the below
59+
# to a dict. Converting that to a list so that the upgrade
60+
# properly migrate it to the newer format, and also keep it empty.
61+
if isinstance(config['gateways'][name], dict):
62+
config['gateways'][name] = []
63+
else:
64+
existing_gateways = config['gateways'][name]
65+
for gateway in existing_gateways:
66+
if 'daemon_name' not in gateway:
67+
gateway['daemon_name'] = daemon_name
68+
break
69+
if gateway['service_url'] == service_url:
70+
return
6571

6672
new_gateway = {
6773
'service_url': service_url,

src/pybind/mgr/dashboard/tests/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ def mock_kv_store(cls):
7373
def get_key(cls, key):
7474
return cls.CONFIG_KEY_DICT.get(key, None)
7575

76+
@classmethod
77+
def set_key(cls, key, value):
78+
cls.CONFIG_KEY_DICT[key] = value
79+
7680

7781
# pylint: disable=protected-access
7882
class CLICommandTestMixin(KVStoreMockMixin):

src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import errno
2+
import json
3+
import unittest
24
from unittest.mock import MagicMock
35

46
import pytest
57
from mgr_module import CLICommand, HandleCommandResult
68

79
from ..services.nvmeof_cli import NvmeofCLICommand
10+
from ..tests import CLICommandTestMixin
811

912

1013
@pytest.fixture(scope="class", name="sample_command")
@@ -85,3 +88,176 @@ def test_command_return_empty_cmd_result(self, base_call_return_none_mock, sampl
8588
assert result.stdout == ''
8689
assert result.stderr == ''
8790
base_call_return_none_mock.assert_called_once()
91+
92+
93+
class TestNVMeoFConfCLI(unittest.TestCase, CLICommandTestMixin):
94+
def setUp(self):
95+
self.mock_kv_store()
96+
97+
def test_cli_add_gateway(self):
98+
99+
self.exec_cmd(
100+
'nvmeof-gateway-add',
101+
name='nvmeof.pool.group',
102+
inbuf='http://nvmf:port',
103+
daemon_name='nvmeof_daemon',
104+
group='group'
105+
)
106+
107+
config = json.loads(self.get_key('_nvmeof_config'))
108+
self.assertEqual(
109+
config['gateways'], {
110+
'nvmeof.pool.group': [{
111+
'group': 'group',
112+
'daemon_name': 'nvmeof_daemon',
113+
'service_url': 'http://nvmf:port'
114+
}]
115+
}
116+
)
117+
118+
def test_cli_migration_from_legacy_config(self):
119+
legacy_config = json.dumps({
120+
'gateways': {
121+
'nvmeof.pool': {
122+
'service_url': 'http://nvmf:port'
123+
}
124+
}
125+
})
126+
self.set_key('_nvmeof_config', legacy_config)
127+
128+
self.exec_cmd(
129+
'nvmeof-gateway-add',
130+
name='nvmeof.pool',
131+
inbuf='http://nvmf:port',
132+
daemon_name='nvmeof_daemon',
133+
group=''
134+
)
135+
136+
config = json.loads(self.get_key('_nvmeof_config'))
137+
self.assertEqual(
138+
config['gateways'], {
139+
'nvmeof.pool': [{
140+
'daemon_name': 'nvmeof_daemon',
141+
'group': '',
142+
'service_url': 'http://nvmf:port'
143+
}]
144+
}
145+
)
146+
147+
def test_cli_add_gw_to_existing(self):
148+
# add first gw
149+
self.exec_cmd(
150+
'nvmeof-gateway-add',
151+
name='nvmeof.pool',
152+
inbuf='http://nvmf:port',
153+
daemon_name='nvmeof_daemon',
154+
group=''
155+
)
156+
157+
# add another daemon to the first gateway
158+
self.exec_cmd(
159+
'nvmeof-gateway-add',
160+
name='nvmeof.pool',
161+
inbuf='http://nvmf-2:port',
162+
daemon_name='nvmeof_daemon_2',
163+
group=''
164+
)
165+
166+
config = json.loads(self.get_key('_nvmeof_config'))
167+
168+
# make sure its appended to the existing gateway
169+
self.assertEqual(
170+
config['gateways'], {
171+
'nvmeof.pool': [{
172+
'daemon_name': 'nvmeof_daemon',
173+
'group': '',
174+
'service_url': 'http://nvmf:port'
175+
}, {
176+
'daemon_name': 'nvmeof_daemon_2',
177+
'group': '',
178+
'service_url': 'http://nvmf-2:port'
179+
}]
180+
}
181+
)
182+
183+
def test_cli_add_new_gw(self):
184+
# add first config
185+
self.exec_cmd(
186+
'nvmeof-gateway-add',
187+
name='nvmeof.pool',
188+
inbuf='http://nvmf:port',
189+
daemon_name='nvmeof_daemon',
190+
group=''
191+
)
192+
193+
# add another gateway
194+
self.exec_cmd(
195+
'nvmeof-gateway-add',
196+
name='nvmeof2.pool.group',
197+
inbuf='http://nvmf-2:port',
198+
daemon_name='nvmeof_daemon_2',
199+
group='group'
200+
)
201+
202+
config = json.loads(self.get_key('_nvmeof_config'))
203+
204+
# make sure its added as a new entry
205+
self.assertEqual(
206+
config['gateways'], {
207+
'nvmeof.pool': [{
208+
'daemon_name': 'nvmeof_daemon',
209+
'group': '',
210+
'service_url': 'http://nvmf:port'
211+
}],
212+
'nvmeof2.pool.group': [{
213+
'daemon_name': 'nvmeof_daemon_2',
214+
'group': 'group',
215+
'service_url': 'http://nvmf-2:port'
216+
}]
217+
}
218+
)
219+
220+
def test_cli_rm_gateway(self):
221+
self.test_cli_add_gateway()
222+
self.exec_cmd('nvmeof-gateway-rm', name='nvmeof.pool.group')
223+
224+
config = json.loads(self.get_key('_nvmeof_config'))
225+
self.assertEqual(
226+
config['gateways'], {}
227+
)
228+
229+
def test_cli_rm_daemon_from_gateway(self):
230+
self.test_cli_add_gw_to_existing()
231+
self.exec_cmd(
232+
'nvmeof-gateway-rm',
233+
name='nvmeof.pool',
234+
daemon_name='nvmeof_daemon'
235+
)
236+
237+
config = json.loads(self.get_key('_nvmeof_config'))
238+
self.assertEqual(
239+
config['gateways'], {
240+
'nvmeof.pool': [{
241+
'daemon_name': 'nvmeof_daemon_2',
242+
'group': '',
243+
'service_url': 'http://nvmf-2:port'
244+
}]
245+
}
246+
)
247+
248+
def test_cli_legacy_config_rm(self):
249+
legacy_config = json.dumps({
250+
'gateways': {
251+
'nvmeof.pool': {
252+
'service_url': 'http://nvmf:port'
253+
}
254+
}
255+
})
256+
self.set_key('_nvmeof_config', legacy_config)
257+
258+
self.exec_cmd('nvmeof-gateway-rm', name='nvmeof.pool')
259+
260+
config = json.loads(self.get_key('_nvmeof_config'))
261+
self.assertEqual(
262+
config['gateways'], {}
263+
)

0 commit comments

Comments
 (0)