modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
| 26
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
| 316 +++++++---
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
| 11
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/scheduler/jobs/StorageClusterCredentialsJob.java
| 45 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
| 108 ++-
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
| 35 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/system/SystemManagerBean.java
| 6
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
| 18
8 files changed, 442 insertions(+), 123 deletions(-)
New commits:
commit d5cd83d5008374b87e590197aad459cf09dbc737
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Mon Oct 28 16:02:06 2013 -0500
[BZ 1016175] Update and simplifiy some verbiage based on feedback.
(cherry picked from commit dda907b836f7b127e9f4968a8237fce49ae9aa51)
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
index 18079e1..066b9ab 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
@@ -148,8 +148,8 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
private void prepareForms() {
setWidth100();
clusterForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'>"
- + "<b>Cluster Settings</b></span><br/>On save,
these setting will not be propagated to existing Storage"
- + " Nodes. Please review the documentation on how update the CQL and
Gossip ports for all Storage Nodes.</div>");
+ + "<b>Cluster Settings</b></span><br/>Before
changing these settings, storage nodes require updates to have equivalent port numbers.
"
+ + "Port changes below will only be saved in the RHQ server
configuration.</div>");
List<FormItem> items = buildHeaderItems();
IsIntegerValidator validator = new IsIntegerValidator();
@@ -184,7 +184,7 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
clusterForm.setFields(items.toArray(new FormItem[items.size()]));
deploymentForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'>"
- + "<b>Deployment Settings</b></span><br/>Only
applies to new installations.</div>");
+ + "<b>New Deployment
Settings</b></span><br/>Only applies to new
installations.</div>");
FormItemBuilder.resetOddRow();
items = buildHeaderItems();
commit 9f460f107d825ac3df5b5c485affc9761935b559
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Mon Oct 28 16:01:43 2013 -0500
[BZ 1016175] Workaround to SmartGWT bug: password validators were changing the focus
after each user input making it unusable
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
index 52b17b5..a2251fa 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
@@ -88,6 +88,6 @@ public class StorageClusterSettings implements Serializable {
@Override
public String toString() {
return "StorageClusterSettings[cqlPort=" + cqlPort + ",
gossipPort=" + gossipPort + ", automaticDeployment="
- + automaticDeployment + ", username=" + username + ",
passwordHash=********]";
+ + automaticDeployment + ", username (read-only)=" + username +
", passwordHash=********]";
}
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
index 6834c1b..18079e1 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
@@ -147,7 +147,8 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
private void prepareForms() {
setWidth100();
- clusterForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'><b>Cluster
Settings</b></span><br/>On save, these setting will not be propagated to
existing Storage"
+ clusterForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'>"
+ + "<b>Cluster Settings</b></span><br/>On save,
these setting will not be propagated to existing Storage"
+ " Nodes. Please review the documentation on how update the CQL and
Gossip ports for all Storage Nodes.</div>");
List<FormItem> items = buildHeaderItems();
@@ -182,7 +183,8 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
items.addAll(gossipPortItems);
clusterForm.setFields(items.toArray(new FormItem[items.size()]));
- deploymentForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'><b>Deployment
Settings</b></span><br/>Only applies to new
installations.</div>");
+ deploymentForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'>"
+ + "<b>Deployment Settings</b></span><br/>Only
applies to new installations.</div>");
FormItemBuilder.resetOddRow();
items = buildHeaderItems();
@@ -206,7 +208,8 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
items.addAll(automaticDeploymentItems);
deploymentForm.setFields(items.toArray(new FormItem[items.size()]));
- credentialsForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'><b>Cluster
Credentials</b></span><br/>Password changes are propagated to the
Storage Cluster.</div>");
+ credentialsForm = buildForm("<div align='left'><span
style='text-align: left; font-family: Arial, Verdana, sans-serif
!important;'>"
+ + "<b>Cluster
Credentials</b></span><br/>Password changes are propagated to the
Storage Cluster.</div>");
FormItemBuilder.resetOddRow();
items = buildHeaderItems();
@@ -223,6 +226,9 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
// password field
StringLengthValidator passwordValidator1 = new StringLengthValidator(6, 100,
false);
+ passwordValidator1.setErrorMessage("The password length must be at least 6
characters.");
+ // due to SmartGWT bug that changes focus after each input
(
https://code.google.com/p/smartgwt/issues/detail?id=309)
+ passwordValidator1.setValidateOnChange(false);
builder = new FormItemBuilder();
List<FormItem> passwordItems = builder
.withName(FIELD_PASSWORD)
@@ -237,9 +243,14 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
// password_verify field
builder = new FormItemBuilder();
+ passwordValidator1 = new StringLengthValidator(6, 100, false);
+ passwordValidator1.setErrorMessage("The password length must be at least 6
characters.");
MatchesFieldValidator passwordValidator2 = new MatchesFieldValidator();
passwordValidator2.setOtherField(FIELD_PASSWORD);
passwordValidator2.setErrorMessage("This should be the same string as in the
Password field.");
+ // due to same bug in SmartGWT as above
+ passwordValidator1.setValidateOnChange(false);
+ passwordValidator2.setValidateOnChange(false);
List<FormItem> passwordVerifyItems =
builder.withName(FIELD_PASSWORD_VERIFY).withTitle("Verify Password")
.withValue(settings.getPasswordHash()).withDescription("Validation
(needs to match Password)")
.withReadOnlySetTo(readOnly).withValidators(passwordValidator1,
passwordValidator2)
commit c7650fa9381a4d3bd30ae69d1cc87ae54db10ec0
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Mon Oct 28 16:00:59 2013 -0500
[BZ 1016175] Add support for changing the storage password
Add code to update the storage session when credentials get update in the system
settings table. Created a quartz job to trigger the refresh every 2 minutes.
The process to refresh the session is as follows:
1) Get new credentials from the database
2) Create a new session with new credentials
3) Replace existing session with newly created one
4) Allow few minutes for existing session drainage
5) Shutdown old session
Other changes:
- Add code to update Cassandra password via a direct CQL query using the existing open
session.
- Update log level and text for the storage cluster credentials update job.
- Make the username read-only; adding more intuitive description to the other
properties.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
index 11846df..6834c1b 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
@@ -59,7 +59,8 @@ import org.rhq.coregui.client.util.message.Message;
*/
public class ClusterConfigurationEditor extends EnhancedVLayout implements
RefreshableView {
- private EnhancedDynamicForm form;
+ private EnhancedDynamicForm clusterForm;
+ private EnhancedDynamicForm deploymentForm;
private EnhancedDynamicForm credentialsForm;
private EnhancedIButton saveButton;
private StorageClusterSettings settings;
@@ -123,46 +124,67 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
return unsetHeader;
}
- @Override
- protected void onDraw() {
- super.onDraw();
- refresh();
- }
-
- private void prepareForms() {
- setWidth100();
- form = new EnhancedDynamicForm();
+ private EnhancedDynamicForm buildForm(String groupTitle) {
+ EnhancedDynamicForm form = new EnhancedDynamicForm();
form.setHiliteRequiredFields(true);
form.setNumCols(3);
form.setCellPadding(5);
form.setColWidths(190, 220, "*");
form.setIsGroup(true);
- form.setGroupTitle("Cluster Wide Settings");
+ form.setGroupTitle(groupTitle);
form.setBorder("1px solid #AAA");
form.setWidth100();
form.setOverflow(Overflow.VISIBLE);
- form.setExtraSpace(10);
+ form.setExtraSpace(15);
+ return form;
+ }
+
+ @Override
+ protected void onDraw() {
+ super.onDraw();
+ refresh();
+ }
+
+ private void prepareForms() {
+ setWidth100();
+ clusterForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'><b>Cluster
Settings</b></span><br/>On save, these setting will not be propagated to
existing Storage"
+ + " Nodes. Please review the documentation on how update the CQL and
Gossip ports for all Storage Nodes.</div>");
List<FormItem> items = buildHeaderItems();
IsIntegerValidator validator = new IsIntegerValidator();
-
+
// cql port field
FormItemBuilder builder = new FormItemBuilder();
- List<FormItem> cqlPortItems =
builder.withName(FIELD_CQL_PORT).withTitle("CQL Port")
+ List<FormItem> cqlPortItems = builder
+ .withName(FIELD_CQL_PORT)
+ .withTitle("CQL Port")
.withValue(String.valueOf(settings.getCqlPort()))
- .withDescription("The port on which the Storage Nodes listens for CQL
client connections.")
- .withValidators(validator).build();
+ .withDescription(
+ "Port on which the Storage Nodes listen for CQL client connections.
On save this setting"
+ + " will not be propagated to existing Storage Nodes. Please
review the documentation on how update"
+ + " the CQL port for all Storage Nodes.
<b>Warning:</b> if this setting does not match the configured"
+ + " Storage Cluster CQL port, the server will not be able to
communicate with the Storage Cluster"
+ + " and will go into maintenance
mode.").withValidators(validator).build();
items.addAll(cqlPortItems);
// gossip port field
builder = new FormItemBuilder();
- List<FormItem> gossipPortItems =
builder.withName(FIELD_GOSSIP_PORT).withTitle("Gossip Port")
+ List<FormItem> gossipPortItems = builder
+ .withName(FIELD_GOSSIP_PORT)
+ .withTitle("Gossip Port")
.withValue(String.valueOf(settings.getGossipPort()))
- .withDescription("The port used for internode communication in the
storage cluster.")
- .withValidators(validator).build();
+ .withDescription(
+ "The port used for internode communication in the Storage Cluster.
On save this setting"
+ + " will not be propagated to existing Storage Nodes. Please
review the documentation on how update"
+ + " the Gossip port for all Storage Nodes.
<b>Warning:</b> if this setting does not match the"
+ + " configured Storage Cluster Gossip port, any new Storage
Nodes will be able to communicate"
+ + " and be part of the existing Storage
Cluster.").withValidators(validator).build();
items.addAll(gossipPortItems);
-
-
+ clusterForm.setFields(items.toArray(new FormItem[items.size()]));
+
+ deploymentForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'><b>Deployment
Settings</b></span><br/>Only applies to new
installations.</div>");
+ FormItemBuilder.resetOddRow();
+ items = buildHeaderItems();
// automatic deployment field
builder = new FormItemBuilder();
@@ -171,8 +193,9 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
.withTitle("Automatic Deployment")
.withValue(Boolean.toString(settings.getAutomaticDeployment()))
.withDescription(
- "If this is set, the newly installed storage nodes will be
automatically deployed to the storage cluster.")
- .withReadOnlySetTo(readOnly).build((FormItem)
GWT.create(RadioGroupItem.class));
+ "If this is set, the newly installed storage nodes will be
automatically deployed to the storage cluster."
+ + " It only applies to new
installations.").withReadOnlySetTo(readOnly)
+ .build((FormItem) GWT.create(RadioGroupItem.class));
RadioGroupItem autoDeployRadio = (RadioGroupItem)
automaticDeploymentItems.get(1);
autoDeployRadio.setVertical(false);
LinkedHashMap<String, String> values = new LinkedHashMap<String,
String>(2);
@@ -181,58 +204,57 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
autoDeployRadio.setValueMap(values);
autoDeployRadio.setValue(settings.getAutomaticDeployment());
items.addAll(automaticDeploymentItems);
-
- form.setFields(items.toArray(new FormItem[items.size()]));
-
- credentialsForm = new EnhancedDynamicForm();
- credentialsForm.setHiliteRequiredFields(true);
- credentialsForm.setNumCols(3);
- credentialsForm.setCellPadding(5);
- credentialsForm.setColWidths(190, 220, "*");
- credentialsForm.setIsGroup(true);
- credentialsForm.setGroupTitle("Storage Cluster Credentials");
- credentialsForm.setBorder("1px solid #AAA");
- credentialsForm.setWidth100();
- credentialsForm.setOverflow(Overflow.VISIBLE);
-
+ deploymentForm.setFields(items.toArray(new FormItem[items.size()]));
+
+ credentialsForm = buildForm("<div align='left'><span
style='font-family: Arial, Verdana, sans-serif !important;'><b>Cluster
Credentials</b></span><br/>Password changes are propagated to the
Storage Cluster.</div>");
FormItemBuilder.resetOddRow();
items = buildHeaderItems();
// username field
StringLengthValidator usernameValidator = new StringLengthValidator(4, 100,
false);
builder = new FormItemBuilder();
- List<FormItem> usernameItems =
builder.withName(FIELD_USERNAME).withTitle("Username")
-
.withValue(settings.getUsername()).withDescription("Username").withReadOnlySetTo(readOnly)
- .withValidators(usernameValidator).build();
+ List<FormItem> usernameItems = builder
+ .withName(FIELD_USERNAME)
+ .withTitle("Username")
+ .withDescription(
+ "Username for Storage Node. This property is read-only because
changes to the username are not allowed.")
+
.withValue(settings.getUsername()).withReadOnlySetTo(true).withValidators(usernameValidator).build();
items.addAll(usernameItems);
// password field
StringLengthValidator passwordValidator1 = new StringLengthValidator(6, 100,
false);
builder = new FormItemBuilder();
- List<FormItem> passwordItems =
builder.withName(FIELD_PASSWORD).withTitle("Password")
-
.withValue(settings.getPasswordHash()).withDescription("Password").withReadOnlySetTo(readOnly)
- .withValidators(passwordValidator1).build((FormItem)
GWT.create(PasswordItem.class));
+ List<FormItem> passwordItems = builder
+ .withName(FIELD_PASSWORD)
+ .withTitle("Password")
+ .withDescription(
+ "Password for all Storage Node CQL authentication. Changing will get
propagated to the all deployed"
+ + " Storage Nodes and appliad to newly installed nodes. All HA
servers will have Storage Cluster"
+ + " sessions refreshed automatically to use the new
password.")
+
.withValue(settings.getPasswordHash()).withReadOnlySetTo(readOnly).withValidators(passwordValidator1)
+ .build((FormItem) GWT.create(PasswordItem.class));
items.addAll(passwordItems);
// password_verify field
builder = new FormItemBuilder();
MatchesFieldValidator passwordValidator2 = new MatchesFieldValidator();
passwordValidator2.setOtherField(FIELD_PASSWORD);
- passwordValidator2.setErrorMessage("This should be the same string as the
Password.");
+ passwordValidator2.setErrorMessage("This should be the same string as in the
Password field.");
List<FormItem> passwordVerifyItems =
builder.withName(FIELD_PASSWORD_VERIFY).withTitle("Verify Password")
- .withValue(settings.getPasswordHash()).withDescription("This should be
the same string as the Password.")
+ .withValue(settings.getPasswordHash()).withDescription("Validation
(needs to match Password)")
.withReadOnlySetTo(readOnly).withValidators(passwordValidator1,
passwordValidator2)
.build((FormItem) GWT.create(PasswordItem.class));
-
+
items.addAll(passwordVerifyItems);
credentialsForm.setFields(items.toArray(new FormItem[items.size()]));
-
+
LayoutSpacer spacer = new LayoutSpacer();
spacer.setWidth100();
ToolStrip toolStrip = buildToolStrip();
- setMembers(form, credentialsForm, spacer, toolStrip);
- form.validate();
+ setMembers(clusterForm, deploymentForm, credentialsForm, spacer, toolStrip);
+ clusterForm.validate();
+ deploymentForm.validate();
credentialsForm.validate();
markForRedraw();
}
@@ -246,7 +268,7 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
saveButton = new EnhancedIButton(MSG.common_button_save());
saveButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent clickEvent) {
- if (form.validate() && credentialsForm.validate()) {
+ if (clusterForm.validate() && deploymentForm.validate()
&& credentialsForm.validate()) {
SC.ask(
"Changing the cluster wide configuration will eventually
affect all the storage nodes. Do you want to continue?",
new BooleanCallback() {
@@ -271,10 +293,12 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
}
private StorageClusterSettings updateSettings() {
- settings.setCqlPort(Integer.parseInt(form.getValueAsString(FIELD_CQL_PORT)));
-
settings.setGossipPort(Integer.parseInt(form.getValueAsString(FIELD_GOSSIP_PORT)));
-
settings.setAutomaticDeployment(Boolean.parseBoolean(form.getValueAsString(FIELD_AUTOMATIC_DEPLOYMENT)));
-
+
settings.setCqlPort(Integer.parseInt(clusterForm.getValueAsString(FIELD_CQL_PORT)));
+
settings.setGossipPort(Integer.parseInt(clusterForm.getValueAsString(FIELD_GOSSIP_PORT)));
+
+ settings.setAutomaticDeployment(Boolean.parseBoolean(deploymentForm
+ .getValueAsString(FIELD_AUTOMATIC_DEPLOYMENT)));
+
// set the credentials only if there was a change
String wannabeUsername = credentialsForm.getValueAsString(FIELD_USERNAME);
settings.setUsername(wannabeUsername.equals(settings.getUsername()) ? null :
wannabeUsername);
@@ -282,7 +306,7 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
settings.setPasswordHash(wannabePassword.equals(settings.getPasswordHash()) ?
null : wannabePassword);
return settings;
}
-
+
private static class FormItemBuilder {
private String name;
private String title;
@@ -290,47 +314,47 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
private String description;
private Validator[] validators;
private boolean readOnly;
-
+
private static boolean oddRow = true;
-
+
public static void resetOddRow() {
oddRow = true;
}
-
+
public FormItemBuilder withName(String name) {
this.name = name;
return this;
}
-
+
public FormItemBuilder withTitle(String title) {
this.title = title;
return this;
}
-
+
public FormItemBuilder withValue(String value) {
this.value = value;
return this;
}
-
+
public FormItemBuilder withDescription(String description) {
this.description = description;
return this;
}
-
+
public FormItemBuilder withValidators(Validator... validators) {
this.validators = validators;
return this;
}
-
+
public FormItemBuilder withReadOnlySetTo(boolean readOnly) {
this.readOnly = readOnly;
return this;
}
-
+
public List<FormItem> build() {
return build(new TextItem());
}
-
+
// GWT doesn't support reflection by default, therefore this
"hack"
public List<FormItem> build(FormItem valueItem) {
List<FormItem> fields = new ArrayList<FormItem>();
@@ -365,6 +389,6 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
oddRow = !oddRow;
return fields;
}
-
+
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
index 941ae93..90e2345 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
@@ -87,6 +87,7 @@ import
org.rhq.enterprise.server.scheduler.jobs.DynaGroupAutoRecalculationJob;
import org.rhq.enterprise.server.scheduler.jobs.PurgePluginsJob;
import org.rhq.enterprise.server.scheduler.jobs.PurgeResourceTypesJob;
import org.rhq.enterprise.server.scheduler.jobs.SavedSearchResultCountRecalculationJob;
+import org.rhq.enterprise.server.scheduler.jobs.StorageClusterCredentialsJob;
import org.rhq.enterprise.server.scheduler.jobs.StorageClusterInitJob;
import org.rhq.enterprise.server.scheduler.jobs.StorageClusterReadRepairJob;
import org.rhq.enterprise.server.storage.StorageClientManagerBean;
@@ -774,6 +775,16 @@ public class StartupBean implements StartupLocal {
log.error("Cannot create storage cluster init job", e);
}
}
+
+ try {
+ // Storage cluster credentials refresh job
+ final long initialDelay = 1000L * 60 * 5;
+ final long interval = 1000L * 60 * 2;
+ schedulerBean.scheduleSimpleRepeatingJob(StorageClusterCredentialsJob.class,
true, false, initialDelay,
+ interval);
+ } catch (Exception e) {
+ log.error("Cannot schedule storage cluster credentials refresh
job.", e);
+ }
try {
String cronString = "0 30 0 ? * SUN *"; // every sunday starting at
00:30.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/scheduler/jobs/StorageClusterCredentialsJob.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/scheduler/jobs/StorageClusterCredentialsJob.java
new file mode 100644
index 0000000..f5b2bf5
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/scheduler/jobs/StorageClusterCredentialsJob.java
@@ -0,0 +1,45 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.enterprise.server.scheduler.jobs;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import org.rhq.enterprise.server.util.LookupUtil;
+
+/**
+ * @author Stefan Negrea
+ */
+public class StorageClusterCredentialsJob extends AbstractStatefulJob {
+
+ private Log log = LogFactory.getLog(StorageClusterCredentialsJob.class);
+
+ @Override
+ public void executeJobCode(JobExecutionContext context) throws JobExecutionException
{
+ log.debug("Run credential update job for the storage cluster.");
+ boolean success =
LookupUtil.getStorageClientManager().refreshCredentialsAndSession();
+ if (!success) {
+ log.error("Storage session credentials not succesfully
refreshed");
+ } else {
+ log.debug("Storage session credentials succesfully refreshed");
+ }
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
index c57d7cd..f28dc3b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
@@ -88,6 +88,10 @@ public class StorageClientManagerBean {
private boolean initialized;
private StorageClusterMonitor storageClusterMonitor;
+ private String cachedStorageUsername;
+ private String cachedStoragePassword;
+
+
/**
* @return true if the storage subsystem is running
*/
@@ -101,37 +105,9 @@ public class StorageClientManagerBean {
log.info("Initializing storage client subsystem");
- // Always get the creds from the DB, system props may not be up to date at
install time
- // the code assumes the passwords to be obfuscated, because they can also come
that way from other sources
- // (like property files). So let's make our lives easy and always use
obfuscated passwords.
- SystemSettings settings = systemManager.getObfuscatedSystemSettings(true);
- final String username = settings.get(SystemSetting.STORAGE_USERNAME);
- final String password = settings.get(SystemSetting.STORAGE_PASSWORD);
-
- List<StorageNode> storageNodes = new ArrayList<StorageNode>();
- for (StorageNode storageNode : storageNodeManager.getStorageNodes()) {
- // We only want clustered nodes here because we won't be able to connect
to
- // node that is not part of the cluster. The filtering here on the operation
- // mode is somewhat convservative because we could also include
ADD_MAINTENANCE
- // and REMOVE_MAINTENANCE, but this errors on the side of being safe.
Lastly,
- // if a storage node does not have a resource, then that means it was was
- // deployed prior to installing the server.
- if (storageNode.getOperationMode() == StorageNode.OperationMode.NORMAL
- || storageNode.getOperationMode() ==
StorageNode.OperationMode.MAINTENANCE
- || storageNode.getResource() == null) {
- storageNodes.add(storageNode);
- }
- }
-
- if (storageNodes.isEmpty()) {
- throw new IllegalStateException(
- "There is no storage node metadata stored in the relational
database. This may have happened as a "
- + "result of running dbsetup or deleting rows from
rhq_storage_node table. Please re-install the "
- + "storage node to fix this issue.");
- }
-
+ Session wrappedSession;
try {
- checkSchemaCompability(username, password, storageNodes);
+ wrappedSession = createSession();
} catch (NoHostAvailableException e) {
initialized = false;
log.warn("Storage client subsystem wasn't initialized because it
wasn't possible to connect to the"
@@ -139,7 +115,8 @@ public class StorageClientManagerBean {
+ " as soon as possible.");
return initialized;
}
- Session wrappedSession = createSession(username, password, storageNodes);
+
+ wrappedSession = createSession();
session = new StorageSession(wrappedSession);
storageClusterMonitor = new StorageClusterMonitor();
@@ -155,6 +132,40 @@ public class StorageClientManagerBean {
return initialized;
}
+ public synchronized boolean refreshCredentialsAndSession() {
+ if (!initialized) {
+ if (log.isDebugEnabled()) {
+ log.debug("Storage client subsystem not initialized. Skipping
session refresh.");
+ }
+ return false;
+ }
+
+ SystemSettings settings = systemManager.getObfuscatedSystemSettings(true);
+ String username = settings.get(SystemSetting.STORAGE_USERNAME);
+ String password = settings.get(SystemSetting.STORAGE_PASSWORD);
+
+ if ((username != null && !username.equals(this.cachedStorageUsername))
+ || (password != null &&
!password.equals(this.cachedStoragePassword))) {
+
+ Session wrappedSession;
+ try {
+ wrappedSession = createSession();
+ } catch (NoHostAvailableException e) {
+ initialized = false;
+ log.warn("Storage client subsystem wasn't initialized because it
wasn't possible to connect to the"
+ + " storage cluster. The RHQ server is set to MAINTENANCE mode.
Please start the storage cluster"
+ + " as soon as possible.");
+ return initialized;
+ }
+
+ session.registerNewSession(wrappedSession);
+ initialized = true;
+ return true;
+ }
+
+ return true;
+ }
+
/**
* Checks storage node schema compatibility.
*
@@ -229,7 +240,38 @@ public class StorageClientManagerBean {
return storageClusterMonitor != null &&
storageClusterMonitor.isClusterAvailable();
}
- private Session createSession(String username, String password,
List<StorageNode> storageNodes) {
+ private Session createSession() {
+ // Always get the creds from the DB, system props may not be up to date at
install time
+ // the code assumes the passwords to be obfuscated, because they can also come
that way from other sources
+ // (like property files). So let's make our lives easy and always use
obfuscated passwords.
+ SystemSettings settings = systemManager.getObfuscatedSystemSettings(true);
+ this.cachedStorageUsername = settings.get(SystemSetting.STORAGE_USERNAME);
+ this.cachedStoragePassword = settings.get(SystemSetting.STORAGE_PASSWORD);
+
+ List<StorageNode> storageNodes = new ArrayList<StorageNode>();
+ for (StorageNode storageNode : storageNodeManager.getStorageNodes()) {
+ // We only want clustered nodes here because we won't be able to connect
to
+ // node that is not part of the cluster. The filtering here on the operation
+ // mode is somewhat convservative because we could also include
ADD_MAINTENANCE
+ // and REMOVE_MAINTENANCE, but this errors on the side of being safe.
Lastly,
+ // if a storage node does not have a resource, then that means it was was
+ // deployed prior to installing the server.
+ if (storageNode.getOperationMode() == StorageNode.OperationMode.NORMAL
+ || storageNode.getOperationMode() ==
StorageNode.OperationMode.MAINTENANCE
+ || storageNode.getResource() == null) {
+ storageNodes.add(storageNode);
+ }
+ }
+
+ if (storageNodes.isEmpty()) {
+ throw new IllegalStateException(
+ "There is no storage node metadata stored in the relational
database. This may have happened as a "
+ + "result of running dbsetup or deleting rows from
rhq_storage_node table. Please re-install the "
+ + "storage node to fix this issue.");
+ }
+
+ checkSchemaCompability(this.cachedStorageUsername, this.cachedStoragePassword,
storageNodes);
+
if (log.isDebugEnabled()) {
log.debug("Initializing session to connect to storage node
cluster");
}
@@ -252,7 +294,7 @@ public class StorageClientManagerBean {
}
cluster = new ClusterBuilder().addContactPoints(hostNames.toArray(new
String[hostNames.size()]))
- .withCredentialsObfuscated(username, password).withPort(port)
+ .withCredentialsObfuscated(this.cachedStorageUsername,
this.cachedStoragePassword).withPort(port)
.withLoadBalancingPolicy(new RoundRobinPolicy())
.withRetryPolicy(new
LoggingRetryPolicy(DefaultRetryPolicy.INSTANCE)).withCompression(compression).build();
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
index 2a47670..b14db38 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
@@ -10,6 +10,7 @@ import org.rhq.core.domain.common.composite.SystemSetting;
import org.rhq.core.domain.common.composite.SystemSettings;
import org.rhq.enterprise.server.authz.RequiredPermission;
import org.rhq.enterprise.server.system.SystemManagerLocal;
+import org.rhq.server.metrics.StorageSession;
/**
* @author John Sanda
@@ -17,9 +18,14 @@ import org.rhq.enterprise.server.system.SystemManagerLocal;
@Stateless
public class StorageClusterSettingsManagerBean implements
StorageClusterSettingsManagerLocal {
+ private static final String UPDATE_PASSWORD_QUERY = "ALTER USER '%s'
WITH PASSWORD '%s'";
+
@EJB
private SystemManagerLocal systemManager;
+ @EJB
+ private StorageClientManagerBean storageClienManager;
+
@Override
@RequiredPermission(Permission.MANAGE_SETTINGS)
public StorageClusterSettings getClusterSettings(Subject subject) {
@@ -76,9 +82,20 @@ public class StorageClusterSettingsManagerBean implements
StorageClusterSettings
settings.put(SystemSetting.STORAGE_USERNAME, clusterSettings.getUsername());
}
if (clusterSettings.getPasswordHash() != null) {
+ this.updateStorageClusterCredentials(clusterSettings);
settings.put(SystemSetting.STORAGE_PASSWORD,
clusterSettings.getPasswordHash());
}
systemManager.setStorageClusterSettings(subject, settings);
}
+ private void updateStorageClusterCredentials(StorageClusterSettings
newClusterSettings) {
+ SystemSettings currentSettings = systemManager.getUnmaskedSystemSettings(true);
+ String currentPassword = currentSettings.get(SystemSetting.STORAGE_PASSWORD);
+
+ if (!currentPassword.equals(newClusterSettings.getPasswordHash())) {
+ StorageSession session = this.storageClienManager.getSession();
+ session.execute(String.format(UPDATE_PASSWORD_QUERY,
currentSettings.get(SystemSetting.STORAGE_USERNAME),
+ newClusterSettings.getPasswordHash()));
+ }
+ }
}
diff --git
a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
index 60f3fbd..34eada1 100644
---
a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
+++
b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
@@ -34,6 +34,24 @@ public class StorageSession implements Host.StateListener {
this.wrappedSession.getCluster().register(this);
}
+ public void registerNewSession(Session newWrappedSession) {
+ Session oldWrappedSession = this.wrappedSession;
+
+
+ this.wrappedSession = newWrappedSession;
+ this.wrappedSession.getCluster().register(this);
+
+ oldWrappedSession.getCluster().unregister(this);
+
+ // initial waiting before the first check
+ try {
+ Thread.sleep(60000L);
+ } catch (InterruptedException e) {
+ // nothing
+ }
+ oldWrappedSession.shutdown();
+ }
+
public void addStorageStateListener(StorageStateListener listener) {
listeners.add(listener);
}
commit 1e6aee931a92ff658c2def4d5714e81e0a24b4a5
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Mon Oct 28 15:53:40 2013 -0500
[BZ 1016175] Add support for changing the storage password
- adding length validators for username and password; moving the storage node
credentials to its own config section
- renaming "password" field to "passwordHash"; setting the
password to null if there was no change at all to it
- adding support for storing the C* credentials to system settings db table
- adding length validators for username and password; moving the storage node
credentials to its own config section
(cherry picked from commit f592690d232393b5127b637b2541278198f8b001)
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
index b44a2b5..52b17b5 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageClusterSettings.java
@@ -14,6 +14,10 @@ public class StorageClusterSettings implements Serializable {
private int gossipPort;
private Boolean automaticDeployment;
+
+ private String username;
+
+ private String passwordHash;
public int getCqlPort() {
return cqlPort;
@@ -39,6 +43,22 @@ public class StorageClusterSettings implements Serializable {
this.automaticDeployment = automaticDeployment;
}
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPasswordHash() {
+ return passwordHash;
+ }
+
+ public void setPasswordHash(String passwordHash) {
+ this.passwordHash = passwordHash;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -49,6 +69,8 @@ public class StorageClusterSettings implements Serializable {
if (cqlPort != that.cqlPort) return false;
if (gossipPort != that.gossipPort) return false;
if (automaticDeployment != that.automaticDeployment) return false;
+ if (username != that.username) return false;
+ if (passwordHash != that.passwordHash) return false;
return true;
}
@@ -58,12 +80,14 @@ public class StorageClusterSettings implements Serializable {
int result = cqlPort;
result = 29 * result + gossipPort;
result = 29 * result + (automaticDeployment ? 1231 : 1237);
+ result = 29 * result + (username == null ? 0 : username.hashCode());
+ result = 29 * result + (passwordHash == null ? 0 : passwordHash.hashCode());
return result;
}
@Override
public String toString() {
return "StorageClusterSettings[cqlPort=" + cqlPort + ",
gossipPort=" + gossipPort + ", automaticDeployment="
- + automaticDeployment + "]";
+ + automaticDeployment + ", username=" + username + ",
passwordHash=********]";
}
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
index 188a993..11846df 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/admin/storage/ClusterConfigurationEditor.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
+import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.Overflow;
@@ -30,10 +31,12 @@ import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.PasswordItem;
import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.validator.IsIntegerValidator;
+import com.smartgwt.client.widgets.form.validator.MatchesFieldValidator;
import com.smartgwt.client.widgets.form.validator.Validator;
import com.smartgwt.client.widgets.layout.LayoutSpacer;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
@@ -42,6 +45,7 @@ import org.rhq.core.domain.cloud.StorageClusterSettings;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.RefreshableView;
import org.rhq.coregui.client.components.form.EnhancedDynamicForm;
+import org.rhq.coregui.client.components.form.StringLengthValidator;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton;
import org.rhq.coregui.client.util.enhanced.EnhancedToolStrip;
@@ -56,14 +60,17 @@ import org.rhq.coregui.client.util.message.Message;
public class ClusterConfigurationEditor extends EnhancedVLayout implements
RefreshableView {
private EnhancedDynamicForm form;
+ private EnhancedDynamicForm credentialsForm;
private EnhancedIButton saveButton;
- private boolean oddRow;
private StorageClusterSettings settings;
private final boolean readOnly;
private static String FIELD_CQL_PORT = "cql_port";
private static String FIELD_GOSSIP_PORT = "gossip_port";
private static String FIELD_AUTOMATIC_DEPLOYMENT = "automatic_deployment";
+ private static String FIELD_USERNAME = "username";
+ private static String FIELD_PASSWORD = "password";
+ private static String FIELD_PASSWORD_VERIFY = "verify_password";
public ClusterConfigurationEditor(boolean readOnly) {
super();
@@ -81,7 +88,7 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
@Override
public void onSuccess(StorageClusterSettings settings) {
ClusterConfigurationEditor.this.settings = settings;
- prepareForm();
+ prepareForms();
}
});
}
@@ -100,48 +107,6 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
});
}
- private List<FormItem> buildOneFormRowWithValidator(String name, String title,
String value, String description,
- Validator validator) {
- return buildOneFormRow(name, title, value, description, false, validator);
- }
-
- private List<FormItem> buildOneFormRow(String name, String title, String value,
String description,
- boolean unitsDropdown, Validator validator) {
- List<FormItem> fields = new ArrayList<FormItem>();
- StaticTextItem nameItem = new StaticTextItem();
- nameItem.setStartRow(true);
- nameItem.setValue("<b>" + title + "</b>");
- nameItem.setShowTitle(false);
- nameItem.setCellStyle(oddRow ? "OddRow" : "EvenRow");
- fields.add(nameItem);
-
- FormItem valueItem = null;
- valueItem = new TextItem();
- valueItem.setName(name);
- valueItem.setValue(value);
- valueItem.setWidth(220);
- if (validator != null) {
- valueItem.setValidators(validator);
- }
- valueItem.setValidateOnChange(true);
- valueItem.setAlign(Alignment.CENTER);
- valueItem.setShowTitle(false);
- valueItem.setRequired(true);
- valueItem.setCellStyle(oddRow ? "OddRow" : "EvenRow");
- valueItem.setDisabled(readOnly);
- fields.add(valueItem);
-
- StaticTextItem descriptionItem = new StaticTextItem();
- descriptionItem.setValue(description);
- descriptionItem.setShowTitle(false);
- descriptionItem.setEndRow(true);
- descriptionItem.setCellStyle(oddRow ? "OddRow" : "EvenRow");
- fields.add(descriptionItem);
-
- oddRow = !oddRow;
- return fields;
- }
-
private List<FormItem> buildHeaderItems() {
List<FormItem> fields = new ArrayList<FormItem>();
fields.add(createHeaderTextItem(MSG.view_configEdit_property()));
@@ -164,7 +129,8 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
refresh();
}
- private void prepareForm() {
+ private void prepareForms() {
+ setWidth100();
form = new EnhancedDynamicForm();
form.setHiliteRequiredFields(true);
form.setNumCols(3);
@@ -173,49 +139,101 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
form.setIsGroup(true);
form.setGroupTitle("Cluster Wide Settings");
form.setBorder("1px solid #AAA");
- oddRow = true;
+ form.setWidth100();
+ form.setOverflow(Overflow.VISIBLE);
+ form.setExtraSpace(10);
List<FormItem> items = buildHeaderItems();
IsIntegerValidator validator = new IsIntegerValidator();
- items.addAll(buildOneFormRowWithValidator(FIELD_CQL_PORT, "CQL Port",
String.valueOf(settings.getCqlPort()),
- "The port on which the Storage Nodes listens for CQL client
connections.", validator));
+
+ // cql port field
+ FormItemBuilder builder = new FormItemBuilder();
+ List<FormItem> cqlPortItems =
builder.withName(FIELD_CQL_PORT).withTitle("CQL Port")
+ .withValue(String.valueOf(settings.getCqlPort()))
+ .withDescription("The port on which the Storage Nodes listens for CQL
client connections.")
+ .withValidators(validator).build();
+ items.addAll(cqlPortItems);
- items.addAll(buildOneFormRowWithValidator(FIELD_GOSSIP_PORT, "Gossip
Port",
- String.valueOf(settings.getGossipPort()),
- "The port used for internode communication in the storage
cluster.", validator));
+ // gossip port field
+ builder = new FormItemBuilder();
+ List<FormItem> gossipPortItems =
builder.withName(FIELD_GOSSIP_PORT).withTitle("Gossip Port")
+ .withValue(String.valueOf(settings.getGossipPort()))
+ .withDescription("The port used for internode communication in the
storage cluster.")
+ .withValidators(validator).build();
+ items.addAll(gossipPortItems);
+
+
- List<FormItem> automaticDeploymentItems = buildOneFormRow(
- FIELD_AUTOMATIC_DEPLOYMENT,
- "Automatic Deployment",
- Boolean.toString(settings.getAutomaticDeployment()),
- "If this is set, the newly installed storage nodes will be automatically
deployed to the storage cluster.",
- false, null);
- RadioGroupItem autoDeployRadio = new RadioGroupItem(FIELD_AUTOMATIC_DEPLOYMENT);
+ // automatic deployment field
+ builder = new FormItemBuilder();
+ List<FormItem> automaticDeploymentItems = builder
+ .withName(FIELD_AUTOMATIC_DEPLOYMENT)
+ .withTitle("Automatic Deployment")
+ .withValue(Boolean.toString(settings.getAutomaticDeployment()))
+ .withDescription(
+ "If this is set, the newly installed storage nodes will be
automatically deployed to the storage cluster.")
+ .withReadOnlySetTo(readOnly).build((FormItem)
GWT.create(RadioGroupItem.class));
+ RadioGroupItem autoDeployRadio = (RadioGroupItem)
automaticDeploymentItems.get(1);
autoDeployRadio.setVertical(false);
LinkedHashMap<String, String> values = new LinkedHashMap<String,
String>(2);
values.put("true", "On");
values.put("false", "Off");
autoDeployRadio.setValueMap(values);
autoDeployRadio.setValue(settings.getAutomaticDeployment());
- autoDeployRadio.setAlign(Alignment.CENTER);
- autoDeployRadio.setShowTitle(false);
- autoDeployRadio.setRequired(true);
- autoDeployRadio.setCellStyle(!oddRow ? "OddRow" :
"EvenRow");
- autoDeployRadio.setDisabled(readOnly);
- oddRow = !oddRow;
- automaticDeploymentItems.set(1, autoDeployRadio);
items.addAll(automaticDeploymentItems);
+
form.setFields(items.toArray(new FormItem[items.size()]));
- form.setWidth100();
- form.setOverflow(Overflow.VISIBLE);
- setWidth100();
+ credentialsForm = new EnhancedDynamicForm();
+ credentialsForm.setHiliteRequiredFields(true);
+ credentialsForm.setNumCols(3);
+ credentialsForm.setCellPadding(5);
+ credentialsForm.setColWidths(190, 220, "*");
+ credentialsForm.setIsGroup(true);
+ credentialsForm.setGroupTitle("Storage Cluster Credentials");
+ credentialsForm.setBorder("1px solid #AAA");
+ credentialsForm.setWidth100();
+ credentialsForm.setOverflow(Overflow.VISIBLE);
+
+ FormItemBuilder.resetOddRow();
+ items = buildHeaderItems();
+
+ // username field
+ StringLengthValidator usernameValidator = new StringLengthValidator(4, 100,
false);
+ builder = new FormItemBuilder();
+ List<FormItem> usernameItems =
builder.withName(FIELD_USERNAME).withTitle("Username")
+
.withValue(settings.getUsername()).withDescription("Username").withReadOnlySetTo(readOnly)
+ .withValidators(usernameValidator).build();
+ items.addAll(usernameItems);
+
+ // password field
+ StringLengthValidator passwordValidator1 = new StringLengthValidator(6, 100,
false);
+ builder = new FormItemBuilder();
+ List<FormItem> passwordItems =
builder.withName(FIELD_PASSWORD).withTitle("Password")
+
.withValue(settings.getPasswordHash()).withDescription("Password").withReadOnlySetTo(readOnly)
+ .withValidators(passwordValidator1).build((FormItem)
GWT.create(PasswordItem.class));
+ items.addAll(passwordItems);
+
+ // password_verify field
+ builder = new FormItemBuilder();
+ MatchesFieldValidator passwordValidator2 = new MatchesFieldValidator();
+ passwordValidator2.setOtherField(FIELD_PASSWORD);
+ passwordValidator2.setErrorMessage("This should be the same string as the
Password.");
+ List<FormItem> passwordVerifyItems =
builder.withName(FIELD_PASSWORD_VERIFY).withTitle("Verify Password")
+ .withValue(settings.getPasswordHash()).withDescription("This should be
the same string as the Password.")
+ .withReadOnlySetTo(readOnly).withValidators(passwordValidator1,
passwordValidator2)
+ .build((FormItem) GWT.create(PasswordItem.class));
+
+ items.addAll(passwordVerifyItems);
+ credentialsForm.setFields(items.toArray(new FormItem[items.size()]));
+
LayoutSpacer spacer = new LayoutSpacer();
spacer.setWidth100();
ToolStrip toolStrip = buildToolStrip();
- setMembers(form, spacer, toolStrip);
+ setMembers(form, credentialsForm, spacer, toolStrip);
form.validate();
+ credentialsForm.validate();
markForRedraw();
}
@@ -228,7 +246,7 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
saveButton = new EnhancedIButton(MSG.common_button_save());
saveButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent clickEvent) {
- if (form.validate()) {
+ if (form.validate() && credentialsForm.validate()) {
SC.ask(
"Changing the cluster wide configuration will eventually
affect all the storage nodes. Do you want to continue?",
new BooleanCallback() {
@@ -256,6 +274,97 @@ public class ClusterConfigurationEditor extends EnhancedVLayout
implements Refre
settings.setCqlPort(Integer.parseInt(form.getValueAsString(FIELD_CQL_PORT)));
settings.setGossipPort(Integer.parseInt(form.getValueAsString(FIELD_GOSSIP_PORT)));
settings.setAutomaticDeployment(Boolean.parseBoolean(form.getValueAsString(FIELD_AUTOMATIC_DEPLOYMENT)));
+
+ // set the credentials only if there was a change
+ String wannabeUsername = credentialsForm.getValueAsString(FIELD_USERNAME);
+ settings.setUsername(wannabeUsername.equals(settings.getUsername()) ? null :
wannabeUsername);
+ String wannabePassword = credentialsForm.getValueAsString(FIELD_PASSWORD);
+ settings.setPasswordHash(wannabePassword.equals(settings.getPasswordHash()) ?
null : wannabePassword);
return settings;
}
+
+ private static class FormItemBuilder {
+ private String name;
+ private String title;
+ private String value;
+ private String description;
+ private Validator[] validators;
+ private boolean readOnly;
+
+ private static boolean oddRow = true;
+
+ public static void resetOddRow() {
+ oddRow = true;
+ }
+
+ public FormItemBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public FormItemBuilder withTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ public FormItemBuilder withValue(String value) {
+ this.value = value;
+ return this;
+ }
+
+ public FormItemBuilder withDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public FormItemBuilder withValidators(Validator... validators) {
+ this.validators = validators;
+ return this;
+ }
+
+ public FormItemBuilder withReadOnlySetTo(boolean readOnly) {
+ this.readOnly = readOnly;
+ return this;
+ }
+
+ public List<FormItem> build() {
+ return build(new TextItem());
+ }
+
+ // GWT doesn't support reflection by default, therefore this
"hack"
+ public List<FormItem> build(FormItem valueItem) {
+ List<FormItem> fields = new ArrayList<FormItem>();
+ StaticTextItem nameItem = new StaticTextItem();
+ nameItem.setStartRow(true);
+ nameItem.setValue("<b>" + title + "</b>");
+ nameItem.setShowTitle(false);
+ nameItem.setCellStyle(oddRow ? "OddRow" : "EvenRow");
+ fields.add(nameItem);
+
+ valueItem.setName(name);
+ valueItem.setValue(value);
+ valueItem.setWidth(220);
+ if (validators != null && validators.length > 0) {
+ valueItem.setValidators(validators);
+ }
+ valueItem.setValidateOnChange(true);
+ valueItem.setAlign(Alignment.CENTER);
+ valueItem.setShowTitle(false);
+ valueItem.setRequired(true);
+ valueItem.setCellStyle(oddRow ? "OddRow" : "EvenRow");
+ valueItem.setDisabled(readOnly);
+ fields.add(valueItem);
+
+ StaticTextItem descriptionItem = new StaticTextItem();
+ descriptionItem.setValue(description);
+ descriptionItem.setShowTitle(false);
+ descriptionItem.setEndRow(true);
+ descriptionItem.setCellStyle(oddRow ? "OddRow" :
"EvenRow");
+ fields.add(descriptionItem);
+
+ oddRow = !oddRow;
+ return fields;
+ }
+
+ }
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
index a00200d..2a47670 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterSettingsManagerBean.java
@@ -46,6 +46,18 @@ public class StorageClusterSettingsManagerBean implements
StorageClusterSettings
clusterSettings.setAutomaticDeployment(Boolean.parseBoolean(settings
.get(SystemSetting.STORAGE_AUTOMATIC_DEPLOYMENT)));
}
+
+ if (!settings.containsKey(SystemSetting.STORAGE_USERNAME)) {
+ return null;
+ } else {
+ clusterSettings.setUsername(settings.get(SystemSetting.STORAGE_USERNAME));
+ }
+
+ if (!settings.containsKey(SystemSetting.STORAGE_PASSWORD)) {
+ return null;
+ } else {
+
clusterSettings.setPasswordHash(settings.get(SystemSetting.STORAGE_PASSWORD));
+ }
return clusterSettings;
}
@@ -60,6 +72,12 @@ public class StorageClusterSettingsManagerBean implements
StorageClusterSettings
settings.put(SystemSetting.STORAGE_AUTOMATIC_DEPLOYMENT,
Boolean.toString(clusterSettings.getAutomaticDeployment()));
}
+ if (clusterSettings.getUsername() != null) {
+ settings.put(SystemSetting.STORAGE_USERNAME, clusterSettings.getUsername());
+ }
+ if (clusterSettings.getPasswordHash() != null) {
+ settings.put(SystemSetting.STORAGE_PASSWORD,
clusterSettings.getPasswordHash());
+ }
systemManager.setStorageClusterSettings(subject, settings);
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/system/SystemManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/system/SystemManagerBean.java
index bbef365..610c38d 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/system/SystemManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/system/SystemManagerBean.java
@@ -367,11 +367,11 @@ public class SystemManagerBean implements SystemManagerLocal,
SystemManagerRemot
private boolean isStorageSetting(SystemSetting setting) {
switch (setting) {
case STORAGE_CQL_PORT:
- return true;
case STORAGE_GOSSIP_PORT:
- return true;
case STORAGE_AUTOMATIC_DEPLOYMENT:
- return true;
+ case STORAGE_PASSWORD:
+ case STORAGE_USERNAME:
+ return true;
default:
return false;
}