Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pcs/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ EXTRA_DIST = \
lib/pcs_cfgsync/__init__.py \
lib/pcs_cfgsync/save_sync.py \
lib/pcs_cfgsync/sync_files.py \
lib/pcs_cfgsync/tools.py \
lib/pcs_cfgsync/validations.py \
lib/permissions/checker.py \
lib/permissions/config/exporter.py \
Expand Down
97 changes: 97 additions & 0 deletions pcs/daemon/app/api_v0.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,98 @@ async def _handle_request(self) -> None:
self.write("Sync thread options updated successfully")


class SetConfigsHandler(_BaseApiV0Handler):
_REPORT_CODE_TO_RESULT = {
reports.codes.PCS_CFGSYNC_CONFIG_ACCEPTED: "accepted",
reports.codes.PCS_CFGSYNC_CONFIG_REJECTED: "rejected",
reports.codes.PCS_CFGSYNC_CONFIG_SAVE_ERROR: "error",
reports.codes.PCS_CFGSYNC_CONFIG_UNSUPPORTED: "not_supported",
}

_LEGACY_TO_FILE_TYPE_CODE = {
"pcs_settings.conf": file_type_codes.PCS_SETTINGS_CONF,
"known-hosts": file_type_codes.PCS_KNOWN_HOSTS,
}
_FILE_TYPE_CODE_TO_LEGACY = {
v: k for k, v in _LEGACY_TO_FILE_TYPE_CODE.items()
}

_sync_config_lock: Lock

def initialize( # type: ignore[override]
self,
api_auth_provider_factory: ApiAuthProviderFactoryInterface,
scheduler: Scheduler,
sync_config_lock: Lock,
) -> None:
super().initialize(api_auth_provider_factory, scheduler)
self._sync_config_lock = sync_config_lock

async def _handle_request(self) -> None:
try:
configs_json = json.loads(self.get_argument("configs", ""))
except json.JSONDecodeError:
# original handler returned 200 in this case
self.write({"status": "bad_json"})
return

try:
cluster_name = configs_json.get("cluster_name", "")
force = bool(configs_json.get("force", False))

configs_raw = configs_json.get("configs", {})
configs = {}
non_file_configs = []
for name, data in configs_raw.items():
if data.get("type") == "file":
configs[self._LEGACY_TO_FILE_TYPE_CODE.get(name, name)] = (
str(data.get("text", ""))
)
else:
non_file_configs.append(name)
except AttributeError:
# original handler returned 200 in this case
self.write({"status": "bad_json"})
return

async with self._sync_config_lock:
result = await self._run_library_command(
"pcs_cfgsync.set_configs",
{
"cluster_name": cluster_name,
"configs": configs,
"force_flags": [reports.codes.FORCE] if force else [],
},
)

if any(
rep.message.code
== reports.codes.NODE_REPORTS_UNEXPECTED_CLUSTER_NAME
for rep in result.reports
):
self.write({"status": "wrong_cluster_name"})
return

not_file_results = dict.fromkeys(non_file_configs, "not_supported")
real_results = {
self._FILE_TYPE_CODE_TO_LEGACY.get(
rep.message.payload["file_type_code"],
rep.message.payload["file_type_code"],
): self._REPORT_CODE_TO_RESULT[rep.message.code]
for rep in result.reports
if rep.message.code in self._REPORT_CODE_TO_RESULT
}
# default to "error" for any file that received no status report
for file_type in configs:
legacy_name = self._FILE_TYPE_CODE_TO_LEGACY.get(
file_type, file_type
)
if legacy_name not in real_results:
real_results[legacy_name] = "error"

self.write({"status": "ok", "result": not_file_results | real_results})


class SetPermissionsHandler(_BaseApiV0Handler):
"""
Input format:
Expand Down Expand Up @@ -616,6 +708,11 @@ def r(url: str) -> str:
(r("booth_get_config"), BoothGetConfigHandler, params),
# cfgsync
(r("get_configs"), GetConfigsHandler, params),
(
r("set_configs"),
SetConfigsHandler,
{**params, "sync_config_lock": sync_config_lock},
),
(
r("set_sync_options"),
SetSyncOptionsHandler,
Expand Down
31 changes: 0 additions & 31 deletions pcs/daemon/app/sinatra_remote.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from typing import Optional

from tornado.locks import Lock

from pcs.daemon import log, ruby_pcsd
from pcs.daemon.app.auth_provider import (
ApiAuthProviderFactoryInterface,
Expand Down Expand Up @@ -64,29 +62,6 @@ async def _handle_request(self) -> None:
self.send_sinatra_result(result)


class SyncConfigMutualExclusive(SinatraRemote):
"""
SyncConfigMutualExclusive handles urls which should be directed to the
Sinatra remote (non-GUI) functions that can not run at the same time as
config synchronization. The exclusivity is achieved by sync_config_lock.
"""

__sync_config_lock: Lock

def initialize( # type: ignore[override]
self,
api_auth_provider_factory: ApiAuthProviderFactoryInterface,
ruby_pcsd_wrapper: ruby_pcsd.Wrapper,
sync_config_lock: Lock,
) -> None:
super().initialize(api_auth_provider_factory, ruby_pcsd_wrapper)
self.__sync_config_lock = sync_config_lock

async def _handle_request(self) -> None:
async with self.__sync_config_lock:
await super()._handle_request()


class SetCerts(SinatraRemote):
"""
SetCerts handles url for setting new certificate and key. It calls the
Expand Down Expand Up @@ -117,7 +92,6 @@ async def _handle_request(self) -> None:
def get_routes(
api_auth_provider_factory: ApiAuthProviderFactoryInterface,
ruby_pcsd_wrapper: ruby_pcsd.Wrapper,
sync_config_lock: Lock,
https_server_manage: HttpsServerManage,
) -> RoutesType:
sinatra_remote_options = dict(
Expand All @@ -136,10 +110,5 @@ def get_routes(
https_server_manage=https_server_manage,
),
),
(
r"/remote/set_configs",
SyncConfigMutualExclusive,
dict(**sinatra_remote_options, sync_config_lock=sync_config_lock),
),
(r"/remote/.*", SinatraRemote, sinatra_remote_options),
]
5 changes: 5 additions & 0 deletions pcs/daemon/async_tasks/worker/command_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ class _Cmd:
cmd=pcs_cfgsync.get_configs,
required_permission=p.FULL,
),
"pcs_cfgsync.set_configs": _Cmd(
cmd=pcs_cfgsync.set_configs,
required_permission=p.FULL,
),
"pcs_cfgsync.update_sync_options": _Cmd(
cmd=pcs_cfgsync.update_sync_options,
required_permission=p.FULL,
Expand Down Expand Up @@ -533,6 +537,7 @@ class _Cmd:
"cluster.set_permissions",
"manage_clusters.add_cluster",
"manage_clusters.remove_clusters",
"pcs_cfgsync.set_configs",
"pcs_cfgsync.update_sync_options",
"qdevice.qdevice_net_get_ca_certificate",
"resource_agent.describe_agent",
Expand Down
5 changes: 1 addition & 4 deletions pcs/daemon/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,7 @@ def make_app(https_server_manage: HttpsServerManage):
)
routes.extend(
sinatra_remote.get_routes(
api_auth_factory,
ruby_pcsd_wrapper,
sync_config_lock,
https_server_manage,
api_auth_factory, ruby_pcsd_wrapper, https_server_manage
)
)

Expand Down
Loading
Loading