Skip to content

Commit

Permalink
add validation for unaccessible resource in bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmular authored and tomjelinek committed May 18, 2018
1 parent 6028859 commit 0294f73
Show file tree
Hide file tree
Showing 13 changed files with 536 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Pcsd option to reject client initiated SSL/TLS renegotiation ([rhbz#1566382])
- Commands for listing and testing watchdog devices ([rhbz#1475318]).
- Option for setting netmtu in `pcs cluster setup` command ([rhbz#1535967])
- Validation for an unaccessible resource inside a bundle ([rhbz#1462248])

### Fixed
- `pcs cib-push diff-against=` does not consider an empty diff as an error
Expand All @@ -20,6 +21,7 @@
([rhbz#1475318]).

[ghpull#166]: https://github.com/ClusterLabs/pcs/pull/166
[rhbz#1462248]: https://bugzilla.redhat.com/show_bug.cgi?id=1462248
[rhbz#1475318]: https://bugzilla.redhat.com/show_bug.cgi?id=1475318
[rhbz#1535967]: https://bugzilla.redhat.com/show_bug.cgi?id=1535967
[rhbz#1566382]: https://bugzilla.redhat.com/show_bug.cgi?id=1566382
Expand Down
7 changes: 7 additions & 0 deletions pcs/cli/common/console_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -1415,4 +1415,11 @@ def joined_list(item_list, optional_transformations=None):
,
codes.SYSTEM_WILL_RESET:
"System will reset shortly"
,
codes.RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE: lambda info:
(
"Resource '{resource_id}' will not be accessible by the cluster "
"inside bundle '{bundle_id}'. At least one of bundle options "
"'control-port' or 'ip-range-start' has to be specified."
).format(**info)
}
16 changes: 16 additions & 0 deletions pcs/cli/common/test/test_console_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -2118,3 +2118,19 @@ def test_success(self):
"reason": "some reason",
}
)


class ResourceInBundleNotAccessible(NameBuildTest):
code = codes.RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE
def test_success(self):
self.assert_message_from_info(
(
"Resource 'resourceA' will not be accessible by the cluster "
"inside bundle 'bundleA'. At least one of bundle options "
"'control-port' or 'ip-range-start' has to be specified."
),
dict(
bundle_id="bundleA",
resource_id="resourceA",
)
)
2 changes: 2 additions & 0 deletions pcs/common/report_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
FORCE_STONITH_RESOURCE_DOES_NOT_EXIST = "FORCE_STONITH_RESOURCE_DOES_NOT_EXIST"
FORCE_NOT_SUITABLE_COMMAND = "FORCE_NOT_SUITABLE_COMMAND"
FORCE_CLEAR_CLUSTER_NODE = "FORCE_CLEAR_CLUSTER_NODE"
FORCE_RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE = "FORCE_RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE"
SKIP_OFFLINE_NODES = "SKIP_OFFLINE_NODES"
SKIP_FILE_DISTRIBUTION_ERRORS = "SKIP_FILE_DISTRIBUTION_ERRORS"
SKIP_ACTION_ON_NODES_ERRORS = "SKIP_ACTION_ON_NODES_ERRORS"
Expand Down Expand Up @@ -188,6 +189,7 @@
REQUIRED_OPTION_IS_MISSING = "REQUIRED_OPTION_IS_MISSING"
REQUIRED_OPTION_OF_ALTERNATIVES_IS_MISSING = "REQUIRED_OPTION_OF_ALTERNATIVES_IS_MISSING"
RESOURCE_BUNDLE_ALREADY_CONTAINS_A_RESOURCE = "RESOURCE_BUNDLE_ALREADY_CONTAINS_A_RESOURCE"
RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE = "RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE"
RESOURCE_CANNOT_BE_NEXT_TO_ITSELF_IN_GROUP = "RESOURCE_CANNOT_BE_NEXT_TO_ITSELF_IN_GROUP"
RESOURCE_CLEANUP_ERROR = "RESOURCE_CLEANUP_ERROR"
RESOURCE_DOES_NOT_RUN = "RESOURCE_DOES_NOT_RUN"
Expand Down
58 changes: 57 additions & 1 deletion pcs/lib/cib/resource/bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def validate_update(
else:
report_list.extend(
_validate_network_options_update(
bundle_el,
network_el,
network_options,
force_options
Expand Down Expand Up @@ -270,6 +271,23 @@ def update(
remove_when_pointless(network_element)
remove_when_pointless(storage_element)

def is_pcmk_remote_accessible(bundle_element):
"""
Check whenever pacemaker remote inside the bundle is accessible from
outside. Either a control-port or an ip-range-start have to be specified in
the network element. Returns True if accessible, False otherwise.
etree bundle_element -- bundle element to check
"""
network_el = bundle_element.find("network")
if network_el is None:
return False

for opt in ["control-port", "ip-range-start"]:
if network_el.get(opt):
return True
return False

def add_resource(bundle_element, primitive_element):
"""
Add an existing resource to an existing bundle
Expand Down Expand Up @@ -367,7 +385,43 @@ def _validate_network_options_new(options, force_options):
)
)

def _validate_network_options_update(network_el, options, force_options):
def _is_pcmk_remote_acccessible_after_update(network_el, options):
port_name = "control-port"
ip_name = "ip-range-start"
port = network_el.get(port_name)
ip = network_el.get(ip_name)
removing = lambda opt: options.get(opt) == ""
not_adding = lambda opt: options.get(opt) is None

# 3 cases in which pcmk remote will not be accessible after an update
# case1: port set, IP !set; removing port, !adding IP
case1 = port and not ip and removing(port_name) and not_adding(ip_name)
# case2: port !set, IP set; !adding port, removing IP
case2 = not port and ip and not_adding(port_name) and removing(ip_name)
# case3: port set, IP set; removing port, removing IP
case3 = port and ip and removing(port_name) and removing(ip_name)

return not (case1 or case2 or case3)

def _validate_network_options_update(
bundle_el, network_el, options, force_options
):
report_list = []
inner_primitive = get_inner_resource(bundle_el)
if (
inner_primitive is not None
and
not _is_pcmk_remote_acccessible_after_update(network_el, options)
):
report_list.append(
reports.get_problem_creator(
report_codes.FORCE_OPTIONS, force_options
)(
reports.resource_in_bundle_not_accessible,
bundle_el.get("id"),
inner_primitive.get("id")
)
)
validators = [
# TODO add validators for other keys (ip-range-start - IPv4)
validate.value_empty_or_valid(
Expand All @@ -380,6 +434,8 @@ def _validate_network_options_update(network_el, options, force_options):
),
]
return (
report_list
+
validate.run_collection_of_option_validators(options, validators)
+
validate.names_in(
Expand Down
27 changes: 20 additions & 7 deletions pcs/lib/commands/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ def create_into_bundle(
ensure_disabled=False,
wait=False,
allow_not_suitable_command=False,
allow_not_accessible_resource=False,
):
"""
Create a new resource in a cib and put it into an existing bundle
Expand All @@ -442,6 +443,8 @@ def create_into_bundle(
bool ensure_disabled is flag that keeps resource in target-role "Stopped"
mixed wait is flag for controlling waiting for pacemaker iddle mechanism
bool allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
bool allow_not_accessible_resource -- flag for
FORCE_RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE
"""
resource_agent = get_agent(
env.report_processor,
Expand Down Expand Up @@ -480,14 +483,24 @@ def create_into_bundle(
)
if ensure_disabled:
resource.common.disable(primitive_element)
resource.bundle.add_resource(
find_element_by_tag_and_id(
resource.bundle.TAG,
resources_section,
bundle_id
),
primitive_element

bundle_el = find_element_by_tag_and_id(
resource.bundle.TAG,
resources_section,
bundle_id
)
if not resource.bundle.is_pcmk_remote_accessible(bundle_el):
env.report_processor.process(
reports.get_problem_creator(
report_codes.FORCE_RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE,
allow_not_accessible_resource
)(
reports.resource_in_bundle_not_accessible,
bundle_id,
resource_id
)
)
resource.bundle.add_resource(bundle_el, primitive_element)

def bundle_create(
env, bundle_id, container_type, container_options=None,
Expand Down
Loading

0 comments on commit 0294f73

Please sign in to comment.