/* * This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH * written by Rasto Levrinc. * * Copyright (C) 2009-2010, LINBIT HA-Solutions GmbH. * Copyright (C) 2009-2010, Rasto Levrinc * * DRBD Management Console 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; either version 2, or (at your option) * any later version. * * DRBD Management Console 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 drbd; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ package lcmc.crm.ui.resource; import java.awt.Color; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Matcher; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; import javax.swing.ImageIcon; import javax.swing.tree.DefaultMutableTreeNode; import lcmc.common.domain.Application; import lcmc.common.domain.ColorText; import lcmc.common.ui.main.MainData; import lcmc.common.ui.treemenu.TreeMenuController; import lcmc.common.ui.utils.SwingUtils; import lcmc.crm.domain.CrmXml; import lcmc.crm.domain.ClusterStatus; import lcmc.host.domain.Host; import lcmc.crm.domain.ResourceAgent; import lcmc.common.domain.Value; import lcmc.common.ui.Browser; import lcmc.cluster.ui.widget.Check; import lcmc.cluster.ui.widget.Widget; import lcmc.crm.service.CRM; import lcmc.logger.Logger; import lcmc.logger.LoggerFactory; import lcmc.common.ui.utils.MyButton; import lcmc.common.domain.util.Tools; import lcmc.common.ui.utils.UpdatableItem; import lombok.val; /** * GroupInfo class holds data for heartbeat group, that is in some ways * like normal service, but it can contain other services. */ @Named public class GroupInfo extends ServiceInfo { private static final Logger LOG = LoggerFactory.getLogger(GroupInfo.class); private final List<ServiceInfo> groupServices = new ArrayList<ServiceInfo>(); private final ReadWriteLock mGroupServiceLock = new ReentrantReadWriteLock(); private final Lock mGroupServiceReadLock = mGroupServiceLock.readLock(); private final Lock mGroupServiceWriteLock = mGroupServiceLock.writeLock(); @Inject private GroupMenu groupMenu; @Inject @Named("serviceInfo") private Provider<ServiceInfo> serviceInfoProvider; @Inject private Provider<FilesystemRaInfo> filesystemInfoProvider; @Inject private Provider<LinbitDrbdInfo> linbitDrbdInfoProvider; @Inject private Provider<DrbddiskInfo> drbddiskInfoProvider; @Inject private Provider<IPaddrInfo> ipaddrInfoProvider; @Inject private Provider<VirtualDomainInfo> virtualDomainInfoProvider; @Inject private Application application; @Inject private SwingUtils swingUtils; @Inject private TreeMenuController treeMenuController; @Inject private MainData mainData; void init(final ResourceAgent ra, final Browser browser) { super.init(Application.PACEMAKER_GROUP_NAME, ra, browser); } /** Applies the the whole group if for example an order has changed. */ void applyWhole(final Host dcHost, final boolean createGroup, final List<ServiceInfo> servicesInNewOrder, final Application.RunMode runMode) { final String[] params = getParametersFromXML(); if (Application.isLive(runMode)) { swingUtils.invokeAndWait(new Runnable() { @Override public void run() { final MyButton ab = getApplyButton(); if (ab != null) { ab.setEnabled(false); } } }); } getInfoPanel(); waitForInfoPanel(); final Map<String, String> groupMetaArgs = new LinkedHashMap<String, String>(); for (final String param : params) { if (GUI_ID.equals(param) || PCMK_ID.equals(param)) { continue; } final Value value = getComboBoxValue(param); if (Tools.areEqual(value, getParamDefault(param))) { continue; } if (!value.isNothingSelected()) { if (CrmXml.GROUP_ORDERED_META_ATTR.equals(param)) { groupMetaArgs.put("ordered", value.getValueForConfig()); } else { groupMetaArgs.put(param, value.getValueForConfig()); } } } final Map<String, Map<String, String>> pacemakerResAttrs = new HashMap<String, Map<String, String>>(); final Map<String, Map<String, String>> pacemakerResArgs = new HashMap<String, Map<String, String>>(); final Map<String, Map<String, String>> pacemakerMetaArgs = new HashMap<String, Map<String, String>>(); final Map<String, String> instanceAttrId = new HashMap<String, String>(); final Map<String, Map<String, String>> nvpairIdsHash = new HashMap<String, Map<String, String>>(); final Map<String, Map<String, Map<String, String>>> pacemakerOps = new HashMap<String, Map<String, Map<String, String>>>(); final Map<String, String> operationsId = new HashMap<String, String>(); final Map<String, String> metaAttrsRefId = new HashMap<String, String>(); final Map<String, String> operationsRefId = new HashMap<String, String>(); final Map<String, Boolean> stonith = new HashMap<String, Boolean>(); final ClusterStatus cs = getBrowser().getClusterStatus(); for (final ServiceInfo gsi : servicesInNewOrder) { if (gsi == null) { continue; } swingUtils.invokeAndWait(new Runnable() { @Override public void run() { gsi.getInfoPanel(); } }); } swingUtils.waitForSwing(); for (final ServiceInfo gsi : servicesInNewOrder) { if (gsi == null) { continue; } final String resId = gsi.getHeartbeatId(runMode); pacemakerResAttrs.put(resId, gsi.getPacemakerResAttrs(runMode)); pacemakerResArgs.put(resId, gsi.getPacemakerResArgs()); pacemakerMetaArgs.put(resId, gsi.getPacemakerMetaArgs()); instanceAttrId.put(resId, cs.getResourceInstanceAttrId(resId)); nvpairIdsHash.put(resId, cs.getParametersNvpairsIds(resId)); pacemakerOps.put(resId, gsi.getOperations(resId)); operationsId.put(resId, cs.getOperationsId(resId)); metaAttrsRefId.put(resId, gsi.getMetaAttrsRefId()); operationsRefId.put(resId, gsi.getOperationsRefId()); stonith.put(resId, gsi.getResourceAgent().isStonith()); } final CloneInfo ci = getCloneInfo(); String cloneId = null; boolean master = false; final Map<String, String> cloneMetaArgs = new LinkedHashMap<String, String>(); String cloneMetaAttrsRefIds = null; if (createGroup && ci != null) { cloneId = ci.getHeartbeatId(runMode); final String[] cloneParams = ci.getParametersFromXML(); master = ci.getService().isMaster(); cloneMetaAttrsRefIds = ci.getMetaAttrsRefId(); for (final String param : cloneParams) { if (GUI_ID.equals(param) || PCMK_ID.equals(param)) { continue; } final Value value = ci.getComboBoxValue(param); if (Tools.areEqual(value, ci.getParamDefault(param))) { continue; } if (!GUI_ID.equals(param) && !value.isNothingSelected()) { cloneMetaArgs.put(param, value.getValueForConfig()); } } } final List<String> servicesCrmIds = new ArrayList<String>(); for (final ServiceInfo gsi : servicesInNewOrder) { servicesCrmIds.add(gsi.getHeartbeatId(runMode)); } CRM.replaceGroup(createGroup, dcHost, cloneId, master, cloneMetaArgs, cloneMetaAttrsRefIds, servicesCrmIds, groupMetaArgs, getHeartbeatId(runMode), pacemakerResAttrs, pacemakerResArgs, pacemakerMetaArgs, instanceAttrId, nvpairIdsHash, pacemakerOps, operationsId, metaAttrsRefId, getMetaAttrsRefId(), operationsRefId, stonith, runMode); if (Application.isLive(runMode)) { storeComboBoxValues(params); treeMenuController.reloadNode(getNode(), false); } mGroupServiceWriteLock.lock(); try { groupServices.clear(); groupServices.addAll(servicesInNewOrder); } finally { mGroupServiceWriteLock.unlock(); } getBrowser().getCrmGraph().repaint(); } /** Applies the changes to the group parameters. */ @Override public void apply(final Host dcHost, final Application.RunMode runMode) { if (Application.isLive(runMode)) { swingUtils.invokeAndWait(new Runnable() { @Override public void run() { getApplyButton().setEnabled(false); getRevertButton().setEnabled(false); } }); } getInfoPanel(); waitForInfoPanel(); final String[] params = getParametersFromXML(); if (Application.isLive(runMode)) { swingUtils.invokeLater(new Runnable() { @Override public void run() { getApplyButton().setToolTipText(""); final Widget idField = getWidget(GUI_ID, null); idField.setEnabled(false); } }); /* add myself to the hash with service name and id as * keys */ getBrowser().removeFromServiceInfoHash(this); final String oldHeartbeatId = getHeartbeatId(runMode); if (oldHeartbeatId != null) { getBrowser().mHeartbeatIdToServiceLock(); getBrowser().getHeartbeatIdToServiceInfo().remove(oldHeartbeatId); getBrowser().mHeartbeatIdToServiceUnlock(); } if (getService().isNew()) { final String id = getComboBoxValue(GUI_ID).getValueForConfig(); getService().setIdAndCrmId(id); if (getTypeRadioGroup() != null) { getTypeRadioGroup().setEnabled(false); } } getBrowser().addNameToServiceInfoHash(this); getBrowser().addToHeartbeatIdList(this); } /* MSG_ADD_GRP group param_id1 param_name1 param_value1 param_id2 param_name2 param_value2 ... param_idn param_namen param_valuen */ final String heartbeatId = getHeartbeatId(runMode); if (getService().isNew()) { final Set<ServiceInfo> parents = getBrowser().getCrmGraph().getParents(this); final List<Map<String, String>> colAttrsList = new ArrayList<Map<String, String>>(); final List<Map<String, String>> ordAttrsList = new ArrayList<Map<String, String>>(); final List<String> parentIds = new ArrayList<String>(); for (final ServiceInfo parentInfo : parents) { final String parentId = parentInfo.getService().getCrmId(); parentIds.add(parentId); final Map<String, String> colAttrs = new LinkedHashMap<String, String>(); final Map<String, String> ordAttrs = new LinkedHashMap<String, String>(); colAttrs.put(CrmXml.SCORE_CONSTRAINT_PARAM, CrmXml.INFINITY_VALUE.getValueForConfig()); ordAttrs.put(CrmXml.SCORE_CONSTRAINT_PARAM, CrmXml.INFINITY_VALUE.getValueForConfig()); if (getService().isMaster()) { colAttrs.put("with-rsc-role", "Master"); ordAttrs.put("first-action", "promote"); ordAttrs.put("then-action", "start"); } colAttrsList.add(colAttrs); ordAttrsList.add(ordAttrs); } CRM.setOrderAndColocation(dcHost, heartbeatId, parentIds.toArray(new String [parentIds.size()]), colAttrsList, ordAttrsList, runMode); final Collection<String> newOrder = new ArrayList<String>(); applyWhole(dcHost, true, getSubServices(), runMode); if (Application.isLive(runMode)) { setApplyButtons(null, params); } getBrowser().getCrmGraph().repaint(); return; } else { final Map<String, String> groupMetaArgs = new LinkedHashMap<String, String>(); for (final String param : params) { if (GUI_ID.equals(param) || PCMK_ID.equals(param)) { continue; } final Value value = getComboBoxValue(param); if (Tools.areEqual(value, getParamDefault(param))) { continue; } if (value != null && !value.isNothingSelected()) { if (CrmXml.GROUP_ORDERED_META_ATTR.equals(param)) { groupMetaArgs.put("ordered", value.getValueForConfig()); } else { groupMetaArgs.put(param, value.getValueForConfig()); } } } CRM.setParameters(dcHost, CRM.CIB_OP_REPLACE, null, /* crm id */ null, /* TODO: clone id */ false, /* master */ null, /* cloneMetaArgs, */ groupMetaArgs, heartbeatId, /* group id */ null, /* pacemakerResAttrs, */ null, /* pacemakerResArgs, */ null, /* pacemakerMetaArgs, */ null, /* cs.getResourceInstanceAttrId(heartbeatId), */ null, /* cs.getParametersNvpairsIds(heartbeatId), */ null, /* getOperations(heartbeatId), */ null, /* cs.getOperationsId(heartbeatId), */ null, /* getMetaAttrsRefId(), */ null, /* cloneMetaAttrsRefIds, */ getMetaAttrsRefId(), null, /* getOperationsRefId(), */ false, /* stonith */ runMode); } final CloneInfo ci = getCloneInfo(); if (ci == null) { setLocations(heartbeatId, dcHost, runMode); } else { ci.setLocations(heartbeatId, dcHost, runMode); } if (Application.isLive(runMode)) { storeComboBoxValues(params); treeMenuController.reloadNode(getNode(), false); } for (final ServiceInfo child : getSubServices()) { final Check childCheck = child.checkResourceFields(null, child.getParametersFromXML(), false, false, true); if (childCheck.isCorrect() && childCheck.isChanged()) { child.apply(dcHost, runMode); } } if (Application.isLive(runMode)) { setApplyButtons(null, params); } getBrowser().getCrmGraph().repaint(); } Iterable<ResourceAgent> getAddGroupServiceList(final String cl) { return getBrowser().getCrmXml().getServices(cl); } /** * Adds service to this group. Adds it in the submenu in the menu tree * and initializes it. * * @param newServiceInfo * service info object of the new service */ public void addGroupServicePanel(final ServiceInfo newServiceInfo, final boolean reloadNode) { final DefaultMutableTreeNode groupNode = getNode(); if (groupNode == null) { return; } newServiceInfo.getService().setResourceClass(newServiceInfo.getResourceAgent().getResourceClass()); newServiceInfo.setGroupInfo(this); getBrowser().addNameToServiceInfoHash(newServiceInfo); getBrowser().addToHeartbeatIdList(newServiceInfo); final DefaultMutableTreeNode newServiceNode = treeMenuController.createMenuItem(groupNode, newServiceInfo); mGroupServiceWriteLock.lock(); try { groupServices.add(newServiceInfo); } finally { mGroupServiceWriteLock.unlock(); } if (reloadNode) { treeMenuController.reloadNode(groupNode, false); treeMenuController.reloadNode(newServiceNode, true); } } ServiceInfo addGroupServicePanel(final ResourceAgent newRA, final boolean reloadNode) { final ServiceInfo newServiceInfo; final String name = newRA.getServiceName(); if (newRA.isFilesystem()) { newServiceInfo = filesystemInfoProvider.get(); } else if (newRA.isLinbitDrbd()) { newServiceInfo = linbitDrbdInfoProvider.get(); } else if (newRA.isDrbddisk()) { newServiceInfo = drbddiskInfoProvider.get(); } else if (newRA.isIPaddr()) { newServiceInfo = ipaddrInfoProvider.get(); } else if (newRA.isVirtualDomain()) { newServiceInfo = virtualDomainInfoProvider.get(); } else if (newRA.isGroup()) { LOG.appError("addGroupServicePanel: no groups in group allowed"); return null; } else { newServiceInfo = serviceInfoProvider.get(); } newServiceInfo.init(name, newRA, getBrowser()); addGroupServicePanel(newServiceInfo, reloadNode); return newServiceInfo; } /** * Returns on which node this group is running, meaning on which node * all the services are running. Null if they running on different * nodes or not at all. */ @Override protected List<String> getRunningOnNodes(final Application.RunMode runMode) { final ClusterStatus cs = getBrowser().getClusterStatus(); val resources = cs.getGroupResources(getHeartbeatId(runMode), runMode); final List<String> allNodes = new ArrayList<String>(); if (resources.isPresent()) { for (final String hbId : resources.get()) { final List<String> ns = cs.getRunningOnNodes(hbId, runMode); if (ns != null) { for (final String n : ns) { if (!allNodes.contains(n)) { allNodes.add(n); } } } } } return allNodes; } /** Returns node name of the host where this service is running. */ @Override List<String> getMasterOnNodes(final Application.RunMode runMode) { final ClusterStatus cs = getBrowser().getClusterStatus(); final val resources = cs.getGroupResources(getHeartbeatId(runMode), runMode); final List<String> allNodes = new ArrayList<String>(); if (resources.isPresent()) { for (final String hbId : resources.get()) { final List<String> ns = cs.getMasterOnNodes(hbId, runMode); if (ns != null) { for (final String n : ns) { if (!allNodes.contains(n)) { allNodes.add(n); } } } } } return allNodes; } /** Starts all resources in the group. */ @Override void startResource(final Host dcHost, final Application.RunMode runMode) { if (Application.isLive(runMode)) { setUpdated(true); } for (final ServiceInfo child : getSubServices()) { CRM.startResource(dcHost, child.getHeartbeatId(runMode), runMode); } } /** Stops all resources in the group. */ @Override void stopResource(final Host dcHost, final Application.RunMode runMode) { if (Application.isLive(runMode)) { setUpdated(true); } for (final ServiceInfo child : getSubServices()) { CRM.stopResource(dcHost, child.getHeartbeatId(runMode), runMode); } } /** Cleans up all resources in the group. */ @Override void cleanupResource(final Host dcHost, final Application.RunMode runMode) { if (Application.isLive(runMode)) { setUpdated(true); } for (final ServiceInfo child : getSubServices()) { child.cleanupResource(dcHost, runMode); } } /** Sets whether the group services are managed. */ @Override void setManaged(final boolean isManaged, final Host dcHost, final Application.RunMode runMode) { if (Application.isLive(runMode)) { setUpdated(true); } for (final ServiceInfo child : getSubServices()) { CRM.setManaged(dcHost, child.getHeartbeatId(runMode), isManaged, runMode); } } @Override public List<UpdatableItem> createPopup() { return groupMenu.getPulldownMenu(this); } /** Removes this group from the cib. */ @Override public void removeMyself(final Application.RunMode runMode) { if (getService().isNew()) { removeMyselfNoConfirm(getBrowser().getDCHost(), runMode); getService().setNew(false); super.removeInfo(); getService().doneRemoving(); return; } String desc = Tools.getString("ClusterBrowser.confirmRemoveGroup.Description"); final StringBuilder services = new StringBuilder(); boolean first = true; for (final ServiceInfo child : getSubServices()) { if (!first) { services.append(", "); } services.append(child); first = false; } desc = desc.replaceAll("@GROUP@", '\'' + Matcher.quoteReplacement(toString()) + '\''); desc = desc.replaceAll("@SERVICES@", Matcher.quoteReplacement(services.toString())); if (application.confirmDialog(Tools.getString("ClusterBrowser.confirmRemoveGroup.Title"), desc, Tools.getString("ClusterBrowser.confirmRemoveGroup.Yes"), Tools.getString("ClusterBrowser.confirmRemoveGroup.No"))) { if (Application.isLive(runMode)) { getService().setRemoved(true); } removeMyselfNoConfirm(getBrowser().getDCHost(), runMode); super.removeInfo(); getService().setNew(false); } getService().doneRemoving(); } @Override public void removeInfo() { for (final ServiceInfo child : getSubServices()) { child.removeInfo(); } super.removeInfo(); } /** Remove all the services in the group and the group. */ @Override public void removeMyselfNoConfirm(final Host dcHost, final Application.RunMode runMode) { final Collection<ServiceInfo> children = new ArrayList<ServiceInfo>(); if (Application.isLive(runMode)) { for (final ServiceInfo child : getSubServices()) { child.getService().setRemoved(true); children.add(child); } } if (getService().isNew()) { if (Application.isLive(runMode)) { getService().setNew(false); getBrowser().getCrmGraph().killRemovedVertices(); getService().doneRemoving(); } } else { String cloneId = null; boolean master = false; final CloneInfo ci = getCloneInfo(); if (ci != null) { cloneId = ci.getHeartbeatId(runMode); master = ci.getService().isMaster(); } super.removeMyselfNoConfirm(dcHost, runMode); for (final ServiceInfo child : children) { child.removeConstraints(dcHost, runMode); } CRM.removeResource(dcHost, null, getHeartbeatId(runMode), cloneId, master, runMode); for (final ServiceInfo child : children) { child.cleanupResource(dcHost, runMode); } } if (Application.isLive(runMode)) { for (final ServiceInfo child : children) { getBrowser().mHeartbeatIdToServiceLock(); getBrowser().getHeartbeatIdToServiceInfo().remove(child.getService().getCrmId()); getBrowser().mHeartbeatIdToServiceUnlock(); getBrowser().removeFromServiceInfoHash(child); child.cleanup(); child.getService().doneRemoving(); } } } /** Removes the group, but not the services. */ void removeMyselfNoConfirmFromChild(final Host dcHost, final Application.RunMode runMode) { super.removeMyselfNoConfirm(dcHost, runMode); } /** Returns tool tip for the group vertex. */ @Override public String getToolTipText(final Application.RunMode runMode) { final List<String> hostNames = getRunningOnNodes(runMode); final StringBuilder sb = new StringBuilder(220); sb.append("<b>"); sb.append(this); if (hostNames == null || hostNames.isEmpty()) { sb.append(" not running"); } else if (hostNames.size() == 1) { sb.append(" running on node: "); } else { sb.append(" running on nodes: "); } if (hostNames != null && !hostNames.isEmpty()) { sb.append(Tools.join(", ", hostNames.toArray(new String[hostNames.size()]))); } sb.append("</b>"); for (final ServiceInfo child : getSubServices()) { sb.append("\n   "); sb.append(child.getToolTipText(runMode)); } return sb.toString(); } @Override boolean isOneFailed(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (child.isOneFailed(runMode)) { return true; } } return false; } @Override boolean isOneFailedCount(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (child.isOneFailedCount(runMode)) { return true; } } return false; } /** Returns whether one of the services failed to start. */ @Override public boolean isFailed(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (child.isFailed(runMode)) { return true; } } return false; } /** Returns subtexts that appears in the service vertex. */ @Override public ColorText[] getSubtextsForGraph(final Application.RunMode runMode) { final List<ColorText> texts = new ArrayList<ColorText>(); ColorText prevColorText = null; for (final ServiceInfo child : getSubServices()) { final ColorText[] colorTexts = child.getSubtextsForGraph(runMode); if (colorTexts == null || colorTexts.length == 0) { continue; } final ColorText sColorText = colorTexts[0]; if (prevColorText == null || !sColorText.getSubtext().equals(prevColorText.getSubtext())) { texts.add(new ColorText(sColorText.getSubtext() + ':', sColorText.getColor(), Color.BLACK)); prevColorText = sColorText; } String unmanaged = ""; if (!child.isManaged(runMode)) { unmanaged = " / unmanaged"; } String migrated = ""; if (child.getMigratedTo(runMode) != null || child.getMigratedFrom(runMode) != null) { migrated = " / migrated"; } final HbConnectionInfo[] hbcis = getBrowser().getCrmGraph().getHbConnections(child); String constraintLeft = ""; String constraint = ""; if (hbcis != null) { boolean scoreFirst = false; boolean scoreThen = false; boolean someConnection = false; for (final HbConnectionInfo hbci : hbcis) { if (hbci == null) { continue; } if (!someConnection && hbci.hasColocationOrOrder(child)) { someConnection = true; } if (!scoreFirst && !hbci.isOrdScoreNull(child, null)) { scoreFirst = true; } if (!scoreThen && !hbci.isOrdScoreNull(null, child)) { scoreThen = true; } } if (someConnection) { if (scoreFirst || scoreThen) { if (scoreFirst) { constraint = " \u2192"; /* -> */ } if (scoreThen) { constraintLeft = "\u2192 "; /* -> */ } } else { /* just colocation */ constraint = " --"; /* -- */ } } } texts.add(new ColorText(" " + constraintLeft + child + unmanaged + migrated + constraint, sColorText.getColor(), Color.BLACK)); boolean skip = true; for (final ColorText st : colorTexts) { if (skip) { skip = false; continue; } texts.add(new ColorText(" " + st.getSubtext(), st.getColor(), Color.BLACK)); } } return texts.toArray(new ColorText[texts.size()]); } /** * Returns from which hosts the services or the whole group was migrated. */ @Override public List<Host> getMigratedFrom(final Application.RunMode runMode) { List<Host> hosts = super.getMigratedFrom(runMode); final List<ServiceInfo> gs = getSubServices(); if (gs.isEmpty()) { return null; } for (final ServiceInfo child : gs) { final List<Host> siHosts = child.getMigratedFrom(runMode); if (siHosts != null) { if (hosts == null) { hosts = new ArrayList<Host>(); } hosts.addAll(siHosts); } } return hosts; } /** Returns whether at least one service is unmanaged. */ @Override public boolean isManaged(final Application.RunMode runMode) { final List<ServiceInfo> gs = getSubServices(); if (gs.isEmpty()) { return true; } for (final ServiceInfo child : gs) { if (!child.isManaged(runMode)) { return false; } } return true; } /** Returns whether all of the services are started. */ @Override boolean isStarted(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (!child.isStarted(runMode)) { return false; } } return true; } /** Returns whether one of the services is stopped. */ @Override public boolean isStopped(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (child.isStopped(runMode)) { return true; } } return false; } /** Returns whether the group is stopped. */ @Override boolean isGroupStopped(final Application.RunMode runMode) { return super.isStopped(runMode); } /** Returns true if at least one service in the group are running. */ boolean isOneRunning(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (child.isRunning(runMode)) { return true; } } return false; } /** Returns true if all services in the group are running. */ @Override public boolean isRunning(final Application.RunMode runMode) { for (final ServiceInfo child : getSubServices()) { if (!child.isRunning(runMode)) { return false; } } return true; } /** * Returns whether the specified parameter or any of the parameters * have changed. If group does not have any services, its changes * cannot by applied. */ @Override public Check checkResourceFields(final String param, final String[] params) { return checkResourceFields(param, params, false, false); } /** * Returns whether all the parameters are correct. If param is null, * all paremeters will be checked, otherwise only the param, but other * parameters will be checked only in the cache. This is good if only * one value is changed and we don't want to check everything. */ Check checkResourceFields(final String param, final String[] params, final boolean fromServicesInfo, final boolean fromCloneInfo) { final List<String> incorrect = new ArrayList<String>(); final List<String> changed = new ArrayList<String>(); final Check check = new Check(incorrect, changed); check.addCheck(super.checkResourceFields(param, params, fromServicesInfo, fromCloneInfo, true)); boolean hasSevices = false; for (final ServiceInfo child : getSubServices()) { check.addCheck(child.checkResourceFields(null, child.getParametersFromXML(), fromServicesInfo, fromCloneInfo, true)); hasSevices = true; } if (!hasSevices) { incorrect.add("no services"); } return check; } /** Update menus with positions and calles their update methods. */ @Override public void updateMenus(final Point2D pos) { super.updateMenus(pos); if (!mainData.isSlow()) { for (final ServiceInfo child : getSubServices()) { child.updateMenus(pos); } } } @Override public ImageIcon getCategoryIcon(final Application.RunMode runMode) { if (getBrowser().allHostsWithoutClusterStatus() || !isOneRunning(runMode)) { return ServiceInfo.SERVICE_STOPPED_ICON_SMALL; } return ServiceInfo.SERVICE_RUNNING_ICON_SMALL; } /** Revert all values. */ @Override public void revert() { super.revert(); for (final ServiceInfo child : getSubServices()) { if (child.checkResourceFields(null, child.getParametersFromXML(), true, false, false).isChanged()) { child.revert(); } } } /** Return copy of the group services. */ @Override public List<ServiceInfo> getSubServices() { mGroupServiceReadLock.lock(); try { return Collections.unmodifiableList(new ArrayList<ServiceInfo>(groupServices)); } finally { mGroupServiceReadLock.unlock(); } } }