From a9e2928e1dcce71e20c333b299ee08852fe2fde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Sl=C3=A1vik?= Date: Thu, 10 Nov 2022 17:49:36 +0100 Subject: [PATCH 1/5] Install nvme-cli on boot.iso Install external tooling for managing NVMe-FC devices in the installation environment. The utilities are needed to manipulate NVM-Express devices, in this case mainly because of NVMe over Fabrics. Resolves: rhbz#2107346 (cherry picked from commit 369d83ab4192ebab061fad695b626176013cdd12) --- anaconda.spec.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/anaconda.spec.in b/anaconda.spec.in index 9bf435bd12d..13e518d3592 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -217,6 +217,8 @@ Requires: f2fs-tools Requires: xfsprogs Requires: dosfstools Requires: e2fsprogs +# External tooling for managing NVMe-FC devices in the installation environment +Recommends: nvme-cli %description install-env-deps The anaconda-install-env-deps metapackage lists all installation environment @@ -260,6 +262,8 @@ Requires: rpm-ostree >= %{rpmostreever} Requires: ostree # used by ostree command for native containers Requires: skopeo +# External tooling for managing NVMe-FC devices in the installation environment +Requires: nvme-cli %description install-img-deps The anaconda-install-img-deps metapackage lists all boot.iso installation From 4e78f3e5cb0c637cdd6d92e3a2c614ff4ce828d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Sl=C3=A1vik?= Date: Thu, 3 Nov 2022 15:41:44 +0100 Subject: [PATCH 2/5] Filter out NVMe over Fibre Channel from local disks Resolves: rhbz#2107346 (cherry picked from commit 4b16bc5848d832b1ae644c595bfa65d3d9e3042d) --- pyanaconda/ui/lib/storage.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyanaconda/ui/lib/storage.py b/pyanaconda/ui/lib/storage.py index 9b6cefee0ac..7c8c4e40bb7 100644 --- a/pyanaconda/ui/lib/storage.py +++ b/pyanaconda/ui/lib/storage.py @@ -322,14 +322,15 @@ def is_local_disk(device_type): While technically local disks, zFCP and NVDIMM devices are advanced storage and should not be considered local. - :param device_type: a device type - :return: True or False + :param str device_type: a device type + :return bool: True or False """ return device_type not in ( "dm-multipath", "iscsi", "fcoe", "zfcp", + "nvme-fabrics", ) From 4da1322bc8e52c696bd0dd73b112e7e07228dc1a Mon Sep 17 00:00:00 2001 From: Vendula Poncova Date: Mon, 2 Jan 2023 18:28:39 +0100 Subject: [PATCH 3/5] Update the .glade file for the Advanced Storage screen Apply changes generated by Glade 3.40.0. Related: rhbz#2107346 (cherry picked from commit f63b0edee226231abffccfaaaecdf134576d87e5) --- .../ui/gui/spokes/advanced_storage.glade | 425 +++++++++--------- 1 file changed, 213 insertions(+), 212 deletions(-) diff --git a/pyanaconda/ui/gui/spokes/advanced_storage.glade b/pyanaconda/ui/gui/spokes/advanced_storage.glade index 6c40d4e77f3..912b96c0e21 100644 --- a/pyanaconda/ui/gui/spokes/advanced_storage.glade +++ b/pyanaconda/ui/gui/spokes/advanced_storage.glade @@ -1,5 +1,5 @@ - + @@ -46,39 +46,40 @@ - diskStore + diskStore - diskStore + diskStore - diskStore + diskStore - diskStore + diskStore - False + False True True - INSTALLATION DESTINATION + INSTALLATION DESTINATION - False + False vertical 6 - False + False + - False - 6 - 6 - 18 - 18 - 6 + False + 6 + 6 + 18 + 18 + 6 @@ -90,18 +91,18 @@ - False + False True True 0 0 - 6 - 6 - 6 + 6 + 6 + 6 - False - 6 + False + 6 True True vertical @@ -109,30 +110,30 @@ True - True + True True True - False - 6 - 6 - 12 + False + 6 + 6 + 12 vertical 6 True - False + False 6 True - False + False Search _By: - True - searchTypeCombo + True + searchTypeCombo 0 @@ -144,7 +145,7 @@ True - False + False None Port / Target / LUN # @@ -161,15 +162,15 @@ True - False - True + False + True True - False - False + False + False True - False + False True @@ -179,17 +180,17 @@ True - False - True + False + True True 6 True - False + False _Port: - True - searchPortCombo + True + searchPortCombo False @@ -200,7 +201,7 @@ True - False + False @@ -212,10 +213,10 @@ True - False + False _Target: - True - searchTargetEntry + True + searchTargetEntry False @@ -226,8 +227,8 @@ True - True - edit-clear-symbolic + True + edit-clear-symbolic @@ -240,10 +241,10 @@ True - False + False _LUN: - True - searchLUNEntry + True + searchLUNEntry False @@ -254,8 +255,8 @@ True - True - edit-clear-symbolic + True + edit-clear-symbolic @@ -276,16 +277,16 @@ True - False + False True 6 True - False + False _WWID: - True - searchWWIDEntry + True + searchWWIDEntry False @@ -296,8 +297,8 @@ True - True - edit-clear-symbolic + True + edit-clear-symbolic @@ -332,10 +333,10 @@ True - False + False Search Res_ults: - True - searchTreeView + True + searchTreeView 0 @@ -347,20 +348,20 @@ True - True + True True True - in + in True - True + True True True searchModel - True - False - True + True + False + True @@ -546,35 +547,35 @@ True - False + False Searc_h - True + True - False + False True - False - 6 - 6 - 12 + False + 6 + 6 + 12 vertical 6 True - False + False 6 True - False + False Filter _By: - True - multipathTypeCombo + True + multipathTypeCombo 0 @@ -586,7 +587,7 @@ True - False + False None Vendor @@ -604,15 +605,15 @@ True - False - True + False + True True - False - False + False + False True - False + False True @@ -622,16 +623,16 @@ True - False + False True 6 True - False + False Show Only _Devices From: - True - multipathVendorCombo + True + multipathVendorCombo False @@ -642,7 +643,7 @@ True - False + False True @@ -663,16 +664,16 @@ True - False + False True 6 True - False + False Show Only _Devices With: - True - multipathInterconnectCombo + True + multipathInterconnectCombo False @@ -683,7 +684,7 @@ True - False + False True @@ -704,16 +705,16 @@ True - False + False True 6 True - False + False Show Only _Devices Containing: - True - multipathWWIDEntry + True + multipathWWIDEntry False @@ -724,9 +725,9 @@ True - True + True True - edit-clear-symbolic + edit-clear-symbolic @@ -761,20 +762,20 @@ True - True + True True True - in + in True - True + True True True multipathModel - True - False - True + True + False + True @@ -879,36 +880,36 @@ True - False + False _Multipath Devices - True + True 1 - False + False True - False - 6 - 6 - 12 + False + 6 + 6 + 12 vertical 6 True - False + False 6 True - False + False Filter _By: - True - otherTypeCombo + True + otherTypeCombo 0 @@ -920,7 +921,7 @@ True - False + False None Vendor @@ -938,15 +939,15 @@ True - False - True + False + True True - False - False + False + False True - False + False True @@ -956,16 +957,16 @@ True - False + False True 6 True - False + False Show Only _Devices From: - True - otherVendorCombo + True + otherVendorCombo False @@ -976,7 +977,7 @@ True - False + False True @@ -997,16 +998,16 @@ True - False + False True 6 True - False + False Show Only _Devices With: - True - otherInterconnectCombo + True + otherInterconnectCombo False @@ -1017,7 +1018,7 @@ True - False + False True @@ -1038,16 +1039,16 @@ True - False + False True 6 True - False + False Show Only _Devices Containing: - True - otherIDEntry + True + otherIDEntry False @@ -1058,9 +1059,9 @@ True - True + True True - edit-clear-symbolic + edit-clear-symbolic @@ -1095,20 +1096,20 @@ True - True + True True True - in + in True - True + True True True otherModel - True - False - True + True + False + True @@ -1199,36 +1200,36 @@ True - False + False _Other SAN Devices - True + True 2 - False + False True - False - 6 - 6 - 12 + False + 6 + 6 + 12 vertical 6 True - False + False 6 True - False + False Filter B_y: - True - zTypeCombo + True + zTypeCombo 0 @@ -1240,7 +1241,7 @@ True - False + False None CCW @@ -1258,15 +1259,15 @@ True - False - True + False + True True - False - False + False + False True - False + False True @@ -1276,15 +1277,15 @@ True - False + False 6 True - False + False _CCW: - True - zCCWEntry + True + zCCWEntry False @@ -1295,9 +1296,9 @@ True - True + True True - edit-clear-symbolic + edit-clear-symbolic @@ -1318,16 +1319,16 @@ True - False + False True 6 True - False + False _WWPN: - True - zWWPNEntry + True + zWWPNEntry False @@ -1338,9 +1339,9 @@ True - True + True True - edit-clear-symbolic + edit-clear-symbolic @@ -1361,16 +1362,16 @@ True - False + False True 6 True - False + False _LUN: - True - zLUNEntry + True + zLUNEntry False @@ -1381,9 +1382,9 @@ True - True + True True - edit-clear-symbolic + edit-clear-symbolic @@ -1418,20 +1419,20 @@ True - True + True True True - in + in True - True + True True True zModel - True - False - True + True + False + True @@ -1564,13 +1565,13 @@ True - False + False _zSeries Devices - True + True 4 - False + False @@ -1583,16 +1584,16 @@ True - False + False 6 - end + end _Add zFCP LUN... True - True - True - True + True + True + True @@ -1605,9 +1606,9 @@ Add EC_KD DASD... True - True - True - True + True + True + True @@ -1620,9 +1621,9 @@ Add _iSCSI Target... True - True - True - True + True + True + True @@ -1635,9 +1636,9 @@ Add FCo_E SAN... True - True - True - True + True + True + True @@ -1650,15 +1651,15 @@ Refresh _List True - True - True - True + True + True + True False True - 5 + 4 @@ -1671,9 +1672,9 @@ True - True - False - True + True + False + True start none 0 @@ -1681,9 +1682,9 @@ True - False + False %d _storage devices selected - True + True From d40cb44ee92ace90f8e683e2bec90cb5ca40adcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Sl=C3=A1vik?= Date: Wed, 9 Nov 2022 11:54:00 +0100 Subject: [PATCH 4/5] Add a NVMe-FC tab to the Advanced Storage screen Resolves: rhbz#2107346 (cherry picked from commit bd0c061c57149a89788740d0dfe657ec43304290) --- .../modules/common/structures/storage.py | 9 + .../modules/storage/devicetree/viewer.py | 34 ++ .../ui/gui/spokes/advanced_storage.glade | 512 +++++++++++++++++- pyanaconda/ui/gui/spokes/advanced_storage.py | 124 ++++- .../storage/test_module_device_tree.py | 96 +++- 5 files changed, 749 insertions(+), 26 deletions(-) diff --git a/pyanaconda/modules/common/structures/storage.py b/pyanaconda/modules/common/structures/storage.py index 9bbaba96d8b..7e6085e2b75 100644 --- a/pyanaconda/modules/common/structures/storage.py +++ b/pyanaconda/modules/common/structures/storage.py @@ -181,6 +181,15 @@ def attrs(self) -> Dict[Str, Str]: target path-id + Attributes for NVMe Fabrics: + nsid + eui64 + nguid + controllers-id + transports-type + transports-address + subsystems-nqn + Attributes for ZFCP: fcp-lun wwpn diff --git a/pyanaconda/modules/storage/devicetree/viewer.py b/pyanaconda/modules/storage/devicetree/viewer.py index 3541f85fad9..6b1e6d090f9 100644 --- a/pyanaconda/modules/storage/devicetree/viewer.py +++ b/pyanaconda/modules/storage/devicetree/viewer.py @@ -18,6 +18,7 @@ # Red Hat, Inc. # from abc import abstractmethod, ABC +from functools import partial from blivet.formats import get_format from blivet.size import Size @@ -104,6 +105,8 @@ def get_device_data(self, name): self._set_device_data_fcoe(device, data) elif device.type == "iscsi": self._set_device_data_iscsi(device, data) + elif device.type == "nvme-fabrics": + self._set_device_data_nvme_fabrics(device, data) elif device.type == "zfcp": self._set_device_data_zfcp(device, data) @@ -150,6 +153,18 @@ def _set_device_data_iscsi(self, device, data): data.attrs["target"] = self._get_attribute(device, "target") data.attrs["path-id"] = self._get_attribute(device, "id_path") + def _set_device_data_nvme_fabrics(self, device, data): + """Set data for an NVMe Fabrics device.""" + data.attrs["nsid"] = self._get_attribute(device, "nsid") + data.attrs["eui64"] = self._get_attribute(device, "eui64") + data.attrs["nguid"] = self._get_attribute(device, "nguid") + + get_attrs = partial(self._get_attribute_list, device.controllers) + data.attrs["controllers-id"] = get_attrs("id") + data.attrs["transports-type"] = get_attrs("transport") + data.attrs["transports-address"] = get_attrs("transport_address") + data.attrs["subsystems-nqn"] = get_attrs("subsysnqn") + def _set_device_data_zfcp(self, device, data): """Set data for a ZFCP device.""" data.attrs["fcp-lun"] = self._get_attribute(device, "fcp_lun") @@ -271,6 +286,25 @@ def _get_attribute(self, obj, name): return str(value) + def _get_attribute_list(self, iterable, name): + """Get a list of attributes of the given objects. + + Create a comma-separated list of sorted unique attribute values. + See the _get_attribute method for more info. + + :param iterable: a list of objects + :param name: an attribute name + :return: a string or None + """ + # Collect values. + values = [self._get_attribute(obj, name) for obj in iterable] + + # Skip duplicates and unset values. + values = set(filter(None, values)) + + # Format sorted values if any. + return ", ".join(sorted(values)) or None + def _prune_attributes(self, attrs): """Prune the unset values of attributes. diff --git a/pyanaconda/ui/gui/spokes/advanced_storage.glade b/pyanaconda/ui/gui/spokes/advanced_storage.glade index 912b96c0e21..6fc16a9a5f6 100644 --- a/pyanaconda/ui/gui/spokes/advanced_storage.glade +++ b/pyanaconda/ui/gui/spokes/advanced_storage.glade @@ -1,7 +1,7 @@ - + @@ -43,11 +43,24 @@ + + + + + + + + + + diskStore + + diskStore + diskStore @@ -533,6 +546,76 @@ + + + Controllers + True + + + + 2 + 0 + 19 + + + + + + + Transport + True + + + + 2 + 0 + 20 + + + + + + + Transport address + True + + + + 2 + 0 + 21 + + + + + + + Subsystem NQN + True + + + + 2 + 0 + 22 + + + + + + + Namespace ID + True + + + + 2 + 0 + 23 + + + + @@ -1209,12 +1292,432 @@ False + + + True + False + 12 + vertical + 6 + + + True + False + 6 + + + True + False + Filter B_y: + True + nvmefTypeCombo + 0 + + + False + True + 0 + + + + + True + False + + None + Controller + Transport + Subsystem NQN + Namespace ID + + + + + False + True + 1 + + + + + True + False + True + True + False + False + + + True + False + True + + + + + True + False + 6 + + + True + False + _Controller ID: + True + nvmefControllerEntry + + + False + True + 0 + + + + + True + True + True + edit-clear-symbolic + + + + + True + True + 1 + + + + + 1 + + + + + True + False + 6 + + + True + False + _Type: + True + nvmefTransportCombo + + + False + True + 0 + + + + + True + False + + + + False + True + 1 + + + + + True + False + _Address: + True + nvmefTransportAddressEntry + + + False + True + 2 + + + + + True + True + True + edit-clear-symbolic + + + + + True + True + 3 + + + + + 2 + + + + + True + False + 6 + + + True + False + _Subsystem NQN: + True + nvmefSubsystemNqnEntry + + + False + True + 0 + + + + + True + True + True + edit-clear-symbolic + + + + + True + True + 1 + + + + + 3 + + + + + True + False + 6 + + + True + False + _EUI-64 / NGUID / UUID: + True + nvmefNamespaceIdEntry + + + False + True + 0 + + + + + True + True + True + edit-clear-symbolic + + + + + True + True + 1 + + + + + 4 + + + + + + + + + + + True + True + 2 + + + + + False + True + 0 + + + + + True + True + True + True + in + + + True + True + True + True + nvmefModel + True + False + 0 + True + + + + + + + + + + + 2 + 0 + 1 + + + + + + + Namespace + True + + + + 2 + 0 + 17 + + + + + + + Capacity + True + + + + 2 + 0 + 6 + + + + + + + Controllers + True + + + + 2 + 0 + 19 + + + + + + + Transport + True + + + + 2 + 0 + 20 + + + + + + + Transport address + True + + + + 2 + 0 + 21 + + + + + + + Subsystem NQN + True + + + + 2 + 0 + 22 + + + + + + + Namespace ID + True + + + + 2 + 0 + 23 + + + + + + + + + True + True + 1 + + + + + 3 + + + + + True + False + NVMe _Fabrics Devices + True + + + 3 + False + + True False - 6 - 6 12 vertical 6 @@ -1432,9 +1935,10 @@ zModel True False + 0 True - + diff --git a/pyanaconda/ui/gui/spokes/advanced_storage.py b/pyanaconda/ui/gui/spokes/advanced_storage.py index 6822354875e..c1d2316d79f 100644 --- a/pyanaconda/ui/gui/spokes/advanced_storage.py +++ b/pyanaconda/ui/gui/spokes/advanced_storage.py @@ -49,14 +49,19 @@ PAGE_SEARCH = 0 PAGE_MULTIPATH = 1 PAGE_OTHER = 2 -PAGE_Z = 3 +PAGE_NVMEFABRICS = 3 +PAGE_Z = 4 +# The Z page must be last = highest number, because it is dynamically removed, which would reorder +# the items and invalidate the indices hardcoded here. DiskStoreRow = namedtuple("DiskStoreRow", [ "visible", "selected", "mutable", "name", "type", "model", "capacity", "vendor", "interconnect", "serial", "wwid", "paths", "port", "target", - "lun", "ccw", "wwpn", "namespace", "mode" + "lun", "ccw", "wwpn", "namespace", "mode", + "controllers", "transport", "transport_address", + "subsystem_nqn", "namespace_id" ]) @@ -68,26 +73,40 @@ def create_row(device_data, selected, mutable): :param mutable: False if the device is protected, otherwise True :return: an instance of DiskStoreRow """ + device = device_data + attrs = device_data.attrs + + controller_ids = attrs.get("controllers-id", "").split(", ") + transports_type = attrs.get("transports-type", "").split(", ") + transports_address = attrs.get("transports-address", "").split(", ") + subsystems_nqn = attrs.get("subsystems-nqn", "").split(", ") + namespace_ids = list(filter(None, map(attrs.get, ["eui64", "nguid", "uuid"]))) + return DiskStoreRow( visible=True, selected=selected, - mutable=mutable and not device_data.protected, - name=device_data.name, - type=device_data.type, - model=device_data.attrs.get("model", ""), - capacity=str(Size(device_data.size)), - vendor=device_data.attrs.get("vendor", ""), - interconnect=device_data.attrs.get("bus", ""), - serial=device_data.attrs.get("serial", ""), - wwid=device_data.attrs.get("path-id", "") or device_data.attrs.get("wwn", ""), - paths="\n".join(device_data.parents), - port=device_data.attrs.get("port", ""), - target=device_data.attrs.get("target", ""), - lun=device_data.attrs.get("lun", "") or device_data.attrs.get("fcp-lun", ""), - ccw=device_data.attrs.get("hba-id", ""), - wwpn=device_data.attrs.get("wwpn", ""), - namespace=device_data.attrs.get("namespace", ""), - mode=device_data.attrs.get("mode", "") + mutable=mutable and not device.protected, + name=device.name, + type=device.type, + model=attrs.get("model", ""), + capacity=str(Size(device.size)), + vendor=attrs.get("vendor", ""), + interconnect=attrs.get("bus", ""), + serial=attrs.get("serial", ""), + wwid=attrs.get("path-id", ""), + paths="\n".join(device.parents), + port=attrs.get("port", ""), + target=attrs.get("target", ""), + lun=attrs.get("lun", "") or attrs.get("fcp-lun", ""), + ccw=attrs.get("hba-id", ""), + wwpn=attrs.get("wwpn", ""), + namespace=attrs.get("namespace", "") or attrs.get("nsid", ""), + mode=attrs.get("mode", ""), + controllers="\n".join(controller_ids), + transport="\n".join(transports_type), + transport_address="\n".join(transports_address), + subsystem_nqn="\n".join(subsystems_nqn), + namespace_id="\n".join(namespace_ids), ) @@ -429,13 +448,73 @@ def _filter_func(self, filter_by, row): return False +class NVMeFabricsPage(FilterPage): + # Match these to nvmefTypeCombo ids in glade + SEARCH_TYPE_CONTROLLER = 'Controller' + SEARCH_TYPE_TRANSPORT = 'Transport' + SEARCH_TYPE_SUBSYSTEM_NQN = 'Subsystem NQN' + SEARCH_TYPE_NAMESPACE_ID = 'Namespace ID' + + def __init__(self, builder): + super().__init__(builder, "nvmefModel", "nvmefTypeCombo") + self._controller_entry = self._builder.get_object("nvmefControllerEntry") + self._transport_combo = self._builder.get_object("nvmefTransportCombo") + self._address_entry = self._builder.get_object("nvmefTransportAddressEntry") + self._subsystem_nqn_entry = self._builder.get_object("nvmefSubsystemNqnEntry") + self._namespace_id_entry = self._builder.get_object("nvmefNamespaceIdEntry") + + def is_member(self, device_type): + return device_type == "nvme-fabrics" + + def setup(self, store, disks, selected_names, protected_names): + transports = set() + + for device_data in disks: + row = create_row( + device_data, + device_data.name in selected_names, + device_data.name not in protected_names, + ) + store.append([*row]) + transports.update(row.transport.split("\n")) + + self._setup_combo(self._transport_combo, transports) + self._transport_combo.set_active(0) + self._setup_search_type() + + def clear(self): + self._controller_entry.set_text("") + self._transport_combo.set_active(0) + self._address_entry.set_text("") + self._subsystem_nqn_entry.set_text("") + self._namespace_id_entry.set_text("") + + def _filter_func(self, filter_by, row): + if filter_by == self.SEARCH_TYPE_CONTROLLER: + return self._controller_entry.get_text().strip() in row.controllers + + if filter_by == self.SEARCH_TYPE_TRANSPORT: + transports = [""] + row.transport.split("\n") + + return self._transport_combo.get_active_text() in transports \ + and self._address_entry.get_text().strip() in row.transport_address + + if filter_by == self.SEARCH_TYPE_SUBSYSTEM_NQN: + return self._subsystem_nqn_entry.get_text().strip() in row.subsystem_nqn + + if filter_by == self.SEARCH_TYPE_NAMESPACE_ID: + return self._namespace_id_entry.get_text().strip() in row.namespace_id + + return False + + class FilterSpoke(NormalSpoke): """ .. inheritance-diagram:: FilterSpoke :parts: 3 """ builderObjects = ["diskStore", "filterWindow", - "searchModel", "multipathModel", "otherModel", "zModel"] + "searchModel", "multipathModel", "otherModel", "zModel", "nvmefModel"] mainWidgetName = "filterWindow" uiFile = "spokes/advanced_storage.glade" category = SystemCategory @@ -483,6 +562,7 @@ def initialize(self): PAGE_SEARCH: SearchPage(self.builder), PAGE_MULTIPATH: MultipathPage(self.builder), PAGE_OTHER: OtherPage(self.builder), + PAGE_NVMEFABRICS: NVMeFabricsPage(self.builder), PAGE_Z: ZPage(self.builder), } @@ -676,6 +756,10 @@ def on_z_type_combo_changed(self, combo): self._set_notebook_page("zTypeNotebook", combo.get_active()) self._refilter_current_page() + def on_nvmef_type_combo_changed(self, combo): + self._set_notebook_page("nvmefTypeNotebook", combo.get_active()) + self._refilter_current_page() + def _set_notebook_page(self, notebook_name, page_index): notebook = self.builder.get_object(notebook_name) notebook.set_current_page(page_index) diff --git a/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py b/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py index d476fb11a11..f620744c5de 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py +++ b/tests/unit_tests/pyanaconda_tests/modules/storage/test_module_device_tree.py @@ -22,11 +22,11 @@ import pytest from unittest.mock import patch, Mock, PropertyMock - from tests.unit_tests.pyanaconda_tests import patch_dbus_publish_object, check_task_creation from blivet.devices import StorageDevice, DiskDevice, DASDDevice, ZFCPDiskDevice, PartitionDevice, \ - LUKSDevice, iScsiDiskDevice, FcoeDiskDevice, OpticalDevice + LUKSDevice, iScsiDiskDevice, FcoeDiskDevice, OpticalDevice, NVMeFabricsNamespaceDevice +from blivet.devices.disk import NVMeController from blivet.errors import StorageError, FSError from blivet.formats import get_format, device_formats, DeviceFormat from blivet.formats.fs import FS, Iso9660FS @@ -70,6 +70,46 @@ def _add_device(self, device): """Add a device to the device tree.""" self.storage.devicetree._add_device(device) + def test_get_attribute(self): + """Test the _get_attribute method.""" + value = None + assert self.module._get_attribute(value, "x") is None + + value = Mock(x=None) + assert self.module._get_attribute(value, "x") is None + + value = Mock(x="") + assert self.module._get_attribute(value, "x") is None + + value = Mock(x=0) + assert self.module._get_attribute(value, "x") == "0" + + value = Mock(x=1) + assert self.module._get_attribute(value, "x") == "1" + + value = Mock(x="test") + assert self.module._get_attribute(value, "x") == "test" + + def test_get_attribute_list(self): + """Test the _get_attribute_list method.""" + values = [] + assert self.module._get_attribute_list(values, "x") is None + + values = [None, Mock(x=None), Mock(x="")] + assert self.module._get_attribute_list(values, "x") is None + + values = [Mock(x=0)] + assert self.module._get_attribute_list(values, "x") == "0" + + values = [Mock(x=1), Mock(x=0)] + assert self.module._get_attribute_list(values, "x") == "0, 1" + + values = [Mock(x=1), Mock(x=0), Mock(x=2)] + assert self.module._get_attribute_list(values, "x") == "0, 1, 2" + + values = [Mock(x=1), Mock(x=0), Mock(x=2), Mock(x=0), Mock(x=1)] + assert self.module._get_attribute_list(values, "x") == "0, 1, 2" + def test_get_root_device(self): """Test GetRootDevice.""" assert self.interface.GetRootDevice() == "" @@ -228,6 +268,58 @@ def test_get_iscsi_device_data(self): "path-id": "pci-0000:00:00.0-bla-1" }) + def test_get_nvme_fabrics_device_data(self): + """Test GetDeviceData for NVMe Fabrics.""" + dev = NVMeFabricsNamespaceDevice( + "nvme4n1", + fmt=get_format("ext4"), + size=Size("10 GiB"), + nsid=5, + eui64="eui.f04xxxxxxxxx0000000100000001", + nguid="0xf04xxxxxxxxx0000000100000001" + ) + ctrl1 = NVMeController( + id=124, + name='nvme1', + nvme_ver='1.0', + serial='80BgLFM7xMoBLABLABLA', + transport='fc', + transport_address='0x10000000', + subsysnqn='nqn.1992-08.com.example:blabla:subsystem.nvme_4', + ) + ctrl2 = NVMeController( + id=123, + name='nvme2', + nvme_ver='1.0', + serial='80BgLFM7xMoBLABLABLA', + transport='fc', + transport_address='0x20000000', + subsysnqn='nqn.1992-08.com.example:blabla:subsystem.nvme_4', + ) + ctrl3 = NVMeController( + id=126, + name='nvme3', + nvme_ver='1.0', + serial='80BgLFM7xMoBLABLABLA', + transport='fc', + transport_address='0x30000000', + subsysnqn='nqn.1992-08.com.example:blabla:subsystem.nvme_4', + ) + dev._controllers = [ctrl1, ctrl2, ctrl3] + self._add_device(dev) + + data = self.interface.GetDeviceData("nvme4n1") + assert data['type'] == get_variant(Str, 'nvme-fabrics') + assert data['attrs'] == get_variant(Dict[Str, Str], { + "nsid": "5", + "eui64": "eui.f04xxxxxxxxx0000000100000001", + "nguid": "0xf04xxxxxxxxx0000000100000001", + "controllers-id": "123, 124, 126", + "transports-type": "fc", + "transports-address": "0x10000000, 0x20000000, 0x30000000", + "subsystems-nqn": "nqn.1992-08.com.example:blabla:subsystem.nvme_4", + }) + def test_get_zfcp_device_data(self): """Test GetDeviceData for zFCP.""" self._add_device(ZFCPDiskDevice( From c9686d6f616e146e6625745442903b55ea369a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Sl=C3=A1vik?= Date: Thu, 21 Sep 2023 15:54:43 +0200 Subject: [PATCH 5/5] Add release note for NVMe-oF --- docs/release-notes/nvme-over-fabrics.rst | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/release-notes/nvme-over-fabrics.rst diff --git a/docs/release-notes/nvme-over-fabrics.rst b/docs/release-notes/nvme-over-fabrics.rst new file mode 100644 index 00000000000..8fa5c21fe30 --- /dev/null +++ b/docs/release-notes/nvme-over-fabrics.rst @@ -0,0 +1,9 @@ +:Type: Storage +:Summary: NVMe Fabrics support + +:Description: + Anaconda now recognizes NVMe Fabrics drives. These drives are now shown in the + Advanced Storage screen, together with further details. + +:Links: + - https://github.com/rhinstaller/anaconda/pull/4514