Skip to content

Commit b3efb78

Browse files
committed
feat: ha_cluster_info: export firewall and selinux
1 parent 9002611 commit b3efb78

17 files changed

+576
-6
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,10 @@ may not be present in the export.
15771577
* [`ha_cluster_enable_repos`](#ha_cluster_enable_repos) - RHEL and CentOS only
15781578
* [`ha_cluster_enable_repos_resilient_storage`](#ha_cluster_enable_repos_resilient_storage) -
15791579
RHEL and CentOS only
1580+
* [`ha_cluster_manage_firewall`](#ha_cluster_manage_firewall) (requires
1581+
`python3-firewall` to be installed on managed nodes)
1582+
* [`ha_cluster_manage_selinux`](#ha_cluster_manage_selinux) (requires
1583+
`python3-policycoreutils` to be installed on managed nodes)
15801584
* [`ha_cluster_cluster_present`](#ha_cluster_cluster_present)
15811585
* [`ha_cluster_start_on_boot`](#ha_cluster_start_on_boot)
15821586
* [`ha_cluster_install_cloud_agents`](#ha_cluster_install_cloud_agents) -
@@ -1613,6 +1617,10 @@ may not be present in the export.
16131617
These are supposed to contain paths to files with TLS certificate and
16141618
private key for pcsd. Since the certificate and key themselves are not
16151619
exported, these variables are not present in the export either.
1620+
* [`ha_cluster_pcsd_certificates`](#ha_cluster_pcsd_certificates) - The value
1621+
of this variable is set to the variable `certificate_requests` in the
1622+
`certificate` role. See the `certificate` role documentation to check if it
1623+
provides any means for exporting configuration.
16161624
* [`ha_cluster_regenerate_keys`](#ha_cluster_regenerate_keys) - It is your
16171625
responsibility to decide if you want to use existing keys or generate new
16181626
ones.

library/ha_cluster_info.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
requirements:
2828
- pcs-0.10.8 or newer installed on managed nodes
2929
- pcs-0.10.8 or newer for exporting corosync configuration
30+
- python3-firewall for exporting ha_cluster_manage_firewall
31+
- python3-policycoreutils for exporting ha_cluster_manage_selinux
3032
- python 3.6 or newer
3133
"""
3234

@@ -51,6 +53,8 @@
5153
- Following variables are present in the output
5254
- ha_cluster_enable_repos
5355
- ha_cluster_enable_repos_resilient_storage
56+
- ha_cluster_manage_firewall
57+
- ha_cluster_manage_selinux
5458
- ha_cluster_cluster_present
5559
- ha_cluster_start_on_boot
5660
- ha_cluster_install_cloud_agents
@@ -77,6 +81,7 @@
7781
- ha_cluster_fence_virt_key_src
7882
- ha_cluster_pcsd_public_key_src
7983
- ha_cluster_pcsd_private_key_src
84+
- ha_cluster_pcsd_certificates
8085
- ha_cluster_regenerate_keys
8186
- HORIZONTALLINE
8287
"""
@@ -88,6 +93,34 @@
8893
# pylint: disable=no-name-in-module
8994
from ansible.module_utils.ha_cluster_lsr.info import exporter, loader
9095

96+
try:
97+
# firewall module doesn't provide type hints
98+
from firewall.client import FirewallClient # type:ignore
99+
100+
HAS_FIREWALL = True
101+
except ImportError:
102+
# create the class so it can be replaced by a mock in unit tests
103+
class FirewallClient: # type: ignore
104+
# pylint: disable=missing-class-docstring
105+
# pylint: disable=too-few-public-methods
106+
pass
107+
108+
HAS_FIREWALL = False
109+
110+
try:
111+
# selinux module doesn't provide type hints
112+
from seobject import portRecords as SelinuxPortRecords # type: ignore
113+
114+
HAS_SELINUX = True
115+
except ImportError:
116+
# create the class so it can be replaced by a mock in unit tests
117+
class SelinuxPortRecords: # type: ignore
118+
# pylint: disable=missing-class-docstring
119+
# pylint: disable=too-few-public-methods
120+
pass
121+
122+
HAS_SELINUX = False
123+
91124

92125
def get_cmd_runner(module: AnsibleModule) -> loader.CommandRunner:
93126
"""
@@ -129,6 +162,29 @@ def export_os_configuration(module: AnsibleModule) -> Dict[str, Any]:
129162
exporter.export_install_cloud_agents(installed_packages)
130163
)
131164

165+
if HAS_FIREWALL:
166+
fw_client = FirewallClient()
167+
fw_config = loader.get_firewall_config(fw_client)
168+
manage_firewall = False
169+
if fw_config is not None:
170+
manage_firewall = exporter.export_manage_firewall(fw_config)
171+
result["ha_cluster_manage_firewall"] = manage_firewall
172+
173+
# ha_cluster_manage_selinux is irrelevant when running the role if
174+
# ha_cluster_manage_firewall is not True
175+
if HAS_SELINUX and manage_firewall:
176+
selinux_ports = SelinuxPortRecords()
177+
ha_ports_firewall = loader.get_firewall_ha_cluster_ports(fw_client)
178+
ha_ports_selinux = loader.get_selinux_ha_cluster_ports(
179+
selinux_ports
180+
)
181+
if ha_ports_firewall is not None and ha_ports_selinux is not None:
182+
result["ha_cluster_manage_selinux"] = (
183+
exporter.export_manage_selinux(
184+
ha_ports_firewall, ha_ports_selinux
185+
)
186+
)
187+
132188
return result
133189

134190

module_utils/ha_cluster_lsr/info/exporter.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
__metaclass__ = type
1313

1414
from contextlib import contextmanager
15-
from typing import Any, Dict, Iterator, List, Optional
15+
from typing import Any, Dict, Iterator, List, Optional, Tuple
1616

1717

1818
class JsonMissingKey(Exception):
@@ -106,6 +106,35 @@ def export_start_on_boot(
106106
return corosync_enabled or pacemaker_enabled
107107

108108

109+
def export_manage_firewall(zone_config: Dict[str, Any]) -> bool:
110+
"""
111+
Export whether HA cluster is enabled in firewall
112+
113+
zone_config -- configuration of a firewall zone
114+
"""
115+
return (
116+
"high-availability" in zone_config["services"]
117+
or ("1229", "tcp") in zone_config["ports"]
118+
)
119+
120+
121+
def export_manage_selinux(
122+
ha_ports_used: List[Tuple[str, str]],
123+
ha_ports_selinux: Tuple[List[str], List[str]],
124+
) -> bool:
125+
"""
126+
Export whether HA cluster ports are managed by selinux
127+
128+
ha_ports_used -- ports used by HA cluster
129+
ha_ports_selinux -- ports labelled for HA cluster in selinux
130+
"""
131+
# convert selinux ports to the same format as used ports
132+
ports_selinux_tuples = [(port, "tcp") for port in ha_ports_selinux[0]] + [
133+
(port, "udp") for port in ha_ports_selinux[1]
134+
]
135+
return bool(frozenset(ports_selinux_tuples) & frozenset(ha_ports_used))
136+
137+
109138
def export_corosync_cluster_name(corosync_conf_dict: Dict[str, Any]) -> str:
110139
"""
111140
Extract cluster name form corosync config in pcs format

module_utils/ha_cluster_lsr/info/loader.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,67 @@ def is_service_enabled(run_command: CommandRunner, service: str) -> bool:
147147
return rc == 0
148148

149149

150+
def get_firewall_config(
151+
fw: Any, # firewall module doesn't provide type hints
152+
) -> Optional[Dict[str, Any]]:
153+
"""
154+
Get simplified firewall config of a given zone or None on error
155+
156+
fw -- firewall client instance
157+
"""
158+
try:
159+
settings = fw.config().getZoneByName(fw.getDefaultZone()).getSettings()
160+
return {
161+
"services": settings.getServices(),
162+
"ports": settings.getPorts(),
163+
}
164+
# pylint: disable=broad-exception-caught
165+
# catch any exception, firewall is not installed or not running, etc.
166+
except Exception:
167+
return None
168+
169+
170+
def get_firewall_ha_cluster_ports(
171+
fw: Any, # firewall module doesn't provide type hints
172+
) -> Optional[List[Tuple[str, str]]]:
173+
"""
174+
Get ports used by HA cluster or None on error
175+
176+
fw -- firewall client instance
177+
"""
178+
try:
179+
return (
180+
fw.config()
181+
.getServiceByName("high-availability")
182+
.getSettings()
183+
.getPorts()
184+
)
185+
# pylint: disable=broad-exception-caught
186+
# catch any exception, firewall is not installed or not running, etc.
187+
except Exception:
188+
return None
189+
190+
191+
def get_selinux_ha_cluster_ports(
192+
selinux_ports: Any, # selinux module doesn't provide type hints
193+
) -> Optional[Tuple[List[str], List[str]]]:
194+
"""
195+
Get TCP and UDP ports labelled for HA cluster in selinux or None on error
196+
197+
selinux_ports -- selinux port records instance
198+
"""
199+
try:
200+
all_ports = selinux_ports.get_all_by_type()
201+
return (
202+
all_ports.get(("cluster_port_t", "tcp"), []),
203+
all_ports.get(("cluster_port_t", "udp"), []),
204+
)
205+
# pylint: disable=broad-exception-caught
206+
# catch any exception, selinux not available, etc.
207+
except Exception:
208+
return None
209+
210+
150211
def _call_pcs_cli(
151212
run_command: CommandRunner, command: List[str]
152213
) -> Dict[str, Any]:

tests/tests_cluster_advanced_knet_full.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@
132132
ha_cluster_facts | combine({
133133
'ha_cluster_enable_repos': 'it depends on test environment',
134134
'ha_cluster_enable_repos_resilient_storage': 'it depends on test environment',
135+
'ha_cluster_manage_firewall': 'it depends on test environment',
136+
'ha_cluster_manage_selinux': 'it depends on test environment',
135137
'ha_cluster_install_cloud_agents': 'it depends on test environment',
136138
'ha_cluster_node_options': 'it depends on test environment'
137139
})
@@ -166,6 +168,8 @@
166168
allow_list: ["grant", "read", "write"]
167169
ha_cluster_enable_repos: "it depends on test environment"
168170
ha_cluster_enable_repos_resilient_storage: "it depends on test environment"
171+
ha_cluster_manage_firewall: "it depends on test environment"
172+
ha_cluster_manage_selinux: "it depends on test environment"
169173
ha_cluster_install_cloud_agents: "it depends on test environment"
170174
ha_cluster_node_options: "it depends on test environment"
171175
block:

tests/tests_cluster_advanced_knet_implicit.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
ha_cluster_facts | combine({
7171
'ha_cluster_enable_repos': 'it depends on test environment',
7272
'ha_cluster_enable_repos_resilient_storage': 'it depends on test environment',
73+
'ha_cluster_manage_firewall': 'it depends on test environment',
74+
'ha_cluster_manage_selinux': 'it depends on test environment',
7375
'ha_cluster_install_cloud_agents': 'it depends on test environment',
7476
'ha_cluster_node_options': 'it depends on test environment'
7577
})
@@ -87,6 +89,8 @@
8789
allow_list: ["grant", "read", "write"]
8890
ha_cluster_enable_repos: "it depends on test environment"
8991
ha_cluster_enable_repos_resilient_storage: "it depends on test environment"
92+
ha_cluster_manage_firewall: "it depends on test environment"
93+
ha_cluster_manage_selinux: "it depends on test environment"
9094
ha_cluster_install_cloud_agents: "it depends on test environment"
9195
ha_cluster_node_options: "it depends on test environment"
9296
block:

tests/tests_cluster_advanced_udp_full.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@
9494
ha_cluster_facts | combine({
9595
'ha_cluster_enable_repos': 'it depends on test environment',
9696
'ha_cluster_enable_repos_resilient_storage': 'it depends on test environment',
97+
'ha_cluster_manage_firewall': 'it depends on test environment',
98+
'ha_cluster_manage_selinux': 'it depends on test environment',
9799
'ha_cluster_install_cloud_agents': 'it depends on test environment',
98100
'ha_cluster_node_options': 'it depends on test environment'
99101
})
@@ -111,6 +113,8 @@
111113
allow_list: ["grant", "read", "write"]
112114
ha_cluster_enable_repos: "it depends on test environment"
113115
ha_cluster_enable_repos_resilient_storage: "it depends on test environment"
116+
ha_cluster_manage_firewall: "it depends on test environment"
117+
ha_cluster_manage_selinux: "it depends on test environment"
114118
ha_cluster_install_cloud_agents: "it depends on test environment"
115119
ha_cluster_node_options: "it depends on test environment"
116120
block:

tests/tests_cluster_basic.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@
120120
ha_cluster_facts | combine({
121121
'ha_cluster_enable_repos': 'it depends on test environment',
122122
'ha_cluster_enable_repos_resilient_storage': 'it depends on test environment',
123+
'ha_cluster_manage_firewall': 'it depends on test environment',
124+
'ha_cluster_manage_selinux': 'it depends on test environment',
123125
'ha_cluster_install_cloud_agents': 'it depends on test environment',
124126
'ha_cluster_node_options': 'it depends on test environment'
125127
})
@@ -141,6 +143,8 @@
141143
allow_list: ["grant", "read", "write"]
142144
ha_cluster_enable_repos: "it depends on test environment"
143145
ha_cluster_enable_repos_resilient_storage: "it depends on test environment"
146+
ha_cluster_manage_firewall: "it depends on test environment"
147+
ha_cluster_manage_selinux: "it depends on test environment"
144148
ha_cluster_install_cloud_agents: "it depends on test environment"
145149
ha_cluster_node_options: "it depends on test environment"
146150
block:

tests/tests_cluster_basic_cloud_packages.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
ha_cluster_facts | combine({
6767
'ha_cluster_enable_repos': 'it depends on test environment',
6868
'ha_cluster_enable_repos_resilient_storage': 'it depends on test environment',
69+
'ha_cluster_manage_firewall': 'it depends on test environment',
70+
'ha_cluster_manage_selinux': 'it depends on test environment',
6971
'ha_cluster_node_options': 'it depends on test environment'
7072
})
7173
}}
@@ -87,6 +89,8 @@
8789
allow_list: ["grant", "read", "write"]
8890
ha_cluster_enable_repos: "it depends on test environment"
8991
ha_cluster_enable_repos_resilient_storage: "it depends on test environment"
92+
ha_cluster_manage_firewall: "it depends on test environment"
93+
ha_cluster_manage_selinux: "it depends on test environment"
9094
ha_cluster_node_options: "it depends on test environment"
9195
block:
9296
- name: Print exported configuration

tests/tests_cluster_basic_disabled.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
ha_cluster_facts | combine({
4646
'ha_cluster_enable_repos': 'it depends on test environment',
4747
'ha_cluster_enable_repos_resilient_storage': 'it depends on test environment',
48+
'ha_cluster_manage_firewall': 'it depends on test environment',
49+
'ha_cluster_manage_selinux': 'it depends on test environment',
4850
'ha_cluster_install_cloud_agents': 'it depends on test environment',
4951
'ha_cluster_node_options': 'it depends on test environment'
5052
})
@@ -66,6 +68,8 @@
6668
allow_list: ["grant", "read", "write"]
6769
ha_cluster_enable_repos: "it depends on test environment"
6870
ha_cluster_enable_repos_resilient_storage: "it depends on test environment"
71+
ha_cluster_manage_firewall: "it depends on test environment"
72+
ha_cluster_manage_selinux: "it depends on test environment"
6973
ha_cluster_install_cloud_agents: "it depends on test environment"
7074
ha_cluster_node_options: "it depends on test environment"
7175
block:

0 commit comments

Comments
 (0)