diff --git a/.gitignore b/.gitignore index 21c95da..0fd37ba 100644 --- a/.gitignore +++ b/.gitignore @@ -3,14 +3,15 @@ SOURCES/backports-3.11.4.gem SOURCES/ethon-0.11.0.gem SOURCES/ffi-1.9.25.gem SOURCES/json-2.1.0.gem -SOURCES/multi_json-1.13.1.gem SOURCES/mustermann-1.0.3.gem -SOURCES/open4-1.3.4.gem -SOURCES/pcs-0.10.1.tar.gz +SOURCES/open4-1.3.4-1.gem +SOURCES/pcs-0.10.2.tar.gz +SOURCES/pcs-web-ui-0.1.1.tar.gz +SOURCES/pcs-web-ui-node-modules-0.1.1.tar.xz SOURCES/pyagentx-0.4.pcs.2.tar.gz SOURCES/rack-2.0.6.gem SOURCES/rack-protection-2.0.4.gem SOURCES/rack-test-1.0.0.gem SOURCES/sinatra-2.0.4.gem -SOURCES/tilt-2.0.8.gem +SOURCES/tilt-2.0.9.gem SOURCES/tornado-5.0.2.tar.gz diff --git a/.pcs.metadata b/.pcs.metadata index dfc1f83..ccc1089 100644 --- a/.pcs.metadata +++ b/.pcs.metadata @@ -1,16 +1,17 @@ -80dc7788a3468fb7dd362a4b8bedd9efb373de89 SOURCES/HAM-logo.png +679a4ce22a33ffd4d704261a17c00cff98d9499a SOURCES/HAM-logo.png edf08f3a0d9e202048857d78ddda44e59294084c SOURCES/backports-3.11.4.gem 3c921ceeb2847be8cfa25704be74923e233786bd SOURCES/ethon-0.11.0.gem 86fa011857f977254ccf39f507587310f9ade768 SOURCES/ffi-1.9.25.gem 8b9e81a2a6ff57f97bec1f65940c61cc6b6d81be SOURCES/json-2.1.0.gem -ff6e0965061cb6f604ee4d87a2cf96a2917f9f88 SOURCES/multi_json-1.13.1.gem 2d090e7d3cd2a35efeaeacf006100fb83b828686 SOURCES/mustermann-1.0.3.gem -41a7fe9f8e3e02da5ae76c821b89c5b376a97746 SOURCES/open4-1.3.4.gem -e933ccad637141fc4814890d82c5d274cee45b32 SOURCES/pcs-0.10.1.tar.gz +41a7fe9f8e3e02da5ae76c821b89c5b376a97746 SOURCES/open4-1.3.4-1.gem +0d10fd24bb7268013766c01867395486bad62dcb SOURCES/pcs-0.10.2.tar.gz +c81162a6dc4811a8b988c51182cf675938bcf227 SOURCES/pcs-web-ui-0.1.1.tar.gz +175427fbf15f292a0a3454eda132543a952cca96 SOURCES/pcs-web-ui-node-modules-0.1.1.tar.xz 3176b2f2b332c2b6bf79fe882e83feecf3d3f011 SOURCES/pyagentx-0.4.pcs.2.tar.gz b15267e1f94e69238a00a6f1bd48fb7683c03a78 SOURCES/rack-2.0.6.gem c1376e5678322b401d988d261762a78bf2cf3361 SOURCES/rack-protection-2.0.4.gem 4c99cf0a82372a1bc5968c1551d9e606b68b4879 SOURCES/rack-test-1.0.0.gem 1c85f05c874bc8c0bf9c40291ea2d430090cdfd9 SOURCES/sinatra-2.0.4.gem -ac4b5bc216a961287b3c2ebde877c039d2f1a83d SOURCES/tilt-2.0.8.gem +55a75a80e29731d072fe44dfaf865479b65c27fd SOURCES/tilt-2.0.9.gem c8690c8108ce9edd6c55151f66ade61e0a11ab10 SOURCES/tornado-5.0.2.tar.gz diff --git a/SOURCES/bz1656953-01-drop-removed-command-pcs-resource-show-from-help.patch b/SOURCES/bz1656953-01-drop-removed-command-pcs-resource-show-from-help.patch deleted file mode 100644 index 47c051e..0000000 --- a/SOURCES/bz1656953-01-drop-removed-command-pcs-resource-show-from-help.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 5c564d478916abda776215cdec2a0ecf7f1bd782 Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Fri, 11 Jan 2019 15:13:09 +0100 -Subject: [PATCH] drop removed command "pcs resource show" from help - ---- - pcs/pcs.8 | 4 ++-- - pcs/usage.py | 4 ++-- - 2 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/pcs/pcs.8 b/pcs/pcs.8 -index 47d0dda7..aa72aa91 100644 ---- a/pcs/pcs.8 -+++ b/pcs/pcs.8 -@@ -961,10 +961,10 @@ Authenticate current user to local pcsd. This is requiered to run some pcs comma - .SH EXAMPLES - .TP - Show all resources --.B # pcs resource show -+.B # pcs resource config - .TP - Show options specific to the 'VirtualIP' resource --.B # pcs resource show VirtualIP -+.B # pcs resource config VirtualIP - .TP - Create a new resource called 'VirtualIP' with options - .B # pcs resource create VirtualIP ocf:heartbeat:IPaddr2 ip=192.168.0.99 cidr_netmask=32 nic=eth2 op monitor interval=30s -diff --git a/pcs/usage.py b/pcs/usage.py -index 21c6e486..4487d844 100644 ---- a/pcs/usage.py -+++ b/pcs/usage.py -@@ -558,10 +558,10 @@ Commands: - - Examples: - -- pcs resource show -+ pcs resource config - Show all resources. - -- pcs resource show VirtualIP -+ pcs resource config VirtualIP - Show options specific to the 'VirtualIP' resource. - - --- -2.17.0 - diff --git a/SOURCES/bz1657166-01-Updating-a-bundle-is-a-bit-cumber.patch b/SOURCES/bz1657166-01-Updating-a-bundle-is-a-bit-cumber.patch new file mode 100644 index 0000000..f973ced --- /dev/null +++ b/SOURCES/bz1657166-01-Updating-a-bundle-is-a-bit-cumber.patch @@ -0,0 +1,1100 @@ +From 1ae4be91077978e324f2e463eaa97dcccfdb7057 Mon Sep 17 00:00:00 2001 +From: Ivan Devat +Date: Thu, 20 Jun 2019 11:44:46 +0200 +Subject: [PATCH 2/3] squash bz1657166 Updating a bundle is a bit cumber + +disallow to specify container type in bundle reset + +fix id conflict in bundle reset +--- + pcs/cli/resource/parse_args.py | 21 ++ + pcs/lib/cib/resource/bundle.py | 216 +++++++------- + pcs/lib/commands/resource.py | 41 +-- + pcs/lib/xml_tools.py | 15 + + pcs/pcs.8 | 2 +- + pcs/resource.py | 35 ++- + pcs/usage.py | 2 +- + pcs_test/tier0/cib_resource/test_bundle.py | 2 +- + .../lib/commands/resource/bundle_common.py | 4 +- + .../commands/resource/test_bundle_create.py | 6 +- + .../commands/resource/test_bundle_reset.py | 263 +++++++++++++++--- + 11 files changed, 402 insertions(+), 205 deletions(-) + +diff --git a/pcs/cli/resource/parse_args.py b/pcs/cli/resource/parse_args.py +index 88da12a6..ff86f477 100644 +--- a/pcs/cli/resource/parse_args.py ++++ b/pcs/cli/resource/parse_args.py +@@ -101,6 +101,27 @@ def parse_bundle_create_options(arg_list): + } + return parts + ++def parse_bundle_reset_options(arg_list): ++ """ ++ Commandline options: no options ++ """ ++ groups = _parse_bundle_groups(arg_list) ++ container_options = groups.get("container", []) ++ parts = { ++ "container": prepare_options(container_options), ++ "network": prepare_options(groups.get("network", [])), ++ "port_map": [ ++ prepare_options(port_map) ++ for port_map in groups.get("port-map", []) ++ ], ++ "storage_map": [ ++ prepare_options(storage_map) ++ for storage_map in groups.get("storage-map", []) ++ ], ++ "meta": prepare_options(groups.get("meta", [])) ++ } ++ return parts ++ + def _split_bundle_map_update_op_and_options( + map_arg_list, result_parts, map_name + ): +diff --git a/pcs/lib/cib/resource/bundle.py b/pcs/lib/cib/resource/bundle.py +index 5c2910c8..2b80608e 100644 +--- a/pcs/lib/cib/resource/bundle.py ++++ b/pcs/lib/cib/resource/bundle.py +@@ -18,6 +18,7 @@ from pcs.lib.xml_tools import ( + append_when_useful, + get_sub_element, + update_attributes_remove_empty, ++ reset_element, + ) + + TAG = "bundle" +@@ -84,15 +85,13 @@ def validate_new( + id_provider=id_provider + ).validate({"id": bundle_id}) + + +- validate_reset( +- id_provider, +- container_type, +- container_options, +- network_options, +- port_map, +- storage_map, +- force_options +- ) ++ _validate_container(container_type, container_options, force_options) ++ + ++ _validate_network_options_new(network_options, force_options) ++ + ++ _validate_port_map_list(port_map, id_provider, force_options) ++ + ++ _validate_storage_map_list(storage_map, id_provider, force_options) + ) + + def append_new( +@@ -130,14 +129,14 @@ def append_new( + return bundle_element + + def validate_reset( +- id_provider, container_type, container_options, network_options, +- port_map, storage_map, force_options=False ++ id_provider, bundle_el, container_options, network_options, port_map, ++ storage_map, force_options=False + ): + """ + Validate bundle parameters, return list of report items + + IdProvider id_provider -- elements' ids generator and uniqueness checker +- string container_type -- bundle container type ++ etree bundle_el -- the bundle to be reset + dict container_options -- container options + dict network_options -- network options + list of dict port_map -- list of port mapping options +@@ -145,7 +144,7 @@ def validate_reset( + bool force_options -- return warnings instead of forceable errors + """ + return ( +- _validate_container(container_type, container_options, force_options) ++ _validate_container_reset(bundle_el, container_options, force_options) + + + _validate_network_options_new(network_options, force_options) + + +@@ -154,72 +153,46 @@ def validate_reset( + _validate_storage_map_list(storage_map, id_provider, force_options) + ) + +-def reset( +- bundle_element, id_provider, bundle_id, container_type, container_options, +- network_options, port_map, storage_map, meta_attributes +-): ++def validate_reset_to_minimal(bundle_element): + """ +- Remove configuration of bundle_element and create new one. ++ Validate removing configuration of bundle_element and keep the minimal one. + + etree bundle_element -- the bundle element that will be reset +- IdProvider id_provider -- elements' ids generator +- string bundle_id -- id of the bundle +- string container_type -- bundle container type +- dict container_options -- container options +- dict network_options -- network options +- list of dict port_map -- list of port mapping options +- list of dict storage_map -- list of storage mapping options +- dict meta_attributes -- meta attributes + """ +- # pylint: disable=too-many-arguments ++ if not _is_supported_container(_get_container_element(bundle_element)): ++ return [_get_report_unsupported_container(bundle_element)] ++ return [] + +- # Old bundle configuration is removed and re-created. We aren't trying +- # to keep ids: +- # * It doesn't make sense to reference these ids. +- # * Newly created ids are based on (are prefixed by) the bundle element id, +- # which does not change. Therefore, it is VERY HIGHLY probable the newly +- # created ids will be the same as the original ones. +- elements_without_reset_impact = [] ++def reset_to_minimal(bundle_element): ++ """ ++ Remove configuration of bundle_element and keep the minimal one. + ++ etree bundle_element -- the bundle element that will be reset ++ """ + # Elements network, storage and meta_attributes must be kept even if they + # are without children. + # See https://bugzilla.redhat.com/show_bug.cgi?id=1642514 +- # +- # The only scenario that makes sense is that these elements are empty +- # and no attributes or children are requested for them. So we collect only +- # deleted tags and we will ensure creation minimal relevant elements at +- # least. +- indelible_tags = [] +- for child in list(bundle_element): +- if child.tag in ["network", "storage", META_ATTRIBUTES_TAG]: +- indelible_tags.append(child.tag) +- elif child.tag not in list(GENERIC_CONTAINER_TYPES): +- # Only primitive should be found here, currently. +- # The order of various element tags has no practical impact so we +- # don't care about it here. +- elements_without_reset_impact.append(child) +- bundle_element.remove(child) ++ # Element of container type is required. + +- _append_container(bundle_element, container_type, container_options) +- if network_options or port_map or "network" in indelible_tags: +- _append_network( +- bundle_element, +- id_provider, +- bundle_id, +- network_options, +- port_map, +- ) +- if storage_map or "storage" in indelible_tags: +- _append_storage(bundle_element, id_provider, bundle_id, storage_map) +- if meta_attributes or META_ATTRIBUTES_TAG in indelible_tags: +- append_new_meta_attributes( +- bundle_element, +- meta_attributes, +- id_provider, +- enforce_append=True, +- ) +- for element in elements_without_reset_impact: +- bundle_element.append(element) ++ # There can be other elements beside bundle configuration (e.g. primitive). ++ # These elements stay untouched. ++ # Like any function that manipulates with cib, this also assumes prior ++ # validation that container is supported. ++ for child in list(bundle_element): ++ if child.tag in ["network", "storage"]: ++ reset_element(child) ++ if child.tag == META_ATTRIBUTES_TAG: ++ reset_element(child, keep_attrs=["id"]) ++ if child.tag in list(GENERIC_CONTAINER_TYPES): ++ # GENERIC_CONTAINER_TYPES elements require the "image" attribute to ++ # be set. ++ reset_element(child, keep_attrs=["image"]) ++ ++def _get_report_unsupported_container(bundle_el): ++ return reports.resource_bundle_unsupported_container_type( ++ bundle_el.get("id"), ++ GENERIC_CONTAINER_TYPES, ++ ) + + def validate_update( + id_provider, bundle_el, container_options, network_options, +@@ -240,65 +213,26 @@ def validate_update( + list of string storage_map_remove -- list of storage mapping ids to remove + bool force_options -- return warnings instead of forceable errors + """ +- report_list = [] +- +- # validate container options only if they are being updated +- if container_options: +- container_el = _get_container_element(bundle_el) +- if ( +- container_el is not None +- and +- container_el.tag in GENERIC_CONTAINER_TYPES +- ): +- report_list.extend( +- _validate_generic_container_options_update( +- container_el, +- container_options, +- force_options +- ) +- ) +- else: +- report_list.append( +- reports.resource_bundle_unsupported_container_type( +- bundle_el.get("id"), GENERIC_CONTAINER_TYPES +- ) +- ) +- +- network_el = bundle_el.find("network") +- if network_el is None: +- report_list.extend( +- _validate_network_options_new(network_options, force_options) +- ) +- else: +- report_list.extend( +- _validate_network_options_update( +- bundle_el, +- network_el, +- network_options, +- force_options +- ) +- ) +- + # TODO It will probably be needed to split the following validators to + # create and update variants. It should be done once the need exists and + # not sooner. +- report_list.extend( ++ return ( ++ _validate_container_update(bundle_el, container_options, force_options) ++ + ++ _validate_network_update(bundle_el, network_options, force_options) ++ + + _validate_port_map_list(port_map_add, id_provider, force_options) +- ) +- report_list.extend( ++ + + _validate_storage_map_list(storage_map_add, id_provider, force_options) +- ) +- report_list.extend( ++ + + _validate_map_ids_exist( + bundle_el, "port-mapping", "port-map", port_map_remove + ) +- ) +- report_list.extend( ++ + + _validate_map_ids_exist( + bundle_el, "storage-mapping", "storage-map", storage_map_remove + ) + ) +- return report_list + + def update( + id_provider, bundle_el, container_options, network_options, +@@ -420,8 +354,15 @@ def get_inner_resource(bundle_el): + return resources[0] + return None + ++def _is_supported_container(container_el): ++ return ( ++ container_el is not None ++ and ++ container_el.tag in GENERIC_CONTAINER_TYPES ++ ) ++ + def _validate_container(container_type, container_options, force_options=False): +- if not container_type in GENERIC_CONTAINER_TYPES: ++ if container_type not in GENERIC_CONTAINER_TYPES: + return [ + reports.invalid_option_value( + "container type", +@@ -429,7 +370,10 @@ def _validate_container(container_type, container_options, force_options=False): + GENERIC_CONTAINER_TYPES, + ) + ] ++ return _validate_generic_container_options(container_options, force_options) ++ + ++def _validate_generic_container_options(container_options, force_options=False): + validators = [ + validate.NamesIn( + GENERIC_CONTAINER_OPTIONS, +@@ -463,8 +407,32 @@ def _validate_container(container_type, container_options, force_options=False): + deprecation_reports + ) + ++def _validate_container_reset(bundle_el, container_options, force_options): ++ # Unlike in the case of update, in reset empty options are not necessary ++ # valid - user MUST set everything (including required options e.g. image). ++ if ( ++ container_options ++ and ++ not _is_supported_container(_get_container_element(bundle_el)) ++ ): ++ return [_get_report_unsupported_container(bundle_el)] ++ return _validate_generic_container_options(container_options, force_options) ++ ++def _validate_container_update(bundle_el, options, force_options): ++ # Validate container options only if they are being updated. Empty options ++ # are valid - user DOESN'T NEED to change anything. ++ if not options: ++ return [] ++ ++ container_el = _get_container_element(bundle_el) ++ if not _is_supported_container(container_el): ++ return [_get_report_unsupported_container(bundle_el)] ++ return _validate_generic_container_options_update( ++ container_el, options, force_options ++ ) ++ + def _validate_generic_container_options_update( +- docker_el, options, force_options ++ container_el, options, force_options + ): + validators_optional_options = [ + validate.ValueNonnegativeInteger("masters"), +@@ -517,7 +485,7 @@ def _validate_generic_container_options_update( + if ( + options.get("masters") + and +- docker_el.get("promoted-max") and options.get("promoted-max") != "" ++ container_el.get("promoted-max") and options.get("promoted-max") != "" + ): + deprecation_reports.append( + reports.prerequisite_option_must_not_be_set( +@@ -527,7 +495,7 @@ def _validate_generic_container_options_update( + if ( + options.get("promoted-max") + and +- docker_el.get("masters") and options.get("masters") != "" ++ container_el.get("masters") and options.get("masters") != "" + ): + deprecation_reports.append( + reports.prerequisite_option_must_not_be_set( +@@ -571,6 +539,14 @@ def _is_pcmk_remote_acccessible_after_update(network_el, options): + + return not (case1 or case2 or case3) + ++def _validate_network_update(bundle_el, options, force_options): ++ network_el = bundle_el.find("network") ++ if network_el is None: ++ return _validate_network_options_new(options, force_options) ++ return _validate_network_options_update( ++ bundle_el, network_el, options, force_options ++ ) ++ + def _validate_network_options_update( + bundle_el, network_el, options, force_options + ): +diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py +index 89e7f225..af648022 100644 +--- a/pcs/lib/commands/resource.py ++++ b/pcs/lib/commands/resource.py +@@ -180,7 +180,9 @@ def _check_special_cases( + + _find_bundle = partial(find_element_by_tag_and_id, resource.bundle.TAG) + +-def _get_required_cib_version_for_container(container_type, container_options): ++def _get_required_cib_version_for_container( ++ container_options, container_type=None ++): + if container_type == "podman": + return Version(3, 2, 0) + +@@ -567,8 +569,8 @@ def bundle_create( + resource.common.are_meta_disabled(meta_attributes) + ), + required_cib_version=_get_required_cib_version_for_container( ++ container_options, + container_type, +- container_options + ), + ) as resources_section: + # no need to run validations related to remote and guest nodes as those +@@ -602,7 +604,7 @@ def bundle_create( + resource.common.disable(bundle_element, id_provider) + + def bundle_reset( +- env, bundle_id, container_type, container_options=None, ++ env, bundle_id, container_options=None, + network_options=None, port_map=None, storage_map=None, meta_attributes=None, + force_options=False, + ensure_disabled=False, +@@ -614,7 +616,6 @@ def bundle_reset( + + LibraryEnvironment env -- provides communication with externals + string bundle_id -- id of the bundle to reset +- string container_type -- container engine name (docker, lxc...) + dict container_options -- container options + dict network_options -- network options + list of dict port_map -- a list of port mapping options +@@ -640,15 +641,20 @@ def bundle_reset( + resource.common.are_meta_disabled(meta_attributes) + ), + required_cib_version=_get_required_cib_version_for_container( +- container_type, + container_options + ), + ) as resources_section: ++ bundle_element = _find_bundle(resources_section, bundle_id) ++ env.report_processor.process_list( ++ resource.bundle.validate_reset_to_minimal(bundle_element) ++ ) ++ resource.bundle.reset_to_minimal(bundle_element) ++ + id_provider = IdProvider(resources_section) + env.report_processor.process_list( + resource.bundle.validate_reset( + id_provider, +- container_type, ++ bundle_element, + container_options, + network_options, + port_map, +@@ -658,23 +664,21 @@ def bundle_reset( + ) + ) + +- bundle_element = _find_bundle(resources_section, bundle_id) +- resource.bundle.reset( +- bundle_element, ++ resource.bundle.update( + id_provider, +- bundle_id, +- container_type, ++ bundle_element, + container_options, + network_options, +- port_map, +- storage_map, +- meta_attributes, ++ port_map_add=port_map, ++ port_map_remove=[], ++ storage_map_add=storage_map, ++ storage_map_remove=[], ++ meta_attributes=meta_attributes, + ) + + if ensure_disabled: + resource.common.disable(bundle_element, id_provider) + +- + def bundle_update( + env, bundle_id, container_options=None, network_options=None, + port_map_add=None, port_map_remove=None, storage_map_add=None, +@@ -706,14 +710,13 @@ def bundle_update( + storage_map_remove = storage_map_remove or [] + meta_attributes = meta_attributes or {} + +- required_cib_version = Version(2, 8, 0) +- if "promoted-max" in container_options: +- required_cib_version = Version(3, 0, 0) + with resource_environment( + env, + wait, + [bundle_id], +- required_cib_version=required_cib_version ++ required_cib_version=_get_required_cib_version_for_container( ++ container_options ++ ), + ) as resources_section: + # no need to run validations related to remote and guest nodes as those + # nodes can only be created from primitive resources +diff --git a/pcs/lib/xml_tools.py b/pcs/lib/xml_tools.py +index 43cde3b5..c058c288 100644 +--- a/pcs/lib/xml_tools.py ++++ b/pcs/lib/xml_tools.py +@@ -154,3 +154,18 @@ def remove_when_pointless(element, attribs_important=True): + """ + if not is_element_useful(element, attribs_important): + element.getparent().remove(element) ++ ++def reset_element(element, keep_attrs=None): ++ """ ++ Remove all subelements and all attributes (except mentioned in keep_attrs) ++ of given element. ++ ++ lxml.etree.element element -- element to reset ++ list keep_attrs -- names of attributes thas should be kept ++ """ ++ keep_attrs = keep_attrs or [] ++ for child in list(element): ++ element.remove(child) ++ for key in element.attrib.keys(): ++ if key not in keep_attrs: ++ del element.attrib[key] +diff --git a/pcs/pcs.8 b/pcs/pcs.8 +index 1e794c60..4ae646e2 100644 +--- a/pcs/pcs.8 ++++ b/pcs/pcs.8 +@@ -191,7 +191,7 @@ Remove the clone which contains the specified group or resource (the resource or + bundle create container [] [network ] [port\-map ]... [storage\-map ]... [meta ] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]] + Create a new bundle encapsulating no resources. The bundle can be used either as it is or a resource may be put into it at any time. If \fB\-\-disabled\fR is specified, the bundle is not started automatically. If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the bundle to start and then return 0 on success or 1 on error. If 'n' is not specified it defaults to 60 minutes. + .TP +-bundle reset container [] [network ] [port\-map ]... [storage\-map ]... [meta ] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]] ++bundle reset [container ] [network ] [port\-map ]... [storage\-map ]... [meta ] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]] + Configure specified bundle with given options. Unlike bundle update, this command resets the bundle according given options - no previous options are kept. Resources inside the bundle are kept as they are. If \fB\-\-disabled\fR is specified, the bundle is not started automatically. If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the bundle to start and then return 0 on success or 1 on error. If 'n' is not specified it defaults to 60 minutes. + .TP + bundle update [container ] [network ] [port\-map (add ) | (delete | remove ...)]... [storage\-map (add ) | (delete | remove ...)]... [meta ] [\fB\-\-wait\fR[=n]] +diff --git a/pcs/resource.py b/pcs/resource.py +index a29ace5f..973f9e64 100644 +--- a/pcs/resource.py ++++ b/pcs/resource.py +@@ -22,6 +22,7 @@ from pcs.cli.common.parse_args import ( + ) + from pcs.cli.resource.parse_args import ( + parse_bundle_create_options, ++ parse_bundle_reset_options, + parse_bundle_update_options, + parse_create as parse_create_args, + ) +@@ -2929,19 +2930,26 @@ def resource_bundle_create_cmd(lib, argv, modifiers): + * --wait + * -f - CIB file + """ +- _resource_bundle_configure(lib.resource.bundle_create, argv, modifiers) ++ modifiers.ensure_only_supported("--force", "--disabled", "--wait", "-f") ++ if not argv: ++ raise CmdLineInputError() + +-def resource_bundle_reset_cmd(lib, argv, modifiers): +- """ +- Options: +- * --force - allow unknown options +- * --disabled - create as a stopped bundle +- * --wait +- * -f - CIB file +- """ +- _resource_bundle_configure(lib.resource.bundle_reset, argv, modifiers) ++ bundle_id = argv[0] ++ parts = parse_bundle_create_options(argv[1:]) ++ lib.resource.bundle_create( ++ bundle_id, ++ parts["container_type"], ++ container_options=parts["container"], ++ network_options=parts["network"], ++ port_map=parts["port_map"], ++ storage_map=parts["storage_map"], ++ meta_attributes=parts["meta"], ++ force_options=modifiers.get("--force"), ++ ensure_disabled=modifiers.get("--disabled"), ++ wait=modifiers.get("--wait"), ++ ) + +-def _resource_bundle_configure(call_lib, argv, modifiers): ++def resource_bundle_reset_cmd(lib, argv, modifiers): + """ + Options: + * --force - allow unknown options +@@ -2954,10 +2962,9 @@ def _resource_bundle_configure(call_lib, argv, modifiers): + raise CmdLineInputError() + + bundle_id = argv[0] +- parts = parse_bundle_create_options(argv[1:]) +- call_lib( ++ parts = parse_bundle_reset_options(argv[1:]) ++ lib.resource.bundle_reset( + bundle_id, +- parts["container_type"], + container_options=parts["container"], + network_options=parts["network"], + port_map=parts["port_map"], +diff --git a/pcs/usage.py b/pcs/usage.py +index 2566d522..c0c6d712 100644 +--- a/pcs/usage.py ++++ b/pcs/usage.py +@@ -476,7 +476,7 @@ Commands: + to start and then return 0 on success or 1 on error. If 'n' is not + specified it defaults to 60 minutes. + +- bundle reset container [] ++ bundle reset [container ] + [network ] [port-map ]... + [storage-map ]... [meta ] + [--disabled] [--wait[=n]] +diff --git a/pcs_test/tier0/cib_resource/test_bundle.py b/pcs_test/tier0/cib_resource/test_bundle.py +index b80b0606..96fcc082 100644 +--- a/pcs_test/tier0/cib_resource/test_bundle.py ++++ b/pcs_test/tier0/cib_resource/test_bundle.py +@@ -79,7 +79,7 @@ class BundleReset(BundleCreateCommon): + "resource bundle create B2 container docker image=pcs:test" + ) + self.assert_effect( +- "resource bundle reset B1 container docker image=pcs:new", ++ "resource bundle reset B1 container image=pcs:new", + """ + + +diff --git a/pcs_test/tier0/lib/commands/resource/bundle_common.py b/pcs_test/tier0/lib/commands/resource/bundle_common.py +index 4fbf00e4..5d7a4b42 100644 +--- a/pcs_test/tier0/lib/commands/resource/bundle_common.py ++++ b/pcs_test/tier0/lib/commands/resource/bundle_common.py +@@ -50,13 +50,13 @@ class SetUpMixin: + ) + + class UpgradeMixin(FixturesMixin): +- upgraded_cib_filename = None ++ old_version_cib_filename = None + + def test_cib_upgrade(self): + (self.config + .runner.cib.load( + name="load_cib_old_version", +- filename=self.upgraded_cib_filename, ++ filename=self.old_version_cib_filename, + before="runner.cib.load" + ) + .runner.cib.upgrade(before="runner.cib.load") +diff --git a/pcs_test/tier0/lib/commands/resource/test_bundle_create.py b/pcs_test/tier0/lib/commands/resource/test_bundle_create.py +index 0bc9dc65..ba1ee0ac 100644 +--- a/pcs_test/tier0/lib/commands/resource/test_bundle_create.py ++++ b/pcs_test/tier0/lib/commands/resource/test_bundle_create.py +@@ -82,16 +82,16 @@ class CreateParametrizedContainerMixin( + + class CreateDocker(CreateParametrizedContainerMixin, TestCase): + container_type = "docker" +- upgraded_cib_filename = "cib-empty-2.0.xml" ++ old_version_cib_filename = "cib-empty-2.0.xml" + + class CreatePodman(CreateParametrizedContainerMixin, TestCase): + container_type = "podman" +- upgraded_cib_filename = "cib-empty-3.1.xml" ++ old_version_cib_filename = "cib-empty-3.1.xml" + + + class CreateRkt(CreateParametrizedContainerMixin, TestCase): + container_type = "rkt" +- upgraded_cib_filename = "cib-empty-2.9.xml" ++ old_version_cib_filename = "cib-empty-2.9.xml" + + class CreateWithNetwork(CreateCommandMixin, NetworkMixin, TestCase): + container_type = "docker" +diff --git a/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py b/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py +index e5d6ef16..cfc84500 100644 +--- a/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py ++++ b/pcs_test/tier0/lib/commands/resource/test_bundle_reset.py +@@ -12,6 +12,7 @@ from pcs_test.tier0.lib.commands.resource.bundle_common import( + AllOptionsMixin, + WaitMixin, + ) ++from pcs_test.tools import fixture + + from pcs.common import report_codes + from pcs.lib.commands.resource import bundle_reset +@@ -26,16 +27,13 @@ class BaseMixin(FixturesMixin): + def initial_resources(self): + return self.fixture_resources_bundle_simple + +- def bundle_reset( +- self, bundle_id=None, container_type=None, **params +- ): ++ def bundle_reset(self, bundle_id=None, **params): + if "container_options" not in params: + params["container_options"] = {"image": self.image} + + bundle_reset( + self.env_assist.get_env(), + bundle_id=bundle_id or self.bundle_id, +- container_type=container_type or self.container_type, + **params + ) + +@@ -44,10 +42,11 @@ class BaseMixin(FixturesMixin): + + class MinimalMixin(BaseMixin, SetUpMixin): + container_type = None +- new_container_type = None + initial_cib_filename = "cib-empty-3.2.xml" + + def test_success_zero_change(self): ++ # Resets a bundle with only an image set to a bundle with the same ++ # image set and no other options. + self.config.env.push_cib(resources=self.initial_resources) + self.bundle_reset() + +@@ -63,13 +62,12 @@ class MinimalMixin(BaseMixin, SetUpMixin): + """ + .format( + bundle_id=self.bundle_id, +- container_type=self.new_container_type, ++ container_type=self.container_type, + image=new_image, + ) + , + }) + self.bundle_reset( +- container_type=self.new_container_type, + container_options={"image": new_image}, + ) + +@@ -92,9 +90,20 @@ class MinimalMixin(BaseMixin, SetUpMixin): + expected_in_processor=False, + ) + ++ def test_no_options_set(self): ++ self.env_assist.assert_raise_library_error( ++ lambda: bundle_reset(self.env_assist.get_env(), self.bundle_id), ++ [ ++ fixture.error( ++ report_codes.REQUIRED_OPTIONS_ARE_MISSING, ++ option_names=["image"], ++ option_type="container", ++ ), ++ ] ++ ) ++ + class FullMixin(SetUpMixin, BaseMixin): + container_type = None +- new_container_type = None + fixture_primitive = """ + + """ +@@ -104,25 +113,12 @@ class FullMixin(SetUpMixin, BaseMixin): + return """ + + +- +- +- + <{container_type} + image="{image}" + promoted-max="0" +- replicas="0" +- replicas-per-host="0" ++ replicas="1" ++ replicas-per-host="1" + /> +- +- +- + + ++ ++ ++ + {fixture_primitive} + + +@@ -174,7 +177,7 @@ class FullMixin(SetUpMixin, BaseMixin): + + """ + .format( +- container_type=self.new_container_type, ++ container_type=self.container_type, + bundle_id=self.bundle_id, + fixture_primitive=self.fixture_primitive, + image=new_image, +@@ -183,7 +186,6 @@ class FullMixin(SetUpMixin, BaseMixin): + }) + + self.bundle_reset( +- container_type=self.new_container_type, + container_options={"image": new_image}, + ) + +@@ -220,12 +222,13 @@ class FullMixin(SetUpMixin, BaseMixin): + + + +- +@@ -234,7 +237,7 @@ class FullMixin(SetUpMixin, BaseMixin): + + """ + .format( +- container_type=self.new_container_type, ++ container_type=self.container_type, + bundle_id=self.bundle_id, + fixture_primitive=self.fixture_primitive, + image=new_image, +@@ -242,7 +245,6 @@ class FullMixin(SetUpMixin, BaseMixin): + , + }) + self.bundle_reset( +- container_type=self.new_container_type, + container_options={ + "image": new_image, + "promoted-max": "1", +@@ -262,8 +264,8 @@ class FullMixin(SetUpMixin, BaseMixin): + storage_map=[ + { + "options": "extra options 2", +- "source-dir": f"/tmp/{self.new_container_type}2a", +- "target-dir": f"/tmp/{self.new_container_type}2b", ++ "source-dir": f"/tmp/{self.container_type}2aa", ++ "target-dir": f"/tmp/{self.container_type}2bb", + }, + ], + meta_attributes={ +@@ -271,6 +273,80 @@ class FullMixin(SetUpMixin, BaseMixin): + } + ) + ++ def test_success_keep_map_ids(self): ++ self.config.env.push_cib(replace={ ++ ".//resources/bundle/network": ++ f""" ++ ++ ++ ++ ++ """ ++ , ++ ".//resources/bundle/storage": ++ f""" ++ ++ ++ ++ """ ++ , ++ }) ++ ++ # Every value is kept as before except port_map and storage_map. ++ self.bundle_reset( ++ container_options={ ++ "image": self.image, ++ "promoted-max": "0", ++ "replicas": "1", ++ "replicas-per-host": "1", ++ }, ++ network_options={ ++ "control-port": "12345", ++ "host-interface": "eth0", ++ "host-netmask": "24", ++ "ip-range-start": "192.168.100.200", ++ }, ++ port_map=[ ++ { ++ "id": f"{self.bundle_id}-port-map-1001", ++ "internal-port": "3002", ++ "port": "3000", ++ }, ++ { ++ "id": f"{self.bundle_id}-port-map-3000-3300", ++ "range": "4000-4400", ++ }, ++ ], ++ storage_map=[ ++ { ++ "id": f"{self.bundle_id}-storage-map", ++ "options": "extra options 2", ++ "source-dir": f"/tmp/{self.container_type}2aa", ++ "target-dir": f"/tmp/{self.container_type}2bb", ++ }, ++ ], ++ meta_attributes={ ++ "target-role": "Stopped", ++ } ++ ) ++ + class ResetParametrizedContainerMixin( + BaseMixin, ParametrizedContainerMixin, UpgradeMixin + ): +@@ -278,39 +354,33 @@ class ResetParametrizedContainerMixin( + + class MinimalRkt(MinimalMixin, TestCase): + container_type = "rkt" +- new_container_type = "docker" + + class MinimalPodman(MinimalMixin, TestCase): + container_type = "podman" +- new_container_type = "rkt" + + class MinimalDocker(MinimalMixin, TestCase): + container_type = "docker" +- new_container_type = "rkt" + + class FullRkt(FullMixin, TestCase): + container_type = "rkt" +- new_container_type = "docker" + + class FullPodman(FullMixin, TestCase): + container_type = "podman" +- new_container_type = "rkt" + + class FullDocker(FullMixin, TestCase): + container_type = "docker" +- new_container_type = "docker" + +-class CreateParametrizedPodman(ResetParametrizedContainerMixin, TestCase): ++class ResetParametrizedPodman(ResetParametrizedContainerMixin, TestCase): + container_type = "podman" +- upgraded_cib_filename = "cib-empty-3.1.xml" ++ old_version_cib_filename = "cib-empty-2.6.xml" + +-class CreateParametrizedDocker(ResetParametrizedContainerMixin, TestCase): ++class ResetParametrizedDocker(ResetParametrizedContainerMixin, TestCase): + container_type = "docker" +- upgraded_cib_filename = "cib-empty-2.0.xml" ++ old_version_cib_filename = "cib-empty-2.0.xml" + +-class CreateParametrizedRkt(ResetParametrizedContainerMixin, TestCase): ++class ResetParametrizedRkt(ResetParametrizedContainerMixin, TestCase): + container_type = "rkt" +- upgraded_cib_filename = "cib-empty-2.9.xml" ++ old_version_cib_filename = "cib-empty-2.6.xml" + + class ResetWithNetwork(BaseMixin, NetworkMixin, TestCase): + container_type = "docker" +@@ -323,9 +393,114 @@ class ResetWithStorageMap(BaseMixin, StorageMapMixin, TestCase): + + class ResetWithMetaMap(BaseMixin, MetaMixin, TestCase): + container_type = "docker" ++ def test_success(self): ++ # When there is no meta attributes the new one are put on the first ++ # possition (since reset now uses update internally). This is the reason ++ # for reimplementation of this MetaMixin test. ++ self.config.env.push_cib( ++ resources=""" ++ ++ ++ ++ ++ ++ ++ <{container_type} image="{image}" /> ++ ++ ++ """ ++ .format( ++ container_type=self.container_type, ++ bundle_id=self.bundle_id, ++ image=self.image, ++ ) ++ ) ++ self.run_bundle_cmd( ++ meta_attributes={ ++ "target-role": "Stopped", ++ "is-managed": "false", ++ } ++ ) + + class ResetWithAllOptions(BaseMixin, AllOptionsMixin, TestCase): + container_type = "docker" + + class ResetWithWait(BaseMixin, WaitMixin, TestCase): + container_type = "docker" ++ ++class ResetUnknownContainerType(BaseMixin, SetUpMixin, TestCase): ++ container_type = "unknown" ++ def test_error_or_unknown_container(self): ++ self.env_assist.assert_raise_library_error( ++ lambda: bundle_reset(self.env_assist.get_env(), self.bundle_id), ++ [ ++ fixture.error( ++ report_codes.RESOURCE_BUNDLE_UNSUPPORTED_CONTAINER_TYPE, ++ bundle_id="B1", ++ supported_container_types=["docker", "podman", "rkt"], ++ ), ++ ] ++ ) ++ ++class NoMetaIdRegenerationMixin(BaseMixin, SetUpMixin): ++ @property ++ def initial_resources(self): ++ return """ ++ ++ ++ <{container_type} ++ image="{image}" ++ promoted-max="0" ++ replicas="1" ++ replicas-per-host="1" ++ /> ++ ++ ++ ++ ++ ++ """.format( ++ container_type=self.container_type, ++ bundle_id=self.bundle_id, ++ image=self.image, ++ ) ++ def test_dont_regenerate_meta_attributes_id(self): ++ self.config.env.push_cib(replace={ ++ ".//resources/bundle/meta_attributes": ++ f""" ++ ++ ++ ++ """ ++ , ++ }) ++ self.bundle_reset( ++ container_options={ ++ "image": self.image, ++ "promoted-max": "0", ++ "replicas": "1", ++ "replicas-per-host": "1", ++ }, ++ meta_attributes={ ++ "target-role": "Stopped", ++ } ++ ) ++ ++class NoMetaIdRegenerationDocker(NoMetaIdRegenerationMixin, TestCase): ++ container_type = "docker" ++ ++class NoMetaIdRegenerationPodman(NoMetaIdRegenerationMixin, TestCase): ++ container_type = "podman" ++ ++class NoMetaIdRegenerationRkt(NoMetaIdRegenerationMixin, TestCase): ++ container_type = "rkt" +-- +2.21.0 + diff --git a/SOURCES/bz1659051-01-fix-link-options-names-in-cluster-setup.patch b/SOURCES/bz1659051-01-fix-link-options-names-in-cluster-setup.patch deleted file mode 100644 index cab57f7..0000000 --- a/SOURCES/bz1659051-01-fix-link-options-names-in-cluster-setup.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 0a672951ac07341a254dfd52d97348be6a13db33 Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Fri, 11 Jan 2019 15:13:09 +0100 -Subject: [PATCH] fix link options' names in cluster setup - ---- - pcs/lib/commands/test/cluster/test_setup.py | 27 +++++++++++++++------ - pcs/lib/corosync/config_facade.py | 15 +++++++++--- - 2 files changed, 32 insertions(+), 10 deletions(-) - -diff --git a/pcs/lib/commands/test/cluster/test_setup.py b/pcs/lib/commands/test/cluster/test_setup.py -index bc7b51f0..441c4d67 100644 ---- a/pcs/lib/commands/test/cluster/test_setup.py -+++ b/pcs/lib/commands/test/cluster/test_setup.py -@@ -95,10 +95,10 @@ def add_key_prefix(prefix, options): - options = options or {} - return {f"{prefix}{key}": value for key, value in options.items()} - --def options_fixture(options): -+def options_fixture(options, template=OPTION_TEMPLATE): - options = options or {} - return "".join([ -- OPTION_TEMPLATE.format(option=o, value=v) -+ template.format(option=o, value=v) - for o, v in sorted(options.items()) - ]) - -@@ -107,7 +107,7 @@ def corosync_conf_fixture( - links_numbers=None, quorum_options=None, totem_options=None, - transport_options=None, compression_options=None, crypto_options=None, - ): -- # pylint: disable=too-many-arguments -+ # pylint: disable=too-many-arguments, too-many-locals - if transport_type == "knet" and not crypto_options: - crypto_options = { - "cipher": "aes256", -@@ -115,18 +115,31 @@ def corosync_conf_fixture( - } - interface_list = "" - if link_list: -+ knet_options = { -+ "link_priority", -+ "ping_interval", -+ "ping_precision", -+ "ping_timeout", -+ "pong_count", -+ "transport", -+ } -+ - link_list = [dict(link) for link in link_list] - links_numbers = links_numbers if links_numbers else list( - range(constants.LINKS_KNET_MAX) - ) - for i, link in enumerate(link_list): - link["linknumber"] = links_numbers[i] -+ link_translated = {} -+ for name, value in link.items(): -+ if name in knet_options: -+ name = f"knet_{name}" -+ link_translated[name] = value -+ link_list[i] = link_translated -+ - interface_list = "".join([ - INTERFACE_TEMPLATE.format( -- option_list="".join([ -- INTERFACE_OPTION_TEMPLATE.format(option=o, value=v) -- for o, v in sorted(link.items()) -- ]) -+ option_list=options_fixture(link, INTERFACE_OPTION_TEMPLATE) - ) for link in sorted( - link_list, key=lambda item: item["linknumber"] - ) -diff --git a/pcs/lib/corosync/config_facade.py b/pcs/lib/corosync/config_facade.py -index 13cc61b2..db98f435 100644 ---- a/pcs/lib/corosync/config_facade.py -+++ b/pcs/lib/corosync/config_facade.py -@@ -260,6 +260,14 @@ class ConfigFacade: - - dict options -- link options - """ -+ options_translate = { -+ "link_priority": "knet_link_priority", -+ "ping_interval": "knet_ping_interval", -+ "ping_precision": "knet_ping_precision", -+ "ping_timeout": "knet_ping_timeout", -+ "pong_count": "knet_pong_count", -+ "transport": "knet_transport", -+ } - totem_section = self.__ensure_section(self.config, "totem")[-1] - new_link_section = config_parser.Section("interface") - options_to_set = {} -@@ -268,9 +276,10 @@ class ConfigFacade: - # If broadcast == 1, transform it to broadcast == yes. Else do - # not put the option to the config at all. - if value in ("1", 1): -- options_to_set[name] = "yes" -- continue -- options_to_set[name] = value -+ value = "yes" -+ else: -+ continue -+ options_to_set[options_translate.get(name, name)] = value - self.__set_section_options([new_link_section], options_to_set) - totem_section.add_section(new_link_section) - self.__remove_empty_sections(self.config) --- -2.17.0 - diff --git a/SOURCES/bz1661059-01-re-add-and-deprecate-pcs-resource-show.patch b/SOURCES/bz1661059-01-re-add-and-deprecate-pcs-resource-show.patch deleted file mode 100644 index cdfb840..0000000 --- a/SOURCES/bz1661059-01-re-add-and-deprecate-pcs-resource-show.patch +++ /dev/null @@ -1,150 +0,0 @@ -From be56e49edd4e844e20c8b41a05b42728f66e1455 Mon Sep 17 00:00:00 2001 -From: Tomas Jelinek -Date: Fri, 18 Jan 2019 17:18:18 +0100 -Subject: [PATCH] re-add and deprecate 'pcs resource show' - ---- - pcs/cli/common/parse_args.py | 6 +++ - pcs/cli/common/test/test_parse_args.py | 3 ++ - pcs/resource.py | 63 ++++++++++++++++++++++++++ - pcs/stonith.py | 5 ++ - 4 files changed, 77 insertions(+) - -diff --git a/pcs/cli/common/parse_args.py b/pcs/cli/common/parse_args.py -index 9b5a2be6..efce977f 100644 ---- a/pcs/cli/common/parse_args.py -+++ b/pcs/cli/common/parse_args.py -@@ -22,6 +22,9 @@ PCS_LONG_OPTIONS = [ - "hide-inactive", - # pcs resource (un)manage - enable or disable monitor operations - "monitor", -+ # TODO remove -+ # used only in deprecated 'pcs resource|stonith show' -+ "groups", - ] - - def split_list(arg_list, separator): -@@ -319,6 +322,9 @@ class InputModifiers(): - "--enable": "--enable" in options, - "--force": "--force" in options, - "--full": "--full" in options, -+ # TODO remove -+ # used only in deprecated 'pcs resource|stonith show' -+ "--groups": "--groups" in options, - "--hide-inactive": "--hide-inactive" in options, - "--interactive": "--interactive" in options, - "--local": "--local" in options, -diff --git a/pcs/cli/common/test/test_parse_args.py b/pcs/cli/common/test/test_parse_args.py -index 09b98162..56bb2f41 100644 ---- a/pcs/cli/common/test/test_parse_args.py -+++ b/pcs/cli/common/test/test_parse_args.py -@@ -496,6 +496,9 @@ class InputModifiersTest(TestCase): - "--enable", - "--force", - "--full", -+ # TODO remove -+ # used only in deprecated 'pcs resource|stonith show' -+ "--groups", - "--hide-inactive", - "--interactive", - "--local", -diff --git a/pcs/resource.py b/pcs/resource.py -index eaca7126..862553e0 100644 ---- a/pcs/resource.py -+++ b/pcs/resource.py -@@ -111,6 +111,10 @@ def resource_cmd(lib, argv, modifiers): - resource_meta(lib, argv_next, modifiers) - elif sub_cmd in {"delete", "remove"}: - resource_remove_cmd(lib, argv_next, modifiers) -+ # TODO remove, deprecated command -+ # replaced with 'resource status' and 'resource config' -+ elif sub_cmd == "show": -+ resource_show(lib, argv_next, modifiers) - elif sub_cmd == "status": - resource_status(lib, argv_next, modifiers) - elif sub_cmd == "config": -@@ -2225,6 +2229,65 @@ def resource_group_list(lib, argv, modifiers): - line_parts.append(resource.getAttribute("id")) - print(" ".join(line_parts)) - -+def resource_show(lib, argv, modifiers, stonith=False): -+ # TODO remove, deprecated command -+ # replaced with 'resource status' and 'resource config' -+ """ -+ Options: -+ * -f - CIB file -+ * --full - print all configured options -+ * --groups - print resource groups -+ * --hide-inactive - print only active resources -+ """ -+ modifiers.ensure_only_supported( -+ "-f", "--full", "--groups", "--hide-inactive" -+ ) -+ mutually_exclusive_opts = ("--full", "--groups", "--hide-inactive") -+ specified_modifiers = [ -+ opt for opt in mutually_exclusive_opts if modifiers.is_specified(opt) -+ ] -+ if (len(specified_modifiers) > 1) or (argv and specified_modifiers): -+ utils.err( -+ "you can specify only one of resource id, {0}".format( -+ ", ".join(mutually_exclusive_opts) -+ ) -+ ) -+ -+ if modifiers.get("--groups"): -+ warn( -+ "This command is deprecated and will be removed. " -+ "Please use 'pcs resource group list' instead." -+ ) -+ resource_group_list(lib, argv, modifiers.get_subset("-f")) -+ return -+ -+ if modifiers.get("--full") or argv: -+ warn( -+ "This command is deprecated and will be removed. " -+ "Please use 'pcs {} config' instead.".format( -+ "stonith" if stonith else "resource" -+ ) -+ ) -+ resource_config( -+ lib, -+ argv, -+ modifiers.get_subset("-f"), -+ stonith=stonith -+ ) -+ return -+ -+ warn( -+ "This command is deprecated and will be removed. " -+ "Please use 'pcs {} status' instead.".format( -+ "stonith" if stonith else "resource" -+ ) -+ ) -+ resource_status( -+ lib, -+ argv, -+ modifiers.get_subset("-f", "--hide-inactive"), -+ stonith=stonith -+ ) - - def resource_status(lib, argv, modifiers, stonith=False): - """ -diff --git a/pcs/stonith.py b/pcs/stonith.py -index 3f08efe3..c5270c84 100644 ---- a/pcs/stonith.py -+++ b/pcs/stonith.py -@@ -40,6 +40,11 @@ def stonith_cmd(lib, argv, modifiers): - resource.resource_update(lib, argv_next, modifiers) - elif sub_cmd in {"delete", "remove"}: - resource.resource_remove_cmd(lib, argv_next, modifiers) -+ # TODO remove, deprecated command -+ # replaced with 'stonith status' and 'stonith config' -+ elif sub_cmd == "show": -+ resource.resource_show(lib, argv_next, modifiers, stonith=True) -+ print_stonith_levels(lib) - elif sub_cmd == "status": - resource.resource_status(lib, argv_next, modifiers, stonith=True) - print_stonith_levels(lib) --- -2.17.0 - diff --git a/SOURCES/bz1664057-01-gui-issues-in-create-new-cluster-form.patch b/SOURCES/bz1664057-01-gui-issues-in-create-new-cluster-form.patch deleted file mode 100644 index 7b3ff0a..0000000 --- a/SOURCES/bz1664057-01-gui-issues-in-create-new-cluster-form.patch +++ /dev/null @@ -1,248 +0,0 @@ -From 52b6ec5ac3ffc03d42217f85cda4fdff0f441735 Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Fri, 11 Jan 2019 15:13:09 +0100 -Subject: [PATCH] squash bz1664057 [GUI] Issues in 'create new clust - -2dfc71c34bd5 refresh changed node addresses in link details - -17241fa845f6 omit empty node addresses in api call - -90558e69fb9f remove old err messages after successfull setup - -b073db339cf2 omit "addrs" in webui api call on empty addresses ---- - pcsd/public/js/api.js | 10 ++-- - pcsd/public/js/cluster-setup.js | 60 ++++++++++++++++++----- - pcsd/public/js/dev/tests-cluster-setup.js | 25 ++++++++-- - pcsd/views/manage.erb | 2 +- - 4 files changed, 75 insertions(+), 22 deletions(-) - -diff --git a/pcsd/public/js/api.js b/pcsd/public/js/api.js -index c6eb20d6..6c433b54 100644 ---- a/pcsd/public/js/api.js -+++ b/pcsd/public/js/api.js -@@ -212,10 +212,12 @@ api.clusterSetup = function(submitData, processOptions){ - var data = { - cluster_name: setupData.clusterName, - nodes: setupData.nodeList.map(function(node){ -- return { -- name: node.name, -- addrs: node.addrs, -- }; -+ apiNode = { name: node.name }; -+ var addrs = node.addrs.filter(function(addr){return addr.length > 0}); -+ if (addrs.length > 0) { -+ apiNode["addrs"] = addrs; -+ } -+ return apiNode; - }), - transport_type: setupData.transportType, - transport_options: setupData.transportType == "knet" -diff --git a/pcsd/public/js/cluster-setup.js b/pcsd/public/js/cluster-setup.js -index e14b314d..2b7b0a4e 100644 ---- a/pcsd/public/js/cluster-setup.js -+++ b/pcsd/public/js/cluster-setup.js -@@ -50,12 +50,7 @@ clusterSetup.link.detail.create = function(transportType, nodesNames){ - var addrList = detail.find(".transport-addresses"); - - $(nodesNames).each(function(j, nodeName){ -- var address = tools.snippet.take("transport-addr").find("tr"); -- $(".node-name", address).text(nodeName+":"); -- $(".address", address) -- .attr("name", clusterSetup.link.detail.addressName(nodeName)) -- ; -- $(addrList).append(address); -+ $(addrList).append(clusterSetup.link.detail.createAddress(nodeName)); - }); - - detail.find(".options").append( -@@ -67,6 +62,34 @@ clusterSetup.link.detail.create = function(transportType, nodesNames){ - return detail; - }; - -+clusterSetup.link.detail.createAddress = function(nodeName){ -+ var address = tools.snippet.take("transport-addr").find("tr"); -+ address.attr("data-transport-addr-host", nodeName) -+ $(".node-name", address).text(nodeName+":"); -+ $(".address", address) -+ .attr("name", clusterSetup.link.detail.addressName(nodeName)) -+ ; -+ return address; -+}; -+ -+clusterSetup.link.detail.refreshNodesNames = function(linkDetail, nodesNames){ -+ // Is fast enough. No cache required. -+ var previousNodesNames = $.makeArray( -+ linkDetail -+ .find(".transport-addresses [data-transport-addr-host]") -+ .map(function(){return $(this).attr("data-transport-addr-host")}) -+ ); -+ -+ var newAddresses = nodesNames.map(function(nodeName){ -+ return previousNodesNames.contains(nodeName) -+ ? linkDetail.find("[data-transport-addr-host="+nodeName+"]") -+ : clusterSetup.link.detail.createAddress(nodeName) -+ ; -+ }); -+ -+ linkDetail.find(".transport-addresses").empty().append(newAddresses); -+}; -+ - //------------------------------------------------------------------------------ - clusterSetup.netmap.updateAddLinkAbility = function(transportType, linkList){ - var addLinkButton = $("#csetup-transport-netmaps .add-link"); -@@ -108,10 +131,16 @@ clusterSetup.netmap.current.get = function(){ - return $(clusterSetup.netmap.current.selector()); - }; - --clusterSetup.netmap.current.detailList = function(){ -+clusterSetup.netmap.current.detailsContainer = function(){ - return $(clusterSetup.netmap.current.selector() +" .link-detail-list"); - }; - -+clusterSetup.netmap.current.detailList = function(){ -+ return $( -+ clusterSetup.netmap.current.selector() +" .link-detail-list .detail" -+ ); -+}; -+ - clusterSetup.netmap.current.linksContainer = function(){ - return $(clusterSetup.netmap.current.selector() +" .link-container"); - }; -@@ -143,7 +172,7 @@ clusterSetup.netmap.current.createLink = function(id, nodesNames){ - .attr(clusterSetup.link.pairAttr, id) - ); - -- clusterSetup.netmap.current.detailList().append( -+ clusterSetup.netmap.current.detailsContainer().append( - clusterSetup.link.detail - .create(clusterSetup.transportType.current(), nodesNames) - .attr(clusterSetup.link.pairAttr, id) -@@ -171,8 +200,8 @@ clusterSetup.netmap.current.setCurrentLink = function(id){ - linkList.children().each(function(){ $(this).removeClass("current") }); - linkList.children(pairSelector).addClass("current"); - -- clusterSetup.netmap.current.detailList().find(".detail").hide(); -- clusterSetup.netmap.current.detailList().find(pairSelector).show() -+ clusterSetup.netmap.current.detailList().hide(); -+ clusterSetup.netmap.current.detailsContainer().find(pairSelector).show() - ; - }; - -@@ -320,7 +349,7 @@ clusterSetup.data.settings = function(clusterName, nodesNames){ - { - clusterName: clusterName, - nodeList: clusterSetup.data.nodes(nodesNames, function(nodeName){ -- return clusterSetup.netmap.current.detailList() -+ return clusterSetup.netmap.current.detailsContainer() - .find("[name='"+clusterSetup.link.detail.addressName(nodeName)+"']") - .map(function(){ return $(this).val().trim() }) - .toArray() -@@ -371,7 +400,7 @@ clusterSetup.data.settings = function(clusterName, nodesNames){ - clusterSetup.transportType.current() === "knet" - - ? { -- linkList: clusterSetup.netmap.current.detailList().find(".detail") -+ linkList: clusterSetup.netmap.current.detailList() - .map(function(linknumber, form){ - return $.extend({linknumber: linknumber}, fromForm(form, [ - "ip_version", -@@ -414,7 +443,7 @@ clusterSetup.data.settings = function(clusterName, nodesNames){ - ), - compression: {}, - crypto: {}, -- linkList: clusterSetup.netmap.current.detailList().find(".detail") -+ linkList: clusterSetup.netmap.current.detailList() - .map(function(linknumber, form){ - return fromForm( - form, -@@ -481,6 +510,10 @@ clusterSetup.step.clusterSettings = function(clusterName, nodesNames, actions){ - - $("#csetup .cluster-settings").tabs(); - -+ clusterSetup.netmap.current.detailList().each(function(){ -+ clusterSetup.link.detail.refreshNodesNames($(this), nodesNames); -+ }); -+ - $("#csetup-transport-netmaps .add-link").unbind("click").click(function(){ - clusterSetup.netmap.current.createLink( - clusterSetup.generateUid(), -@@ -688,6 +721,7 @@ clusterSetup.submit.run = function(useAdvancedOptions){ - ); - - }).then(function(){ -+ clusterSetup.dialog.resetMessages([]); - return api.rememberCluster(formData.clusterName, formData.nodesNames); - - }).then(function(){ -diff --git a/pcsd/public/js/dev/tests-cluster-setup.js b/pcsd/public/js/dev/tests-cluster-setup.js -index 54b7fbdd..b3b9f5c8 100644 ---- a/pcsd/public/js/dev/tests-cluster-setup.js -+++ b/pcsd/public/js/dev/tests-cluster-setup.js -@@ -16,15 +16,16 @@ dev.utils.clusterSetupDialog.prefill = function(url, nodesNames){ - clusterSetup.submit.run(true); - - setTimeout(function(){ -- // dev.utils.clusterSetupDialog.prefillKnet(); -+ dev.utils.clusterSetupDialog.prefillKnet(); -+ dev.utils.clusterSetupDialog.prefillTransportOptionsKnet(); - dev.utils.clusterSetupDialog.prefillCompression(); - dev.utils.clusterSetupDialog.prefillCrypto(); - dev.utils.clusterSetupDialog.prefillTotem(); - dev.utils.clusterSetupDialog.prefillQuorum(); -- dev.utils.clusterSetupDialog.prefillTransportOptionsUdp("udp"); -- $("[href='#csetup-transport-options']").trigger("click"); -+ // dev.utils.clusterSetupDialog.prefillTransportOptionsUdp("udp"); -+ // $("[href='#csetup-transport-options']").trigger("click"); - // $("[href='#csetup-quorum']").trigger("click"); -- dev.utils.clusterSetupDialog.prefillUdp("udpu"); -+ // dev.utils.clusterSetupDialog.prefillUdp("udpu"); - $(".ui-dialog:has('#csetup') button:contains('Create cluster')") - // .trigger("click") - ; -@@ -390,6 +391,21 @@ testClusterSetup.clusterSetupUnforcible = function(url, data, success, fail){ - } - }; - -+testClusterSetup.clusterSetupUnforcibleFirstTime = -+function(url, data, success, fail){ -+ switch(url){ -+ case "/manage/cluster-setup": -+ if (dev.flags.cluster_setup_test_setup_first_time_was_run === undefined) { -+ dev.flags.cluster_setup_test_setup_first_time_was_run = true; -+ return success( -+ JSON.stringify(dev.fixture.libErrorUnforcibleLarge) -+ ); -+ } -+ default: -+ return testClusterSetup.successPath(url, data, success, fail); -+ } -+}; -+ - testClusterSetup.clusterSetupException = function(url, data, success, fail){ - switch(url){ - case "/manage/cluster-setup": -@@ -467,6 +483,7 @@ dev.runScenario( - // testClusterSetup.clusterSetup403 - // testClusterSetup.clusterSetup500 - // testClusterSetup.clusterSetupUnforcible -+ // testClusterSetup.clusterSetupUnforcibleFirstTime - // testClusterSetup.clusterSetupException - // testClusterSetup.clusterSetupForceFail - // testClusterSetup.clusterSetupForceFailForcible -diff --git a/pcsd/views/manage.erb b/pcsd/views/manage.erb -index 7b0c9daa..c0569d40 100644 ---- a/pcsd/views/manage.erb -+++ b/pcsd/views/manage.erb -@@ -1075,7 +1075,7 @@ It is very useful when combined with Last Man Standing. - - - -- -+ - -
- --- -2.17.0 - diff --git a/SOURCES/bz1668422-01-fix-crashes-when-using-unsupported-options.patch b/SOURCES/bz1668422-01-fix-crashes-when-using-unsupported-options.patch deleted file mode 100644 index a4f8fdb..0000000 --- a/SOURCES/bz1668422-01-fix-crashes-when-using-unsupported-options.patch +++ /dev/null @@ -1,46 +0,0 @@ -From ff470338e6232d72f3e9f697d5c90c0f7e2a7993 Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Wed, 30 Jan 2019 15:13:17 +0100 -Subject: [PATCH] fix crashes when using unsupported options - ---- - pcs/config.py | 9 +++++---- - pcs/status.py | 1 + - 2 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/pcs/config.py b/pcs/config.py -index 304c9040..f0d80222 100644 ---- a/pcs/config.py -+++ b/pcs/config.py -@@ -48,11 +48,12 @@ from pcs.cli.common.errors import CmdLineInputError - # pylint: disable=too-many-branches, too-many-locals, too-many-statements - - def config_cmd(lib, argv, modifiers): -- if not argv: -- config_show(lib, argv, modifiers) -- return -- - try: -+ if not argv: -+ sub_cmd = "show" -+ config_show(lib, argv, modifiers) -+ return -+ - sub_cmd = argv.pop(0) - if sub_cmd == "help": - usage.config(argv) -diff --git a/pcs/status.py b/pcs/status.py -index 5b3af0d1..3ba7d152 100644 ---- a/pcs/status.py -+++ b/pcs/status.py -@@ -23,6 +23,7 @@ from pcs.lib.sbd import get_sbd_service_name - def status_cmd(lib, argv, modifiers): - try: - if not argv: -+ sub_cmd = "status" - full_status(lib, argv, modifiers) - sys.exit(0) - --- -2.17.0 - diff --git a/SOURCES/bz1676957-01-fix-crashes-in-pcs-host-auth.patch b/SOURCES/bz1676957-01-fix-crashes-in-pcs-host-auth.patch new file mode 100644 index 0000000..4a69ea8 --- /dev/null +++ b/SOURCES/bz1676957-01-fix-crashes-in-pcs-host-auth.patch @@ -0,0 +1,26 @@ +From a6bcd7f5a387722b1cdec2d8cd8b9e3fafc36da4 Mon Sep 17 00:00:00 2001 +From: Tomas Jelinek +Date: Mon, 17 Jun 2019 12:51:32 +0200 +Subject: [PATCH 1/3] fix crashes in 'pcs host auth' + +--- + pcsd/cfgsync.rb | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/pcsd/cfgsync.rb b/pcsd/cfgsync.rb +index 703c0e39..f5d0ba86 100644 +--- a/pcsd/cfgsync.rb ++++ b/pcsd/cfgsync.rb +@@ -826,7 +826,8 @@ module Cfgsync + PCSAuth.getSuperuserAuth(), [config_new.class], target_nodes, + cluster_name, new_hosts + ) +- fetched_hosts, _ = fetcher.fetch_all()[config_new.class.name] ++ fetched_configs, _node_connected = fetcher.fetch_all() ++ fetched_hosts = fetched_configs[config_new.class.name] + config_new = Cfgsync::merge_known_host_files( + config_old, fetched_hosts, new_hosts, remove_hosts_names + ) +-- +2.21.0 + diff --git a/SOURCES/bz1690290-GUI-cannot-create-a-cluster-if-k.patch b/SOURCES/bz1690290-GUI-cannot-create-a-cluster-if-k.patch deleted file mode 100644 index ba4dfcc..0000000 --- a/SOURCES/bz1690290-GUI-cannot-create-a-cluster-if-k.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 77760bdeb1642e30f109ba88a98fff8b1faba206 Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Tue, 19 Mar 2019 10:05:40 +0100 -Subject: [PATCH] squash bz1690290 GUI cannot create a cluster if k - -fix missing addrs field in cluster setup in webUi - -fix transport options in cluster setup in webui ---- - pcsd/public/js/api.js | 41 ++++++++++++++++++++++++--------- - pcsd/public/js/cluster-setup.js | 8 +++---- - 2 files changed, 33 insertions(+), 16 deletions(-) - -diff --git a/pcsd/public/js/api.js b/pcsd/public/js/api.js -index 6c433b54..b9c94720 100644 ---- a/pcsd/public/js/api.js -+++ b/pcsd/public/js/api.js -@@ -209,10 +209,39 @@ api.checkAuthAgainstNodes = function(nodesNames){ - - api.clusterSetup = function(submitData, processOptions){ - var setupData = submitData.setupData; -+ var transportOptions = {}; -+ if (setupData.transportType === "knet") { -+ transportOptions = { -+ ip_version: setupData.transportOptions.ip_version, -+ knet_pmtud_interval: setupData.transportOptions.knet_pmtud_interval, -+ link_mode: setupData.transportOptions.link_mode, -+ }; -+ } else if(setupData.transportType !== undefined) { -+ transportOptions = { -+ ip_version: setupData.transportOptions.ip_version, -+ netmtu: setupData.transportOptions.netmtu, -+ }; -+ } - var data = { - cluster_name: setupData.clusterName, - nodes: setupData.nodeList.map(function(node){ - apiNode = { name: node.name }; -+ // The backend defaults addresses. But for the defaulting addresses there -+ // must be no key "addrs". -+ // There can be following (valid) scenarios: -+ // 1 User uses no link. We omit key "addrs" for every node. The backend -+ // defaults addresses. -+ // 2 User uses 1 link and keep all addresses fields empty. We omit key -+ // "addrs" for every node. The backend defaults addresses. -+ // 3 User uses 1 link and keep some addresses fields empty. We omit key -+ // "addrs" for respective nodes. The backend then refuses it because -+ // nodes addreses are inconsistent. -+ // 4 User uses more links. We omit key "addrs" when no address for node is -+ // filled. The backend then refuses it because nodes addresses count -+ // is inconsistent with links count. -+ // Because we need to support scenario 2 and the backend defaults -+ // addresses only when key "addrs" is not specified we cannot simply -+ // sent empty addresses or empty address list (i.e. key "addrs"). - var addrs = node.addrs.filter(function(addr){return addr.length > 0}); - if (addrs.length > 0) { - apiNode["addrs"] = addrs; -@@ -220,17 +249,7 @@ api.clusterSetup = function(submitData, processOptions){ - return apiNode; - }), - transport_type: setupData.transportType, -- transport_options: setupData.transportType == "knet" -- ? { -- ip_version: setupData.transportOptions.ip_version, -- knet_pmtud_interval: setupData.transportOptions.knet_pmtud_interval, -- link_mode: setupData.transportOptions.link_mode, -- } -- : { -- ip_version: setupData.transportOptions.ip_version, -- netmtu: setupData.transportOptions.netmtu, -- } -- , -+ transport_options: transportOptions, - link_list: setupData.linkList.map(function(link){ - return setupData.transportType == "knet" - ? { -diff --git a/pcsd/public/js/cluster-setup.js b/pcsd/public/js/cluster-setup.js -index 2b7b0a4e..be0a22d5 100644 ---- a/pcsd/public/js/cluster-setup.js -+++ b/pcsd/public/js/cluster-setup.js -@@ -322,11 +322,9 @@ clusterSetup.data.validateNameAndNodes = function(formData){ - - clusterSetup.data.nodes = function(nodesNames, getAddrs){ - return nodesNames.map(function(nodeName){ -- addrs = getAddrs ? getAddrs(nodeName) : []; -- return addrs.length > 0 -- ? { name: nodeName, addrs: addrs } -- : { name: nodeName } -- ; -+ // The field addrs is here always. The details of backend is solved within -+ // api.js module. -+ return { name: nodeName, addrs: getAddrs ? getAddrs(nodeName) : [] }; - }); - }; - --- -2.20.1 - diff --git a/SOURCES/bz1690304-GUI-submitting-of-create-cluster.patch b/SOURCES/bz1690304-GUI-submitting-of-create-cluster.patch deleted file mode 100644 index 7a5d8b8..0000000 --- a/SOURCES/bz1690304-GUI-submitting-of-create-cluster.patch +++ /dev/null @@ -1,48 +0,0 @@ -From f0ee2b03681f192c4779e934108eabf771b65c3c Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Tue, 19 Mar 2019 10:48:10 +0100 -Subject: [PATCH] squash bz1690304 GUI submitting of create cluster - -add quotes around html attribute value - -It fixes issue with node names containg dots. JQuery parsing failed when -quotes were not around html attribute value in the case of value -containing dots. - -fix omitting link/quorum opts: webui cluster setup ---- - pcsd/public/js/cluster-setup.js | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/pcsd/public/js/cluster-setup.js b/pcsd/public/js/cluster-setup.js -index be0a22d5..fc927507 100644 ---- a/pcsd/public/js/cluster-setup.js -+++ b/pcsd/public/js/cluster-setup.js -@@ -82,7 +82,7 @@ clusterSetup.link.detail.refreshNodesNames = function(linkDetail, nodesNames){ - - var newAddresses = nodesNames.map(function(nodeName){ - return previousNodesNames.contains(nodeName) -- ? linkDetail.find("[data-transport-addr-host="+nodeName+"]") -+ ? linkDetail.find("[data-transport-addr-host='"+nodeName+"']") - : clusterSetup.link.detail.createAddress(nodeName) - ; - }); -@@ -391,6 +391,7 @@ clusterSetup.data.settings = function(clusterName, nodesNames){ - ) { - return value == "on" ? "1" : "0"; - } -+ return value; - }, - ), - }, -@@ -450,6 +451,7 @@ clusterSetup.data.settings = function(clusterName, nodesNames){ - if (name === "broadcast"){ - return value == "yes" ? "1" : "0"; - } -+ return value; - }, - ); - }) --- -2.20.1 - diff --git a/SOURCES/bz1725183-01-fix-and-options-for-non-root-users.patch b/SOURCES/bz1725183-01-fix-and-options-for-non-root-users.patch new file mode 100644 index 0000000..0aec508 --- /dev/null +++ b/SOURCES/bz1725183-01-fix-and-options-for-non-root-users.patch @@ -0,0 +1,97 @@ +From b667913a72f9516e3c9ae1452784874ff01a7688 Mon Sep 17 00:00:00 2001 +From: Tomas Jelinek +Date: Tue, 2 Jul 2019 13:35:58 +0200 +Subject: [PATCH] fix - and -- options for non-root users + +--- + pcs/app.py | 32 ++++++++++++++++++++++---------- + 1 file changed, 22 insertions(+), 10 deletions(-) + +diff --git a/pcs/app.py b/pcs/app.py +index abb9277e..c13cb15c 100644 +--- a/pcs/app.py ++++ b/pcs/app.py +@@ -37,12 +37,21 @@ from pcs.cli.routing import ( + from pcs.lib.errors import LibraryError + + +-def non_root_run(argv_cmd): ++def _non_root_run(argv_cmd): + """ + This function will run commands which has to be run as root for users which + are not root. If it required to run such command as root it will do that by + sending it to the local pcsd and then it will exit. + """ ++ # matching the commands both in here and in pcsd expects -o and --options ++ # to be at the end of a command ++ argv_and_options = argv_cmd[:] ++ for option, value in utils.pcs_options.items(): ++ if parse_args.is_option_expecting_value(option): ++ argv_and_options.extend([option, value]) ++ else: ++ argv_and_options.append(option) ++ + # specific commands need to be run under root account, pass them to pcsd + # don't forget to allow each command in pcsd.rb in "post /run_pcs do" + root_command_list = [ +@@ -67,29 +76,29 @@ def non_root_run(argv_cmd): + ['status', 'quorum', '...'], + ['status', 'pcsd', '...'], + ] +- orig_argv = argv_cmd[:] ++ + for root_cmd in root_command_list: + if ( +- (argv_cmd == root_cmd) ++ (argv_and_options == root_cmd) + or + ( + root_cmd[-1] == "..." + and +- argv_cmd[:len(root_cmd)-1] == root_cmd[:-1] ++ argv_and_options[:len(root_cmd)-1] == root_cmd[:-1] + ) + ): + # handle interactivity of 'pcs cluster auth' +- if argv_cmd[0:2] in [["cluster", "auth"], ["host", "auth"]]: ++ if argv_and_options[0:2] in [["cluster", "auth"], ["host", "auth"]]: + if "-u" not in utils.pcs_options: + username = utils.get_terminal_input('Username: ') +- orig_argv.extend(["-u", username]) ++ argv_and_options.extend(["-u", username]) + if "-p" not in utils.pcs_options: + password = utils.get_terminal_password() +- orig_argv.extend(["-p", password]) ++ argv_and_options.extend(["-p", password]) + + # call the local pcsd + err_msgs, exitcode, std_out, std_err = utils.call_local_pcsd( +- orig_argv ++ argv_and_options + ) + if err_msgs: + for msg in err_msgs: +@@ -105,7 +114,10 @@ logging.basicConfig() + usefile = False + filename = "" + def main(argv=None): +- # pylint: disable=too-many-locals, too-many-branches, too-many-statements, global-statement ++ # pylint: disable=global-statement ++ # pylint: disable=too-many-branches ++ # pylint: disable=too-many-locals ++ # pylint: disable=too-many-statements + if completion.has_applicable_environment(os.environ): + print(completion.make_suggestions( + os.environ, +@@ -207,7 +219,7 @@ def main(argv=None): + logger.handlers = [] + + if (os.getuid() != 0) and (argv and argv[0] != "help") and not usefile: +- non_root_run(argv) ++ _non_root_run(argv) + cmd_map = { + "resource": resource.resource_cmd, + "cluster": cluster.cluster_cmd, +-- +2.21.0 + diff --git a/SOURCES/bz1740218-01-set-authkey-length-to-256-bytes.patch b/SOURCES/bz1740218-01-set-authkey-length-to-256-bytes.patch new file mode 100644 index 0000000..7171520 --- /dev/null +++ b/SOURCES/bz1740218-01-set-authkey-length-to-256-bytes.patch @@ -0,0 +1,79 @@ +From 77cce4b737c8d242e3c550e3d14cb4893b4ad73c Mon Sep 17 00:00:00 2001 +From: Tomas Jelinek +Date: Tue, 13 Aug 2019 10:06:29 +0200 +Subject: [PATCH] set authkey length to 256 bytes + +--- + pcs/settings_default.py | 7 +++++-- + pcs_test/tier0/lib/commands/remote_node/test_node_add_guest.py | 4 ++-- + pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py | 4 ++-- + 3 files changed, 9 insertions(+), 6 deletions(-) + +diff --git a/pcs/settings_default.py b/pcs/settings_default.py +index 60647f5d..07014e33 100644 +--- a/pcs/settings_default.py ++++ b/pcs/settings_default.py +@@ -20,11 +20,14 @@ corosync_qdevice_net_client_certs_dir = os.path.join( + ) + corosync_qdevice_net_client_ca_file_name = "qnetd-cacert.crt" + corosync_authkey_file = os.path.join(corosync_conf_dir, "authkey") +-corosync_authkey_bytes = 384 ++# Must be set to 256 for corosync to work in FIPS environment. ++corosync_authkey_bytes = 256 + corosync_log_file = "/var/log/cluster/corosync.log" + pacemaker_authkey_file = "/etc/pacemaker/authkey" +-pacemaker_authkey_bytes = 384 ++# Using the same value as for corosync. Higher values MAY work in FIPS. ++pacemaker_authkey_bytes = 256 + booth_authkey_file_mode = 0o600 ++# Booth does not support keys longer than 64 bytes. + booth_authkey_bytes = 64 + cluster_conf_file = "/etc/cluster/cluster.conf" + fence_agent_binaries = "/usr/sbin/" +diff --git a/pcs_test/tier0/lib/commands/remote_node/test_node_add_guest.py b/pcs_test/tier0/lib/commands/remote_node/test_node_add_guest.py +index 4a68c9d1..ee463c70 100644 +--- a/pcs_test/tier0/lib/commands/remote_node/test_node_add_guest.py ++++ b/pcs_test/tier0/lib/commands/remote_node/test_node_add_guest.py +@@ -158,7 +158,7 @@ class AddGuest(TestCase): + .local.push_cib() + ) + node_add_guest(self.env_assist.get_env()) +- generate_binary_key.assert_called_once_with(random_bytes_count=384) ++ generate_binary_key.assert_called_once_with(random_bytes_count=256) + self.env_assist.assert_reports( + REPORTS + .adapt( +@@ -531,7 +531,7 @@ class AddGuest(TestCase): + .local.push_cib() + ) + node_add_guest(self.env_assist.get_env(), skip_offline_nodes=True) +- generate_binary_key.assert_called_once_with(random_bytes_count=384) ++ generate_binary_key.assert_called_once_with(random_bytes_count=256) + self.env_assist.assert_reports( + fixture_reports_new_node_unreachable(NODE_NAME) + + [ +diff --git a/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py b/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py +index bb2b6615..d34d7126 100644 +--- a/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py ++++ b/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py +@@ -216,7 +216,7 @@ class AddRemote(TestCase): + .env.push_cib(resources=FIXTURE_RESOURCES) + ) + node_add_remote(self.env_assist.get_env()) +- generate_binary_key.assert_called_once_with(random_bytes_count=384) ++ generate_binary_key.assert_called_once_with(random_bytes_count=256) + self.env_assist.assert_reports( + REPORTS + .adapt( +@@ -511,7 +511,7 @@ class AddRemote(TestCase): + .env.push_cib(resources=FIXTURE_RESOURCES) + ) + node_add_remote(self.env_assist.get_env(), skip_offline_nodes=True) +- generate_binary_key.assert_called_once_with(random_bytes_count=384) ++ generate_binary_key.assert_called_once_with(random_bytes_count=256) + self.env_assist.assert_reports( + fixture_reports_new_node_unreachable(NODE_NAME) + + [ +-- +2.11.0 + diff --git a/SOURCES/bz1746565-01-set-authkey-length-to-256-bytes.patch b/SOURCES/bz1746565-01-set-authkey-length-to-256-bytes.patch deleted file mode 100644 index 56d8c33..0000000 --- a/SOURCES/bz1746565-01-set-authkey-length-to-256-bytes.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 3a1439a74eb64b3473cb5ce84914cdd533563573 Mon Sep 17 00:00:00 2001 -From: Tomas Jelinek -Date: Tue, 13 Aug 2019 10:06:29 +0200 -Subject: [PATCH] set authkey length to 256 bytes - ---- - pcs/lib/commands/test/remote_node/test_node_add_guest.py | 4 ++-- - pcs/lib/commands/test/remote_node/test_node_add_remote.py | 4 ++-- - pcs/settings_default.py | 7 +++++-- - 3 files changed, 9 insertions(+), 6 deletions(-) - -diff --git a/pcs/lib/commands/test/remote_node/test_node_add_guest.py b/pcs/lib/commands/test/remote_node/test_node_add_guest.py -index cb385150..d013b255 100644 ---- a/pcs/lib/commands/test/remote_node/test_node_add_guest.py -+++ b/pcs/lib/commands/test/remote_node/test_node_add_guest.py -@@ -154,7 +154,7 @@ class AddGuest(TestCase): - .local.push_cib() - ) - node_add_guest(self.env_assist.get_env()) -- generate_binary_key.assert_called_once_with(random_bytes_count=384) -+ generate_binary_key.assert_called_once_with(random_bytes_count=256) - self.env_assist.assert_reports( - REPORTS - .adapt( -@@ -523,7 +523,7 @@ class AddGuest(TestCase): - .local.push_cib() - ) - node_add_guest(self.env_assist.get_env(), skip_offline_nodes=True) -- generate_binary_key.assert_called_once_with(random_bytes_count=384) -+ generate_binary_key.assert_called_once_with(random_bytes_count=256) - self.env_assist.assert_reports( - fixture_reports_new_node_unreachable(NODE_NAME) - + [ -diff --git a/pcs/lib/commands/test/remote_node/test_node_add_remote.py b/pcs/lib/commands/test/remote_node/test_node_add_remote.py -index 46f82587..b0b3d6d3 100644 ---- a/pcs/lib/commands/test/remote_node/test_node_add_remote.py -+++ b/pcs/lib/commands/test/remote_node/test_node_add_remote.py -@@ -212,7 +212,7 @@ class AddRemote(TestCase): - .env.push_cib(resources=FIXTURE_RESOURCES) - ) - node_add_remote(self.env_assist.get_env()) -- generate_binary_key.assert_called_once_with(random_bytes_count=384) -+ generate_binary_key.assert_called_once_with(random_bytes_count=256) - self.env_assist.assert_reports( - REPORTS - .adapt( -@@ -507,7 +507,7 @@ class AddRemote(TestCase): - .env.push_cib(resources=FIXTURE_RESOURCES) - ) - node_add_remote(self.env_assist.get_env(), skip_offline_nodes=True) -- generate_binary_key.assert_called_once_with(random_bytes_count=384) -+ generate_binary_key.assert_called_once_with(random_bytes_count=256) - self.env_assist.assert_reports( - fixture_reports_new_node_unreachable(NODE_NAME) - + [ -diff --git a/pcs/settings_default.py b/pcs/settings_default.py -index e3a55b58..0d025b85 100644 ---- a/pcs/settings_default.py -+++ b/pcs/settings_default.py -@@ -21,11 +21,14 @@ corosync_qdevice_net_client_certs_dir = os.path.join( - ) - corosync_qdevice_net_client_ca_file_name = "qnetd-cacert.crt" - corosync_authkey_file = os.path.join(corosync_conf_dir, "authkey") --corosync_authkey_bytes = 384 -+# Must be set to 256 for corosync to work in FIPS environment. -+corosync_authkey_bytes = 256 - corosync_log_file = "/var/log/cluster/corosync.log" - pacemaker_authkey_file = "/etc/pacemaker/authkey" --pacemaker_authkey_bytes = 384 -+# Using the same value as for corosync. Higher values MAY work in FIPS. -+pacemaker_authkey_bytes = 256 - booth_authkey_file_mode = 0o600 -+# Booth does not support keys longer than 64 bytes. - booth_authkey_bytes = 64 - cluster_conf_file = "/etc/cluster/cluster.conf" - fence_agent_binaries = "/usr/sbin/" --- -2.21.0 - diff --git a/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch b/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch index 0d914c6..f45f056 100644 --- a/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch +++ b/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch @@ -1,4 +1,4 @@ -From ae936f1a9b148e635963290a1bc645013ee422fe Mon Sep 17 00:00:00 2001 +From 588620d73c3bfac080f6a1918de0e7fdc1550646 Mon Sep 17 00:00:00 2001 From: Ivan Devat Date: Tue, 20 Nov 2018 15:03:56 +0100 Subject: [PATCH] do not support cluster setup with udp(u) transport @@ -10,10 +10,10 @@ Subject: [PATCH] do not support cluster setup with udp(u) transport 3 files changed, 6 insertions(+) diff --git a/pcs/pcs.8 b/pcs/pcs.8 -index aa72aa91..e1cae477 100644 +index 4ae646e2..85575412 100644 --- a/pcs/pcs.8 +++ b/pcs/pcs.8 -@@ -249,6 +249,8 @@ By default, encryption is enabled with cipher=aes256 and hash=sha256. To disable +@@ -254,6 +254,8 @@ By default, encryption is enabled with cipher=aes256 and hash=sha256. To disable Transports udp and udpu: .br @@ -23,10 +23,10 @@ index aa72aa91..e1cae477 100644 .br Transport options are: ip_version, netmtu diff --git a/pcs/usage.py b/pcs/usage.py -index 4487d844..4c968366 100644 +index c0c6d712..d9f240da 100644 --- a/pcs/usage.py +++ b/pcs/usage.py -@@ -631,6 +631,7 @@ Commands: +@@ -647,6 +647,7 @@ Commands: hash=sha256. To disable encryption, set cipher=none and hash=none. Transports udp and udpu: @@ -49,5 +49,5 @@ index b857cbae..b8d48d92 100644 #csetup-transport-options.knet .without-knet { -- -2.17.0 +2.11.0 diff --git a/SOURCES/fix-communication-over-IPv6.patch b/SOURCES/fix-communication-over-IPv6.patch deleted file mode 100644 index 4f51e9d..0000000 --- a/SOURCES/fix-communication-over-IPv6.patch +++ /dev/null @@ -1,29 +0,0 @@ -From c14ed896c3edc910037cb9765881005a3d3683b2 Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Fri, 24 May 2019 09:26:04 +0200 -Subject: [PATCH] fix communication over IPv6 - ---- - pcs/utils.py | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/pcs/utils.py b/pcs/utils.py -index aa45e0dc..fd29e2fd 100644 ---- a/pcs/utils.py -+++ b/pcs/utils.py -@@ -476,8 +476,10 @@ def sendHTTPRequest( - token = known_host.token - if port is None: - port = settings.pcsd_default_port -- url = "https://{addr}:{port}/{request}".format( -- addr=addr, request=request, port=port -+ url = "https://{host}:{port}/{request}".format( -+ host="[{0}]".format(addr) if ":" in addr else addr, -+ request=request, -+ port=port - ) - if "--debug" in pcs_options: - print("Sending HTTP Request to: " + url) --- -2.21.0 - diff --git a/SOURCES/fix-web-UI-dashboard-data-loading-timeout.patch b/SOURCES/fix-web-UI-dashboard-data-loading-timeout.patch deleted file mode 100644 index 5c70020..0000000 --- a/SOURCES/fix-web-UI-dashboard-data-loading-timeout.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 45732498ed6d77ee783b6713a50190098b1678ac Mon Sep 17 00:00:00 2001 -From: Ivan Devat -Date: Thu, 7 Feb 2019 11:08:11 +0100 -Subject: [PATCH] squash, fix web UI dashboard data loading timeout - -load systemctl/chkconfig services list only once - -Systemctl/chkconfig services have been retrived repeatedly for each -service (pacemaker, pacemaker_remote, corosync, pcsd, sbd). It had -a performace impact on loading clusters overview. - -clean up a check whether services are installed ---- - pcsd/cluster_entity.rb | 3 +- - pcsd/pcs.rb | 81 ++++++++++++++++++++++++++++++------------ - pcsd/remote.rb | 4 ++- - 3 files changed, 64 insertions(+), 24 deletions(-) - -diff --git a/pcsd/cluster_entity.rb b/pcsd/cluster_entity.rb -index 79f25450..861dd0c5 100644 ---- a/pcsd/cluster_entity.rb -+++ b/pcsd/cluster_entity.rb -@@ -1177,10 +1177,11 @@ module ClusterEntity - - def self.load_current_node(crm_dom=nil) - node = ClusterEntity::Node.new -+ service_checker = get_service_installed_checker() - node.services.each do |service, info| - info[:running] = is_service_running?(service.to_s) - info[:enabled] = is_service_enabled?(service.to_s) -- info[:installed] = is_service_installed?(service.to_s) -+ info[:installed] = service_checker.is_installed?(service.to_s) - end - node.corosync = node.services[:corosync][:running] - node.corosync_enabled = node.services[:corosync][:enabled] -diff --git a/pcsd/pcs.rb b/pcsd/pcs.rb -index 1f86ae04..7035f4f2 100644 ---- a/pcsd/pcs.rb -+++ b/pcsd/pcs.rb -@@ -1728,35 +1728,72 @@ def is_service_running?(service) - return (retcode == 0) - end - --def is_service_installed?(service) -- unless ISSYSTEMCTL -- stdout, _, retcode = run_cmd(PCSAuth.getSuperuserAuth(), 'chkconfig') -- if retcode != 0 -+class ServiceInstalledChecker -+ def initialize() -+ @list_unit_files_output = self.load_unit_files_list() -+ end -+ -+ def is_installed?(service) -+ if @list_unit_files_output.nil? - return nil - end -- stdout.each { |line| -- if line.split(' ')[0] == service -+ -+ @list_unit_files_output.each { |line| -+ if self.contains_line_service?(line, service) - return true - end - } - return false - end - -- # currently we are not using systemd instances (service_name@instance) in pcsd -- # for proper implementation of is_service_installed see -- # pcs/lib/external.py:is_service_installed -- stdout, _, retcode = run_cmd( -- PCSAuth.getSuperuserAuth(), 'systemctl', 'list-unit-files', '--full' -- ) -- if retcode != 0 -- return nil -- end -- stdout.each { |line| -- if line.strip().start_with?("#{service}.service") -- return true -+ protected -+ def load_unit_files_list() -+ stdout, _, retcode = self.run_command() -+ if retcode != 0 -+ return nil - end -- } -- return false -+ return stdout -+ end -+end -+ -+class ServiceInstalledCheckerSystemctl < ServiceInstalledChecker -+ protected -+ def run_command -+ # currently we are not using systemd instances (service_name@instance) in pcsd -+ # for proper implementation of is_service_installed see -+ # pcs/lib/external.py:is_service_installed -+ return run_cmd( -+ PCSAuth.getSuperuserAuth(), 'systemctl', 'list-unit-files', '--full' -+ ) -+ end -+ -+ def contains_line_service?(line, service) -+ return line.strip().start_with?("#{service}.service") -+ end -+end -+ -+class ServiceInstalledCheckerChkconfig < ServiceInstalledChecker -+ protected -+ def run_command -+ return run_cmd(PCSAuth.getSuperuserAuth(), 'chkconfig') -+ end -+ -+ def contains_line_service?(line, service) -+ return line.split(' ')[0] == service -+ end -+end -+ -+def get_service_installed_checker -+ if ISSYSTEMCTL -+ return ServiceInstalledCheckerSystemctl.new -+ else -+ return ServiceInstalledCheckerChkconfig.new -+ end -+end -+ -+ -+def is_service_installed?(service) -+ return get_service_installed_checker().is_installed?(service) - end - - def enable_service(service) -@@ -1888,9 +1925,9 @@ def get_alerts(auth_user) - end - end - --def get_service_info(service_name) -+def get_service_info(service_name, service_checker) - return { -- :installed => is_service_installed?(service_name), -+ :installed => service_checker.is_installed?(service_name), - :enabled => is_service_enabled?(service_name), - :running => is_service_running?(service_name), - :version => nil, -diff --git a/pcsd/remote.rb b/pcsd/remote.rb -index 0154c78c..6f454681 100644 ---- a/pcsd/remote.rb -+++ b/pcsd/remote.rb -@@ -2911,8 +2911,10 @@ def check_host(params, request, auth_user) - File.exist?(Cfgsync::CorosyncConf.file_path) or File.exist?(CIB_PATH) - ) - } -+ -+ service_checker = get_service_installed_checker - service_list.each do |service| -- output[:services][service] = get_service_info(service.to_s) -+ output[:services][service] = get_service_info(service.to_s, service_checker) - end - service_version_getter.each do |service, version_getter| - version = version_getter.call() --- -2.20.1 - diff --git a/SOURCES/pcsd-bundle-config-1 b/SOURCES/pcsd-bundle-config-1 deleted file mode 100644 index c067a62..0000000 --- a/SOURCES/pcsd-bundle-config-1 +++ /dev/null @@ -1,5 +0,0 @@ ---- -BUNDLE_FROZEN: '1' -BUNDLE_PATH: vendor/bundle -BUNDLE_DISABLE_SHARED_GEMS: '1' -BUNDLE_BUILD: --with-ldflags="-Wl,-z,now -Wl,-z,relro" diff --git a/SOURCES/pcsd-bundle-config-2 b/SOURCES/pcsd-bundle-config-2 new file mode 100644 index 0000000..c067a62 --- /dev/null +++ b/SOURCES/pcsd-bundle-config-2 @@ -0,0 +1,5 @@ +--- +BUNDLE_FROZEN: '1' +BUNDLE_PATH: vendor/bundle +BUNDLE_DISABLE_SHARED_GEMS: '1' +BUNDLE_BUILD: --with-ldflags="-Wl,-z,now -Wl,-z,relro" diff --git a/SPECS/pcs.spec b/SPECS/pcs.spec index db13c0b..31610e9 100644 --- a/SPECS/pcs.spec +++ b/SPECS/pcs.spec @@ -1,6 +1,6 @@ Name: pcs -Version: 0.10.1 -Release: 4%{?dist}.4 +Version: 0.10.2 +Release: 4%{?dist} # https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses # GPLv2: pcs # ASL 2.0: tornado @@ -10,8 +10,15 @@ URL: https://github.com/ClusterLabs/pcs Group: System Environment/Base Summary: Pacemaker Configuration System #building only for architectures with pacemaker and corosync available -ExclusiveArch: i686 x86_64 s390x ppc64le aarch64 %{arm} +ExclusiveArch: i686 x86_64 s390x ppc64le aarch64 +%global version_or_commit %{version} +# %%global version_or_commit f556c4adb538c8a95d763472d370f3f5d27eb38a +%global pcs_source_name %{name}-%{version_or_commit} + +# ui_commit can be determined by hash, tag or branch +%global ui_commit 0.1.1 +%global ui_src_name pcs-web-ui-%{ui_commit} %global pcs_snmp_pkg_name pcs-snmp @@ -21,14 +28,13 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64 %{arm} %global version_rubygem_ethon 0.11.0 %global version_rubygem_ffi 1.9.25 %global version_rubygem_json 2.1.0 -%global version_rubygem_multi_json 1.13.1 %global version_rubygem_mustermann 1.0.3 %global version_rubygem_open4 1.3.4 %global version_rubygem_rack 2.0.6 %global version_rubygem_rack_protection 2.0.4 %global version_rubygem_rack_test 1.0.0 %global version_rubygem_sinatra 2.0.4 -%global version_rubygem_tilt 2.0.8 +%global version_rubygem_tilt 2.0.9 # We do not use _libdir macro because upstream is not prepared for it. # Pcs does not include binaries and thus it should live in /usr/lib. Tornado @@ -38,6 +44,7 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64 %{arm} # different content in different architectures. %global pcs_libdir %{_prefix}/lib %global bundled_src_dir pcs/bundled +%global pcsd_public_dir pcsd/public %global rubygem_cache_dir pcsd/vendor/cache %global rubygem_bundle_dir pcsd/vendor/bundle/ruby @@ -56,11 +63,9 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64 %{arm} # /usr/bin/python will be removed or switched to Python 3 in the future. %global __python %{__python3} -#part after last slash is recognized as filename in look-aside repository -#desired name is achived by trick with hash anchor -Source0: %{url}/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz +Source0: %{url}/archive/%{version_or_commit}/%{pcs_source_name}.tar.gz Source1: HAM-logo.png -Source2: pcsd-bundle-config-1 +Source2: pcsd-bundle-config-2 Source41: https://github.com/ondrejmular/pyagentx/archive/v%{pyagentx_version}.tar.gz#/pyagentx-%{pyagentx_version}.tar.gz Source42: https://github.com/tornadoweb/tornado/archive/v%{tornado_version}.tar.gz#/tornado-%{tornado_version}.tar.gz @@ -69,28 +74,32 @@ Source81: https://rubygems.org/downloads/backports-%{version_rubygem_backports}. Source82: https://rubygems.org/downloads/ethon-%{version_rubygem_ethon}.gem Source83: https://rubygems.org/downloads/ffi-%{version_rubygem_ffi}.gem Source84: https://rubygems.org/downloads/json-%{version_rubygem_json}.gem -Source85: https://rubygems.org/downloads/multi_json-%{version_rubygem_multi_json}.gem Source86: https://rubygems.org/downloads/mustermann-%{version_rubygem_mustermann}.gem -Source87: https://rubygems.org/downloads/open4-%{version_rubygem_open4}.gem +# We needed to re-upload open4 rubygem because of issues with sources in gating. +# Unfortunately, there was no newer version available, therefore we had to +# change its 'version' ourselves. +Source87: https://rubygems.org/downloads/open4-%{version_rubygem_open4}.gem#/open4-%{version_rubygem_open4}-1.gem Source88: https://rubygems.org/downloads/rack-%{version_rubygem_rack}.gem Source89: https://rubygems.org/downloads/rack-protection-%{version_rubygem_rack_protection}.gem Source90: https://rubygems.org/downloads/rack-test-%{version_rubygem_rack_test}.gem Source91: https://rubygems.org/downloads/sinatra-%{version_rubygem_sinatra}.gem Source92: https://rubygems.org/downloads/tilt-%{version_rubygem_tilt}.gem -# patches cherry-picked from upstream -Patch1: bz1656953-01-drop-removed-command-pcs-resource-show-from-help.patch -Patch2: bz1659051-01-fix-link-options-names-in-cluster-setup.patch -Patch3: bz1664057-01-gui-issues-in-create-new-cluster-form.patch -Patch4: bz1661059-01-re-add-and-deprecate-pcs-resource-show.patch -# downstream patches -Patch100: bz1668422-01-fix-crashes-when-using-unsupported-options.patch +Source100: https://github.com/idevat/pcs-web-ui/archive/%{ui_commit}/%{ui_src_name}.tar.gz +Source101: https://github.com/idevat/pcs-web-ui/releases/download/%{ui_commit}/pcs-web-ui-node-modules-%{ui_commit}.tar.xz + +# Patches from upstream. +# They should come before downstream patches to avoid unnecessary conflicts. +# Z-streams are exception here: they can come from upstream but should be +# applied at the end to keep z-stream changes as straightforward as possible. +Patch1: bz1676957-01-fix-crashes-in-pcs-host-auth.patch +Patch2: bz1657166-01-Updating-a-bundle-is-a-bit-cumber.patch +Patch3: bz1725183-01-fix-and-options-for-non-root-users.patch +Patch4: bz1740218-01-set-authkey-length-to-256-bytes.patch + +# Downstream patches do not come from upstream. They adapt pcs for specific +# RHEL needs. Patch101: do-not-support-cluster-setup-with-udp-u-transport.patch -Patch102: bz1690290-GUI-cannot-create-a-cluster-if-k.patch -Patch103: bz1690304-GUI-submitting-of-create-cluster.patch -Patch104: fix-web-UI-dashboard-data-loading-timeout.patch -Patch105: fix-communication-over-IPv6.patch -Patch106: bz1746565-01-set-authkey-length-to-256-bytes.patch # git for patches BuildRequires: git @@ -128,6 +137,11 @@ BuildRequires: booth-site BuildRequires: fontconfig BuildRequires: liberation-sans-fonts BuildRequires: overpass-fonts +# Red Hat logo for creating symlink of favicon +BuildRequires: redhat-logos + +# for building web ui +BuildRequires: npm # python and libraries for pcs, setuptools for pcs entrypoint Requires: platform-python @@ -154,16 +168,20 @@ Requires: pacemaker-cli >= 2.0.0 Requires(post): systemd Requires(preun): systemd Requires(postun): systemd +# pam is used for authentication inside daemon (python ctypes) +# more details: https://bugzilla.redhat.com/show_bug.cgi?id=1717113 +Requires: pam # pcsd fonts Requires: liberation-sans-fonts Requires: overpass-fonts +# favicon Red Hat logo +Requires: redhat-logos Provides: bundled(tornado) = %{tornado_version} Provides: bundled(backports) = %{version_rubygem_backports} Provides: bundled(ethon) = %{version_rubygem_ethon} Provides: bundled(ffi) = %{version_rubygem_ffi} Provides: bundled(json) = %{version_rubygem_json} -Provides: bundled(multi_json) = %{version_rubygem_multi_json} Provides: bundled(mustermann) = %{version_rubygem_mustermann} Provides: bundled(open4) = %{version_rubygem_open4} Provides: bundled(rack) = %{version_rubygem_rack} @@ -199,7 +217,7 @@ Provides: bundled(pyagentx) = %{pyagentx_version} SNMP agent that provides information about pacemaker cluster to the master agent (snmpd) %prep -%autosetup -p1 -S git +%autosetup -p1 -S git -n %{pcs_source_name} # -- following is inspired by python-simplejon.el5 -- # Update timestamps on the files touched by a patch, to avoid non-equal @@ -240,10 +258,13 @@ update_times_patch %{PATCH1} update_times_patch %{PATCH2} update_times_patch %{PATCH3} update_times_patch %{PATCH4} -update_times_patch %{PATCH105} -update_times_patch %{PATCH106} +update_times_patch %{PATCH101} cp -f %SOURCE1 pcsd/public/images +# prepare dirs/files necessary for building web ui +# inside SOURCE100 is only directory %%{ui_src_name} +tar -xzf %SOURCE100 -C %{pcsd_public_dir} +tar -xf %SOURCE101 -C %{pcsd_public_dir}/%{ui_src_name} # prepare dirs/files necessary for building all bundles # ----------------------------------------------------- @@ -257,9 +278,10 @@ cp -f %SOURCE81 pcsd/vendor/cache cp -f %SOURCE82 pcsd/vendor/cache cp -f %SOURCE83 pcsd/vendor/cache cp -f %SOURCE84 pcsd/vendor/cache -cp -f %SOURCE85 pcsd/vendor/cache cp -f %SOURCE86 pcsd/vendor/cache -cp -f %SOURCE87 pcsd/vendor/cache +# For reason why we are renaming open4 rubygem, see comment of source +# definition above. +cp -f %SOURCE87 pcsd/vendor/cache/open4-%{version_rubygem_open4}.gem cp -f %SOURCE88 pcsd/vendor/cache cp -f %SOURCE89 pcsd/vendor/cache cp -f %SOURCE90 pcsd/vendor/cache @@ -301,7 +323,6 @@ gem install \ %{rubygem_cache_dir}/ethon-%{version_rubygem_ethon}.gem \ %{rubygem_cache_dir}/ffi-%{version_rubygem_ffi}.gem \ %{rubygem_cache_dir}/json-%{version_rubygem_json}.gem \ - %{rubygem_cache_dir}/multi_json-%{version_rubygem_multi_json}.gem \ %{rubygem_cache_dir}/mustermann-%{version_rubygem_mustermann}.gem \ %{rubygem_cache_dir}/open4-%{version_rubygem_open4}.gem \ %{rubygem_cache_dir}/rack-%{version_rubygem_rack}.gem \ @@ -323,6 +344,10 @@ for fname in `find %{rubygem_bundle_dir}/extensions -type f -name "*.so"`; do strip ${fname} done +# build web ui and put it to pcsd +make -C %{pcsd_public_dir}/%{ui_src_name} build +mv %{pcsd_public_dir}/%{ui_src_name}/build pcsd/public/ui +rm -r %{pcsd_public_dir}/%{ui_src_name} # main pcs install make install \ @@ -347,6 +372,9 @@ for fname in `find ${RPM_BUILD_ROOT}%{pcs_libdir}/pcs/bundled/packages/tornado/ strip ${fname} done +# symlink favicon into pcsd directories +ln -fs /etc/favicon.png ${RPM_BUILD_ROOT}%{pcs_libdir}/%{pcsd_public_dir}/images/favicon.png + #after the ruby gem compilation we do not need ruby gems in the cache rm -r -v $RPM_BUILD_ROOT%{pcs_libdir}/%{rubygem_cache_dir} @@ -366,13 +394,14 @@ run_all_tests(){ # disabled tests: # - # pcs.lib.commands.test.test_resource_agent.DescribeAgentUtf8.test_describe - # For a unknwon reason this test is passing outside the mock environment. + # pcs_test.tier0.lib.commands.test_resource_agent.DescribeAgentUtf8.test_describe + # For an unknown reason this test is failing in mock environment and + # passing outside the mock environment. # TODO: Investigate the issue BUNDLED_LIB_LOCATION=$RPM_BUILD_ROOT%{pcs_libdir}/pcs/bundled/packages \ - %{__python3} pcs/test/suite.py -v --vanilla --all-but \ - pcs.lib.commands.test.test_resource_agent.DescribeAgentUtf8.test_describe \ + %{__python3} pcs_test/suite.py -v --vanilla --all-but \ + pcs_test.tier0.lib.commands.test_resource_agent.DescribeAgentUtf8.test_describe \ test_result_python=$? @@ -383,11 +412,6 @@ run_all_tests(){ -I${pcsd_dir}/test \ ${pcsd_dir}/test/test_all_suite.rb test_result_ruby=$? - #remove pcsd tests, we do not distribute them in the rpm - rm -r -v ${pcsd_dir}/test - - # remove javascript testing files - rm -r -v ${pcsd_dir}/public/js/dev if [ $test_result_python -ne 0 ]; then return $test_result_python @@ -395,7 +419,17 @@ run_all_tests(){ return $test_result_ruby } +remove_all_tests() { + pcsd_dir=$RPM_BUILD_ROOT%{pcs_libdir}/pcsd + #remove pcsd tests, we do not distribute them in the rpm + rm -r -v ${pcsd_dir}/test + + # remove javascript testing files + rm -r -v ${pcsd_dir}/public/js/dev +} + run_all_tests +remove_all_tests %post %systemd_post pcsd.service @@ -422,7 +456,7 @@ run_all_tests %license tornado_LICENSE %license COPYING %{python3_sitelib}/pcs -%{python3_sitelib}/pcs-0.10.1-py3.*.egg-info +%{python3_sitelib}/pcs-%{version}-py3.*.egg-info %{_sbindir}/pcs %{_sbindir}/pcsd %{pcs_libdir}/pcs/pcs_internal @@ -436,13 +470,13 @@ run_all_tests %dir %{_var}/log/pcsd %config(noreplace) %{_sysconfdir}/logrotate.d/pcsd %config(noreplace) %{_sysconfdir}/sysconfig/pcsd -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/cfgsync_ctl -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/known-hosts -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/pcsd.cookiesecret -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/pcsd.crt -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/pcsd.key -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/pcs_settings.conf -%ghost %config(noreplace) %{_sharedstatedir}/pcsd/pcs_users.conf +%ghost %config(noreplace) %attr(0600,root,root) %{_sharedstatedir}/pcsd/cfgsync_ctl +%ghost %config(noreplace) %attr(0600,root,root) %{_sharedstatedir}/pcsd/known-hosts +%ghost %config(noreplace) %attr(0600,root,root) %{_sharedstatedir}/pcsd/pcsd.cookiesecret +%ghost %config(noreplace) %attr(0600,root,root) %{_sharedstatedir}/pcsd/pcsd.crt +%ghost %config(noreplace) %attr(0600,root,root) %{_sharedstatedir}/pcsd/pcsd.key +%ghost %config(noreplace) %attr(0644,root,root) %{_sharedstatedir}/pcsd/pcs_settings.conf +%ghost %config(noreplace) %attr(0644,root,root) %{_sharedstatedir}/pcsd/pcs_users.conf %{_mandir}/man8/pcs.* %{_mandir}/man8/pcsd.* %exclude %{pcs_libdir}/pcsd/*.debian @@ -450,6 +484,9 @@ run_all_tests %exclude %{pcs_libdir}/pcsd/pcsd.conf %exclude %{pcs_libdir}/pcsd/pcsd.8 %exclude %{pcs_libdir}/pcsd/public/js/dev/* +%exclude %{pcs_libdir}/pcsd/Gemfile +%exclude %{pcs_libdir}/pcsd/Gemfile.lock +%exclude %{pcs_libdir}/pcsd/Makefile %exclude %{python3_sitelib}/pcs/bash_completion %exclude %{python3_sitelib}/pcs/pcs.8 %exclude %{python3_sitelib}/pcs/pcs @@ -468,22 +505,51 @@ run_all_tests %license pyagentx_LICENSE.txt %changelog -* Thu Aug 29 2019 Ivan Devat - 0.10.1-4.el8_0.4 -- Generate 256 bytes long corosync authkey, longer keys are not supported when FIPS is enabled -- Resolves: rhbz#1746565 - -* Fri May 24 2019 Ivan Devat - 0.10.1-4.el8_0.3 -- Fixed communication over IPv6 -- Resolves: rhbz#1710067 - -* Mon Apr 8 2019 Ondrej Mular - 0.10.1-4.el8_0.2 -- Fixed web UI dashboard data loading -- Resolves: rhbz#1690304 - -* Thu Mar 28 2019 Ivan Devat - 0.10.1-4.el8_0.1 -- Fixed crash of the cluster create without knet links specified in webUI -- Fixed submitting of 'create cluster' form when full domain names are used -- Resolves: rhbz#1690290 rhbz#1690304 +* Tue Aug 13 2019 Tomas Jelinek - 0.10.2-4 +- Generate 256 bytes long corosync authkey so clusters can start when FIPS is enabled +- Resolves: rhbz#1740218 + +* Mon Jul 08 2019 Ivan Devat - 0.10.2-3 +- Options starting with - and -- are no longer ignored for non-root users +- Resolves: rhbz#1725183 + +* Thu Jun 27 2019 Ivan Devat - 0.10.2-2 +- Fixed crashes in the `pcs host auth` command +- Command `pcs resource bundle reset` no longer accepts the container type +- Fixed id conflict with current bundle configuration in i`pcs resource bundle reset` +- Resolves: rhbz#1657166 rhbz#1676957 + +* Thu Jun 13 2019 Ivan Devat - 0.10.2-1 +- Rebased to latest upstream sources (see CHANGELOG.md) +- Added pam as required package +- An alternative webUI rebased to latest upstream sources +- Resolves: rhbz#1673907 rhbz#1679196 rhbz#1714663 rhbz#1717113 + +* Tue May 21 2019 Ivan Devat - 0.10.1-9 +- Added git as required package in tests/tests.yml +- Resolves: rhbz#1673907 + +* Mon May 20 2019 Ivan Devat - 0.10.1-8 +- Rebased to latest upstream sources (see CHANGELOG.md) +- Added an alternative webUI +- Resolves: rhbz#1673907 rhbz#1679197 rhbz#1667058 + +* Mon May 06 2019 Ondrej Mular - 0.10.1-7 +- Enable upstream tests in gating +- Update tilt ruby gem +- Resolves: rhbz#1682129 + +* Thu May 02 2019 Ondrej Mular - 0.10.1-6 +- Rebased to latest upstream sources (see CHANGELOG.md) +- Updated Red Hat logo +- Improved configuration files permissions in rpm +- Removed multi_json rubygem +- Excluded files required for building gems from rpm +- Resolves: rhbz#1625386 rhbz#1653316 rhbz#1655055 rhbz#1657166 rhbz#1659144 rhbz#1660702 rhbz#1664828 rhbz#1665404 rhbz#1667040 rhbz#1667053 rhbz#1667058 rhbz#1667090 rhbz#1668223 rhbz#1673822 rhbz#1673825 rhbz#1674005 rhbz#1676945 rhbz#1676957 rhbz#1682129 rhbz#1687562 rhbz#1687965 rhbz#1698373 rhbz#1700543 + +* Mon Mar 25 2019 Ondrej Mular - 0.10.1-5 +- Enable gating +- Resolves: rhbz#1682129 * Wed Jan 30 2019 Ivan Devat - 0.10.1-4 - Fixed crash when using unsupported options in commands `pcs status` and `pcs config`