Skip to content

Commit

Permalink
Add cm_service_config module and supporting reconciliation utilities (c…
Browse files Browse the repository at this point in the history
…loudera-labs#257)

* Update parameter reconciliation and value normalization
* Create ServiceConfigUpdates class for service-wide configuration management
* Add cm_service_config module for service-wide configuration management
* Add fixtures for CMS and CMS service-wide configurations

Signed-off-by: Webster Mudge <[email protected]>
  • Loading branch information
wmudge authored Dec 18, 2024
1 parent 7c6fd61 commit b5d0659
Show file tree
Hide file tree
Showing 8 changed files with 626 additions and 168 deletions.
31 changes: 24 additions & 7 deletions plugins/module_utils/cm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,37 @@ def parse_role_config_group_result(role_config_group: ApiRoleConfigGroup) -> dic


def normalize_values(add: dict) -> dict:
"""Normalize whitespace of parameter values.
"""Normalize parameter values. Strings have whitespace trimmed, integers are
converted to strings, and Boolean values are converted their string representation
and lowercased.
Args:
add (dict): Parameters to review
Returns:
dict: Normalized parameters
"""
return {k: (v.strip() if isinstance(v, str) else v) for k, v in add.items()}

def _normalize(value):
if isinstance(value, str):
return value.strip()
elif isinstance(value, int):
return str(value)
elif isinstance(value, bool):
return str(value).lower()
else:
return value

return {k: _normalize(v) for k, v in add.items()}


def resolve_parameter_updates(
current: dict, incoming: dict, purge: bool = False
) -> dict:
"""Produce a change set between two parameter dictionaries.
The function will normalize parameter values to remove whitespace.
The function will normalize parameter values to remove whitespace from strings,
convert integers and Booleans to their string representations.
Args:
current (dict): Existing parameters
Expand All @@ -131,20 +145,23 @@ def resolve_parameter_updates(
dict: A change set of the updates
"""
updates = {}
diff = recursive_diff(current, incoming)

diff = recursive_diff(current, normalize_values(incoming))

if diff is not None:
updates = {
k: v
for k, v in normalize_values(diff[1]).items()
for k, v in diff[1].items()
if k in current or (k not in current and v is not None)
}

if purge:
# Add the other non-defaults
# Add the remaining non-default values for removal
updates = {
**updates,
**{k: None for k in diff[0].keys() if k not in diff[1]},
}

return updates


Expand Down Expand Up @@ -384,7 +401,7 @@ def initialize_client(self):
"""Creates the CM API client"""
config = Configuration()

# If provided a CML endpoint URL, use it directly
# If provided a CM endpoint URL, use it directly
if self.url:
config.host = str(self.url).rstrip(" /")
# Otherwise, run discovery on missing parts
Expand Down
28 changes: 26 additions & 2 deletions plugins/module_utils/service_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@
# limitations under the License.

"""
A common functions for Cloudera Manager service management
A common functions for service management
"""

from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import (
_parse_output,
resolve_parameter_updates,
)

from cm_client import ApiService
from cm_client import (
ApiConfig,
ApiService,
ApiServiceConfig,
)

SERVICE_OUTPUT = [
"client_config_staleness_status",
Expand All @@ -44,3 +49,22 @@ def parse_service_result(service: ApiService) -> dict:
output = dict(cluster_name=service.cluster_ref.cluster_name)
output.update(_parse_output(service.to_dict(), SERVICE_OUTPUT))
return output


class ServiceConfigUpdates(object):
def __init__(self, existing: ApiServiceConfig, updates: dict, purge: bool) -> None:
current = {r.name: r.value for r in existing.items}
changeset = resolve_parameter_updates(current, updates, purge)

self.diff = dict(
before={k: current[k] if k in current else None for k in changeset.keys()},
after=changeset,
)

self.config = ApiServiceConfig(
items=[ApiConfig(name=k, value=v) for k, v in changeset.items()]
)

@property
def changed(self) -> bool:
return bool(self.config.items)
Loading

0 comments on commit b5d0659

Please sign in to comment.