diff --git a/CHANGES.md b/CHANGES.md index d4cff3247e8..7550f6b41af 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,7 +15,10 @@ ones in. --> ### Enhancements -[#5461](https://github.com/cylc/cylc-flow/pull/5461) - Preserve colour +[#5405](https://github.com/cylc/cylc-flow/pull/5405) - Improve scan command +help, and add scheduler PID to the output. + +[#5461](https://github.com/cylc/cylc-flow/pull/5461) - preserve colour formatting when starting workflows in distributed mode using `run hosts`. [#5291](https://github.com/cylc/cylc-flow/pull/5291) - re-implement old-style diff --git a/cylc/flow/scripts/scan.py b/cylc/flow/scripts/scan.py index ea2885140c0..73f6eb199d7 100644 --- a/cylc/flow/scripts/scan.py +++ b/cylc/flow/scripts/scan.py @@ -17,21 +17,26 @@ # along with this program. If not, see . """cylc scan [OPTIONS] -List Cylc workflows. +List your source, and/or installed, and/or running workflows. -By default this shows only running or paused workflows. +By default, running workflows are listed as indicated by the presence of +scheduler contact files, ~/cylc-run//.service/contact. + +With "--ping" or "-t rich", attempt to contact the schedulers. If any are not +found to be running, their contact files will be removed (these files may left +behind if the scheduler did not shut down cleanly). Examples: - # list all "active" workflows (i.e. running or paused) + # list all active workflows (i.e. running or paused) $ cylc scan - # show more information about these workflows + # show more information about active workflows $ cylc scan -t rich # don't rely on colour for job state totals $ cylc scan -t rich --colour-blind - # list all "inactive" workflows (i.e. registered or stopped) + # list workflows that are installed but stopped or not run yet $ cylc scan --state stopped # list all workflows (active or inactive) @@ -41,11 +46,11 @@ # filter workflows by name $ cylc scan --name '^f.*' # show only flows starting with "f" - # list source workflows in a tree + # list source workflows in tree format # (looks in the dirs configured by "global.cylc[install]source dirs") $ cylc scan --source -t tree - # get results in JSON format + # print contact file data in JSON format $ cylc scan -t json """ @@ -204,12 +209,12 @@ def get_option_parser() -> COP: parser.add_option( '--format', '-t', help=( - 'Set the output format.' - ' (rich: multi-line, human readable)' - ' (plain: single-line)' - ' (json: machine readable)' - ' (tree: display registration hierarchy as a tree)' - ' (name: just show flow names, machine readable)' + 'Output data and format (default "plain").' + ' ("name": list the workflow names only)' + ' ("plain": name,host:port,PID on one line)' + ' ("tree": name,host:port,PID in tree format)' + ' ("json": full contact data in JSON format)' + ' ("rich": include task state summary data)' ), choices=('rich', 'plain', 'json', 'tree', 'name'), default='plain' @@ -225,7 +230,7 @@ def get_option_parser() -> COP: '--colour-blind', '--color-blind', help=( "Don't depend on colour to convey information. " - ' Use this rather than --color=never so you still get bold text.' + 'Use this rather than --color=never so you still get bold text.' ), action='store_true' ) @@ -233,11 +238,9 @@ def get_option_parser() -> COP: parser.add_option( '--ping', help=( - 'Test the connection to the flow. Scan normally just reads flow' - ' contact files, but --ping forces a connection to the scheduler' - ' and removes the contact file if it is not found to be running' - " (this can happen if the scheduler gets killed and can't clean" - ' up after itself).' + "Connect to schedulers and remove contact files if they are not " + "found to be running. Contact files can be left behind when " + "schedulers get killed." ), action='store_true' ) @@ -297,7 +300,11 @@ def _format_plain(flow, _): """A single line format of the form: [:]""" if flow.get('contact'): try: - return f"{flow['name']} {flow[Cont.HOST]}:{flow[Cont.PORT]}" + return ( + f"{flow['name']} " + f"{flow[Cont.HOST]}:{flow[Cont.PORT]} " + f"{flow[Cont.PID]}" + ) except KeyError: LOG.warning(BAD_CONTACT_FILE_MSG.format(flow_name=flow['name'])) return None @@ -340,7 +347,8 @@ def _format_rich(flow, opts): for name, key in ( ('version', 'cylcVersion'), ('host', Cont.HOST), - ('port', Cont.PORT) + ('port', Cont.PORT), + ('PID', Cont.PID) ) } } @@ -407,7 +415,8 @@ async def _tree(pipe, formatter, opts, write): # print tree ret = get_tree(tree, '', sort=False, use_unicode=True) - write('\n'.join(ret)) + if ret: + write('\n'.join(ret)) def _construct_tree(flows, tree, formatter, opts, write): @@ -555,9 +564,8 @@ async def main( ) ): raise InputError( - '--states must be set to a comma separated list of workflow' - ' states. \nSee `cylc scan --help` for a list of supported' - ' states.' + '--states must be a comma separated list of workflow states.' + '\nSee `cylc scan --help` for supported states.' ) if not color: diff --git a/tests/unit/scripts/test_scan.py b/tests/unit/scripts/test_scan.py index d104a8bfdc7..cf709d29186 100644 --- a/tests/unit/scripts/test_scan.py +++ b/tests/unit/scripts/test_scan.py @@ -44,17 +44,19 @@ def test_good_contact_info() -> None: port = 8888 host = "wizard" name = "blargh/run1" + pid = 12345 res = _format_plain( { "name": name, "contact": Path(f"/path/to/{name}"), "CYLC_WORKFLOW_HOST": host, "CYLC_WORKFLOW_PORT": port, + "CYLC_WORKFLOW_PID": pid, }, None ) assert name in res - assert f"{host}:{port}" in res + assert f"{host}:{port} {pid}" in res def test_bad_contact_info(caplog: pytest.LogCaptureFixture) -> None: