Skip to content

Commit 47229b8

Browse files
authored
Merge pull request ceph#58860 from adk3798/cephadm-nvmeof-require-group
mgr/cephadm: require "group" parameter in nvmeof specs Reviewed-by: Redouane Kachach <[email protected]>
2 parents 182d803 + 41c5dbe commit 47229b8

File tree

7 files changed

+223
-14
lines changed

7 files changed

+223
-14
lines changed

qa/suites/orch/cephadm/smoke-roleless/2-services/nvmeof.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ tasks:
33
host.a:
44
- ceph osd pool create foo
55
- rbd pool init foo
6-
- ceph orch apply nvmeof foo
6+
- ceph orch apply nvmeof foo default
77
- cephadm.wait_for_service:
8-
service: nvmeof.foo
8+
service: nvmeof.foo.default

src/pybind/mgr/cephadm/inventory.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
from cephadm.services.cephadmservice import CephadmDaemonDeploySpec
2727

2828
from .utils import resolve_ip, SpecialHostLabels
29-
from .migrations import queue_migrate_nfs_spec, queue_migrate_rgw_spec
29+
from .migrations import (
30+
queue_migrate_nfs_spec,
31+
queue_migrate_rgw_spec,
32+
queue_migrate_nvmeof_spec,
33+
)
3034

3135
if TYPE_CHECKING:
3236
from .module import CephadmOrchestrator
@@ -286,6 +290,12 @@ def load(self):
286290
):
287291
queue_migrate_rgw_spec(self.mgr, j)
288292

293+
if (
294+
(self.mgr.migration_current or 0) < 8
295+
and j['spec'].get('service_type') == 'nvmeof'
296+
):
297+
queue_migrate_nvmeof_spec(self.mgr, j)
298+
289299
spec = ServiceSpec.from_json(j['spec'])
290300
created = str_to_datetime(cast(str, j['created']))
291301
self._specs[service_name] = spec

src/pybind/mgr/cephadm/migrations.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
import logging
44
from typing import TYPE_CHECKING, Iterator, Optional, Dict, Any, List
55

6-
from ceph.deployment.service_spec import PlacementSpec, ServiceSpec, HostPlacementSpec, RGWSpec
6+
from ceph.deployment.service_spec import (
7+
PlacementSpec,
8+
ServiceSpec,
9+
HostPlacementSpec,
10+
RGWSpec,
11+
NvmeofServiceSpec,
12+
)
713
from cephadm.schedule import HostAssignment
814
from cephadm.utils import SpecialHostLabels
915
import rados
@@ -14,7 +20,7 @@
1420
if TYPE_CHECKING:
1521
from .module import CephadmOrchestrator
1622

17-
LAST_MIGRATION = 7
23+
LAST_MIGRATION = 8
1824

1925
logger = logging.getLogger(__name__)
2026

@@ -41,6 +47,9 @@ def __init__(self, mgr: "CephadmOrchestrator"):
4147
r = mgr.get_store('rgw_migration_queue')
4248
self.rgw_migration_queue = json.loads(r) if r else []
4349

50+
n = mgr.get_store('nvmeof_migration_queue')
51+
self.nvmeof_migration_queue = json.loads(n) if n else []
52+
4453
# for some migrations, we don't need to do anything except for
4554
# incrementing migration_current.
4655
# let's try to shortcut things here.
@@ -109,6 +118,10 @@ def migrate(self, startup: bool = False) -> None:
109118
if self.migrate_6_7():
110119
self.set(7)
111120

121+
if self.mgr.migration_current == 7:
122+
if self.migrate_7_8():
123+
self.set(8)
124+
112125
def migrate_0_1(self) -> bool:
113126
"""
114127
Migration 0 -> 1
@@ -442,6 +455,51 @@ def migrate_6_7(self) -> bool:
442455
# was set to true. Therefore we have nothing to migrate for those daemons
443456
return True
444457

458+
def migrate_nvmeof_spec(self, spec: Dict[Any, Any], migration_counter: int) -> Optional[NvmeofServiceSpec]:
459+
""" Add value for group parameter to nvmeof spec """
460+
new_spec = spec.copy()
461+
# Note: each spec must have a different group name so we append
462+
# the value of a counter to the end
463+
new_spec['spec']['group'] = f'default{str(migration_counter + 1)}'
464+
return NvmeofServiceSpec.from_json(new_spec)
465+
466+
def nvmeof_spec_needs_migration(self, spec: Dict[Any, Any]) -> bool:
467+
spec = spec.get('spec', None)
468+
return (bool(spec) and spec.get('group', None) is None)
469+
470+
def migrate_7_8(self) -> bool:
471+
"""
472+
Add a default value for the "group" parameter to nvmeof specs that don't have one
473+
"""
474+
self.mgr.log.debug(f'Starting nvmeof migration (queue length is {len(self.nvmeof_migration_queue)})')
475+
migrated_spec_counter = 0
476+
for s in self.nvmeof_migration_queue:
477+
spec = s['spec']
478+
if self.nvmeof_spec_needs_migration(spec):
479+
nvmeof_spec = self.migrate_nvmeof_spec(spec, migrated_spec_counter)
480+
if nvmeof_spec is not None:
481+
logger.info(f"Added group 'default{migrated_spec_counter + 1}' to nvmeof spec {spec}")
482+
self.mgr.spec_store.save(nvmeof_spec)
483+
migrated_spec_counter += 1
484+
else:
485+
logger.info(f"No Migration is needed for nvmeof spec: {spec}")
486+
self.nvmeof_migration_queue = []
487+
return True
488+
489+
490+
def queue_migrate_nvmeof_spec(mgr: "CephadmOrchestrator", spec_dict: Dict[Any, Any]) -> None:
491+
"""
492+
group has become a required field for nvmeof specs and has been added
493+
to spec validation. We need to assign a default group to nvmeof specs
494+
that don't have one
495+
"""
496+
service_id = spec_dict['spec']['service_id']
497+
queued = mgr.get_store('nvmeof_migration_queue') or '[]'
498+
ls = json.loads(queued)
499+
ls.append(spec_dict)
500+
mgr.set_store('nvmeof_migration_queue', json.dumps(ls))
501+
mgr.log.info(f'Queued nvmeof.{service_id} for migration')
502+
445503

446504
def queue_migrate_rgw_spec(mgr: "CephadmOrchestrator", spec_dict: Dict[Any, Any]) -> None:
447505
"""

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ def daemon_check_post(self, daemon_descrs: List[DaemonDescription]) -> None:
9292
""" Overrides the daemon_check_post to add nvmeof gateways safely
9393
"""
9494
self.mgr.log.info(f"nvmeof daemon_check_post {daemon_descrs}")
95-
spec = cast(NvmeofServiceSpec,
96-
self.mgr.spec_store.all_specs.get(daemon_descrs[0].service_name(), None))
97-
if not spec:
98-
self.mgr.log.error(f'Failed to find spec for {daemon_descrs[0].name()}')
99-
return
100-
pool = spec.pool
101-
group = spec.group
10295
for dd in daemon_descrs:
96+
spec = cast(NvmeofServiceSpec,
97+
self.mgr.spec_store.all_specs.get(dd.service_name(), None))
98+
if not spec:
99+
self.mgr.log.error(f'Failed to find spec for {dd.name()}')
100+
return
101+
pool = spec.pool
102+
group = spec.group
103103
# Notify monitor about this gateway creation
104104
cmd = {
105105
'prefix': 'nvme-gw create',

src/pybind/mgr/cephadm/tests/test_migration.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,139 @@ def test_migrate_cert_store(cephadm_module: CephadmOrchestrator):
382382
assert cephadm_module.cert_key_store.get_cert('grafana_cert', host='host2')
383383
assert cephadm_module.cert_key_store.get_key('grafana_key', host='host1')
384384
assert cephadm_module.cert_key_store.get_key('grafana_key', host='host2')
385+
386+
387+
@pytest.mark.parametrize(
388+
"nvmeof_spec_store_entries, should_migrate_specs, expected_groups",
389+
[
390+
(
391+
[
392+
{'spec': {
393+
'service_type': 'nvmeof',
394+
'service_name': 'nvmeof.foo',
395+
'service_id': 'foo',
396+
'placement': {
397+
'hosts': ['host1']
398+
},
399+
'spec': {
400+
'pool': 'foo',
401+
},
402+
},
403+
'created': datetime_to_str(datetime_now())},
404+
{'spec': {
405+
'service_type': 'nvmeof',
406+
'service_name': 'nvmeof.bar',
407+
'service_id': 'bar',
408+
'placement': {
409+
'hosts': ['host2']
410+
},
411+
'spec': {
412+
'pool': 'bar',
413+
},
414+
},
415+
'created': datetime_to_str(datetime_now())}
416+
],
417+
[True, True], ['default1', 'default2']),
418+
(
419+
[
420+
{'spec':
421+
{
422+
'service_type': 'nvmeof',
423+
'service_name': 'nvmeof.foo',
424+
'service_id': 'foo',
425+
'placement': {
426+
'hosts': ['host1']
427+
},
428+
'spec': {
429+
'pool': 'foo',
430+
},
431+
},
432+
'created': datetime_to_str(datetime_now())},
433+
{'spec':
434+
{
435+
'service_type': 'nvmeof',
436+
'service_name': 'nvmeof.bar',
437+
'service_id': 'bar',
438+
'placement': {
439+
'hosts': ['host2']
440+
},
441+
'spec': {
442+
'pool': 'bar',
443+
'group': 'bar'
444+
},
445+
},
446+
'created': datetime_to_str(datetime_now())},
447+
{'spec':
448+
{
449+
'service_type': 'nvmeof',
450+
'service_name': 'nvmeof.testing_testing',
451+
'service_id': 'testing_testing',
452+
'placement': {
453+
'label': 'baz'
454+
},
455+
'spec': {
456+
'pool': 'baz',
457+
},
458+
},
459+
'created': datetime_to_str(datetime_now())}
460+
], [True, False, True], ['default1', 'bar', 'default2']
461+
),
462+
]
463+
)
464+
@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]'))
465+
def test_migrate_nvmeof_spec(
466+
cephadm_module: CephadmOrchestrator,
467+
nvmeof_spec_store_entries,
468+
should_migrate_specs,
469+
expected_groups
470+
):
471+
with with_host(cephadm_module, 'host1'):
472+
for spec in nvmeof_spec_store_entries:
473+
cephadm_module.set_store(
474+
SPEC_STORE_PREFIX + spec['spec']['service_name'],
475+
json.dumps(spec, sort_keys=True),
476+
)
477+
478+
# make sure nvmeof_migration_queue is populated accordingly
479+
cephadm_module.migration_current = 1
480+
cephadm_module.spec_store.load()
481+
ls = json.loads(cephadm_module.get_store('nvmeof_migration_queue'))
482+
assert all([s['spec']['service_type'] == 'nvmeof' for s in ls])
483+
# shortcut nvmeof_migration_queue loading by directly assigning
484+
# ls output to nvmeof_migration_queue list
485+
cephadm_module.migration.nvmeof_migration_queue = ls
486+
487+
# skip other migrations and go directly to 7_8 migration (nvmeof spec)
488+
cephadm_module.migration_current = 7
489+
cephadm_module.migration.migrate()
490+
assert cephadm_module.migration_current == LAST_MIGRATION
491+
492+
print(cephadm_module.spec_store.all_specs)
493+
494+
for i in range(len(nvmeof_spec_store_entries)):
495+
nvmeof_spec_store_entry = nvmeof_spec_store_entries[i]
496+
should_migrate = should_migrate_specs[i]
497+
expected_group = expected_groups[i]
498+
499+
service_name = nvmeof_spec_store_entry['spec']['service_name']
500+
service_id = nvmeof_spec_store_entry['spec']['service_id']
501+
placement = nvmeof_spec_store_entry['spec']['placement']
502+
pool = nvmeof_spec_store_entry['spec']['spec']['pool']
503+
504+
if should_migrate:
505+
assert service_name in cephadm_module.spec_store.all_specs
506+
nvmeof_spec = cephadm_module.spec_store.all_specs[service_name]
507+
nvmeof_spec_json = nvmeof_spec.to_json()
508+
assert nvmeof_spec_json['service_type'] == 'nvmeof'
509+
assert nvmeof_spec_json['service_id'] == service_id
510+
assert nvmeof_spec_json['service_name'] == service_name
511+
assert nvmeof_spec_json['placement'] == placement
512+
assert nvmeof_spec_json['spec']['pool'] == pool
513+
# make sure spec has the "group" parameter set to "default<counter>"
514+
assert nvmeof_spec_json['spec']['group'] == expected_group
515+
else:
516+
nvmeof_spec = cephadm_module.spec_store.all_specs[service_name]
517+
nvmeof_spec_json = nvmeof_spec.to_json()
518+
assert nvmeof_spec_json['spec']['pool'] == pool
519+
# make sure spec has the "group" parameter still set to its old value
520+
assert nvmeof_spec_json['spec']['group'] == expected_group

src/pybind/mgr/orchestrator/module.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1831,6 +1831,7 @@ def _apply_mgmt_gateway(self,
18311831
@_cli_write_command('orch apply nvmeof')
18321832
def _apply_nvmeof(self,
18331833
pool: str,
1834+
group: str,
18341835
placement: Optional[str] = None,
18351836
unmanaged: bool = False,
18361837
dry_run: bool = False,
@@ -1842,8 +1843,9 @@ def _apply_nvmeof(self,
18421843
raise OrchestratorValidationError('unrecognized command -i; -h or --help for usage')
18431844

18441845
spec = NvmeofServiceSpec(
1845-
service_id=pool,
1846+
service_id=f'{pool}.{group}',
18461847
pool=pool,
1848+
group=group,
18471849
placement=PlacementSpec.from_string(placement),
18481850
unmanaged=unmanaged,
18491851
preview_only=dry_run

src/python-common/ceph/deployment/service_spec.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1381,7 +1381,7 @@ def __init__(self,
13811381
#: ``name`` name of the nvmeof gateway
13821382
self.name = name
13831383
#: ``group`` name of the nvmeof gateway
1384-
self.group = group or ''
1384+
self.group = group
13851385
#: ``enable_auth`` enables user authentication on nvmeof gateway
13861386
self.enable_auth = enable_auth
13871387
#: ``state_update_notify`` enables automatic update from OMAP in nvmeof gateway
@@ -1473,6 +1473,9 @@ def validate(self) -> None:
14731473
if not self.pool:
14741474
raise SpecValidationError('Cannot add NVMEOF: No Pool specified')
14751475

1476+
if not self.group:
1477+
raise SpecValidationError('Cannot add NVMEOF: No group specified')
1478+
14761479
if self.enable_auth:
14771480
if not all([self.server_key, self.server_cert, self.client_key,
14781481
self.client_cert, self.root_ca_cert]):

0 commit comments

Comments
 (0)