Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Update option box in crm configuration debug #279

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions e2e_test/hawk_test_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class Xpath:
HREF_CONFIGURATION = '//a[contains(@href, "#configurationMenu")]'
HREF_CONFIG_EDIT = '//a[contains(@href, "config/edit")]'
HREF_CRM_CONFIG_EDIT = '//a[contains(@href, "crm_config/edit")]'
HREF_CRM_CONFIG_FENCE_REACTION = '//option[contains(@value, "fence-reaction")]'
HREF_CRM_CONFIG_NO_QUORUM_POLICY = '//option[contains(@value, "no-quorum-policy")]'
HREF_CRM_CONFIG_STONITH_ACTION = '//option[contains(@value, "stonith-action")]'
HREF_CRM_CONFIG_NODE_HEALTH_STRATEGY = '//option[contains(@value, "node-health-strategy")]'
HREF_CRM_CONFIG_NODE_PLACEMENT_STRATEGY = '//option[contains(@value, "placement-strategy")]'
HREF_CONSTRAINTS = '//a[contains(@href, "#constraints")]'
HREF_DASHBOARD = '//a[contains(@href, "/dashboard")]'
HREF_DELETE_FORMAT = '//a[contains(@href, "{}") and contains(@title, "Delete")]'
Expand Down Expand Up @@ -551,10 +556,17 @@ def test_add_group(self, group):
return False

def test_check_cluster_configuration(self, ssh):
"""
The test does two things.
First, it checks that the available resources are correct.
Second, that the options of select-type resources are correct.
The test doesn't create those resources.
"""
print(f"TEST: test_check_cluster_configuration: Check crm options")
self.click_if_major_version("15", 'configuration')
self.check_and_click_by_xpath("Click on Cluster Configuration", [Xpath.HREF_CRM_CONFIG_EDIT])

### 1.
# The rsc_defaults and op_defaults are hardcoded in app/models/tableless.rb
elem = self.find_element(By.NAME, 'temp_crm_config[rsc_defaults]')
if not elem:
Expand All @@ -572,9 +584,9 @@ def test_check_cluster_configuration(self, ssh):
print(f"ERROR: temp_crm_config[op_defaults] has WRONG values")
return False

# The crm_config is trickier. We should get those attribute from the crm_attribute
# in the newer versions of pacemaker.If it's an older version of pacemaker
# let's simply compare with a literal.
# The crm_config is trickier. We should get those attributes from the crm_attribute
# in the newer versions of pacemaker. If it's an older version of pacemaker
# let's simply compare it against LongLiterals.CRM_CONFIG_ATTRIBUTES.
elem = self.find_element(By.NAME, 'temp_crm_config[crm_config]')
if not elem:
print(f"ERROR: Couldn't find element temp_crm_config[crm_config]")
Expand All @@ -598,6 +610,36 @@ def test_check_cluster_configuration(self, ssh):
print(f'ERROR: {Error.CRM_CONFIG_ADVANCED_ATTRIBUTES}, attr: {a}')
return False

### 2.
# Show out all but fence-reaction select-type resources
# (fence-reaction is a string the pacemaker-controld
# and a select in the crm_attribute. Let's not overengineer.)
elem.click()
for xref in [Xpath.HREF_CRM_CONFIG_NO_QUORUM_POLICY,
Xpath.HREF_CRM_CONFIG_STONITH_ACTION,
Xpath.HREF_CRM_CONFIG_NODE_HEALTH_STRATEGY,
Xpath.HREF_CRM_CONFIG_NODE_PLACEMENT_STRATEGY]:
self.check_and_click_by_xpath(f'Couldn\'t find {xref} resource in the drop-down list',
[xref])
time.sleep(1)

# ["<resource name>", ["<option1>", "<option2>,..."]]
for check_options in [ ["no-quorum-policy", ["stop", "freeze", "ignore", "demote", "suicide"]],
["stonith-action", ["reboot", "off", "poweroff"]],
["node-health-strategy", ["none", "migrate-on-red", "only-green", "progressive", "custom"]],
["placement-strategy", ["default", "utilization", "minimal", "balanced"]]]:
elem = self.find_element(By.NAME, f'crm_config[crm_config][{check_options[0]}]')
if not elem:
print(f'ERROR: Couldn\'t find {check_options[0]}')
return False

lst = elem.text.split('\n')
lst.sort()
check_options[1].sort()
if lst != check_options[1]:
print(f'ERROR: {check_options[0]} options. Expected: {lst}, are: {check_options[1]}')
return False

time.sleep(self.timeout_scale)
return self.test_status

Expand Down
2 changes: 1 addition & 1 deletion hawk/app/assets/javascripts/module/attrlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

'<div class="col-sm-7">',
'<div class="input-group">',
'{^{if ~root.mapping[key]["type"] == "enum"}}',
'{^{if (~root.mapping[key]["type"] == "enum" || ~root.mapping[key]["type"] == "select" )}}',
'<select class="form-control select" name="{{>~root.prefix}}[{{>key}}]" id="{{>key}}" data-link="prop">',
'<option></option>',
'{^{for ~root.mapping[key]["values"]}}',
Expand Down
34 changes: 22 additions & 12 deletions hawk/app/models/crm_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,28 +62,38 @@ def get_parameters_from(crm_config, cmd)
type = content.attributes["type"]
default = content.attributes["default"]

advanced = param.attributes["advanced"] || shortdesc.match(/advanced use only/i) || longdesc.match(/advanced use only/i)
advanced = false
advanced = true if param.attributes["advanced"] && (param.attributes["advanced"]=="1")
advanced = true if shortdesc.match(/advanced use only/i) || longdesc.match(/advanced use only/i)

crm_config[name] = {
type: content.attributes["type"],
readonly: false,
shortdesc: shortdesc,
longdesc: longdesc,
advanced: (advanced and (advanced=="1")) ? true : false,
advanced: advanced,
default: default
}

if type == "enum"
match = longdesc.match(/Allowed values:(.*)/i)
if type == "enum" or type == "select"

if match
values = match[1].split(",").map do |value|
value.strip
end.reject do |value|
value.empty?
end
# First try to get options from the content section
crm_config[name][:values] = []
content.each_element("option") do |opt|
crm_config[name][:values] << opt.attributes["value"]
end

crm_config[name][:values] = values unless values.empty?
# If didn't find then try to get them from the long description
if crm_config[name][:values].empty?
match = longdesc.match(/Allowed values:(.*)/i)
if match
values = match[1].split(",").map do |value|
value.strip
end.reject do |value|
value.empty?
end
crm_config[name][:values] = values unless values.empty?
end
end
end
end
Expand Down Expand Up @@ -201,7 +211,7 @@ def persist!
new_value, old_value = change

if new_value.nil? || new_value.empty?
Invoker.instance.run("crm_attribute", "--attr-name", key, "--delete-attr")
Invoker.instance.run("crm_attribute", "--attr-name", key, "--delete-attr", key)
else
writer[:crm_config][key] = new_value
end
Expand Down
119 changes: 62 additions & 57 deletions hawk/app/models/tableless.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ def current_cib
default: "true",
longdesc: _("Is the cluster allowed to start and stop the resource?")
},
"maintenancee" => {
type: "boolean",
default: "false",
longdesc: _("Resources in maintenancee mode are not monitored by the cluster.")
},
"maintenance" => {
type: "boolean",
default: "false",
Expand All @@ -99,63 +104,63 @@ def current_cib
default: "0",
longdesc: _("If not all resources can be active, the cluster will stop lower priority resources in order to keep higher priority ones active.")
},
"multiple-active" => {
type: "enum",
default: "stop_start",
values: ["block", "stop_only", "stop_start"],
longdesc: _("What should the cluster do if it ever finds the resource active on more than one node?")
},
"failure-timeout" => {
type: "integer",
default: "0",
longdesc: _("How many seconds to wait before acting as if the failure had not occurred, and potentially allowing the resource back to the node on which it failed. A value of 0 indicates that this feature is disabled.")
},
"resource-stickiness" => {
type: "integer",
default: "0",
longdesc: _("How much does the resource prefer to stay where it is?")
},
"target-role" => {
type: "enum",
default: "Stopped",
values: ["Started", "Stopped", "Master"],
longdesc: _("What state should the cluster attempt to keep this resource in?")
},
"restart-type" => {
type: "enum",
default: "ignore",
values: ["ignore", "restart"]
},
"description" => {
type: "string",
default: ""
},
"requires" => {
type: "enum",
default: "fencing",
values: ["nothing", "quorum", "fencing"],
longdesc: _("Conditions under which the resource can be started.")
},
"remote-node" => {
type: "string",
default: "",
longdesc: _("The name of the remote-node this resource defines. This both enables the resource as a remote-node and defines the unique name used to identify the remote-node. If no other parameters are set, this value will also be assumed as the hostname to connect to at the port specified by remote-port. WARNING: This value cannot overlap with any resource or node IDs. If not specified, this feature is disabled.")
},
"remote-port" => {
type: "integer",
default: 3121,
longdesc: _("Port to use for the guest connection to pacemaker_remote.")
},
"remote-addr" => {
type: "string",
default: "",
longdesc: _("The IP address or hostname to connect to if remote-node's name is not the hostname of the guest.")
},
"remote-connect-timeout" => {
type: "string",
default: "60s",
longdesc: _("How long before a pending guest connection will time out.")
}
#"multiple-active" => {
# type: "enum",
# default: "stop_start",
# values: ["block", "stop_only", "stop_start"],
# longdesc: _("What should the cluster do if it ever finds the resource active on more than one node?")
#},
#"failure-timeout" => {
# type: "integer",
# default: "0",
# longdesc: _("How many seconds to wait before acting as if the failure had not occurred, and potentially allowing the resource back to the node on which it failed. A value of 0 indicates that this feature is disabled.")
#},
#"resource-stickiness" => {
# type: "integer",
# default: "0",
# longdesc: _("How much does the resource prefer to stay where it is?")
#},
#"target-role" => {
# type: "enum",
# default: "Stopped",
# values: ["Started", "Stopped", "Master"],
# longdesc: _("What state should the cluster attempt to keep this resource in?")
#},
#"restart-type" => {
# type: "enum",
# default: "ignore",
# values: ["ignore", "restart"]
#},
#"description" => {
# type: "string",
# default: ""
#},
#"requires" => {
# type: "enum",
# default: "fencing",
# values: ["nothing", "quorum", "fencing"],
# longdesc: _("Conditions under which the resource can be started.")
#},
#"remote-node" => {
# type: "string",
# default: "",
# longdesc: _("The name of the remote-node this resource defines. This both enables the resource as a remote-node and defines the unique name used to identify the remote-node. If no other parameters are set, this value will also be assumed as the hostname to connect to at the port specified by remote-port. WARNING: This value cannot overlap with any resource or node IDs. If not specified, this feature is disabled.")
#},
#"remote-port" => {
# type: "integer",
# default: 3121,
# longdesc: _("Port to use for the guest connection to pacemaker_remote.")
#},
#"remote-addr" => {
# type: "string",
# default: "",
# longdesc: _("The IP address or hostname to connect to if remote-node's name is not the hostname of the guest.")
#},
#"remote-connect-timeout" => {
# type: "string",
# default: "60s",
# longdesc: _("How long before a pending guest connection will time out.")
#}
}.freeze

OP_DEFAULTS = {
Expand Down
Loading