diff --git a/doc/ledctl.pod b/doc/ledctl.pod index 92ca1d21..3e35a06c 100644 --- a/doc/ledctl.pod +++ b/doc/ledctl.pod @@ -306,6 +306,16 @@ Suppresses default behavior, changes state only on devices listed in CLI. It is Prints information (system path and type) of all controllers detected. +=head2 B<-B> or B<--default-controller> + +There is a possibility that device is supported by more than one controller (e.g. NPEM and VMD). +In that case, this command can be used to query ledctl to return preferred controller type. +The returned controller type is used by subset of routines which do not take controller type +directly (e.g. --get-slot, --list-slots, --set-slot). + +Prints preferred controller type which should be used to control LED for this device. +Must be followed by B<--device>, for which controller will be printed. + =head2 B<-P> or B<--list-slots> Prints all slots for the controller type. Must be followed by B<--controller-type>. diff --git a/src/ledctl/help.c b/src/ledctl/help.c index 71919341..bb284b4a 100644 --- a/src/ledctl/help.c +++ b/src/ledctl/help.c @@ -81,6 +81,12 @@ static struct help_option LIST_CTRL_HELP_OPTS[] = { HELP_OPTION_LOG_LEVEL, }; +static struct help_option DEFAULT_CTRL_HELP_OPTS[] = { + HELP_OPTION_DEVICE, + HELP_OPTION_HELP, + HELP_OPTION_LOG_LEVEL, +}; + struct help_mode { enum opt option_id; struct option *opt; @@ -121,6 +127,9 @@ static struct help_mode modes[] = { HELP_MODE(SET_SLOT, "Set given state for given slot or device under the given controller.\n" "Options \"--slot\" and \"--device\" cannot be used simultaneously."), + + HELP_MODE(DEFAULT_CTRL, + "Print the controller with the highest priority for given device."), }; /** @@ -285,6 +294,8 @@ static struct help_option GENERAL_HELP_OPTS[] = { {NULL, "Print slot details for device/slot.", &longopt_all[OPT_GET_SLOT]}, {NULL, "Indicate IBPI mode, it is used as default.", &longopt_all[OPT_IBPI]}, {NULL, "Display list of controllers recognizable by ledctl.", &longopt_all[OPT_LIST_CTRL]}, + {NULL, "Print the preferred supported controller for device.", + &longopt_all[OPT_DEFAULT_CTRL]}, {NULL, "Print all slots for a controller requested.", &longopt_all[OPT_LIST_SLOTS]}, {NULL, "Set state for slot/device by controller requested.", &longopt_all[OPT_SET_SLOT]} }; diff --git a/src/ledctl/ledctl.c b/src/ledctl/ledctl.c index 7646cc6f..30adc747 100644 --- a/src/ledctl/ledctl.c +++ b/src/ledctl/ledctl.c @@ -162,6 +162,7 @@ static int possible_params_modes[] = { OPT_SET_SLOT, OPT_LIST_SLOTS, OPT_LIST_CTRL, + OPT_DEFAULT_CTRL, OPT_IBPI }; @@ -169,6 +170,11 @@ static int possible_params_list_ctrl[] = { COMMON_GETOPT_ARGS }; +static int possible_params_default_ctrl[] = { + OPT_DEVICE, + COMMON_GETOPT_ARGS +}; + static int possible_params_set_slot[] = { OPT_CNTRL_TYPE, OPT_DEVICE, @@ -541,6 +547,9 @@ void _cmdline_parse_modes(int argc, char *argv[], struct request *req) case 'L': req->chosen_opt = OPT_LIST_CTRL; break; + case 'B': + req->chosen_opt = OPT_DEFAULT_CTRL; + break; case 'I': req->chosen_opt = OPT_IBPI; break; @@ -581,6 +590,10 @@ bool _setup_mode_options(struct request * const req, char **shortopts, struct op setup_options(longopts, shortopts, possible_params_list_ctrl, ARRAY_SIZE(possible_params_list_ctrl)); break; + case OPT_DEFAULT_CTRL: + setup_options(longopts, shortopts, possible_params_default_ctrl, + ARRAY_SIZE(possible_params_default_ctrl)); + break; case OPT_IBPI: setup_options(longopts, shortopts, possible_params_ibpi, ARRAY_SIZE(possible_params_ibpi)); @@ -756,6 +769,13 @@ static led_status_t verify_request(struct led_ctx *ctx, struct request *req) { if (req->chosen_opt == OPT_LIST_CTRL) return LED_STATUS_SUCCESS; + if (req->chosen_opt == OPT_DEFAULT_CTRL) { + if (!req->device[0]) { + log_error("Device is missing, aborting."); + return LED_STATUS_CMDLINE_ERROR; + } else + return LED_STATUS_SUCCESS; + } if (req->cntrl == LED_CNTRL_TYPE_UNKNOWN) { log_error("Invalid controller in the request."); return LED_STATUS_INVALID_CONTROLLER; @@ -858,6 +878,21 @@ led_status_t execute_request(struct led_ctx *ctx, struct request *req) if (req->chosen_opt == OPT_LIST_SLOTS) return list_slots(req->cntrl); + if (req->chosen_opt == OPT_DEFAULT_CTRL) { + enum led_cntrl_type cntrl_type; + char device_path[PATH_MAX]; + + led_device_name_lookup(ctx, req->device, device_path); + cntrl_type = led_is_management_supported(ctx, device_path); + + if (cntrl_type == LED_CNTRL_TYPE_UNKNOWN) + lib_log(ctx, LED_LOG_LEVEL_ERROR, + "Unable to determine controller for device\n"); + else + printf("%s\n", led_cntrl_type_to_string(cntrl_type)); + + return STATUS_SUCCESS; + } if (req->chosen_opt == OPT_LIST_CTRL) { struct led_cntrl_list_entry *cntrl = NULL; diff --git a/src/lib/utils.c b/src/lib/utils.c index a6ee29ba..bad35eb4 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -573,6 +573,7 @@ struct option longopt_all[] = { [OPT_SLOT] = {"slot", required_argument, NULL, 'p'}, [OPT_STATE] = {"state", required_argument, NULL, 's'}, [OPT_PRINT_PARAM] = {"print", required_argument, NULL, 'r'}, + [OPT_DEFAULT_CTRL] = {"default-controller", no_argument, NULL, 'B'}, [OPT_TEST] = {"test", no_argument, NULL, 'T'}, [OPT_IBPI] = {"ibpi", no_argument, NULL, 'I' }, [OPT_NULL_ELEMENT] = {NULL, no_argument, NULL, '\0'} diff --git a/src/lib/utils.h b/src/lib/utils.h index cfecd688..f3b9a054 100644 --- a/src/lib/utils.h +++ b/src/lib/utils.h @@ -472,6 +472,7 @@ enum opt { OPT_STATE, OPT_PRINT_PARAM, OPT_IBPI, + OPT_DEFAULT_CTRL, OPT_TEST, OPT_NULL_ELEMENT }; diff --git a/tests/ledctl/ledctl_cmd.py b/tests/ledctl/ledctl_cmd.py index f6a22e56..12a1b336 100644 --- a/tests/ledctl/ledctl_cmd.py +++ b/tests/ledctl/ledctl_cmd.py @@ -105,6 +105,22 @@ def get_slot_by_device(self, slot: Slot): ]).stdout return self.parse_slot_line(slot.cntrl_type, out) + def get_slot_by_device_cntrl(self, dev_node, cntrl): + # While using this method controllers may be not filtered out. + # Do not return slot for controller removed from test. + if cntrl not in self.slot_ctrls: + return None + + out = self.run_ledctl_cmd_valid( + ["--get-slot", "--controller-type", cntrl, "--device", + dev_node]).stdout + return self.parse_slot_line(cntrl, out) + + def default_controller_by_device(self, dev_node): + result = self.run_ledctl_cmd_valid( + ["--default-controller", "--device", dev_node]).stdout + return result.rstrip() + def list_slots(self, controller_type): rc = [] out = self.run_ledctl_cmd_valid( diff --git a/tests/ledctl/parameters_test.py b/tests/ledctl/parameters_test.py index 32cbf59b..935accaf 100644 --- a/tests/ledctl/parameters_test.py +++ b/tests/ledctl/parameters_test.py @@ -33,7 +33,9 @@ def test_parameters_are_valid_long_test_flag(ledctl_binary): "-G -n vmd -p 1 -r state -T", "--get-slot --controller-type=vmd --slot=1 --print=state -T", "-S -n vmd -p 1 -s normal -T", - "--set-slot --controller-type=vmd --slot=1 --state=normal -T" + "--set-slot --controller-type=vmd --slot=1 --state=normal -T", + "-B -d /dev/nvme0n1 -T", + "--default-controller --device /dev/nvme0n1 -T" ], ) def test_parameters_are_valid_short_test_flag(ledctl_binary, @@ -149,10 +151,22 @@ def parse_help(lines): @pytest.mark.parametrize( "help_cmd", [ - "--help", "-h", "--help --badflag", "-hd", "-h -s", - "--set-slot --help", "-S -h --badflag", "-Sh --badflag", - "--get-slot --help", "-Ghb", "--list-controllers --help", - "--ibpi --help", "--list-slots --help", "-L -h" + "--help", + "-h", + "--help --badflag", + "-hd", + "-h -s", + "--set-slot --help", + "-S -h --badflag", + "-Sh --badflag", + "--get-slot --help", + "-Ghb", + "--list-controllers --help", + "--ibpi --help", + "--list-slots --help", + "-L -h", + "--default-controller --help", + "-Bh", ], ) # Check formatting, header and footer. diff --git a/tests/ledctl/slot_test.py b/tests/ledctl/slot_test.py index 21a82c02..9188b554 100644 --- a/tests/ledctl/slot_test.py +++ b/tests/ledctl/slot_test.py @@ -27,6 +27,15 @@ def get_slots_with_device_or_skip(cmd: LedctlCmd, cntrl): return slots_with_device_node +def filter_by_default_controller(cmd: LedctlCmd, slots_to_test): + filtered_slots = [] + for slot in slots_to_test: + default_cntrl = cmd.default_controller_by_device(slot.device_node) + if default_cntrl == slot.cntrl_type: + filtered_slots.append(slot) + return filtered_slots + + def verify_state(slot, current, expected, msg): if slot.cntrl_type == "SCSI" and expected == "rebuild": # No good way to validate this one as read value won't match what we sent down. @@ -43,9 +52,15 @@ def test_ibpi(ledctl_binary, slot_filters, controller_filters, cntrl): """ cmd = LedctlCmd(ledctl_binary, slot_filters, controller_filters) - slots_with_device_node = get_slots_with_device_or_skip(cmd, cntrl) + slots_to_test = get_slots_with_device_or_skip(cmd, cntrl) + slots_to_test = filter_by_default_controller(cmd, slots_to_test) - for slot in slots_with_device_node: + if not slots_to_test: + pytest.skip( + "Devices detected but this is not primary controller for any drive, skipping" + ) + + for slot in slots_to_test: for state in LedctlCmd.base_states: cmd.set_ibpi(slot.device_node, state) cur = cmd.get_slot(slot) @@ -105,9 +120,8 @@ def test_set_slot_by_device(ledctl_binary, slot_filters, controller_filters, slot_set_and_get_by_device_all(cmd, slot) -@pytest.mark.parametrize("cntrl", ["VMD", "NPEM"]) -def test_nvme_multipath_drives(ledctl_binary, slot_filters, controller_filters, - cntrl): +def test_nvme_multipath_drives(ledctl_binary, slot_filters, + controller_filters): """ Special test for multipath drives using both set methods and get via device. We need to check if ledctl provides nvme multipath minimal support. @@ -118,15 +132,11 @@ def test_nvme_multipath_drives(ledctl_binary, slot_filters, controller_filters, if len(mp_drives) == 0: pytest.skip("No nvme multipath drives found") - slots_with_device_node = get_slots_with_device_or_skip(cmd, cntrl) - any_found = False - - for slot in slots_with_device_node: - if slot.device_node not in mp_drives: + for mp_drive in mp_drives: + mp_cntrl = cmd.default_controller_by_device(mp_drive) + slot = cmd.get_slot_by_device_cntrl(mp_drive, mp_cntrl) + if slot is None: continue - any_found = True - - LOGGER.debug(f"Found nvme multipath drive {slot}") for state in cmd.base_states: cmd.set_ibpi(slot.device_node, state) @@ -137,6 +147,3 @@ def test_nvme_multipath_drives(ledctl_binary, slot_filters, controller_filters, ) slot_set_and_get_by_device_all(cmd, slot) - - if not any_found: - pytest.skip("Multipath drives are not connected to tested controller")