diff --git a/doc/man1/flux-module.adoc b/doc/man1/flux-module.adoc index f4b6fbc23d5f..bdc7095c48cf 100644 --- a/doc/man1/flux-module.adoc +++ b/doc/man1/flux-module.adoc @@ -41,6 +41,11 @@ Remove module 'name'. The service that will unload the module is inferred from the name specified on the command line. If '-f, --force' is used, then do not error if module 'name' is not loaded. +*reload* [--force] 'name' ['module-arguments' ...]:: +Reload module 'name'. This is equivalent to running 'flux module remove' +followed by 'flux module load'. It is a fatal error if module 'name' is +not loaded during removal unless the `-f, --force` option is specified. + *list* ['service']:: List modules loaded by 'service', or by flux-broker(1) if 'service' is unspecified. diff --git a/src/cmd/flux-module.c b/src/cmd/flux-module.c index 66a63309ea90..0ea77b899e2f 100644 --- a/src/cmd/flux-module.c +++ b/src/cmd/flux-module.c @@ -32,6 +32,7 @@ const int max_idle = 99; int cmd_list (optparse_t *p, int argc, char **argv); int cmd_remove (optparse_t *p, int argc, char **argv); int cmd_load (optparse_t *p, int argc, char **argv); +int cmd_reload (optparse_t *p, int argc, char **argv); int cmd_info (optparse_t *p, int argc, char **argv); int cmd_stats (optparse_t *p, int argc, char **argv); int cmd_debug (optparse_t *p, int argc, char **argv); @@ -101,6 +102,13 @@ static struct optparse_subcommand subcommands[] = { 0, remove_opts, }, + { "unload", + "[OPTIONS] module", + "Unload module", + cmd_remove, + OPTPARSE_SUBCMD_HIDDEN, + remove_opts, + }, { "load", "[OPTIONS] module", "Load module", @@ -108,6 +116,13 @@ static struct optparse_subcommand subcommands[] = { 0, legacy_opts, }, + { "reload", + "[OPTIONS] module", + "Reload module", + cmd_reload, + 0, + remove_opts, + }, { "info", "[OPTIONS] module", "Display module info", @@ -140,7 +155,8 @@ int usage (optparse_t *p, struct optparse_option *o, const char *optarg) fprintf (stderr, "flux module subcommands:\n"); s = subcommands; while (s->name) { - fprintf (stderr, " %-15s %s\n", s->name, s->doc); + if (!(s->flags & OPTPARSE_SUBCMD_HIDDEN)) + fprintf (stderr, " %-15s %s\n", s->name, s->doc); s++; } exit (1); @@ -265,12 +281,11 @@ char *getservice (const char *modname) return service; } -int cmd_load (optparse_t *p, int argc, char **argv) +static void module_load (flux_t *h, optparse_t *p, int argc, char **argv) { char *modname; char *modpath; int n; - flux_t *h; flux_future_t *f; if ((n = optparse_option_index (p)) == argc) { @@ -291,8 +306,6 @@ int cmd_load (optparse_t *p, int argc, char **argv) n++; } - if (!(h = flux_open (NULL, 0))) - log_err_exit ("flux_open"); if (!(f = flux_rpc_pack (h, topic, optparse_get_int (p, "rank", FLUX_NODEID_ANY), @@ -314,28 +327,24 @@ int cmd_load (optparse_t *p, int argc, char **argv) json_decref (args); free (modpath); free (modname); +} + +int cmd_load (optparse_t *p, int argc, char **argv) +{ + flux_t *h; + if (!(h = flux_open (NULL, 0))) + log_err_exit ("flux_open"); + module_load (h, p, argc, argv); flux_close (h); return 0; } -int cmd_remove (optparse_t *p, int argc, char **argv) +static void module_remove (flux_t *h, const char *modname, optparse_t *p) { - char *modname; - flux_t *h; flux_future_t *f; - int n; - - if ((n = optparse_option_index (p)) != argc - 1) { - optparse_print_usage (p); - exit (1); - } - modname = argv[n++]; - char *service = getservice (modname); char *topic = xasprintf ("%s.rmmod", service); - if (!(h = flux_open (NULL, 0))) - log_err_exit ("flux_open"); if (!(f = flux_rpc_pack (h, topic, optparse_get_int (p, "rank", FLUX_NODEID_ANY), @@ -351,10 +360,50 @@ int cmd_remove (optparse_t *p, int argc, char **argv) flux_future_destroy (f); free (topic); free (service); +} + +int cmd_remove (optparse_t *p, int argc, char **argv) +{ + char *modname; + flux_t *h; + int n; + + if ((n = optparse_option_index (p)) != argc - 1) { + optparse_print_usage (p); + exit (1); + } + modname = argv[n++]; + + if (!(h = flux_open (NULL, 0))) + log_err_exit ("flux_open"); + + module_remove (h, modname, p); + flux_close (h); return (0); } +int cmd_reload (optparse_t *p, int argc, char **argv) +{ + char *modname; + char *modpath; + flux_t *h; + int n; + + if ((n = optparse_option_index (p)) == argc) { + optparse_print_usage (p); + exit (1); + } + parse_modarg (argv[n], &modname, &modpath); + + if (!(h = flux_open (NULL, 0))) + log_err_exit ("flux_open"); + + module_remove (h, modname, p); + module_load (h, p, argc, argv); + return (0); +} + /* Translate an array of service names to a comma-delimited string. * If list is empty, NULL is returned. * If skip != NULL, skip over that name in the list (intended to be diff --git a/t/t0003-module.t b/t/t0003-module.t index 0ce07970d97c..683682ec465e 100755 --- a/t/t0003-module.t +++ b/t/t0003-module.t @@ -18,6 +18,10 @@ test_expect_success 'module: load test module' ' flux module load \ ${FLUX_BUILD_DIR}/t/module/.libs/parent.so ' +test_expect_success 'module: reload test module' ' + flux module reload \ + ${FLUX_BUILD_DIR}/t/module/.libs/parent.so +' test_expect_success 'module: lsmod shows test module' ' flux module list | grep parent @@ -251,6 +255,7 @@ test_expect_success 'flux module -h lists subcommands' ' ! flux module -h 2>module.help && grep -q list module.help && grep -q remove module.help && + grep -q reload module.help && grep -q load module.help && grep -q info module.help && grep -q stats module.help && diff --git a/t/t0015-cron.t b/t/t0015-cron.t index f2f800a2248d..b3fd6e5e0c91 100755 --- a/t/t0015-cron.t +++ b/t/t0015-cron.t @@ -274,11 +274,8 @@ test_expect_success 'flux-cron can set stop-on-failure' ' ' ## Reload cron module with sync enabled -test_expect_success 'flux module remove cron' ' - flux module remove cron -' -test_expect_success 'module load with sync' ' - flux module load cron sync=cron.sync sync_epsilon=0.025 +test_expect_success 'module reload with sync' ' + flux module reload cron sync=cron.sync sync_epsilon=0.025 ' test_expect_success 'sync and sync_epsilon are set as expected' ' flux cron sync | grep "cron\.sync.*epsilon=0.025" diff --git a/t/t1003-kvs-stress.t b/t/t1003-kvs-stress.t index 0a37f84885f6..ca3837cf6bf3 100755 --- a/t/t1003-kvs-stress.t +++ b/t/t1003-kvs-stress.t @@ -126,8 +126,7 @@ test_expect_success 'kvs: test that KVS_NO_MERGE works with kvs_commit()' ' # transaction-merge option test test_expect_success 'kvs: transaction-merge disabling works' ' THREADS=64 && - flux module remove kvs && - flux module load kvs transaction-merge=0 && + flux module reload kvs transaction-merge=0 && OUTPUT=`${FLUX_BUILD_DIR}/t/kvs/transactionmerge ${THREADS} $(basename ${SHARNESS_TEST_FILE})` && test "$OUTPUT" = "${THREADS}" ' diff --git a/t/t2200-job-ingest.t b/t/t2200-job-ingest.t index 7281bf6eaa58..7bcb4fa49a4a 100755 --- a/t/t2200-job-ingest.t +++ b/t/t2200-job-ingest.t @@ -152,8 +152,7 @@ test_expect_success 'submit request with empty payload fails with EPROTO(71)' ' ' test_expect_success 'job-ingest: test validator with version 1 enforced' ' - flux exec -r all flux module remove job-ingest && - flux exec -r all flux module load job-ingest \ + flux exec -r all flux module reload job-ingest \ validator=${BINDINGS_VALIDATOR} validator-args="--require-version,1" ' @@ -162,8 +161,7 @@ test_expect_success 'job-ingest: v1 jobspecs accepted with v1 requirement' ' ' test_expect_success 'job-ingest: test non-python validator' ' - flux exec -r all flux module remove job-ingest && - flux exec -r all flux module load job-ingest \ + flux exec -r all flux module reload job-ingest \ validator=${FAKE_VALIDATOR} ' @@ -172,8 +170,7 @@ test_expect_success 'job-ingest: submit succeeds with non-python validator' ' ' test_expect_success 'job-ingest: test python jsonschema validator' ' - flux exec -r all flux module remove job-ingest && - flux exec -r all flux module load job-ingest \ + flux exec -r all flux module reload job-ingest \ validator=${JSONSCHEMA_VALIDATOR} validator-args=--schema,${SCHEMA} ' @@ -190,8 +187,7 @@ test_expect_success 'job-ingest: invalid jobs rejected by jsonschema validator' ' test_expect_success 'job-ingest: validator unexpected exit is handled' ' - flux exec -r all flux module remove job-ingest && - flux exec -r all flux module load job-ingest \ + flux exec -r all flux module reload job-ingest \ validator=${BAD_VALIDATOR} && test_must_fail flux job submit basic.json 2>badvalidator.out && grep "unexpectedly exited" badvalidator.out diff --git a/t/t2202-job-manager.t b/t/t2202-job-manager.t index d3856d7c42f1..ada57717a3af 100755 --- a/t/t2202-job-manager.t +++ b/t/t2202-job-manager.t @@ -160,8 +160,7 @@ test_expect_success 'job-manager: that job is now the first job' ' ' test_expect_success 'job-manager: reload the job manager' ' - flux module remove job-manager && - flux module load job-manager + flux module reload job-manager ' test_expect_success 'job-manager: queue was successfully reconstructed' ' @@ -229,8 +228,7 @@ test_expect_success 'job-manager: no jobs in the queue' ' ' test_expect_success 'job-manager: reload the job manager' ' - flux module remove job-manager && - flux module load job-manager + flux module reload job-manager ' test_expect_success 'job-manager: still no jobs in the queue' ' diff --git a/t/t2203-job-manager-dummysched.t b/t/t2203-job-manager-dummysched.t index 152d8d9e6996..27aa46f79fb8 100755 --- a/t/t2203-job-manager-dummysched.t +++ b/t/t2203-job-manager-dummysched.t @@ -95,8 +95,7 @@ test_expect_success 'job-manager: canceled job has exception, free events' ' test_expect_success 'job-manager: reload sched-dummy --cores=4' ' flux dmesg -C && - flux module remove sched-dummy && - flux module load ${SCHED_DUMMY} --cores=4 && + flux module reload ${SCHED_DUMMY} --cores=4 && flux dmesg | grep "hello_cb:" >hello.dmesg ' diff --git a/t/t2204-job-info.t b/t/t2204-job-info.t index 5f6acb085841..56f773c8870e 100755 --- a/t/t2204-job-info.t +++ b/t/t2204-job-info.t @@ -343,8 +343,7 @@ wait_inactive() { } test_expect_success 'reload the job-info module' ' - flux exec -r all flux module remove job-info && - flux exec -r all flux module load job-info && + flux exec -r all flux module reload job-info && wait_inactive ' @@ -435,8 +434,7 @@ test_expect_success 'flux job lists full path for job name if first argument not ' test_expect_success 'reload the job-info module' ' - flux exec -r all flux module remove job-info && - flux exec -r all flux module load job-info + flux exec -r all flux module reload job-info ' test_expect_success 'verify job names preserved across restart' ' @@ -471,8 +469,7 @@ test_expect_success 'flux job list outputs ntasks correctly (4 tasks)' ' ' test_expect_success 'reload the job-info module' ' - flux exec -r all flux module remove job-info && - flux exec -r all flux module load job-info + flux exec -r all flux module reload job-info ' test_expect_success 'verify task count preserved across restart' ' @@ -525,8 +522,7 @@ test_expect_success 'flux job list outputs nnodes/ranks correctly (5 tasks, / 3 ' test_expect_success 'reload the job-info module' ' - flux exec -r all flux module remove job-info && - flux exec -r all flux module load job-info + flux exec -r all flux module reload job-info ' test_expect_success 'verify nnodes preserved across restart' ' diff --git a/t/t2300-sched-simple.t b/t/t2300-sched-simple.t index 7e5766951be2..3dc85fb0dcb6 100755 --- a/t/t2300-sched-simple.t +++ b/t/t2300-sched-simple.t @@ -27,8 +27,7 @@ list_R() { } test_expect_success 'sched-simple: reload ingest module with lax validator' ' - flux exec -r all flux module remove job-ingest && - flux exec -r all flux module load job-ingest validator-args="--schema,${SCHEMA}" \ + flux exec -r all flux module reload job-ingest validator-args="--schema,${SCHEMA}" \ validator=${JSONSCHEMA_VALIDATOR} ' test_expect_success 'sched-simple: generate jobspec for simple test job' ' @@ -103,8 +102,7 @@ test_expect_success 'sched-simple: cancel all jobs' ' test "$($query)" = "rank[0-1]/core[0-1]" ' test_expect_success 'sched-simple: reload in best-fit mode' ' - flux module remove sched-simple && - flux module load sched-simple mode=best-fit + flux module reload sched-simple mode=best-fit ' test_expect_success 'sched-simple: submit 5 more jobs' ' flux job submit basic.json >job6.id && @@ -161,8 +159,7 @@ test_expect_success 'sched-simple: check allocations for running jobs' ' test_cmp first-fit-allocs.expected first-fit-allocs.out ' test_expect_success 'sched-simple: reload with outstanding allocations' ' - flux module remove sched-simple && - flux module load sched-simple && + flux module reload sched-simple && flux dmesg | grep "hello: alloc rank0/core0" && test "$($query)" = "" '