Skip to content

Commit 86e736b

Browse files
authored
Merge pull request ceph#63038 from Hezko/remove-map-collection-dec
Remove map collection dec
2 parents 49ba529 + 6323319 commit 86e736b

File tree

4 files changed

+38
-66
lines changed

4 files changed

+38
-66
lines changed

src/pybind/mgr/dashboard/controllers/nvmeof.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
try:
2626
from ..services.nvmeof_client import NVMeoFClient, convert_to_model, \
27-
empty_response, handle_nvmeof_error, map_collection, pick
27+
empty_response, handle_nvmeof_error, pick
2828
except ImportError as e:
2929
logger.error("Failed to import NVMeoFClient and related components: %s", e)
3030
else:
@@ -529,6 +529,11 @@ def delete(
529529
)
530530
)
531531

532+
def _update_hosts(hosts_info_resp):
533+
if hosts_info_resp.get('allow_any_host'):
534+
hosts_info_resp['hosts'].insert(0, {"nqn": "*"})
535+
return hosts_info_resp
536+
532537
@APIRouter("/nvmeof/subsystem/{nqn}/host", Scope.NVME_OF)
533538
@APIDoc("NVMe-oF Subsystem Host Allowlist Management API",
534539
"NVMe-oF Subsystem Host Allowlist")
@@ -540,15 +545,9 @@ class NVMeoFHost(RESTController):
540545
"gw_group": Param(str, "NVMeoF gateway group", True, None),
541546
},
542547
)
548+
@pick('hosts')
543549
@NvmeofCLICommand("nvmeof host list")
544-
@map_collection(
545-
model.Host,
546-
pick="hosts",
547-
# Display the "allow any host" option as another host item
548-
finalize=lambda i, o: [model.Host(nqn="*")._asdict()] + o
549-
if i.allow_any_host
550-
else o,
551-
)
550+
@convert_to_model(model.HostsInfo, finalize=_update_hosts)
552551
@handle_nvmeof_error
553552
def list(self, nqn: str, gw_group: Optional[str] = None, traddr: Optional[str] = None):
554553
return NVMeoFClient(gw_group=gw_group, traddr=traddr).stub.list_hosts(

src/pybind/mgr/dashboard/model/nvmeof.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ class Host(NamedTuple):
140140
nqn: str
141141

142142

143+
class HostsInfo(NamedTuple):
144+
status: int
145+
error_message: str
146+
allow_any_host: bool
147+
subsystem_nqn: str
148+
hosts: List[Host]
149+
150+
143151
class RequestStatus(NamedTuple):
144152
status: int
145153
error_message: str

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

Lines changed: 10 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import functools
22
import logging
3-
from collections.abc import Iterable
43
from typing import Any, Callable, Dict, Generator, List, NamedTuple, Optional, Type
54

65
from ..exceptions import DashboardException
@@ -72,62 +71,9 @@ def __init__(self, gw_group: Optional[str] = None, traddr: Optional[str] = None)
7271
self.channel = grpc.insecure_channel(self.gateway_addr)
7372
self.stub = pb2_grpc.GatewayStub(self.channel)
7473

75-
def make_namedtuple_from_object(cls: Type[NamedTuple], obj: Any) -> NamedTuple:
76-
return cls(
77-
**{
78-
field: getattr(obj, field)
79-
for field in cls._fields
80-
if hasattr(obj, field)
81-
}
82-
) # type: ignore
83-
8474
Model = Dict[str, Any]
85-
86-
def map_model(
87-
model: Type[NamedTuple],
88-
first: Optional[str] = None,
89-
) -> Callable[..., Callable[..., Model]]:
90-
def decorator(func: Callable[..., Message]) -> Callable[..., Model]:
91-
@functools.wraps(func)
92-
def wrapper(*args, **kwargs) -> Model:
93-
message = func(*args, **kwargs)
94-
if first:
95-
try:
96-
message = getattr(message, first)[0]
97-
except IndexError:
98-
raise DashboardException(
99-
msg="Not Found", http_status_code=404, component="nvmeof"
100-
)
101-
102-
return make_namedtuple_from_object(model, message)._asdict()
103-
104-
return wrapper
105-
106-
return decorator
107-
10875
Collection = List[Model]
10976

110-
def map_collection(
111-
model: Type[NamedTuple],
112-
pick: str, # pylint: disable=redefined-outer-name
113-
finalize: Optional[Callable[[Message, Collection], Collection]] = None,
114-
) -> Callable[..., Callable[..., Collection]]:
115-
def decorator(func: Callable[..., Message]) -> Callable[..., Collection]:
116-
@functools.wraps(func)
117-
def wrapper(*args, **kwargs) -> Collection:
118-
message = func(*args, **kwargs)
119-
collection: Iterable = getattr(message, pick)
120-
out = [
121-
make_namedtuple_from_object(model, i)._asdict() for i in collection
122-
]
123-
if finalize:
124-
return finalize(message, out)
125-
return out
126-
127-
return wrapper
128-
129-
return decorator
130-
13177
import errno
13278

13379
NVMeoFError2HTTP = {
@@ -266,21 +212,28 @@ def namedtuple_to_dict(obj):
266212
]
267213
return obj
268214

269-
def convert_to_model(model: Type[NamedTuple]) -> Callable[..., Callable[..., Model]]:
215+
def convert_to_model(model: Type[NamedTuple],
216+
finalize: Optional[Callable[[Dict], Dict]] = None
217+
) -> Callable[..., Callable[..., Model]]:
270218
def decorator(func: Callable[..., Message]) -> Callable[..., Model]:
271219
@functools.wraps(func)
272220
def wrapper(*args, **kwargs) -> Model:
273221
message = func(*args, **kwargs)
274222
msg_dict = MessageToDict(message, including_default_value_fields=True,
275223
preserving_proto_field_name=True)
276-
return namedtuple_to_dict(obj_to_namedtuple(msg_dict, model))
224+
225+
result = namedtuple_to_dict(obj_to_namedtuple(msg_dict, model))
226+
if finalize:
227+
return finalize(result)
228+
return result
277229

278230
return wrapper
279231

280232
return decorator
281233

282234
# pylint: disable-next=redefined-outer-name
283-
def pick(field: str, first: bool = False) -> Callable[..., Callable[..., object]]:
235+
def pick(field: str, first: bool = False,
236+
) -> Callable[..., Callable[..., object]]:
284237
def decorator(func: Callable[..., Dict]) -> Callable[..., object]:
285238
@functools.wraps(func)
286239
def wrapper(*args, **kwargs) -> object:

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,18 @@ def test_empty_model(self, empty_func, disable_message_to_dict):
385385
result = empty_func()
386386
assert result == {}
387387

388+
def test_finalize(self, disable_message_to_dict):
389+
# pylint: disable=unused-argument
390+
def finalizer(output):
391+
output['name'] = output['name'].upper()
392+
return output
393+
394+
@convert_to_model(Boy, finalize=finalizer)
395+
def get_person() -> dict:
396+
return {"name": "Alice", "age": 30}
397+
398+
assert get_person()['name'] == "ALICE"
399+
388400

389401
class TestPick:
390402
def test_basic_field_access(self):

0 commit comments

Comments
 (0)