/* * This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH * written by Rasto Levrinc. * * Copyright (C) 2009, LINBIT HA-Solutions GmbH. * Copyright (C) 2011-2012, Rastislav Levrinc. * * RBD 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.host.ui; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; import javax.swing.ImageIcon; import javax.swing.tree.DefaultMutableTreeNode; import com.google.common.eventbus.Subscribe; import lcmc.ClusterEventBus; import lcmc.cluster.ui.ClusterBrowser; import lcmc.cluster.ui.resource.ClusterViewFactory; import lcmc.common.domain.AccessMode; import lcmc.cluster.domain.Cluster; import lcmc.common.ui.Browser; import lcmc.common.ui.main.ProgressIndicator; import lcmc.common.ui.treemenu.TreeMenuController; import lcmc.common.ui.utils.SwingUtils; import lcmc.drbd.ui.DrbdGraph; import lcmc.event.BlockDevicesChangedEvent; import lcmc.event.FileSystemsChangedEvent; import lcmc.event.NetInterfacesChangedEvent; import lcmc.host.domain.Host; import lcmc.drbd.domain.BlockDevice; import lcmc.drbd.domain.NetInterface; import lcmc.common.ui.CmdLog; import lcmc.common.ui.CategoryInfo; import lcmc.cluster.ui.resource.FSInfo; import lcmc.common.ui.Info; import lcmc.crm.ui.resource.HostInfo; import lcmc.drbd.ui.resource.BlockDevInfo; import lcmc.drbd.ui.resource.HostDrbdInfo; import lcmc.common.domain.EnablePredicate; import lcmc.common.ui.utils.MenuAction; import lcmc.common.ui.utils.MenuFactory; import lcmc.common.ui.utils.MyMenu; import lcmc.common.ui.utils.MyMenuItem; import lcmc.common.domain.util.Tools; import lcmc.cluster.service.ssh.ExecCommandConfig; /** * This class holds host resource data in a tree. It shows panels that allow * to edit data of resources, services etc., hosts and clusters. * Every resource has its Info object, that accessible through the tree view. */ @Named public class HostBrowser extends Browser { public static final ImageIcon HOST_ICON = Tools.createImageIcon(Tools.getDefault("HostBrowser.HostIconSmall")); public static final ImageIcon HOST_ON_ICON = Tools.createImageIcon( Tools.getDefault("HostBrowser.HostOnIconSmall")); public static final ImageIcon HOST_OFF_ICON = Tools.createImageIcon( Tools.getDefault("HostBrowser.HostOffIconSmall")); public static final ImageIcon HOST_ON_ICON_LARGE = Tools.createImageIcon( Tools.getDefault("HostBrowser.HostOnIcon")); public static final ImageIcon HOST_OFF_ICON_LARGE = Tools.createImageIcon( Tools.getDefault("HostBrowser.HostOffIcon")); public static final ImageIcon HOST_ICON_LARGE = Tools.createImageIcon(Tools.getDefault("HostBrowser.HostIcon")); public static final ImageIcon HOST_REMOVE_ICON = Tools.createImageIcon(Tools.getDefault("HostBrowser.RemoveIcon")); /** * Small host in cluster icon (right side). */ public static final ImageIcon HOST_IN_CLUSTER_ICON_RIGHT_SMALL = Tools.createImageIcon(Tools.getDefault("HostBrowser.HostInClusterIconRightSmall")); private DefaultMutableTreeNode netInterfacesNode; private DefaultMutableTreeNode blockDevicesNode; private DefaultMutableTreeNode fileSystemsNode; /** * List of used network interface ports. */ private final Collection<String> usedPorts = new HashSet<String>(); private final Collection<String> usedProxyPorts = new HashSet<String>(); private Host host; @Inject private HostInfo hostInfo; /** * Host info object of the host in drbd view of this browser. */ @Inject private HostDrbdInfo hostDrbdInfo; /** * Map of block devices and their info objects. */ private final Map<BlockDevice, BlockDevInfo> blockDevInfos = new LinkedHashMap<BlockDevice, BlockDevInfo>(); private final ReadWriteLock mBlockDevInfosLock = new ReentrantReadWriteLock(); private final Lock mBlockDevInfosReadLock = mBlockDevInfosLock.readLock(); private final Lock mBlockDevInfosWriteLock = mBlockDevInfosLock.writeLock(); private DefaultMutableTreeNode treeTop; @Inject private ProgressIndicator progressIndicator; @Inject private Provider<BlockDevInfo> blockDevInfoFactory; @Inject private SwingUtils swingUtils; @Inject private MenuFactory menuFactory; @Inject private Provider<CmdLog> cmdLogProvider; @Resource(name="categoryInfo") private CategoryInfo netInterfacesCategory; @Resource(name="categoryInfo") private CategoryInfo blockDevicesCategory; @Resource(name="categoryInfo") private CategoryInfo fileSystemsCategory; @Inject private TreeMenuController treeMenuController; @Inject private ClusterEventBus clusterEventBus; @Inject private ClusterViewFactory clusterViewFactory; public void init(final Host host) { this.host = host; hostInfo.init(host, this); hostDrbdInfo.init(host, this); treeTop = treeMenuController.createMenuTreeTop(hostInfo); swingUtils.invokeInEdt(new Runnable() { @Override public void run() { initHostResources(); } }); clusterEventBus.register(this); } public HostInfo getHostInfo() { return hostInfo; } public Host getHost() { return host; } public DefaultMutableTreeNode getTreeTop() { return treeTop; } public HostDrbdInfo getHostDrbdInfo() { return hostDrbdInfo; } public void initHostResources() { netInterfacesCategory.init(Tools.getString("HostBrowser.NetInterfaces"), this); netInterfacesNode = treeMenuController.createMenuItem(treeTop, netInterfacesCategory); /* block devices */ blockDevicesCategory.init(Tools.getString("HostBrowser.BlockDevices"), this); blockDevicesNode = treeMenuController.createMenuItem(treeTop, blockDevicesCategory); /* file systems */ fileSystemsCategory.init(Tools.getString("HostBrowser.FileSystems"), this); fileSystemsNode = treeMenuController.createMenuItem(treeTop, fileSystemsCategory); } public ClusterBrowser getClusterBrowser() { final Cluster c = host.getCluster(); if (c == null) { return null; } return c.getBrowser(); } @Subscribe public void updateFileSystemsNode(final FileSystemsChangedEvent event) { if (event.getHost() != host) { return; } final Set<String> fileSystems = event.getFileSystems(); treeMenuController.removeChildren(fileSystemsNode); for (final String fileSystem : fileSystems) { final FSInfo fileSystemInfo = clusterViewFactory.createFileSystemView(fileSystem, this); treeMenuController.createMenuItem(fileSystemsNode, fileSystemInfo); } treeMenuController.reloadNode(fileSystemsNode, false); } @Subscribe public void updateBlockDevicesNodes(final BlockDevicesChangedEvent event) { if (event.getHost() != host) { return; } final Collection<BlockDevice> blockDevices = event.getBlockDevices(); mBlockDevInfosWriteLock.lock(); boolean changed = false; try { final Map<BlockDevice, BlockDevInfo> oldBlockDevices = new HashMap<BlockDevice, BlockDevInfo>(blockDevInfos); if (oldBlockDevices.size() != blockDevices.size()) { changed = true; } blockDevInfos.clear(); for (final BlockDevice blockDevice : blockDevices) { final BlockDevInfo blockDevInfo; if (oldBlockDevices.containsKey(blockDevice)) { blockDevInfo = oldBlockDevices.get(blockDevice); ((BlockDevice) blockDevInfo.getResource()).updateFrom(blockDevice); blockDevInfo.updateInfo(); } else { changed = true; blockDevInfo = blockDevInfoFactory.get(); blockDevInfo.init(blockDevice.getName(), blockDevice, this); } blockDevInfos.put(blockDevice, blockDevInfo); } } finally { mBlockDevInfosWriteLock.unlock(); } if (changed) { mBlockDevInfosWriteLock.lock(); try { treeMenuController.removeChildren(blockDevicesNode); for (final Map.Entry<BlockDevice, BlockDevInfo> bdEntry : blockDevInfos.entrySet()) { final BlockDevInfo bdi = bdEntry.getValue(); treeMenuController.createMenuItem(blockDevicesNode, bdi); } treeMenuController.reloadNode(blockDevicesNode, false); } finally { mBlockDevInfosWriteLock.unlock(); } } } @Subscribe public void onNetInterfacesChanged(final NetInterfacesChangedEvent event) { if (event.getHost() != host) { return; } final Collection<NetInterface> netInterfaces = event.getNetInterfaces(); treeMenuController.removeChildren(netInterfacesNode); for (final NetInterface netInterface : netInterfaces) { final Info netInfo = clusterViewFactory.getNetView(netInterface, this); treeMenuController.createMenuItem(netInterfacesNode, netInfo); } treeMenuController.reloadNode(netInterfacesNode, false); } public Set<BlockDevInfo> getSortedBlockDevInfos() { mBlockDevInfosReadLock.lock(); try { return new TreeSet<BlockDevInfo>(blockDevInfos.values()); } finally { mBlockDevInfosReadLock.unlock(); } } /** * Adds advanced submenu to the host menus in drbd and pacemaker view. */ public void addAdvancedMenu(final MyMenu submenu) { if (submenu.getItemCount() > 0) { return; } /* Command log */ final MyMenuItem cmdLogMenuItem = menuFactory.createMenuItem( Tools.getString("HostBrowser.CmdLog"), Info.LOGFILE_ICON, "", new AccessMode(AccessMode.ADMIN, AccessMode.NORMAL), new AccessMode(AccessMode.ADMIN, AccessMode.NORMAL)) .enablePredicate(new EnablePredicate() { @Override public String check() { if (!host.isConnected()) { return Host.NOT_CONNECTED_MENU_TOOLTIP_TEXT; } return null; } }) .addAction(new MenuAction() { @Override public void run(final String text) { final CmdLog cmdLog = cmdLogProvider.get(); cmdLog.init(host); cmdLog.showDialog(); } }); submenu.add(cmdLogMenuItem); /* panic */ final MyMenuItem panicMenuItem = menuFactory.createMenuItem( Tools.getString("HostBrowser.MakeKernelPanic") + host.getName(), null, new AccessMode(AccessMode.GOD, AccessMode.NORMAL), new AccessMode(AccessMode.ADMIN, AccessMode.NORMAL)) .enablePredicate(new EnablePredicate() { @Override public String check() { if (!host.isConnected()) { return Host.NOT_CONNECTED_MENU_TOOLTIP_TEXT; } return null; } }) .addAction(new MenuAction() { @Override public void run(final String text) { final String hostName = host.getName(); final String command = "MakeKernelPanic"; progressIndicator.startProgressIndicator(hostName, host.getDistString(command)); host.execCommand(new ExecCommandConfig().commandString(command)); progressIndicator.stopProgressIndicator(hostName, host.getDistString(command)); } }); submenu.add(panicMenuItem); /* reboot */ final MyMenuItem rebootMenuItem = menuFactory.createMenuItem( Tools.getString("HostBrowser.MakeKernelReboot") + host.getName(), null, new AccessMode(AccessMode.GOD, AccessMode.NORMAL), new AccessMode(AccessMode.ADMIN, AccessMode.NORMAL)) .enablePredicate(new EnablePredicate() { @Override public String check() { if (!host.isConnected()) { return Host.NOT_CONNECTED_MENU_TOOLTIP_TEXT; } return null; } }) .addAction(new MenuAction() { @Override public void run(final String text) { final String hostName = host.getName(); final String command = "MakeKernelReboot"; progressIndicator.startProgressIndicator(hostName, host.getDistString(command)); host.execCommand(new ExecCommandConfig().commandString(command)); progressIndicator.stopProgressIndicator(hostName, host.getDistString(command)); } }); submenu.add(rebootMenuItem); } public String getHostToolTip(final Host host) { final StringBuilder hostToolTip = new StringBuilder(80); hostToolTip.append("<b>").append(host.getName()).append("</b>"); final ClusterBrowser b = getClusterBrowser(); if (b != null && b.isRealDcHost(host)) { hostToolTip.append(" (designated co-ordinator)"); } if (!host.isConnected()) { hostToolTip.append('\n'); hostToolTip.append(Tools.getString("ClusterBrowser.Host.Disconnected")); } else if (!host.getHostParser().isDrbdStatusOk() && !host.isCrmStatusOk()) { hostToolTip.append('\n'); hostToolTip.append(Tools.getString("ClusterBrowser.Host.Offline")); } hostToolTip.append(host.getDrbdInfoAboutInstallation()); hostToolTip.append(host.getPacemakerInfo()); return hostToolTip.toString(); } public DrbdGraph getDrbdGraph() { final ClusterBrowser b = getClusterBrowser(); if (b == null) { return null; } return b.getDrbdGraph(); } /** * Returns a list of used network interface ports. */ public Collection<String> getUsedPorts() { return usedPorts; } public Collection<String> getUsedProxyPorts() { return usedProxyPorts; } public void lockBlockDevInfosRead() { mBlockDevInfosReadLock.lock(); } public void unlockBlockDevInfosRead() { mBlockDevInfosReadLock.unlock(); } }