/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.integration.domain.suites; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEFAULT_INTERFACE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXTENSION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST_STATE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PORT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PORT_OFFSET; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROFILE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CHILDREN_NAMES_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CHILDREN_RESOURCES_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESTART_SERVERS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.helpers.domain.DomainClient; import org.jboss.as.controller.operations.common.Util; import org.jboss.as.network.NetworkUtils; import org.jboss.as.test.integration.domain.extension.ExtensionSetup; import org.jboss.as.test.integration.domain.extension.TestHostCapableExtension; import org.jboss.as.test.integration.domain.management.util.DomainLifecycleUtil; import org.jboss.as.test.integration.domain.management.util.DomainTestSupport; import org.jboss.as.test.integration.domain.management.util.DomainTestUtils; import org.jboss.as.test.shared.TimeoutUtil; import org.jboss.dmr.ModelNode; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * Tests management of host capable extensions. * * @author Kabir Khan */ public class HcExtensionAndSubsystemManagementTestCase { private static final PathElement EXTENSION_ELEMENT = PathElement.pathElement(EXTENSION, TestHostCapableExtension.MODULE_NAME); private static final PathElement SUBSYSTEM_ELEMENT = PathElement.pathElement(SUBSYSTEM, TestHostCapableExtension.SUBSYSTEM_NAME); private static final PathElement MASTER_HOST_ELEMENT = PathElement.pathElement(HOST, "master"); private static final PathElement SLAVE_HOST_ELEMENT = PathElement.pathElement(HOST, "slave"); private static final PathElement PROFILE_ELEMENT = PathElement.pathElement(PROFILE, "default"); private static final PathAddress DOMAIN_EXTENSION_ADDRESS = PathAddress.pathAddress(EXTENSION_ELEMENT); private static final PathAddress MASTER_EXTENSION_ADDRESS = PathAddress.pathAddress(MASTER_HOST_ELEMENT, EXTENSION_ELEMENT); private static final PathAddress SLAVE_EXTENSION_ADDRESS = PathAddress.pathAddress(SLAVE_HOST_ELEMENT, EXTENSION_ELEMENT); private static final PathAddress DOMAIN_SUBSYSTEM_ADDRESS = PathAddress.pathAddress(PROFILE_ELEMENT, SUBSYSTEM_ELEMENT); private static final PathAddress MASTER_SUBSYSTEM_ADDRESS = PathAddress.pathAddress(MASTER_HOST_ELEMENT, SUBSYSTEM_ELEMENT); private static final PathAddress SLAVE_SUBSYSTEM_ADDRESS = PathAddress.pathAddress(SLAVE_HOST_ELEMENT, SUBSYSTEM_ELEMENT); private static final PathAddress MASTER_SERVER_SUBSYSTEM_ADDRESS = PathAddress.pathAddress(MASTER_HOST_ELEMENT, PathElement.pathElement("server", "main-one"), SUBSYSTEM_ELEMENT); private static final PathAddress SLAVE_SERVER_SUBSYSTEM_ADDRESS = PathAddress.pathAddress(SLAVE_HOST_ELEMENT, PathElement.pathElement("server", "main-three"), SUBSYSTEM_ELEMENT); private static final String SOCKET_BINDING_NAME = "test-binding"; private static final PathAddress MASTER_SOCKET_BINDING_GROUP_ADDRESS = PathAddress.pathAddress(MASTER_HOST_ELEMENT).append(SOCKET_BINDING_GROUP, "test-group"); private static final PathAddress MASTER_SOCKET_BINDING_ADDRESS = PathAddress.pathAddress(MASTER_SOCKET_BINDING_GROUP_ADDRESS).append(SOCKET_BINDING, SOCKET_BINDING_NAME); private static final PathAddress SLAVE_SOCKET_BINDING_GROUP_ADDRESS = PathAddress.pathAddress(SLAVE_HOST_ELEMENT).append(SOCKET_BINDING_GROUP, "test-group"); private static final PathAddress SLAVE_SOCKET_BINDING_ADDRESS = PathAddress.pathAddress(SLAVE_SOCKET_BINDING_GROUP_ADDRESS).append(SOCKET_BINDING, SOCKET_BINDING_NAME); private static final int ADJUSTED_SECOND = TimeoutUtil.adjust(1000); private static DomainTestSupport testSupport; private static DomainLifecycleUtil domainMasterLifecycleUtil; private static DomainLifecycleUtil domainSlaveLifecycleUtil; private static final Map<DomainLifecycleUtil, Set<String>> serversByHost = new HashMap<>(); @BeforeClass public static void setupDomain() throws Exception { testSupport = DomainTestSuite.createSupport(HcExtensionAndSubsystemManagementTestCase.class.getSimpleName()); domainMasterLifecycleUtil = testSupport.getDomainMasterLifecycleUtil(); domainSlaveLifecycleUtil = testSupport.getDomainSlaveLifecycleUtil(); // Initialize the test extension ExtensionSetup.initializeHostTestExtension(testSupport); Set<String> servers = getRunningServers(domainMasterLifecycleUtil, "master"); serversByHost.put(domainMasterLifecycleUtil, servers); servers = getRunningServers(domainSlaveLifecycleUtil, "slave"); serversByHost.put(domainSlaveLifecycleUtil, servers); } private static Set<String> getRunningServers(DomainLifecycleUtil lifecycleUtil, String hcName) throws IOException { ModelNode op = Util.createEmptyOperation(READ_CHILDREN_RESOURCES_OPERATION, PathAddress.pathAddress(HOST, hcName)); op.get(CHILD_TYPE).set(RUNNING_SERVER); ModelNode response = lifecycleUtil.getDomainClient().execute(op); Assert.assertEquals(response.toString(), SUCCESS, response.get(OUTCOME).asString()); Set<String> result = new HashSet<>(); for (ModelNode node : response.get(RESULT).asList()) { result.add(node.asString()); } return result; } @AfterClass public static void tearDownDomain() throws Exception { testSupport = null; domainMasterLifecycleUtil = null; domainSlaveLifecycleUtil = null; serversByHost.clear(); DomainTestSuite.stopSupport(); } @Test public void testAddRemoveExtension() throws Exception { final ModelControllerClient masterClient = domainMasterLifecycleUtil.getDomainClient(); final ModelControllerClient slaveClient = domainSlaveLifecycleUtil.getDomainClient(); /* *It should not be possible to add the subsystem anywhere yet, since the extension has not been initialised */ addSubsystem(masterClient, DOMAIN_SUBSYSTEM_ADDRESS, false); addSubsystem(masterClient, MASTER_SUBSYSTEM_ADDRESS, false); addSubsystem(slaveClient, SLAVE_SUBSYSTEM_ADDRESS, false); checkSubsystemNeedsExtensionInLocalModel(masterClient, slaveClient, DOMAIN_EXTENSION_ADDRESS); checkSubsystemNeedsExtensionInLocalModel(masterClient, slaveClient, MASTER_EXTENSION_ADDRESS); checkSubsystemNeedsExtensionInLocalModel(masterClient, slaveClient, SLAVE_EXTENSION_ADDRESS); } @Test public void testServices() throws Exception { final ModelControllerClient masterClient = domainMasterLifecycleUtil.getDomainClient(); final ModelControllerClient slaveClient = domainSlaveLifecycleUtil.getDomainClient(); checkServices(masterClient, slaveClient, DOMAIN_EXTENSION_ADDRESS); checkServices(masterClient, slaveClient, MASTER_EXTENSION_ADDRESS); checkServices(masterClient, slaveClient, SLAVE_EXTENSION_ADDRESS); } @Test public void testSocketBindingCapabilities() throws Exception { //I think for the DC this is, or at least will/should be tested properly elsewhere. //The main aim of this test is to make sure that the host capability context provides isolation checkSocketBindingCapabilities(MASTER_EXTENSION_ADDRESS); checkSocketBindingCapabilities(SLAVE_EXTENSION_ADDRESS); } private void checkSubsystemNeedsExtensionInLocalModel(ModelControllerClient masterClient, ModelControllerClient slaveClient, PathAddress extensionAddress) throws Exception { Target target = Target.determineFromExtensionAddress(extensionAddress); Exception err = null; try { ModelControllerClient extensionClient = target == Target.SLAVE ? slaveClient : masterClient; //A) Check the subsystem can only be added to the model which has the extension was added to addExtension(extensionClient, extensionAddress); addSubsystem(masterClient, DOMAIN_SUBSYSTEM_ADDRESS, target == Target.DOMAIN); addSubsystem(masterClient, MASTER_SUBSYSTEM_ADDRESS, target == Target.MASTER); addSubsystem(slaveClient, SLAVE_SUBSYSTEM_ADDRESS, target == Target.SLAVE); //B) Should not be possible to remove the extension before removing the subsystem // from the model containing the extension removeExtension(extensionClient, extensionAddress, false); //C Now remove the subsystem, and the remaining extension switch (target) { case DOMAIN: removeSubsystem(extensionClient, DOMAIN_SUBSYSTEM_ADDRESS); break; case MASTER: removeSubsystem(extensionClient, MASTER_SUBSYSTEM_ADDRESS); break; case SLAVE: removeSubsystem(extensionClient, SLAVE_SUBSYSTEM_ADDRESS); break; } removeExtension(extensionClient, extensionAddress, true); //D check that we cannot add the subsystem anywhere since there is no extension addSubsystem(masterClient, DOMAIN_SUBSYSTEM_ADDRESS, false); addSubsystem(masterClient, MASTER_SUBSYSTEM_ADDRESS, false); addSubsystem(slaveClient, SLAVE_SUBSYSTEM_ADDRESS, false); } catch (Exception e) { err = e; } finally { //Cleanup removeIgnoreFailure(masterClient, DOMAIN_SUBSYSTEM_ADDRESS); removeIgnoreFailure(masterClient, MASTER_SUBSYSTEM_ADDRESS); removeIgnoreFailure(slaveClient, SLAVE_SUBSYSTEM_ADDRESS); removeIgnoreFailure(masterClient, DOMAIN_EXTENSION_ADDRESS); removeIgnoreFailure(masterClient, MASTER_EXTENSION_ADDRESS); removeIgnoreFailure(masterClient, SLAVE_EXTENSION_ADDRESS); } if (err != null) { throw err; } } private void checkServices(ModelControllerClient masterClient, ModelControllerClient slaveClient, PathAddress extensionAddress) throws Exception { Target target = Target.determineFromExtensionAddress(extensionAddress); final PathAddress subsystemAddress; switch (target) { case DOMAIN: subsystemAddress = DOMAIN_SUBSYSTEM_ADDRESS; break; case MASTER: subsystemAddress = MASTER_SUBSYSTEM_ADDRESS; break; case SLAVE: subsystemAddress = SLAVE_SUBSYSTEM_ADDRESS; break; default: throw new IllegalStateException("Unknown address"); } ModelControllerClient extensionClient = target == Target.SLAVE ? slaveClient : masterClient; Exception err = null; try { addExtension(extensionClient, extensionAddress); addSubsystem(extensionClient, subsystemAddress, true); switch (target) { case DOMAIN: checkService(masterClient, DOMAIN_SUBSYSTEM_ADDRESS, false); checkNoSubsystem(masterClient, MASTER_SUBSYSTEM_ADDRESS); checkService(masterClient, MASTER_SERVER_SUBSYSTEM_ADDRESS, true); checkNoSubsystem(slaveClient, SLAVE_SUBSYSTEM_ADDRESS); checkService(slaveClient, SLAVE_SERVER_SUBSYSTEM_ADDRESS, true); break; case MASTER: checkNoSubsystem(masterClient, DOMAIN_SUBSYSTEM_ADDRESS); checkService(masterClient, MASTER_SUBSYSTEM_ADDRESS, true); checkNoSubsystem(masterClient, MASTER_SERVER_SUBSYSTEM_ADDRESS); checkNoSubsystem(slaveClient, SLAVE_SUBSYSTEM_ADDRESS); checkNoSubsystem(slaveClient, SLAVE_SERVER_SUBSYSTEM_ADDRESS); break; case SLAVE: checkNoSubsystem(masterClient, DOMAIN_SUBSYSTEM_ADDRESS); checkNoSubsystem(masterClient, MASTER_SUBSYSTEM_ADDRESS); checkNoSubsystem(masterClient, MASTER_SERVER_SUBSYSTEM_ADDRESS); checkService(slaveClient, SLAVE_SUBSYSTEM_ADDRESS, true); checkNoSubsystem(slaveClient, SLAVE_SERVER_SUBSYSTEM_ADDRESS); break; } } catch (Exception e) { err = e; } finally { //Cleanup removeIgnoreFailure(extensionClient, subsystemAddress); removeIgnoreFailure(extensionClient, extensionAddress); } if (err != null) { throw err; } } private void checkSocketBindingCapabilities(PathAddress extensionAddress) throws Exception { ModelControllerClient masterClient = domainMasterLifecycleUtil.getDomainClient(); ModelControllerClient slaveClient = domainSlaveLifecycleUtil.getDomainClient(); Target target = Target.determineFromExtensionAddress(extensionAddress); final PathAddress subsystemAddress; final PathAddress socketBindingGroupAddress; final PathAddress socketBindingAddress; final int portOffset; switch (target) { case MASTER: subsystemAddress = MASTER_SUBSYSTEM_ADDRESS; socketBindingGroupAddress = MASTER_SOCKET_BINDING_GROUP_ADDRESS; socketBindingAddress = MASTER_SOCKET_BINDING_ADDRESS; portOffset = 0; break; case SLAVE: subsystemAddress = SLAVE_SUBSYSTEM_ADDRESS; socketBindingGroupAddress = SLAVE_SOCKET_BINDING_GROUP_ADDRESS; socketBindingAddress = SLAVE_SOCKET_BINDING_ADDRESS; portOffset = 100; break; default: throw new IllegalStateException("Unknown address"); } ModelControllerClient client = target == Target.SLAVE ? slaveClient : masterClient; Exception err = null; try { addExtension(client, extensionAddress); addSubsystemWithSocketBinding(client, subsystemAddress, false); addSocketBindingGroup(client, socketBindingGroupAddress, portOffset); int port = addSocketBinding(client, socketBindingAddress) + portOffset; addSubsystemWithSocketBinding(client, subsystemAddress, true); try(Socket socket = new Socket()) { InetAddress addr = InetAddress.getByName(NetworkUtils.formatPossibleIpv6Address( testSupport.getDomainMasterConfiguration().getHostControllerManagementAddress())); socket.connect(new InetSocketAddress( addr, port )); } } catch (Exception e) { err = e; } finally { //Cleanup removeIgnoreFailure(client, socketBindingAddress); removeIgnoreFailure(client, socketBindingGroupAddress); removeIgnoreFailure(client, subsystemAddress); removeIgnoreFailure(client, extensionAddress); reloadHostsIfReloadRequired(); } if (err != null) { throw err; } } private void checkNoSubsystem(ModelControllerClient client, PathAddress subsystemAddress) throws Exception { PathAddress parent = subsystemAddress.subAddress(0, subsystemAddress.size() - 1); ModelNode op = Util.createEmptyOperation("read-children-resources", parent); op.get("child-type").set(SUBSYSTEM); ModelNode result = DomainTestUtils.executeForResult(op, client); Assert.assertFalse(result.hasDefined(subsystemAddress.getLastElement().getValue())); } private void checkService(ModelControllerClient client, PathAddress address, boolean services) throws Exception { ModelNode op = Util.createEmptyOperation("test-op", address); ModelNode result = DomainTestUtils.executeForResult(op, client); Assert.assertEquals(services, result.asBoolean()); } private void addExtension(ModelControllerClient client, PathAddress extensionAddress) throws Exception { ModelNode op = Util.createAddOperation(extensionAddress); DomainTestUtils.executeForResult(op, client); } private void addSubsystem(ModelControllerClient client, PathAddress subsystemAddress, boolean success) throws Exception { ModelNode op = Util.createAddOperation(subsystemAddress); op.get(NAME).set(TestHostCapableExtension.MODULE_NAME); if (success) { DomainTestUtils.executeForResult(op, client); } else { DomainTestUtils.executeForFailure(op, client); } } private void addSubsystemWithSocketBinding(ModelControllerClient client, PathAddress subsystemAddress, boolean success) throws Exception { ModelNode op = Util.createAddOperation(subsystemAddress); op.get(NAME).set(TestHostCapableExtension.MODULE_NAME); op.get(SOCKET_BINDING).set(SOCKET_BINDING_NAME); if (success) { DomainTestUtils.executeForResult(op, client); } else { DomainTestUtils.executeForFailure(op, client); } } private void removeExtension(ModelControllerClient client, PathAddress extensionAddress, boolean success) throws Exception { ModelNode op = Util.createRemoveOperation(extensionAddress); if (success) { DomainTestUtils.executeForResult(op, client); } else { DomainTestUtils.executeForFailure(op, client); } } private void removeSubsystem(ModelControllerClient client, PathAddress subsystemAddress) throws Exception { ModelNode op = Util.createRemoveOperation(subsystemAddress); DomainTestUtils.executeForResult(op, client); } private void removeIgnoreFailure(ModelControllerClient client, PathAddress subsystemAddress) throws Exception { try { ModelNode op = Util.createRemoveOperation(subsystemAddress); client.execute(op); } catch (Exception ignore) { } } private void addSocketBindingGroup(ModelControllerClient client, PathAddress socketBindingGroupAddress, int portOffset) throws Exception{ ModelNode op = Util.createAddOperation(socketBindingGroupAddress); op.get(DEFAULT_INTERFACE).set("management"); op.get(PORT_OFFSET).set(portOffset); DomainTestUtils.executeForResult(op, client); } private int addSocketBinding(ModelControllerClient client, PathAddress socketBindingAddress) throws Exception { int port = 8089; ModelNode op = Util.createAddOperation(socketBindingAddress); op.get(PORT).set(8089); DomainTestUtils.executeForResult(op, client); return port; } private void reloadHostsIfReloadRequired() throws Exception { //Later tests fail if we leave the host in reload-required boolean reloaded = reloadHostsIfReloadRequired(domainMasterLifecycleUtil, PathAddress.pathAddress(MASTER_HOST_ELEMENT)); reloaded = reloaded || reloadHostsIfReloadRequired(domainSlaveLifecycleUtil, PathAddress.pathAddress(SLAVE_HOST_ELEMENT)); if (reloaded) { //Wait for the slave to reconnect, look for the slave in the list of hosts long end = System.currentTimeMillis() + 20 * ADJUSTED_SECOND; boolean slaveReconnected; do { Thread.sleep(ADJUSTED_SECOND); slaveReconnected = checkSlaveReconnected(domainMasterLifecycleUtil.getDomainClient()); } while (!slaveReconnected && System.currentTimeMillis() < end); } } private boolean reloadHostsIfReloadRequired(DomainLifecycleUtil util, PathAddress address) throws Exception { DomainClient client = util.getDomainClient(); String state = DomainTestUtils.executeForResult(Util.getReadAttributeOperation(address, HOST_STATE), client).asString(); if (!state.equals("running")) { ModelNode reload = Util.createEmptyOperation("reload", address); reload.get(RESTART_SERVERS).set(false); util.executeAwaitConnectionClosed(reload); util.connect(); util.awaitHostController(System.currentTimeMillis()); awaitServers(util, address.getLastElement().getValue()); return true; } return false; } private void awaitServers(DomainLifecycleUtil util, String hostName) throws InterruptedException, TimeoutException, IOException { Set<String> required = serversByHost.get(util); Set<String> unstarted; long timeout = TimeoutUtil.adjust(120 * 1000); long deadline = System.currentTimeMillis() + timeout; do { unstarted = new HashSet<>(required); Set<String> running = getRunningServers(util, hostName); unstarted.removeAll(running); if (unstarted.size() == 0) { break; } TimeUnit.MILLISECONDS.sleep(250); } while (System.currentTimeMillis() < deadline); if (unstarted.size() > 0) { throw new TimeoutException(String.format("Managed servers were not started within [%d] seconds: %s", timeout, unstarted)); } } private boolean checkSlaveReconnected(ModelControllerClient masterClient) throws Exception { ModelNode op = Util.createEmptyOperation(READ_CHILDREN_NAMES_OPERATION, PathAddress.EMPTY_ADDRESS); op.get(CHILD_TYPE).set(HOST); try { ModelNode ret = DomainTestUtils.executeForResult(op, masterClient); List<ModelNode> list = ret.asList(); if (list.size() == 2) { for (ModelNode entry : list) { if ("slave".equals(entry.asString())){ return true; } } } } catch (Exception e) { // ignored } return false; } private enum Target { DOMAIN, MASTER, SLAVE; static Target determineFromExtensionAddress(PathAddress extensionAddress) { if (extensionAddress == DOMAIN_EXTENSION_ADDRESS) { return DOMAIN; } else if (extensionAddress == MASTER_EXTENSION_ADDRESS) { return MASTER; } else if (extensionAddress == SLAVE_EXTENSION_ADDRESS) { return SLAVE; } throw new AssertionError("Unknown extension address"); } } }