/* * * * JBoss, Home of Professional Open Source. * * Copyright 2013, 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.host.controller; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.IGNORE_UNUSED_CONFIG; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INITIAL_SERVER_GROUPS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROFILE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_CONFIG; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.extension.ExtensionRegistry; import org.jboss.as.controller.extension.SubsystemInformation; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.registry.Resource.ResourceEntry; import org.jboss.as.domain.controller.logging.DomainControllerLogger; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; /** * Utility to inspect what resources should be ignored on a slave according to its server-configs * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ public class IgnoredNonAffectedServerGroupsUtil { private final ExtensionRegistry extensionRegistry; private IgnoredNonAffectedServerGroupsUtil(final ExtensionRegistry extensionRegistry) { this.extensionRegistry = extensionRegistry; } /** * Static factory * * @param extensionRegistry the extension registry * @return the created instance */ public static IgnoredNonAffectedServerGroupsUtil create(final ExtensionRegistry extensionRegistry) { return new IgnoredNonAffectedServerGroupsUtil(extensionRegistry); } /** * Used by the slave host when creating the host info dmr sent across to the DC during the registration process * * @param ignoreUnaffectedServerGroups whether the slave host is set up to ignore config for server groups it does not have servers for * @param hostModel the resource containing the host model * @param model the dmr sent across to theDC * @return the modified dmr */ public static ModelNode addCurrentServerGroupsToHostInfoModel(boolean ignoreUnaffectedServerGroups, Resource hostModel, ModelNode model) { if (!ignoreUnaffectedServerGroups) { return model; } model.get(IGNORE_UNUSED_CONFIG).set(ignoreUnaffectedServerGroups); addServerGroupsToModel(hostModel, model); return model; } public static void addServerGroupsToModel(Resource hostModel, ModelNode model) { ModelNode initialServerGroups = new ModelNode(); initialServerGroups.setEmptyObject(); for (ResourceEntry entry : hostModel.getChildren(SERVER_CONFIG)) { ModelNode serverNode = new ModelNode(); serverNode.get(GROUP).set(entry.getModel().get(GROUP)); if (entry.getModel().hasDefined(SOCKET_BINDING_GROUP)) { serverNode.get(SOCKET_BINDING_GROUP).set(entry.getModel().get(SOCKET_BINDING_GROUP).asString()); } initialServerGroups.get(entry.getName()).set(serverNode); } model.get(ModelDescriptionConstants.INITIAL_SERVER_GROUPS).set(initialServerGroups); } public static Set<ServerConfigInfo> createConfigsFromModel(final ModelNode model) { final Set<ServerConfigInfo> serverConfigs = new HashSet<>(); ModelNode initialServerGroups = model.get(INITIAL_SERVER_GROUPS); for (Property prop : initialServerGroups.asPropertyList()) { final List<ModelNode> servers = prop.getValue().asList(); for (ModelNode server : servers) { final String socketBindingGroupOverride = server.hasDefined(SOCKET_BINDING_GROUP) ? server.get(SOCKET_BINDING_GROUP).asString() : null; final ServerConfigInfo serverConfigInfo = IgnoredNonAffectedServerGroupsUtil.createServerConfigInfo(prop.getValue().get(GROUP).asString(), socketBindingGroupOverride); serverConfigs.add(serverConfigInfo); } } return serverConfigs; } /** * For the DC to check whether an operation should be ignored on the slave, if the slave is set up to ignore config not relevant to it * * @param domainResource the domain root resource * @param serverConfigs the server configs the slave is known to have * @param pathAddress the address of the operation to check if should be ignored or not */ public boolean ignoreOperation(final Resource domainResource, final Collection<ServerConfigInfo> serverConfigs, final PathAddress pathAddress) { if (pathAddress.size() == 0) { return false; } boolean ignore = ignoreResourceInternal(domainResource, serverConfigs, pathAddress); return ignore; } private boolean ignoreResourceInternal(final Resource domainResource, final Collection<ServerConfigInfo> serverConfigs, final PathAddress pathAddress) { String type = pathAddress.getElement(0).getKey(); switch (type) { case PROFILE: return ignoreProfile(domainResource, serverConfigs, pathAddress.getElement(0).getValue()); case SERVER_GROUP: return ignoreServerGroup(domainResource, serverConfigs, pathAddress.getElement(0).getValue()); // We don't automatically ignore extensions for now // case EXTENSION: // return ignoreExtension(domainResource, serverConfigs, pathAddress.getElement(0).getValue()); case SOCKET_BINDING_GROUP: return ignoreSocketBindingGroups(domainResource, serverConfigs, pathAddress.getElement(0).getValue()); default: return false; } } private boolean ignoreProfile(final Resource domainResource, final Collection<ServerConfigInfo> serverConfigs, final String name) { Set<String> seenGroups = new HashSet<>(); Set<String> profiles = new HashSet<>(); for (ServerConfigInfo serverConfig : serverConfigs) { if (seenGroups.contains(serverConfig.getServerGroup())) { continue; } seenGroups.add(serverConfig.getServerGroup()); Resource serverGroupResource = domainResource.getChild(PathElement.pathElement(SERVER_GROUP, serverConfig.getServerGroup())); String profile = serverGroupResource.getModel().get(PROFILE).asString(); if (profile.equals(name)) { return false; } processProfiles(domainResource, profile, profiles); } return !profiles.contains(name); } private void processProfiles(final Resource domain, final String profile, final Set<String> profiles) { if (!profiles.contains(profile)) { profiles.add(profile); final PathElement pathElement = PathElement.pathElement(PROFILE, profile); if (domain.hasChild(pathElement)) { final Resource resource = domain.getChild(pathElement); final ModelNode model = resource.getModel(); if (model.hasDefined(INCLUDES)) { for (final ModelNode include : model.get(INCLUDES).asList()) { processProfiles(domain, include.asString(), profiles); } } } } } private boolean ignoreServerGroup(final Resource domainResource, final Collection<ServerConfigInfo> serverConfigs, final String name) { for (ServerConfigInfo serverConfig : serverConfigs) { if (serverConfig.getServerGroup().equals(name)) { return false; } } return true; } private boolean ignoreExtension(final Resource domainResource, final Collection<ServerConfigInfo> serverConfigs, final String name) { //Should these be the subsystems on the master, as we have it at present, or the ones from the slave? Map<String, SubsystemInformation> subsystems = extensionRegistry.getAvailableSubsystems(name); for (String subsystem : subsystems.keySet()) { for (ResourceEntry profileEntry : domainResource.getChildren(PROFILE)) { if (profileEntry.hasChild(PathElement.pathElement(SUBSYSTEM, subsystem))) { if (!ignoreProfile(domainResource, serverConfigs, profileEntry.getName())) { return false; } } } } return true; } private boolean ignoreSocketBindingGroups(final Resource domainResource, final Collection<ServerConfigInfo> serverConfigs, final String name) { Set<String> seenGroups = new HashSet<>(); Set<String> socketBindingGroups = new HashSet<>(); for (ServerConfigInfo serverConfig : serverConfigs) { final String socketBindingGroup; if (serverConfig.getSocketBindingGroup() != null) { if (serverConfig.getSocketBindingGroup().equals(name)) { return false; } socketBindingGroup = serverConfig.getSocketBindingGroup(); } else { if (seenGroups.contains(serverConfig.getServerGroup())) { continue; } seenGroups.add(serverConfig.getServerGroup()); Resource serverGroupResource = domainResource.getChild(PathElement.pathElement(SERVER_GROUP, serverConfig.getServerGroup())); socketBindingGroup = serverGroupResource.getModel().get(SOCKET_BINDING_GROUP).asString(); if (socketBindingGroup.equals(name)) { return false; } } processSocketBindingGroups(domainResource, socketBindingGroup, socketBindingGroups); } return !socketBindingGroups.contains(name); } private void processSocketBindingGroups(final Resource domainResource, final String name, final Set<String> socketBindingGroups) { if (!socketBindingGroups.contains(name)) { socketBindingGroups.add(name); final PathElement pathElement = PathElement.pathElement(SOCKET_BINDING_GROUP, name); if (domainResource.hasChild(pathElement)) { final Resource resource = domainResource.getChild(pathElement); final ModelNode model = resource.getModel(); if (model.hasDefined(INCLUDES)) { for (final ModelNode include : model.get(INCLUDES).asList()) { processSocketBindingGroups(domainResource, include.asString(), socketBindingGroups); } } } } } /** * For use on a slave HC to get all the server groups used by the host * * @param hostResource the host resource * @return the server configs on this host */ public Set<ServerConfigInfo> getServerConfigsOnSlave(Resource hostResource){ Set<ServerConfigInfo> groups = new HashSet<>(); for (ResourceEntry entry : hostResource.getChildren(SERVER_CONFIG)) { groups.add(new ServerConfigInfoImpl(entry.getModel())); } return groups; } /** * Creates a server config info from its name, its server group and its socket binding group * * @param serverGroup the name of the server group * @param socketBindingGroup the name of the socket binding override used by the server config. May be {@code null} * @return the server config info */ public static ServerConfigInfo createServerConfigInfo(String serverGroup, String socketBindingGroup) { return new ServerConfigInfoImpl(serverGroup, socketBindingGroup); } public static Set<ServerConfigInfo> createConfigsFromDomainWideData(Set<String> activeServerGroups, Set<String> activeSocketBindingGroups) { final Set<ServerConfigInfo> serverConfigs = new HashSet<>(); if (activeSocketBindingGroups == null || activeSocketBindingGroups.isEmpty()) { for (String serverGroup : activeServerGroups) { ServerConfigInfo sci = new ServerConfigInfoImpl(serverGroup, null); serverConfigs.add(sci); DomainControllerLogger.ROOT_LOGGER.tracef("Domain wide host-exclude needs a simple %s", sci); } } else { // Hosts of this API version use socket binding groups beyond the default ones // for the server groups. Generate a set of ServerConfigInfo objects such that // all provided server groups and socket binding groups are named in at least // one. It doesn't matter what combinations are used. String[] sgs = activeServerGroups.toArray(new String[activeServerGroups.size()]); String[] sbgs = activeSocketBindingGroups.toArray(new String[activeSocketBindingGroups.size()]); if (sgs.length >= sbgs.length) { for (int i = 0; i < sgs.length; i++) { String sbg = i >= sbgs.length ? null : sbgs[i]; ServerConfigInfo sci = new ServerConfigInfoImpl(sgs[i], sbg); serverConfigs.add(sci); DomainControllerLogger.ROOT_LOGGER.tracef("Domain wide host-exclude needs a synthetic active combination of %s", sci); } } else { for (int i = 0, j = 0; j < sbgs.length; i++, j++) { if (i == sgs.length) { // Start over again with the server groups i = 0; } ServerConfigInfo sci = new ServerConfigInfoImpl(sgs[i], sbgs[j]); serverConfigs.add(sci); DomainControllerLogger.ROOT_LOGGER.tracef("Domain wide host-exclude needs a synthetic active combination of %s", sci); } } } return serverConfigs; } /** * Contains info about a server config */ public interface ServerConfigInfo { /** * Gets the server config's server group name * * @return the server group name */ String getServerGroup(); /** * Gets the server config's socket binding group override name * * @return the socket binding group name. May be {@code null} */ String getSocketBindingGroup(); } private static class ServerConfigInfoImpl implements ServerConfigInfo { private final String serverGroup; private final String socketBindingGroup; ServerConfigInfoImpl(ModelNode model) { this.serverGroup = model.get(GROUP).asString(); this.socketBindingGroup = model.hasDefined(SOCKET_BINDING_GROUP) ? model.get(SOCKET_BINDING_GROUP).asString() : null; } ServerConfigInfoImpl(String serverGroup, String socketBindingGroup) { this.serverGroup = serverGroup; this.socketBindingGroup = socketBindingGroup; } @Override public String getServerGroup() { return serverGroup; } @Override public String getSocketBindingGroup() { return socketBindingGroup; } @Override public String toString() { return "ServerConfigInfoImpl{" + "serverGroup='" + serverGroup + '\'' + ", socketBindingGroup='" + socketBindingGroup + '\'' + '}'; } } }