/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.patching; import static org.jboss.as.patching.Constants.BASE; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.jboss.as.controller.client.helpers.ClientConstants; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.test.integration.management.util.CLIOpResult; import org.jboss.as.test.integration.management.util.CLIWrapper; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; /** * @author Jan Martiska */ public class CliUtilsForPatching { private static final Logger logger = Logger.getLogger(CliUtilsForPatching.class); public static final String OVERRIDE_ALL = "--override-all"; public static final String OVERRIDE_MODULES = "--override-modules"; public static final String OVERRIDE = "--override=%s"; public static final String PRESERVE = "--preserve=%s"; public static final String KEEP_CONFIGURATION = "--keep-configuration"; public static final String ROLLBACK_TO = "--rollback-to"; /** * Use the CLI to apply a patch * * @param patchFilePath absolute path to the ZIP file containing the patch * @param args conflict resolution arguments or null * @throws Exception */ public static boolean applyPatch(String patchFilePath, String... args) throws Exception { try (CLIWrapper cli = new CLIWrapper(true)) { StringBuilder builder = new StringBuilder("patch apply"); if (args != null) { for (String arg : args) { builder.append(" ").append(arg); } } builder.append(" ").append(patchFilePath); String command = builder.toString(); logger.debug("----- sending command to CLI: " + command + " -----"); return cli.sendLine(command, true); } } /** * Use the CLI to rollback a patch * * @param oneoffPatchID the ID of the patch that should be rolled back * @param args conflict resolution arguments, rollback arguments * @throws Exception */ public static boolean rollbackPatch(String oneoffPatchID, String... args) throws Exception { try (CLIWrapper cli = new CLIWrapper(true)) { StringBuilder builder = new StringBuilder("patch rollback --reset-configuration=false"); if (args != null) { for (String arg : args) { builder.append(" ").append(arg); } } builder.append(" --patch-id=").append(oneoffPatchID); String command = builder.toString(); logger.debug("----- sending command to CLI: " + command + " -----"); return cli.sendLine(command, true); } } /** * Use the CLI to rollback to a patch * * @param oneoffPatchID the ID of the patch that should be rolled back to * @param args conflict resolution arguments, rollback arguments * @throws Exception */ public static boolean rollbackToPatch(String oneoffPatchID, String... args) throws Exception { try (CLIWrapper cli = new CLIWrapper(true)) { StringBuilder builder = new StringBuilder("patch rollback --reset-configuration=false --rollback-to=true"); if (args != null) { for (String arg : args) { builder.append(" ").append(arg); } } builder.append(" --patch-id=").append(oneoffPatchID); String command = builder.toString(); logger.debug("----- sending command to CLI: " + command + " -----"); return cli.sendLine(command, true); } } /** * Use the CLI to read information about the installed patches * * @return output of "patch info" command or null if output is empty * @throws Exception */ public static ModelNode info() throws Exception { return info(true); } /** * Use the CLI to read information about the installed patches * * @param connect to the server * @return output of "patch info" command or null if output is empty * @throws Exception */ public static ModelNode info(boolean connect) throws Exception { try (CLIWrapper cli = new CLIWrapper(connect)) { String command = "patch info --json-output"; logger.debug("----- sending command to CLI: " + command + " -----"); cli.sendLine(command); String output = cli.readOutput(); return ModelNode.fromJSONString(output); } } /** * Use CLI to get the list of currently installed patches * * @return the currently installed patches as a collection of patch IDs (strings) * @throws Exception */ public static Collection<String> getInstalledPatches() throws Exception { try (CLIWrapper cli = new CLIWrapper(true)) { String command = "patch info --json-output"; logger.debug("----- sending command to CLI: " + command + " -----"); cli.sendLine(command); String response = cli.readOutput(); ModelNode responseNode = ModelNode.fromJSONString(response); List<ModelNode> patchesList = responseNode.get("result").get("patches").asList(); List<String> patchesListString = new ArrayList<String>(); for (ModelNode n : patchesList) { patchesListString.add(n.asString()); } return patchesListString; } } /** * Use CLI to get the cumulative patch id * * @return the current cumulative patch id (string) * @throws Exception */ public static String getCumulativePatchId() throws Exception { try (CLIWrapper cli = new CLIWrapper(true)) { cli.sendLine("patch info --json-output"); String response = cli.readOutput(); ModelNode responseNode = ModelNode.fromJSONString(response); return responseNode.get("result").get("cumulative-patch-id").asString(); } } /** * Check if the server is in restart-required state, that means * management operation return "response-headers" : {"process-state" : "restart-required"} * * @return true if the server is in "restart-required" state * @throws Exception */ public static boolean doesServerRequireRestart() throws Exception { try (CLIWrapper cli = new CLIWrapper(true)) { cli.sendLine("patch info --json-output", true); String response = cli.readOutput(); ModelNode responseNode = ModelNode.fromJSONString(response); ModelNode respHeaders = responseNode.get("response-headers"); if (respHeaders != null && respHeaders.isDefined()) { ModelNode processState = respHeaders.get("process-state"); return processState != null && processState.isDefined() && processState.asString() .equals(ClientConstants.CONTROLLER_PROCESS_STATE_RESTART_REQUIRED); } else { return false; } } } /** * Rollback all installed oneoffs, offline!! * * @return true if operation was successful or false if at least one rollback failed * @throws Exception */ public static boolean rollbackAllOneOffs() throws Exception { boolean success = true; final String infoCommand = "patch info --distribution=%s --json-output"; final String rollbackCommand = "patch rollback --patch-id=%s --distribution=%s --reset-configuration=true --override-all"; try (CLIWrapper cli = new CLIWrapper(false)) { String command = String.format(infoCommand, PatchingTestUtil.AS_DISTRIBUTION); logger.debug("----- sending command to CLI: " + command + " -----"); cli.sendLine(command); String response = cli.readOutput(); ModelNode responseNode = ModelNode.fromJSONString(response); List<ModelNode> patchesList = responseNode.get("result").get("patches").asList(); for (ModelNode n : patchesList) { command = String.format(rollbackCommand, n.asString(), PatchingTestUtil.AS_DISTRIBUTION); logger.debug("----- sending command to CLI: " + command + " -----"); success = success && cli.sendLine(command, true); } return success; } } /** * Rollback cumulative patch, online!! * * @param resetConfiguration * @return true if operation was successful or false if at least one rollback failed * @throws Exception */ public static boolean rollbackCumulativePatch(boolean resetConfiguration) throws Exception { final String infoCommand = "patch info --json-output"; final String rollbackCommand = "patch rollback --patch-id=%s --reset-configuration=%s"; try (CLIWrapper cli = new CLIWrapper(true)) { String command = infoCommand; logger.debug("----- sending command to CLI: " + command + " -----"); cli.sendLine(command); String response = cli.readOutput(); ModelNode responseNode = ModelNode.fromJSONString(response); String cumulativePatchId = responseNode.get("result").get("cumulative-patch-id").asString(); command = String.format(rollbackCommand, cumulativePatchId, resetConfiguration); logger.debug("----- sending command to CLI: " + command + " -----"); return cli.sendLine(command, true); } } /** * Rollback cumulative patch and all picked up one-offs, offline * * @return true if operation was successful or false if at least one rollback failed * @throws Exception */ public static boolean rollbackAll() throws Exception { CLIWrapper cli = null; boolean success = true; try { cli = new CLIWrapper(false); cli.sendLine("patch info --streams --json-output"); final ModelNode response = ModelNode.fromJSONString(cli.readOutput()); for(ModelNode stream : response.get(ModelDescriptionConstants.RESULT).asList()) { success = success && rollbackAll(cli, stream.asString()); } return success; } finally { if (cli != null) { cli.quit(); } } } protected static boolean rollbackAll(CLIWrapper cli, String patchStream) { final String infoCommand = "patch info --patch-stream=%s --distribution=%s --json-output"; final String rollbackCommand = "patch rollback --patch-stream=%s --patch-id=%s --distribution=%s --reset-configuration=true"; boolean success = true; boolean doRollback = true; while (doRollback) { doRollback = false; String command = String.format(infoCommand, patchStream, PatchingTestUtil.AS_DISTRIBUTION); logger.debug("----- sending command to CLI: " + command + " -----"); cli.sendLine(command); String response = cli.readOutput(); ModelNode responseNode = ModelNode.fromJSONString(response); ModelNode result = responseNode.get("result"); if (result.has("patches")) { final List<ModelNode> patchesList = result.get("patches").asList(); if (!patchesList.isEmpty()) { doRollback = true; for (ModelNode n : patchesList) { command = String.format(rollbackCommand, patchStream, n.asString(), PatchingTestUtil.AS_DISTRIBUTION); logger.debug("----- sending command to CLI: " + command + " -----"); success = success && cli.sendLine(command, true); } } } if (result.has("cumulative-patch-id")) { final String cumulativePatchId = result.get("cumulative-patch-id").asString(); if (!cumulativePatchId.equalsIgnoreCase(BASE)) { doRollback = true; command = String.format(rollbackCommand, patchStream, cumulativePatchId, PatchingTestUtil.AS_DISTRIBUTION); logger.debug("----- sending command to CLI: " + command + " -----"); success = success && cli.sendLine(command, true); } } } return success; } public static List<String> getResourceLoaderPathsForModule(String module, boolean ignoreError) throws Exception { try(CLIWrapper cli = new CLIWrapper(true)) { String command = "/core-service=module-loading:list-resource-loader-paths(module=" + module + ")"; logger.debug("CLI command: " + command); if (!cli.sendLine(command, ignoreError)) { throw new RuntimeException(cli.readOutput()); } ModelNode response = ModelNode.fromString(cli.readOutput()); List<ModelNode> pathList = response.get("result").asList(); List<String> patchesListString = new ArrayList<String>(); for (ModelNode n : pathList) { patchesListString.add(n.asString()); } return patchesListString; } } public static List<ModelNode> getPatchingHistory() throws Exception { try(CLIWrapper cli = new CLIWrapper(true)) { String command = "/core-service=patching:show-history"; logger.debug("CLI command: " + command); cli.sendLine(command, false); ModelNode response = ModelNode.fromString(cli.readOutput()); return response.get("result").asList(); } } public static boolean doesModuleExist(String module) throws Exception { try(CLIWrapper cli = new CLIWrapper(true)) { cli.sendLine("/core-service=module-loading:list-resource-loader-paths(module=" + module + ")", true); final CLIOpResult result = cli.readAllAsOpResult(); return result.isIsOutcomeSuccess(); } } }