Skip to content

Commit

Permalink
add support for output formats to pcs resource/stonith config commands
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmular committed Apr 1, 2022
1 parent a4eb88e commit 50b5eef
Show file tree
Hide file tree
Showing 57 changed files with 4,594 additions and 1,145 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
clusters can get a UUID by running the new `pcs cluster config uuid generate`
command ([rhbz#2054671])
- Add warning regarding move constraints to `pcs status` ([rhbz#2058247])
- Support for output formats `json` and `cmd` to `pcs resource config` and `pcs
stonith config` commands ([rhbz#2058251], [rhbz#2058252])

### Fixed
- Booth ticket name validation ([rhbz#2053177])
Expand All @@ -28,6 +30,8 @@
[rhbz#2058243]: https://bugzilla.redhat.com/show_bug.cgi?id=2058243
[rhbz#2058246]: https://bugzilla.redhat.com/show_bug.cgi?id=2058246
[rhbz#2058247]: https://bugzilla.redhat.com/show_bug.cgi?id=2058247
[rhbz#2058251]: https://bugzilla.redhat.com/show_bug.cgi?id=2058251
[rhbz#2058252]: https://bugzilla.redhat.com/show_bug.cgi?id=2058252
[rhbz#2068457]: https://bugzilla.redhat.com/show_bug.cgi?id=2068457
[huntr#220307]: https://huntr.dev/bounties/7aa921fc-a568-4fd8-96f4-7cd826246aa5/

Expand Down
5 changes: 5 additions & 0 deletions pcs/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ EXTRA_DIST = \
common/pacemaker/defaults.py \
common/pacemaker/nvset.py \
common/pacemaker/resource/__init__.py \
common/pacemaker/resource/bundle.py \
common/pacemaker/resource/clone.py \
common/pacemaker/resource/list.py \
common/pacemaker/resource/group.py \
common/pacemaker/resource/operations.py \
common/pacemaker/resource/primitive.py \
common/pacemaker/resource/relations.py \
common/pacemaker/role.py \
common/pacemaker/rule.py \
Expand Down
1 change: 1 addition & 0 deletions pcs/cli/common/lib_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ def load_module(env, middleware_factory, name):
"disable_safe": resource.disable_safe,
"disable_simulate": resource.disable_simulate,
"enable": resource.enable,
"get_configured_resources": resource.get_configured_resources,
"get_failcounts": resource.get_failcounts,
"group_add": resource.group_add,
"is_any_resource_except_stonith": resource.is_any_resource_except_stonith,
Expand Down
72 changes: 47 additions & 25 deletions pcs/cli/common/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,63 @@
from shutil import get_terminal_size
from typing import List

INDENT_STEP = 2
SUBSEQUENT_INDENT_STEP = 4

def format_with_indentation(

def _smart_wrap(
text: str, subsequent_indent: int = SUBSEQUENT_INDENT_STEP
) -> List[str]:
initial_indent = len(text) - len(text.lstrip(" "))
return format_wrap_for_terminal(
text, subsequent_indent=subsequent_indent + initial_indent
)


def smart_wrap_text(
lines: List[str], subsequent_indent: int = SUBSEQUENT_INDENT_STEP
) -> List[str]:
output = []
for line in lines:
if not line:
output.append("")
continue
output.extend(_smart_wrap(line, subsequent_indent=subsequent_indent))
return output


def format_wrap_for_terminal(
text: str,
indentation: int = 0,
indent_first: bool = False,
max_length_trim: int = 0,
max_length: int = 0,
subsequent_indent: int = SUBSEQUENT_INDENT_STEP,
trim: int = 0,
) -> List[str]:
"""
Returns text as a list of lines. Length of a line is determined by a
terminal size if not explicitely specified.
text -- string to format
indentation -- number of spaces to put at the begining of each line (except
first one)
indent_first -- if True also indent the first line by the same number of
spaces as defined as `indentation` argument
max_length_trim -- number which will be substracted from maximal line
length. Can be used in cases lines will be indented later by this
number of spaces.
max_length -- maximal line length. Terminal size is used if less than or
equal to 0.
subsequent_indent -- number of spaces all subsequent lines will be indented
compared to the first one.
trim -- number which will be substracted from terminal size. Can be used in
cases lines will be indented later by this number of spaces.
"""
if any((sys.stdout.isatty(), sys.stderr.isatty())):
indent = " " * indentation
default_max_length = 40
if max_length <= 0:
max_length = get_terminal_size()[0]
return textwrap.wrap(
return format_wrap(
text,
max(
max_length - max_length_trim,
default_max_length,
),
initial_indent=indent if indent_first else "",
subsequent_indent=indent,
# minimal line length is 40
max(get_terminal_size()[0] - trim, 40),
subsequent_indent=subsequent_indent,
)
return [text]


def format_wrap(
text: str,
max_length: int,
subsequent_indent: int = SUBSEQUENT_INDENT_STEP,
) -> List[str]:
return textwrap.wrap(
text,
max_length,
subsequent_indent=" " * subsequent_indent,
)
50 changes: 40 additions & 10 deletions pcs/cli/common/parse_args.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from collections.abc import Set
from typing import (
Iterable,
Mapping,
Union,
cast,
)

from pcs.cli.common.errors import (
Expand All @@ -16,6 +18,11 @@

ModifierValueType = Union[None, bool, str]

_OUTPUT_FORMAT_OPTION_STR = "output-format"
_OUTPUT_FORMAT_OPTION = f"--{_OUTPUT_FORMAT_OPTION_STR}"
OUTPUT_FORMAT_VALUE_TEXT = "text"
_OUTPUT_FORMAT_VALUES = frozenset((OUTPUT_FORMAT_VALUE_TEXT, "cmd", "json"))

ARG_TYPE_DELIMITER = "%"

# h = help, f = file,
Expand Down Expand Up @@ -81,7 +88,7 @@
# allow overwriting existing files, currently meant for / used in CLI only
"overwrite",
# output format of commands, e.g: json, cmd, text, ...
"output-format=",
f"{_OUTPUT_FORMAT_OPTION_STR}=",
# auth token
"token=",
]
Expand Down Expand Up @@ -506,7 +513,9 @@ def __init__(self, options: Mapping[str, ModifierValueType]):
"--group": options.get("--group", None),
"--name": options.get("--name", None),
"--node": options.get("--node", None),
"--output-format": options.get("--output-format", "text"),
_OUTPUT_FORMAT_OPTION: options.get(
_OUTPUT_FORMAT_OPTION, OUTPUT_FORMAT_VALUE_TEXT
),
"--request-timeout": options.get("--request-timeout", None),
"--to": options.get("--to", None),
"--token": options.get("--token", None),
Expand All @@ -525,14 +534,16 @@ def get_subset(self, *options, **custom_options):
return InputModifiers(opt_dict)

def ensure_only_supported(
self, *supported_options, hint_syntax_changed: bool = False
):
unsupported_options = (
# --debug is supported in all commands
self._defined_options
- set(supported_options)
- set(["--debug"])
)
self,
*supported_options: str,
hint_syntax_changed: bool = False,
output_format_supported: bool = False,
) -> None:
# --debug is supported in all commands
supported_options_set = set(supported_options) | {"--debug"}
if output_format_supported:
supported_options_set.add(_OUTPUT_FORMAT_OPTION)
unsupported_options = self._defined_options - supported_options_set
if unsupported_options:
pluralize = lambda word: format_plural(unsupported_options, word)
raise CmdLineInputError(
Expand Down Expand Up @@ -611,3 +622,22 @@ def get(
if option in self._options:
return self._options[option]
raise AssertionError(f"Non existing default value for '{option}'")

def get_output_format(
self, supported_formats: Set[str] = _OUTPUT_FORMAT_VALUES
) -> str:
output_format = self.get(_OUTPUT_FORMAT_OPTION)
if output_format in supported_formats:
return cast(str, output_format)
raise CmdLineInputError(
(
"Unknown value '{value}' for '{option}' option. Supported "
"{value_pl} {is_pl}: {supported}"
).format(
value=output_format,
option=_OUTPUT_FORMAT_OPTION,
value_pl=format_plural(supported_formats, "value"),
is_pl=format_plural(supported_formats, "is"),
supported=format_list(list(supported_formats)),
)
)
4 changes: 2 additions & 2 deletions pcs/cli/nvset.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ def nvset_dto_to_lines(
) -> List[str]:
in_effect_label = get_in_effect_label(nvset.rule) if nvset.rule else None
heading_parts = [
"{label}{in_effect}: {id}".format(
"{label}{in_effect}:{id}".format(
label=nvset_label,
in_effect=format_optional(in_effect_label, " ({})"),
id=nvset.id,
id=format_optional(nvset.id, " {}"),
)
]
if nvset.options:
Expand Down
Loading

0 comments on commit 50b5eef

Please sign in to comment.