Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update cm_service module #265

Merged
merged 58 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4df1dcd
Update pytest marker for 'service_config'
wmudge Dec 17, 2024
4d0dea7
Update message for cms_service_config changes
wmudge Dec 17, 2024
c9798f1
Move role and role config group result parsing to separate modules. U…
wmudge Dec 17, 2024
e72cb3b
Enable service-wide configuration management. Update return object.
wmudge Dec 17, 2024
7612a7e
Update to use pytest fixtures for Cloudera Manager Service
wmudge Dec 17, 2024
0b5c98f
Update conn object from fixture
wmudge Dec 17, 2024
a91c59f
Create generic configuration list reconcilation utility
wmudge Dec 18, 2024
fdfbfb1
Add cm_service_role_config module
wmudge Dec 18, 2024
c0a82f9
Add utilities for discovering base role config group for a given role…
wmudge Dec 18, 2024
eab3d18
Add utilities for getting roles by role type
wmudge Dec 18, 2024
6191de9
Add utility to set Cloudera Manager Service role config group configu…
wmudge Dec 18, 2024
8f6334e
Add cm_service_role_config_group module and tests
wmudge Dec 18, 2024
a4e6394
Add 'config' dictionary to parsed Role Config Group results
wmudge Dec 19, 2024
1a275f2
Update utlity function for Role Config Group tests to handle all para…
wmudge Dec 19, 2024
44af849
Move host_monitor and host_monitor_config fixtures and update pytest …
wmudge Dec 19, 2024
72418dc
Update to use reworked host_monitor_config fixture
wmudge Dec 19, 2024
c7602e5
Add cm_service_role_config_group module and tests
wmudge Dec 19, 2024
f7835a8
Add host utilities
wmudge Dec 20, 2024
e4f5e9f
Add utility function for role config group retrieval
wmudge Dec 20, 2024
e287cbf
Add config parameters to parse_role_result. Add utility for role data…
wmudge Dec 20, 2024
6904d89
Rename Host Monitor fixtures. Add host_monitor_state for general role…
wmudge Dec 20, 2024
26402b2
Add cm_service_role and tests
wmudge Dec 20, 2024
092d3dc
Add 'role' marker
wmudge Dec 20, 2024
904b9c2
Consolidate host_monitor role
wmudge Dec 29, 2024
44e0fc0
Fix invalid fixture references
wmudge Jan 3, 2025
159d0d0
Update testing requirements.txt
wmudge Jan 3, 2025
516af16
Fix name of Cloudera Manager client library
wmudge Jan 3, 2025
4a446ac
Add pytest comment and update formatting
wmudge Jan 3, 2025
3558133
Update docstrings and add missing imports for fixtures and fixture ut…
wmudge Jan 3, 2025
7c5aa31
Remove unused imports
wmudge Jan 3, 2025
b3c5556
Update cm_service_role and tests to support role override configurati…
wmudge Jan 3, 2025
500f16f
Update provision_cm_role to handle absent role on deletion and set_cm…
wmudge Jan 10, 2025
6e4b942
Update CMS and Host Monitor fixtures.
wmudge Jan 10, 2025
9781736
Add TODO note for config defaults
wmudge Jan 10, 2025
a184753
Add stub for role config group reconcilation
wmudge Jan 10, 2025
13dfede
Expand parse_service_result to handle service subelements (service-wi…
wmudge Jan 10, 2025
1585a8d
Update to use parse_role_result utility
wmudge Jan 10, 2025
cdb53f2
Update cm_service for full lifecycle management of the service and it…
wmudge Jan 10, 2025
c882974
Update documentation for cm_service (and add a reference document)
wmudge Jan 10, 2025
b57bb9f
Update cm_service_info to use common utilities for collection and par…
wmudge Jan 10, 2025
a62cb3a
Rename and move return value reference YAML
wmudge Jan 10, 2025
256abbe
Add new modules to module action group in collection
wmudge Jan 10, 2025
853d3f3
Remove unused function
wmudge Jan 10, 2025
68ce861
Add diff_mode and platform documentation attributes
wmudge Jan 10, 2025
d47f5fc
Update cm_service_role_config_group and tests
wmudge Jan 10, 2025
95f8694
Add cm_service_role_config_group_info module
wmudge Jan 10, 2025
becefc7
Remove host_monitor_state fixture
wmudge Jan 13, 2025
eefc7f2
Update to remove custom Exceptions
wmudge Jan 13, 2025
abc0995
Rename get_roles to read_roles and update to retrieve role override c…
wmudge Jan 13, 2025
5f1ef32
Update to remove custom Exception handling
wmudge Jan 13, 2025
d316425
Add cm_service_role_info module and tests
wmudge Jan 13, 2025
200b915
Update documentation and reconcilation logic for maintenance and oper…
wmudge Jan 13, 2025
13d05e9
Update for non-existence of CM service
wmudge Jan 13, 2025
17e25ed
Remove view parameter
wmudge Jan 13, 2025
77c8e35
Add documentation for CM service role return values
wmudge Jan 13, 2025
6634e82
Add 'role_state' to pytest markers
wmudge Jan 13, 2025
67493b3
Update pytest fixture usage
wmudge Jan 13, 2025
d3f5d57
Add test for cm_service_role_config_info
wmudge Jan 13, 2025
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
8 changes: 6 additions & 2 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
# -*- coding: utf-8 -*-

# Copyright 2023 Cloudera, Inc. All Rights Reserved.
# Copyright 2025 Cloudera, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -26,6 +26,10 @@ action_groups:
- cm_resource
- cm_service_info
- cm_service
- cm_service_role_config_group_info
- cm_service_role_config_group
- cm_service_role_info
- cm_service_role
- cm_trial_license
- cm_version_info
- cm_endpoint_info
Expand Down
4 changes: 2 additions & 2 deletions plugins/module_utils/cluster_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"""

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

from cm_client import ApiCluster
Expand All @@ -42,5 +42,5 @@
def parse_cluster_result(cluster: ApiCluster) -> dict:
# Retrieve full_version as version
output = dict(version=cluster.full_version)
output.update(_parse_output(cluster.to_dict(), CLUSTER_OUTPUT))
output.update(normalize_output(cluster.to_dict(), CLUSTER_OUTPUT))
return output
75 changes: 27 additions & 48 deletions plugins/module_utils/cm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@
from cm_client import (
ApiClient,
ApiCommand,
ApiConfig,
ApiConfigList,
ApiRole,
ApiRoleConfigGroup,
Configuration,
)
from cm_client.rest import ApiException, RESTClientObject
Expand All @@ -47,34 +46,8 @@
__credits__ = ["[email protected]"]
__maintainer__ = ["[email protected]"]

ROLE_OUTPUT = [
"commission_state",
"config_staleness_status",
"ha_status",
"health_checks",
"health_summary",
# "host_ref",
"maintenance_mode",
"maintenance_owners",
"name",
# "role_config_group_ref",
"role_state",
# "service_ref",
"tags",
"type",
"zoo_keeper_server_mode",
]

ROLE_CONFIG_GROUP = [
"name",
"role_type",
"base",
"display_name",
# "service_ref",
]


def _parse_output(entity: dict, filter: list) -> dict:

def normalize_output(entity: dict, filter: list) -> dict:
output = {}
for k in filter:
if k == "tags":
Expand All @@ -85,24 +58,6 @@ def _parse_output(entity: dict, filter: list) -> dict:
return output


def parse_role_result(role: ApiRole) -> dict:
# Retrieve only the host_id, role_config_group, and service identifiers
output = dict(
host_id=role.host_ref.host_id,
role_config_group_name=role.role_config_group_ref.role_config_group_name,
service_name=role.service_ref.service_name,
)
output.update(_parse_output(role.to_dict(), ROLE_OUTPUT))
return output


def parse_role_config_group_result(role_config_group: ApiRoleConfigGroup) -> dict:
# Retrieve only the service identifier
output = dict(service_name=role_config_group.service_ref.service_name)
output.update(_parse_output(role_config_group.to_dict(), ROLE_CONFIG_GROUP))
return output


def normalize_values(add: dict) -> dict:
"""Normalize parameter values. Strings have whitespace trimmed, integers are
converted to strings, and Boolean values are converted their string representation
Expand Down Expand Up @@ -149,6 +104,11 @@ def resolve_parameter_updates(
diff = recursive_diff(current, normalize_values(incoming))

if diff is not None:
# TODO Lookup default for v=None to avoid issues with CM
# CM sometimes fails to find the default value for a parameter
# However, a view=full will return the default, so if we can
# change this method's signature to include that reference, we
# can short-circuit CM's problematic lookup of the default value.
updates = {
k: v
for k, v in diff[1].items()
Expand Down Expand Up @@ -191,6 +151,25 @@ def resolve_tag_updates(
return (delta_add, delta_del)


class ConfigListUpdates(object):
def __init__(self, existing: ApiConfigList, 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 = ApiConfigList(
items=[ApiConfig(name=k, value=v) for k, v in changeset.items()]
)

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


class ClusterTemplate(object):
IDEMPOTENT_IDS = frozenset(
["refName", "name", "clusterName", "hostName", "product"]
Expand Down
6 changes: 3 additions & 3 deletions plugins/module_utils/data_context_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"""

from ansible_collections.cloudera.cluster.plugins.module_utils.cm_utils import (
_parse_output,
normalize_output,
)
from cm_client import ApiDataContextList

Expand All @@ -38,9 +38,9 @@
]


def _parse_output(data: dict, keys: list) -> dict:
def normalize_output(data: dict, keys: list) -> dict:
return {key: data[key] for key in keys if key in data}


def parse_data_context_result(data_contexts: ApiDataContextList) -> list:
return [_parse_output(item, DATA_CONTEXT_OUTPUT) for item in data_contexts.items]
return [normalize_output(item, DATA_CONTEXT_OUTPUT) for item in data_contexts.items]
57 changes: 57 additions & 0 deletions plugins/module_utils/host_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright 2024 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
A common functions for Cloudera Manager hosts
"""

from cm_client import (
ApiClient,
ApiHost,
ApiHostRef,
HostsResourceApi,
)
from cm_client.rest import ApiException


def get_host(
api_client: ApiClient, hostname: str = None, host_id: str = None
) -> ApiHost:
if hostname:
return next(
(
h
for h in HostsResourceApi(api_client).read_hosts().items
if h.hostname == hostname
),
None,
)
else:
try:
return HostsResourceApi(api_client).read_host(host_id)
except ApiException as ex:
if ex.status != 404:
raise ex
else:
return None


def get_host_ref(
api_client: ApiClient, hostname: str = None, host_id: str = None
) -> ApiHostRef:
host = get_host(api_client, hostname, host_id)
if host is not None:
return ApiHostRef(host.host_id, host.hostname)
else:
return None
4 changes: 2 additions & 2 deletions plugins/module_utils/parcel_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from cm_client import ApiParcel, ParcelResourceApi

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


Expand Down Expand Up @@ -138,5 +138,5 @@ def activate(self):
def parse_parcel_result(parcel: ApiParcel) -> dict:
# Retrieve only the cluster identifier
output = dict(cluster_name=parcel.cluster_ref.cluster_name)
output.update(_parse_output(parcel.to_dict(), PARCEL))
output.update(normalize_output(parcel.to_dict(), PARCEL))
return output
89 changes: 89 additions & 0 deletions plugins/module_utils/role_config_group_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright 2025 Cloudera, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

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

from cm_client import (
ApiClient,
ApiRoleConfigGroup,
RoleConfigGroupsResourceApi,
MgmtRoleConfigGroupsResourceApi,
)

ROLE_CONFIG_GROUP = [
"name",
"role_type",
"base",
"display_name",
# "service_ref",
]


def parse_role_config_group_result(role_config_group: ApiRoleConfigGroup) -> dict:
"""Parse a Role Config Group into a normalized dictionary.

Returns the following:
- name (str)
- role_type (str)
- base (bool)
- display_name (str)
- config (dict)

Args:
role_config_group (ApiRoleConfigGroup): Role Config Group

Returns:
dict: Normalized dictionary of returned values
"""
# Retrieve only the service identifier
output = dict(service_name=role_config_group.service_ref.service_name)
output.update(normalize_output(role_config_group.to_dict(), ROLE_CONFIG_GROUP))
output.update(config={c.name: c.value for c in role_config_group.config.items})
return output


def get_base_role_config_group(
api_client: ApiClient, cluster_name: str, service_name: str, role_type: str
) -> ApiRoleConfigGroup:
rcg_api = RoleConfigGroupsResourceApi(api_client)
return next(
iter(
[
r
for r in rcg_api.read_role_config_groups(
cluster_name, service_name
).items
if r.role_type == role_type and r.base
]
),
None,
)


def get_mgmt_base_role_config_group(
api_client: ApiClient, role_type: str
) -> ApiRoleConfigGroup:
rcg_api = MgmtRoleConfigGroupsResourceApi(api_client)
return next(
iter(
[
r
for r in rcg_api.read_role_config_groups().items
if r.role_type == role_type and r.base
]
),
None,
)
Loading
Loading