modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java | 13 + 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 | 17 + modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java | 18 + 5 files changed, 167 insertions(+), 34 deletions(-)
New commits: commit 54df17b99aa8ef362a95842ee02b9fd72cfcaef7 Author: Stefan Negrea snegrea@redhat.com Date: Tue Oct 22 16:30:11 2013 -0500
Add code to update Cassandra password via a direct CQL query using the existing open session.
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())); + } + } }
commit 705c15d24c86a52f3c655e3959cc9768d0461888 Author: Stefan Negrea snegrea@redhat.com Date: Tue Oct 22 10:42:50 2013 -0500
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
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 d5878c6..2538bd7 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; @@ -740,6 +741,16 @@ public class StartupBean implements StartupLocal { }
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. schedulerBean.scheduleSimpleCronJob(StorageClusterReadRepairJob.class, true, true, cronString); } catch (Exception e) { @@ -925,4 +936,4 @@ public class StartupBean implements StartupLocal { + ") Server started."); log.info("--------------------------------------------------"); // 50 dashes } -} +} \ No newline at end of file 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..f8eea6a --- /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.info("Preparing to run init job on 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 98d1405..cab45e5 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. * @@ -227,7 +238,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"); } @@ -250,7 +292,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/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); }