Skip to content

Commit

Permalink
cluster UUID support
Browse files Browse the repository at this point in the history
  • Loading branch information
CtrlZmaster authored and ondrejmular committed Mar 17, 2022
1 parent bcf5526 commit d3363a8
Show file tree
Hide file tree
Showing 30 changed files with 863 additions and 188 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,9 @@
### Added
- Add support for fence_mpath to `pcs stonith update-scsi-devices` command
([rhbz#2024522])
- Support for cluster UUIDs. New clusters now get a UUID during setup. Existing
clusters can get a UUID by running the new `pcs cluster config uuid generate`
command ([rhbz#2054671])

### Fixed
- Booth ticket name validation ([rhbz#2053177])
Expand All @@ -13,6 +16,7 @@

[rhbz#2024522]: https://bugzilla.redhat.com/show_bug.cgi?id=2024522
[rhbz#2053177]: https://bugzilla.redhat.com/show_bug.cgi?id=2053177
[rhbz#2054671]: https://bugzilla.redhat.com/show_bug.cgi?id=2054671
[rhbz#2058243]: https://bugzilla.redhat.com/show_bug.cgi?id=2058243


Expand Down
2 changes: 2 additions & 0 deletions pcs/cli/common/lib_wrapper.py
Expand Up @@ -196,6 +196,8 @@ def load_module(env, middleware_factory, name):
"setup_local": cluster.setup_local,
"update_link": cluster.update_link,
"verify": cluster.verify,
"generate_cluster_uuid": cluster.generate_cluster_uuid,
"generate_cluster_uuid_local": cluster.generate_cluster_uuid_local,
},
)

Expand Down
3 changes: 3 additions & 0 deletions pcs/cli/common/parse_args.py
Expand Up @@ -64,6 +64,8 @@
"booth-conf=",
"booth-key=",
"no-watchdog-validation",
# pcs cluster setup
"no-cluster-uuid",
"no-keys-sync",
# in pcs status - do not display resource status on inactive node
"hide-inactive",
Expand Down Expand Up @@ -480,6 +482,7 @@ def __init__(self, options: Mapping[str, ModifierValueType]):
"--no-default-ops": "--no-default-ops" in options,
"--nodesc": "--nodesc" in options,
"--no-expire-check": "--no-expire-check" in options,
"--no-cluster-uuid": "--no-cluster-uuid" in options,
"--no-keys-sync": "--no-keys-sync" in options,
"--no-strict": "--no-strict" in options,
"--no-watchdog-validation": "--no-watchdog-validation"
Expand Down
6 changes: 6 additions & 0 deletions pcs/cli/routing/cluster.py
Expand Up @@ -50,6 +50,12 @@ def pcsd_status(
{
"show": cluster.config_show,
"update": cluster.config_update,
"uuid": create_router(
{
"generate": cluster.generate_uuid,
},
["cluster", "config", "uuid"],
),
},
["cluster", "config"],
default_cmd="show",
Expand Down
126 changes: 92 additions & 34 deletions pcs/cluster.py
Expand Up @@ -10,6 +10,7 @@
import xml.dom.minidom
from typing import (
Any,
Callable,
Iterable,
List,
Mapping,
Expand Down Expand Up @@ -68,6 +69,43 @@
# pylint: disable=too-many-branches, too-many-statements


def _corosync_conf_local_cmd_call(
corosync_conf_path: parse_args.ModifierValueType,
lib_cmd: Callable[[bytes], bytes],
) -> None:
"""
Call a library command that requires modifications of a corosync.conf file
supplied as an argument
The lib command needs to take the corosync.conf file content as its first
argument
lib_cmd -- the lib command to be called
"""
corosync_conf_file = pcs_file.RawFile(
file_metadata.for_file_type(
file_type_codes.COROSYNC_CONF, corosync_conf_path
)
)

try:
corosync_conf_file.write(
lib_cmd(
corosync_conf_file.read(),
),
can_overwrite=True,
)
except pcs_file.RawFileError as e:
raise CmdLineInputError(
reports.messages.FileIoError(
e.metadata.file_type_code,
e.action,
e.reason,
file_path=e.metadata.path,
).message
) from e


def cluster_cib_upgrade_cmd(lib, argv, modifiers):
"""
Options:
Expand Down Expand Up @@ -1620,13 +1658,19 @@ def cluster_setup(lib, argv, modifiers):
as warnings
* --no-keys-sync - do not create and distribute pcsd ssl cert and key,
corosync and pacemaker authkeys
* --no-cluster-uuid - do not generate a cluster UUID during setup
* --corosync_conf - corosync.conf file path, do not talk to cluster nodes
"""
# pylint: disable=too-many-locals
is_local = modifiers.is_specified("--corosync_conf")

allowed_options_common = ["--force"]
allowed_options_live = ["--wait", "--start", "--enable", "--no-keys-sync"]
allowed_options_common = ["--force", "--no-cluster-uuid"]
allowed_options_live = [
"--wait",
"--start",
"--enable",
"--no-keys-sync",
]
allowed_options_local = ["--corosync_conf", "--overwrite"]
modifiers.ensure_only_supported(
*(
Expand Down Expand Up @@ -1700,6 +1744,7 @@ def cluster_setup(lib, argv, modifiers):
start=modifiers.get("--start"),
enable=modifiers.get("--enable"),
no_keys_sync=modifiers.get("--no-keys-sync"),
no_cluster_uuid=modifiers.is_specified("--no-cluster-uuid"),
force_flags=force_flags,
)
return
Expand All @@ -1716,6 +1761,7 @@ def cluster_setup(lib, argv, modifiers):
quorum_options=parse_args.prepare_options(
parsed_args.get("quorum", [])
),
no_cluster_uuid=modifiers.is_specified("--no-cluster-uuid"),
force_flags=force_flags,
)

Expand Down Expand Up @@ -1766,35 +1812,17 @@ def config_update(
parse_args.prepare_options(parsed_args["totem"]),
)
return
corosync_conf_file = pcs_file.RawFile(
file_metadata.for_file_type(
file_type_codes.COROSYNC_CONF, modifiers.get("--corosync_conf")
)
)

try:
corosync_conf_file.write(
lib.cluster.config_update_local(
corosync_conf_file.read(),
parse_args.prepare_options(parsed_args["transport"]),
parse_args.prepare_options(parsed_args["compression"]),
parse_args.prepare_options(parsed_args["crypto"]),
parse_args.prepare_options(parsed_args["totem"]),
),
can_overwrite=True,
)
except pcs_file.RawFileError as e:
# TODO do not use LibraryError
raise LibraryError(
reports.ReportItem.error(
reports.messages.FileIoError(
e.metadata.file_type_code,
e.action,
e.reason,
file_path=e.metadata.path,
)
)
) from e
_corosync_conf_local_cmd_call(
modifiers.get("--corosync_conf"),
lambda corosync_conf_content: lib.cluster.config_update_local(
corosync_conf_content,
parse_args.prepare_options(parsed_args["transport"]),
parse_args.prepare_options(parsed_args["compression"]),
parse_args.prepare_options(parsed_args["crypto"]),
parse_args.prepare_options(parsed_args["totem"]),
),
)


def _format_options(label: str, options: Mapping[str, str]) -> List[str]:
Expand Down Expand Up @@ -1854,10 +1882,10 @@ def config_show(


def _config_get_text(corosync_conf: CorosyncConfDto) -> List[str]:
lines = [
f"Cluster Name: {corosync_conf.cluster_name}",
"Transport: {}".format(corosync_conf.transport.lower()),
]
lines = [f"Cluster Name: {corosync_conf.cluster_name}"]
if corosync_conf.cluster_uuid:
lines.append(f"Cluster UUID: {corosync_conf.cluster_uuid}")
lines.append("Transport: {}".format(corosync_conf.transport.lower()))
lines.extend(_format_nodes(corosync_conf.nodes))
if corosync_conf.links_options:
lines.append("Links:")
Expand Down Expand Up @@ -1957,6 +1985,8 @@ def _config_get_cmd(corosync_conf: CorosyncConfDto) -> List[str]:
lines.extend(indent(transport))
lines.extend(_section_to_lines(corosync_conf.totem_options, "totem"))
lines.extend(_section_to_lines(corosync_conf.quorum_options, "quorum"))
if not corosync_conf.cluster_uuid:
lines.extend(indent(["--no-cluster-uuid"]))
return lines


Expand Down Expand Up @@ -2115,3 +2145,31 @@ def link_update(lib, argv, modifiers):
parse_args.prepare_options(parsed["options"]),
force_flags=force_flags,
)


def generate_uuid(
lib: Any, argv: List[str], modifiers: parse_args.InputModifiers
):
"""
Options:
* --force - allow to rewrite an existing UUID in corosync.conf
* --corosync_conf - corosync.conf file path, do not talk to cluster nodes
"""
modifiers.ensure_only_supported("--force", "--corosync_conf")
if argv:
raise CmdLineInputError()

force_flags = []
if modifiers.get("--force"):
force_flags.append(reports.codes.FORCE)

if not modifiers.is_specified("--corosync_conf"):
lib.cluster.generate_cluster_uuid(force_flags=force_flags)
return

_corosync_conf_local_cmd_call(
modifiers.get("--corosync_conf"),
lambda corosync_conf_content: lib.cluster.generate_cluster_uuid_local(
corosync_conf_content, force_flags=force_flags
),
)
1 change: 1 addition & 0 deletions pcs/common/corosync_conf.py
Expand Up @@ -35,6 +35,7 @@ class CorosyncQuorumDeviceSettingsDto(DataTransferObject):
class CorosyncConfDto(DataTransferObject):
# pylint: disable=too-many-instance-attributes
cluster_name: str
cluster_uuid: Optional[str]
transport: CorosyncTransportType
totem_options: Mapping[str, str]
transport_options: Mapping[str, str]
Expand Down
1 change: 1 addition & 0 deletions pcs/common/reports/codes.py
Expand Up @@ -143,6 +143,7 @@
CLUSTER_SETUP_SUCCESS = M("CLUSTER_SETUP_SUCCESS")
CLUSTER_START_STARTED = M("CLUSTER_START_STARTED")
CLUSTER_START_SUCCESS = M("CLUSTER_START_SUCCESS")
CLUSTER_UUID_ALREADY_SET = M("CLUSTER_UUID_ALREADY_SET")
CLUSTER_WILL_BE_DESTROYED = M("CLUSTER_WILL_BE_DESTROYED")
LIVE_ENVIRONMENT_NOT_CONSISTENT = M("LIVE_ENVIRONMENT_NOT_CONSISTENT")
LIVE_ENVIRONMENT_REQUIRED = M("LIVE_ENVIRONMENT_REQUIRED")
Expand Down
13 changes: 13 additions & 0 deletions pcs/common/reports/messages.py
Expand Up @@ -2206,6 +2206,19 @@ def message(self) -> str:
)


@dataclass(frozen=True)
class ClusterUuidAlreadySet(ReportItemMessage):
"""
Cluster UUID has already been set in corosync.conf
"""

_code = codes.CLUSTER_UUID_ALREADY_SET

@property
def message(self) -> str:
return "Cluster UUID has already been set"


@dataclass(frozen=True)
class QdeviceAlreadyDefined(ReportItemMessage):
"""
Expand Down

0 comments on commit d3363a8

Please sign in to comment.