/*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.modules.plugins.wildfly10;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.modules.plugins.wildfly10.helper.Deployer;
import org.rhq.modules.plugins.wildfly10.json.Address;
import org.rhq.modules.plugins.wildfly10.json.CompositeOperation;
import org.rhq.modules.plugins.wildfly10.json.Operation;
import org.rhq.modules.plugins.wildfly10.json.ReadChildrenNames;
import org.rhq.modules.plugins.wildfly10.json.ReadChildrenResources;
import org.rhq.modules.plugins.wildfly10.json.ReadResource;
import org.rhq.modules.plugins.wildfly10.json.Remove;
import org.rhq.modules.plugins.wildfly10.json.Result;
/**
* Handle domain deployments
* @author Heiko W. Rupp
* @author Libor Zoubek
*/
public class DomainDeploymentComponent extends DeploymentComponent implements OperationFacet {
@Override
public AvailabilityType getAvailability() {
// Domain deployments have no 'enabled' attribute
Operation op = new ReadResource(getAddress());
Result res = getASConnection().execute(op, AVAIL_OP_TIMEOUT_SECONDS);
// this resource cannot be down, either UP = exists, or MISSING
if (res != null && res.isSuccess()) {
return AvailabilityType.UP;
} else if (res != null && !res.isSuccess() && res.isTimedout()) {
return AvailabilityType.UNKNOWN;
}
return AvailabilityType.MISSING;
}
private String getManagementNodeName() {
String managementNodeName = context.getPluginConfiguration().getSimpleValue("path");
return managementNodeName.substring(managementNodeName.indexOf("=") + 1);
}
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
Exception {
OperationResult operationResult = new OperationResult();
if (name.equals("promote")) {
String serverGroup = parameters.getSimpleValue("server-group", "-not set-");
List<String> serverGroups = new ArrayList<String>();
if (serverGroup.equals("__all")) {
serverGroups.addAll(getServerGroups());
} else {
serverGroups.add(serverGroup);
}
String managementNodeName = getManagementNodeName();
getLog().info("Promoting [" + managementNodeName + "] to server group(s) [" + serverGroups + "]...");
PropertySimple simple = parameters.getSimple("enabled");
Boolean enabled = false;
if (simple != null && simple.getBooleanValue() != null)
enabled = simple.getBooleanValue();
PropertySimple runtimeNameProperty = parameters.getSimple("runtime-name");
String runtimeName = null;
if (runtimeNameProperty != null)
runtimeName = runtimeNameProperty.getStringValue();
CompositeOperation operation = new CompositeOperation();
for (String theGroup : serverGroups) {
Operation step = createServerGroupAssignmentStep("add", theGroup, runtimeName, enabled);
operation.addStep(step);
}
Result res = getASConnection().execute(operation, 120); // wait up to 2 minutes
if (res.isSuccess()) {
operationResult.setSimpleResult("Successfully deployed to server groups " + serverGroups);
//request the server to discover child resources to allow the discovery of the deployments
//on server groups immediately
requestDiscovery();
} else {
operationResult.setErrorMessage("Deployment to server groups failed: " + res.getFailureDescription());
}
} else if (name.equals("restart")) {
String serverGroup = parameters.getSimpleValue("server-group", "-not set-");
List<String> serverGroups = new ArrayList<String>();
List<String> assignedGroups = findAssignedServerGroups();
if (serverGroup.equals("__all")) {
serverGroups.addAll(assignedGroups);
} else {
if (!assignedGroups.contains(serverGroup)) {
operationResult.setErrorMessage("Deployment could not be restarted in server-group [" + serverGroup
+ "] because it is not assigned to it.");
return operationResult;
}
serverGroups.add(serverGroup);
}
if (serverGroups.isEmpty()) {
operationResult
.setErrorMessage("Deployment could not be restarted because it is not assigned to any server-group");
return operationResult;
}
CompositeOperation operation = new CompositeOperation();
for (String theGroup : serverGroups) {
Operation step = createServerGroupAssignmentStep("redeploy", theGroup, null, false);
operation.addStep(step);
}
Result res = getASConnection().execute(operation, 120); // wait up to 2 minutes
if (res.isSuccess()) {
operationResult.setSimpleResult("Successfully restarted in server groups " + serverGroups);
} else {
operationResult.setErrorMessage("Deployment restart in server groups failed: "
+ res.getFailureDescription());
}
} else {
operationResult.setErrorMessage("Unknown operation " + name);
}
return operationResult;
}
private void requestDiscovery() {
if (this.context.getParentResourceComponent().getClass().isInstance(HostControllerComponent.class)) {
HostControllerComponent<?> hostController = (HostControllerComponent<?>) this.context
.getParentResourceComponent();
hostController.requestDeferredChildResourcesDiscovery();
}
}
private List<String> findAssignedServerGroups() {
List<String> groups = new ArrayList<String>();
Configuration config = new Configuration();
loadAssignedServerGroups(config);
for (Property prop : config.getList("*1").getList()) {
PropertyMap map = (PropertyMap) prop;
groups.add(map.getSimpleValue("server-group", null));
}
return groups;
}
@Override
protected Deployer createDeployerForPackageUpdate(String deploymentName, String runtimeName, String hash) {
Deployer deployer = new Deployer(deploymentName, runtimeName, hash, getASConnection());
// we need to find server-groups where this deployment is assigned
Configuration config = Configuration.builder().build();
loadAssignedServerGroups(config);
String originalDeploymentName = getManagementNodeName();
// then we add steps to remove from them, and at the same time we add steps to assign new deployment to same groups with same parameters
for (Property prop : config.getList("*1").getList()) {
PropertyMap map = (PropertyMap) prop;
String sgName = map.getSimpleValue("server-group", null);
String sgRuntimeName = map.getSimpleValue("runtime-name", null);
if (originalDeploymentName.equals(sgRuntimeName)) {
sgRuntimeName = null; // runtimeName was equal to deployment Name => not defined at deploy time
}
boolean sgEnabled = map.getSimple("enabled").getBooleanValue();
deployer.addBeforeDeployStep(createServerGroupAssignmentStep("remove", sgName, null, false));
// new assign-to-group step refers to new deploymentName
deployer.addAfterDeployStep(createServerGroupAssignmentStep("add", sgName, deploymentName, sgRuntimeName,
sgEnabled));
}
// this has to be the last beforeDeploy step as it would fail in case deployment is assigned to some server-group
deployer.addBeforeDeployStep(new Remove(getAddress()));
return deployer;
}
@SuppressWarnings("unchecked")
private void loadAssignedServerGroups(Configuration configuration) {
String managementNodeName = getManagementNodeName();
Address theAddress = new Address("/");
Operation op = new ReadChildrenResources(theAddress, "server-group");
op.addAdditionalProperty("recursive-depth", "1");
Result res = getASConnection().execute(op);
PropertyList propGroups = new PropertyList("*1");
configuration.put(propGroups);
if (res.isSuccess()) {
Map<String, Object> groups = (Map<String, Object>) res.getResult();
for (Map.Entry<String, Object> entry : groups.entrySet()) {
Map<String, Object> groupDetails = (Map<String, Object>) entry.getValue();
Map<String, Object> deployments = (Map<String, Object>) groupDetails.get("deployment");
if (deployments != null) {
Map<String, Object> deployment = (Map<String, Object>) deployments.get(managementNodeName);
if (deployment != null) {
PropertyMap map = new PropertyMap("*");
map.put(new PropertySimple("server-group", entry.getKey()));
map.put(new PropertySimple("runtime-name", deployment.get("runtime-name")));
map.put(new PropertySimple("enabled", deployment.get("enabled")));
propGroups.add(map);
}
}
}
}
}
private Operation createServerGroupAssignmentStep(String action, String serverGroup,String deploymentName, String runtimeName,
boolean enabled) {
Address addr = new Address();
addr.add("server-group", serverGroup);
addr.add("deployment", deploymentName);
Operation op = new Operation(action, addr);
if ("add".equals(action)) {
if (runtimeName != null && !runtimeName.isEmpty()) {
op.addAdditionalProperty("runtime-name", runtimeName);
}
op.addAdditionalProperty("enabled", enabled);
}
return op;
}
private Operation createServerGroupAssignmentStep(String action, String serverGroup, String runtimeName,
boolean enabled) {
return createServerGroupAssignmentStep(action, serverGroup, getManagementNodeName(), runtimeName, enabled);
}
@SuppressWarnings("unchecked")
@Override
public Configuration loadResourceConfiguration() throws Exception {
Configuration configuration = new Configuration();
// load deployment configuration - we cannot use generic method, it would fail
Operation op = new Operation("read-resource", getAddress());
Result res = getASConnection().execute(op);
if (res.isSuccess()) {
Map<String, Object> result = (Map<String, Object>) res.getResult();
configuration.put(new PropertySimple("name", result.get("name")));
configuration.put(new PropertySimple("runtime-name", result.get("runtime-name")));
configuration.put(new PropertySimple("content", result.get("content")));
} else {
throw new IOException("Operation " + op + " failed: " + res.getFailureDescription());
}
includeOOBMessages(res, configuration);
// list all server-groups, iterate them and find the ones we're deployed in
loadAssignedServerGroups(configuration);
return configuration;
}
@Override
public void updateResourceConfiguration(ConfigurationUpdateReport report) {
Configuration sgConfig = new Configuration();
loadAssignedServerGroups(sgConfig);
CompositeOperation operation = new CompositeOperation();
boolean needDiscovery = false; // we will request deferred child discovery on parent HC only in case new assignment was added
// load assigned server-groups to map, so we can easily look them up
Map<String, PropertyMap> assignedCurrent = new HashMap<String, PropertyMap>();
for (Property p : sgConfig.getList("*1").getList()) {
PropertyMap map = (PropertyMap) p;
assignedCurrent.put(map.getSimpleValue("server-group", null), map);
}
// also put new assignment to map, to detect possible duplicates
Map<String, PropertyMap> assignedNew = new HashMap<String, PropertyMap>();
// detect changes (enable/disable changes and new assignments)
int processTimeout = 120;
for (Property prop : report.getConfiguration().getList("*1").getList()) {
PropertyMap mapNew = (PropertyMap) prop;
PropertyMap duplicate = assignedNew.put(mapNew.getSimpleValue("server-group", null), mapNew);
if (duplicate != null) {
report.setStatus(ConfigurationUpdateStatus.FAILURE);
report.setErrorMessage("Duplicate assignment to [" + duplicate.getSimpleValue("server-group", null)
+ "] you cannot assign deployment to server-group more than once");
return;
}
String key = mapNew.getSimpleValue("server-group", null);
String runtimeNew = mapNew.getSimpleValue("runtime-name", null);
boolean enabledNew = mapNew.getSimple("enabled").getBooleanValue();
PropertyMap mapCurrent = assignedCurrent.remove(key);
if (mapCurrent == null) {
// new assignment was added
operation.addStep(createServerGroupAssignmentStep("add", key, runtimeNew, enabledNew));
needDiscovery = true;
} else {
boolean enabledCurrent = mapCurrent.getSimple("enabled").getBooleanValue();
if (enabledCurrent != enabledNew) {
// deployment status change
String action = "undeploy";
if (enabledNew) {
action = "deploy";
}
operation.addStep(createServerGroupAssignmentStep(action, key, null, false));
}
}
PropertySimple configuredProcessTimeout = mapNew.getSimple("process-timeout");
if(configuredProcessTimeout != null && configuredProcessTimeout.getIntegerValue() != null) {
processTimeout = configuredProcessTimeout.getIntegerValue();
}
}
// detect removals, items left in map (exist in old config, but were not sent in the new one) should be removed
for (PropertyMap map : assignedCurrent.values()) {
operation.addStep(createServerGroupAssignmentStep("remove", map.getSimpleValue("server-group", null), null,
false));
}
if (operation.numberOfSteps() == 0) {
report.setStatus(ConfigurationUpdateStatus.NOCHANGE);
return;
}
Result res = getASConnection().execute(operation, processTimeout);
if (res.isSuccess()) {
report.setStatus(ConfigurationUpdateStatus.SUCCESS);
} else {
report.setStatus(ConfigurationUpdateStatus.FAILURE);
report.setErrorMessage(res.getFailureDescription());
return;
}
if (needDiscovery) {
requestDiscovery();
}
}
@SuppressWarnings("unchecked")
private Collection<String> getServerGroups() {
Operation op = new ReadChildrenNames(new Address(), "server-group");
Result res = getASConnection().execute(op);
return (Collection<String>) res.getResult();
}
}