/* * 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. * * 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.drbd.ui; import edu.uci.ics.jung.graph.DirectedSparseGraph; import edu.uci.ics.jung.visualization.util.VertexShapeFactory; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; import javax.swing.ImageIcon; import javax.swing.JPopupMenu; import lcmc.common.ui.MainPanel; import lcmc.common.ui.ResourceGraph; import lcmc.common.ui.utils.SwingUtils; import lcmc.drbd.ui.resource.BlockDevInfo; import lcmc.drbd.ui.resource.HostDrbdInfo; import lcmc.drbd.ui.resource.MultiSelectionInfo; import lcmc.drbd.ui.resource.VolumeInfo; import lcmc.common.domain.Application; import lcmc.common.domain.ColorText; import lcmc.cluster.ui.ClusterBrowser; import lcmc.host.ui.HostBrowser; import lcmc.host.domain.Host; import lcmc.drbd.domain.BlockDevice; import lcmc.common.ui.Info; import lcmc.common.domain.util.Tools; /** * This class creates graph and provides methods to add new block device * vertices and drbd volume edges, remove or modify them. */ @Named public class DrbdGraph extends ResourceGraph { /** Horizontal step in pixels by which the block devices are drawn in the graph. */ private static final int BD_STEP_Y = 60; /** Y position of the host. */ private static final int HOST_Y_POS = 40; /** Vertical step in pixels by which the hosts are drawn in the graph. */ private static final int HOST_STEP_X = 280; private static final int VERTEX_SIZE_BD = 200; private static final int VERTEX_SIZE_HOST = 150; private static final int HOST_VERTEX_HEIGHT = 50; /** Height of the block device vertices. */ private static final int VERTEX_HEIGHT = 50; /** Maximum length of the label in the vertex, after which the string will be cut. */ private static final int MAX_VERTEX_STRING_LENGTH = 18; /** Postion offset of block devices from the host x position. */ private static final int BD_X_OFFSET = 15; /** Minimum vertical position. */ private static final int MIN_Y_POS = 20; /** Maximum horizontal position. */ private static final int MAX_X_POS = 2600; /** Maximum vertical position. */ private static final int MAX_Y_POS = 2600; /** Map from vertex to host. */ private final Map<Vertex, HostDrbdInfo> vertexToHostMap = new LinkedHashMap<Vertex, HostDrbdInfo>(); /** Map from host to vertex. */ private final Map<HostDrbdInfo, Vertex> hostToVertexMap = new LinkedHashMap<HostDrbdInfo, Vertex>(); /** Map from block device info object to vertex. */ private final Map<BlockDevInfo, Vertex> bdiToVertexMap = new LinkedHashMap<BlockDevInfo, Vertex>(); /** Map from block device to vertex. */ private final Map<BlockDevice, Vertex> blockDeviceToVertexMap = new LinkedHashMap<BlockDevice, Vertex>(); /** Map from host to the list of block devices. */ private final Map<HostDrbdInfo, List<Vertex>> hostBDVerticesMap = new LinkedHashMap<HostDrbdInfo, List<Vertex>>(); /** Map from graph edge to the drbd volume info object. */ private final Map<Edge, VolumeInfo> edgeToDrbdVolumeMap = new LinkedHashMap<Edge, VolumeInfo>(); /** Map from drbd volume info object to the graph edge. */ private final Map<VolumeInfo, Edge> drbdVolumeToEdgeMap = new LinkedHashMap<VolumeInfo, Edge>(); @Inject private Provider<MultiSelectionInfo> multiSelectionInfoProvider; private MultiSelectionInfo multiSelectionInfo; /** The first X position of the host. */ private int hostDefaultXPos = 10; @Inject private MainPanel mainPanel; @Inject private SwingUtils swingUtils; @Override public void initGraph(final ClusterBrowser clusterBrowser) { super.initGraph(clusterBrowser); super.initGraph(new DirectedSparseGraph<Vertex, Edge>()); } private boolean isVertexBlockDevice(final Vertex v) { return vertexToHostMap.get(v) != getInfo(v); } /** Adds host with all its block devices to the graph. If it is there * already fix the positions of the block devices. */ public void addHost(final HostDrbdInfo hostDrbdInfo) { Vertex v = getVertex(hostDrbdInfo); if (v == null) { /* add host vertex */ v = new Vertex(); somethingChanged(); putInfoToVertex(hostDrbdInfo, v); vertexToHostMap.put(v, hostDrbdInfo); hostToVertexMap.put(hostDrbdInfo, v); putVertexToInfo(v, hostDrbdInfo); Point2D hostPos = getSavedPosition(hostDrbdInfo); if (hostPos == null) { hostPos = new Point2D.Double(hostDefaultXPos + VERTEX_SIZE_HOST / 2, HOST_Y_POS); hostDefaultXPos += HOST_STEP_X; } getVertexLocations().put(v, hostPos); putVertexLocations(); lockGraph(); getGraph().addVertex(v); unlockGraph(); } /* add block devices vertices */ final Host host = hostDrbdInfo.getHost(); final Point2D hostPos = getVertexLocations().get(v); putVertexLocations(); final double hostXPos = hostPos.getX() - VERTEX_SIZE_HOST / 2; final double hostYPos = hostPos.getY(); int devYPos = (int) hostYPos + BD_STEP_Y; List<Vertex> vertexList = hostBDVerticesMap.get(hostDrbdInfo); List<Vertex> oldVertexList = null; if (vertexList == null) { vertexList = new ArrayList<Vertex>(); hostBDVerticesMap.put(hostDrbdInfo, vertexList); } else { oldVertexList = new ArrayList<Vertex>(vertexList); } final Set<BlockDevInfo> blockDevInfos = host.getBrowser().getSortedBlockDevInfos(); if (oldVertexList != null) { for (final Vertex vertex : oldVertexList) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(vertex); if (bdi == null) { continue; } if (!blockDevInfos.contains(bdi)) { /* removing */ final Vertex bdv = bdiToVertexMap.get(bdi); final VolumeInfo dvi = bdi.getDrbdVolumeInfo(); if (dvi != null) { removeDrbdVolume(dvi); dvi.getDrbdResourceInfo().removeDrbdVolumeFromHashes(dvi); } swingUtils.invokeLater(new Runnable() { @Override public void run() { lockGraph(); getGraph().removeVertex(bdv); unlockGraph(); } }); removeInfo(bdv); removeVertex(bdi); getVertexToMenus().remove(bdv); bdiToVertexMap.remove(bdi); blockDeviceToVertexMap.remove(bdi.getBlockDevice()); vertexToHostMap.remove(bdv); vertexList.remove(bdv); somethingChanged(); } } } BlockDevInfo prevBdi = null; for (final BlockDevInfo bdi : blockDevInfos) { stopAnimation(bdi); Vertex bdv = null; if (!blockDeviceToVertexMap.containsKey(bdi.getBlockDevice())) { bdv = new Vertex(); somethingChanged(); bdiToVertexMap.put(bdi, bdv); blockDeviceToVertexMap.put(bdi.getBlockDevice(), bdv); putVertexToInfo(bdv, bdi); putInfoToVertex(bdi, bdv); vertexToHostMap.put(bdv, hostDrbdInfo); vertexList.add(bdv); // TODO: get saved position is disabled at the moment, // because it does more harm than good at the moment. } if (bdv == null) { bdv = blockDeviceToVertexMap.get(bdi.getBlockDevice()); } if (prevBdi != null && bdi.getBlockDevice().isVolumeGroupOnPhysicalVolume() && bdi.getBlockDevice().getVgOnPhysicalVolume().equals( prevBdi.getBlockDevice().getVgOnPhysicalVolume())) { devYPos -= 8; } else if (prevBdi != null && (!bdi.getBlockDevice().isDrbd() || !prevBdi.getBlockDevice().isDrbd())) { devYPos -= 4; } else if (prevBdi != null && bdi.getBlockDevice().isDrbd() && prevBdi.getBlockDevice().isDrbd() && bdi.getDrbdVolumeInfo().getDrbdResourceInfo() == prevBdi.getDrbdVolumeInfo().getDrbdResourceInfo()) { devYPos -= 6; } final Point2D pos = new Point2D.Double(hostXPos + BD_X_OFFSET + VERTEX_SIZE_BD / 2, devYPos); devYPos += BD_STEP_Y; getVertexLocations().put(bdv, pos); putVertexLocations(); getLayout().setLocation(bdv, pos); if (bdv != null) { lockGraph(); getGraph().addVertex(bdv); unlockGraph(); } prevBdi = bdi; } } /** Scale and add hosts if they appeared. */ @Override public void scale() { for (final HostDrbdInfo hostDrbdInfo : hostBDVerticesMap.keySet()) { addHost(hostDrbdInfo); } super.scale(); } /** Removes drbd volume from the graph. */ public void removeDrbdVolume(final VolumeInfo dvi) { final Edge e = drbdVolumeToEdgeMap.get(dvi); if (e == null) { return; } e.reset(); edgeToDrbdVolumeMap.remove(e); drbdVolumeToEdgeMap.remove(dvi); try { lockGraph(); getGraph().removeEdge(e); unlockGraph(); } catch (final Exception ignore) { unlockGraph(); /* ignore */ } } /** * Returns an icon for vertex, depending on if it is host or block device, * if it is started or stopped and so on. */ @Override protected List<ImageIcon> getIconsForVertex(final Vertex v, final Application.RunMode runMode) { final List<ImageIcon> icons = new ArrayList<ImageIcon>(); if (isVertexBlockDevice(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi == null) { return icons; } if (bdi.getBlockDevice().isDrbd()) { icons.add(BlockDevInfo.HARDDISK_DRBD_ICON_LARGE); } else { icons.add(BlockDevInfo.HARDDISK_ICON_LARGE); } if (bdi.isDiskless(runMode)) { icons.add(BlockDevInfo.NO_HARDDISK_ICON_LARGE); return icons; } else { return icons; } } else { final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi == null) { return null; } if (hi.getHost().isDrbdStatusOk() && hi.getHost().isDrbdLoaded()) { icons.add(HostBrowser.HOST_ON_ICON_LARGE); } else { icons.add(HostBrowser.HOST_ICON_LARGE); } return icons; } } /** * Returns label for drbd volume edge. If it is longer than 10 * characters, it is shortened. */ @Override protected String getLabelForEdgeStringer(final Edge edge) { final VolumeInfo dvi = edgeToDrbdVolumeMap.get(edge); if (dvi != null && dvi.getName() != null && dvi.getDrbdResourceInfo() != null) { final Vertex source = edge.getSource(); final Vertex dest = edge.getDest(); final BlockDevInfo sourceBDI = (BlockDevInfo) getInfo(source); if (sourceBDI == null) { return ""; } final BlockDevInfo destBDI = (BlockDevInfo) getInfo(dest); if (destBDI == null) { return ""; } final BlockDevice sourceBD = sourceBDI.getBlockDevice(); final BlockDevice destBD = destBDI.getBlockDevice(); final Application.RunMode runMode = getRunMode(); if (!destBDI.isConnected(runMode)) { if (sourceBDI.isWFConnection(runMode) && !destBDI.isWFConnection(runMode)) { edge.setDirection(dest, source); swingUtils.invokeLater(new Runnable() { @Override public void run() { repaint(); } }); } } else if (!sourceBD.isPrimary() && destBD.isPrimary()) { edge.setDirection(dest, source); swingUtils.invokeLater(new Runnable() { @Override public void run() { repaint(); } }); } final StringBuilder l = new StringBuilder(dvi.getNameForGraph()); final Map<Vertex, Point2D> vl = getVertexLocations(); final Point2D sp = vl.get(source); final Point2D dp = vl.get(dest); putVertexLocations(); final int len = (int) Math.sqrt(Math.pow(sp.getX() - dp.getX(), 2) + Math.pow(sp.getY() - dp.getY(), 2)); final int maxLen = (len - 200) / 7; if (l.length() > maxLen) { l.delete(0, l.length() - maxLen + 3); l.insert(0, "..."); } if (dvi.isSyncing()) { String syncedProgress = dvi.getSyncedProgress(); if (syncedProgress == null) { syncedProgress = "?.?"; } final double sourceX = getLayout().transform(source).getX(); final double destX = getLayout().transform(dest).getX(); if (sourceBD.isPausedSync() || destBD.isPausedSync()) { l.append(" (").append(syncedProgress).append("% paused)"); } else if (sourceBD.isSyncSource() && sourceX < destX || destBD.isSyncSource() && sourceX > destX) { l.append(" (").append(syncedProgress).append("% \u2192)"); /* -> */ } else { l.append(" (\u2190 ").append(syncedProgress).append("%)"); /* <- */ } } else if (dvi.isSplitBrain()) { l.append(" (split-brain)"); } else if (!dvi.isConnected(runMode)) { l.append(" (disconnected)"); } else if (dvi.isVerifying()) { l.append(" (verify)"); } return l.toString(); } return null; } /** Small text that appears above the icon. */ @Override protected String getIconText(final Vertex v, final Application.RunMode runMode) { if (isVertexBlockDevice(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null) { return bdi.getIconTextForGraph(runMode); } } else { final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi != null) { return hi.getIconTextForDrbdGraph(runMode); } } return null; } /** Small text that appears in the right corner. */ @Override protected ColorText getRightCornerText(final Vertex v, final Application.RunMode runMode) { if (isVertexBlockDevice(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null) { return bdi.getRightCornerTextForDrbdGraph(runMode); } } else { final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi != null) { return hi.getRightCornerTextForDrbdGraph(runMode); } } return null; } /** Small text that appears down. */ @Override protected ColorText[] getSubtexts(final Vertex v, final Application.RunMode runMode) { if (isVertexBlockDevice(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null && bdi.getBlockDevice().isDrbd() && bdi.getBlockDevice().getConnectionState() != null && bdi.getBlockDevice().getDiskState() != null) { final String connState = bdi.getBlockDevice().getConnectionState(); final String diskState = bdi.getBlockDevice().getDiskState(); String diskStateOther = null; final BlockDevInfo oBdi = bdi.getOtherBlockDevInfo(); if (oBdi != null && !diskState.equals(oBdi.getBlockDevice().getDiskStateOther())) { diskStateOther = oBdi.getBlockDevice().getDiskStateOther(); } Color color = null; Color textColor = Color.BLACK; final String proxyState = bdi.getProxyStateForGraph(runMode); if ("StandAlone".equals(connState) || !"UpToDate".equals(diskState) || (proxyState != null && !BlockDevInfo.PROXY_UP.equals(proxyState))) { color = Color.RED; textColor = Color.WHITE; } return new ColorText[]{ new ColorText(Tools.join(" / ", new String[]{connState, diskState, diskStateOther, proxyState}), color, textColor)}; } } else { final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi != null) { return hi.getSubtextsForDrbdGraph(runMode); } } return null; } /** * Returns label for block device vertex. If it is longer than 23 * characters, it is shortened. */ @Override public String getMainText(final Vertex v, final Application.RunMode runMode) { if (isVertexBlockDevice(v)) { String l; if (isVertexDrbd(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi == null) { return ""; } l = bdi.getDrbdVolumeInfo().getDevice(); } else { final Info info = getInfo(v); if (info == null) { return ""; } l = info.getMainTextForGraph(); } if (l.length() > MAX_VERTEX_STRING_LENGTH) { l = "..." + l.substring(l.length() - MAX_VERTEX_STRING_LENGTH + 3, l.length()); } return l; } else if (vertexToHostMap.containsKey(v)) { return vertexToHostMap.get(v).toString(); } else { return ""; } } /** Returns shape of the block device vertex. */ @Override protected Shape getVertexShape(final Vertex v, final VertexShapeFactory<Vertex> factory) { return factory.getRectangle(v); } /** Handles popup in when block device vertex is clicked. */ @Override protected void handlePopupVertex(final Vertex v, final List<Vertex> pickedV, final Point2D pos) { final Info info; if (pickedV.size() > 1) { info = multiSelectionInfo; } else if (isVertexBlockDevice(v)) { info = getInfo(v); } else { /* host */ info = getInfo(v); } if (info != null) { final JPopupMenu p = info.getPopup(); info.updateMenus(pos); showPopup(p, pos); } } /** Adds drbd volume edge to the graph. */ public void addDrbdVolume(final VolumeInfo dvi, final BlockDevInfo bdi1, final BlockDevInfo bdi2) { if (bdi1 != null && bdi2 != null) { final Vertex v1 = bdiToVertexMap.get(bdi1); final Vertex v2 = bdiToVertexMap.get(bdi2); lockGraph(); if (getGraph().findEdge(v1, v2) != null || getGraph().findEdge(v2, v1) != null) { unlockGraph(); return; } final Edge e = new Edge(v1, v2); getGraph().addEdge(e, v1, v2); unlockGraph(); edgeToDrbdVolumeMap.put(e, dvi); drbdVolumeToEdgeMap.put(dvi, e); } } /** Returns the source block device in a drbd connection. */ public BlockDevInfo getSource(final VolumeInfo dvi) { final Edge edge = drbdVolumeToEdgeMap.get(dvi); if (edge == null) { return null; } final Vertex source = edge.getSource(); return (BlockDevInfo) getInfo(source); } /** Returns the destination block device in a drbd connection. */ public BlockDevInfo getDest(final VolumeInfo dvi) { final Edge edge = drbdVolumeToEdgeMap.get(dvi); if (edge == null) { return null; } final Vertex dest = edge.getDest(); return (BlockDevInfo) getInfo(dest); } /** Picks vertex, that is associated with the specified info object. */ @Override public void pickInfo(final Info i) { final Edge e = drbdVolumeToEdgeMap.get(i); if (e == null) { super.pickInfo(i); } else { pickEdge(e); } } /** Is called after right click on the resource edge. */ @Override protected void handlePopupEdge(final Edge edge, final Point2D pos) { final VolumeInfo info = edgeToDrbdVolumeMap.get(edge); final JPopupMenu p = info.getPopup(); info.updateMenus(pos); showPopup(p, pos); } /** * Is called after right click on the background and it returns * background popup menu. */ @Override protected void handlePopupBackground(final Point2D pos) { final JPopupMenu p = getClusterBrowser().getGlobalInfo().getPopup(); getClusterBrowser().getGlobalInfo().updateMenus(pos); showPopup(p, pos); } /** * Picks vertex representig specified block device info object in the * graph. */ public void pickBlockDevice(final BlockDevInfo bdi) { final Vertex v = bdiToVertexMap.get(bdi); pickVertex(v); } /** Is called of a host is picked. Its terminal panel is set to view. */ private void pickHost(final Vertex v) { pickVertex(v); final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi == null) { return; } mainPanel.setTerminalPanel(hi.getHost().getTerminalPanel()); } /** Is called when one block device vertex was pressed. */ @Override protected void oneVertexPressed(final Vertex v) { if (isVertexBlockDevice(v)) { pickHost(v); final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi == null) { return; } getClusterBrowser().getGlobalInfo().setSelectedNode(bdi); getClusterBrowser().getGlobalInfo().selectMyself(); getClusterBrowser().setRightComponentInView(bdi); } else { pickHost(v); final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi == null) { return; } getClusterBrowser().setRightComponentInView(hi); } } /** Is called when block device vertex is released. */ @Override protected void vertexReleased(final Vertex v, final Point2D pos) { double x = pos.getX(); double y = pos.getY(); final double minPos = (getVertexWidth(v) - getDefaultVertexWidth(v)) / 2; x = x < minPos ? minPos : x; x = x > MAX_X_POS ? MAX_X_POS : x; y = y < MIN_Y_POS ? MIN_Y_POS : y; y = y > MAX_Y_POS ? MAX_Y_POS : y; pos.setLocation(x + (getDefaultVertexWidth(v) - getVertexWidth(v)) / 2, y); final Point2D loc = new Point2D.Double(x, y); getVertexLocations().put(v, pos); putVertexLocations(); getLayout().setLocation(v, loc); } /** * Returns whether block device belonging to this vertex is available for * drbd or not. Returns false if this is not a block device. */ private boolean isVertexAvailable(final Vertex v) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null) { return bdi.getBlockDevice().isAvailable(); } return false; } /** * Returns true if block device represented by specified vertex is * drbd device. */ private boolean isVertexDrbd(final Vertex v) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null) { return bdi.getBlockDevice().isDrbd(); } return false; } /** * Returns true if block device represented by specified vertex is * primary. */ private boolean isVertexPrimary(final Vertex v) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null) { return bdi.getBlockDevice().isPrimary(); } return false; } /** * Returns true if block device represented by specified vertex is * secondary. */ private boolean isVertexSecondary(final Vertex v) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null) { return bdi.getBlockDevice().isSecondary(); } return false; } /** * Is called when resource edge is pressed. It selects the asspociated * resource. */ @Override protected void oneEdgePressed(final Edge e) { final VolumeInfo dvi = edgeToDrbdVolumeMap.get(e); if (dvi != null) { dvi.selectMyself(); } } /** * Is called, when background of the graph is clicked. It deselects * selected node. */ @Override protected void backgroundClicked() { getClusterBrowser().getGlobalInfo().setSelectedNode(null); getClusterBrowser().getGlobalInfo().selectMyself(); } /** * Returns fill color as paint object for for specified block device * vertex. */ @Override protected Color getVertexFillColor(final Vertex v) { final HostDrbdInfo hi = vertexToHostMap.get(v); final Vertex hostVertex = getVertex(hi); if (v.equals(hostVertex)) { return hi.getHost().getDrbdColors()[0]; } else if (hi != null && (hi.getHost() == null || (!hi.getHost().isDrbdStatusOk() && hi.getHost().isDrbdLoaded()))) { return Tools.getDefaultColor("DrbdGraph.FillPaintUnknown"); } else { if (!isVertexDrbd(v)) { if (isVertexAvailable(v)) { return super.getVertexFillColor(v); } else { return Tools.getDefaultColor("DrbdGraph.FillPaintNotAvailable"); } } if (isVertexPrimary(v)) { return Tools.getDefaultColor("DrbdGraph.FillPaintPrimary"); } else if (isVertexSecondary(v)) { return Tools.getDefaultColor("DrbdGraph.FillPaintSecondary"); } else { return Tools.getDefaultColor("DrbdGraph.FillPaintUnknown"); } } } /** * Returns secondary gradient fill paint color for vertex v. If it is * a volume and there is previous volume don't show gradient. */ @Override protected Color getVertexFillSecondaryColor(final Vertex v) { if (!isVertexBlockDevice(v)) { return Color.WHITE; } final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi == null) { return Color.WHITE; } if (bdi.isFirstDrbdVolume()) { return Color.WHITE; } else { return getVertexFillColor(v); } } /** * Finds BlockDevice object on the specified host for block device * represented as a string and returns it. */ BlockDevice findBlockDevice(final String hostName, final String disk) { final BlockDevInfo bdi = findBlockDevInfo(hostName, disk); if (bdi == null) { return null; } return bdi.getBlockDevice(); } /** * Finds BlockDevInfo object on the specified host for block device * represented as a string and returns it. * TODO: move it to BlockDevInfo */ public BlockDevInfo findBlockDevInfo(final String hostName, final String disk) { HostDrbdInfo hi = null; for (final HostDrbdInfo h : hostBDVerticesMap.keySet()) { hi = h; if (hi.toString().equals(hostName)) { break; } } if (hi == null) { return null; } for (final Vertex v : hostBDVerticesMap.get(hi)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi == null) { continue; } if (disk.equals(bdi.getName()) || disk.equals(bdi.getBlockDevice().getDiskUuid()) || bdi.getBlockDevice().getDiskIds().contains(disk)) { return bdi; } } return null; } /** Returns tool tip when mouse is over a block device vertex. */ @Override public String getVertexToolTip(final Vertex v) { final Info i = getInfo(v); if (i == null) { return null; } return i.getToolTipForGraph(getRunMode()); } /** Returns tool tip when mouse is over a resource edge. */ @Override public String getEdgeToolTip(final Edge edge) { final VolumeInfo dvi = edgeToDrbdVolumeMap.get(edge); return dvi.getToolTipForGraph(getRunMode()); } /** * Returns whether arrow shoud be shown. It is shown on the edge from * primary to secondory, or from connected node to the disconnected or * none at all. */ @Override protected boolean showEdgeArrow(final Edge edge) { final BlockDevInfo sourceBDI = (BlockDevInfo) getInfo(edge.getSource()); final BlockDevInfo destBDI = (BlockDevInfo) getInfo(edge.getDest()); if (sourceBDI == null || destBDI == null) { return false; } final BlockDevice sourceBD = sourceBDI.getBlockDevice(); final BlockDevice destBD = destBDI.getBlockDevice(); final Application.RunMode runMode = getRunMode(); if (sourceBDI.isConnected(runMode) && sourceBD.isPrimary() != destBD.isPrimary()) { return true; } else if (sourceBDI.isWFConnection(runMode) ^ destBDI.isWFConnection(runMode)) { /* show arrow from wf connection */ return true; } return false; } /** * Returns the color of the edge, depending on if the drbds are connected * and so on. */ @Override protected Paint getEdgeDrawPaint(final Edge edge) { final VolumeInfo dvi = edgeToDrbdVolumeMap.get(edge); if (dvi != null && dvi.isConnected(getRunMode()) && !dvi.isSplitBrain()) { return super.getEdgeDrawPaint(edge); } else { return Tools.getDefaultColor("DrbdGraph.EdgeDrawPaintDisconnected"); } } /** * Returns paint for picked edge. It returns different colors if drbd is * disconnected. */ @Override protected Paint getEdgePickedPaint(final Edge edge) { final VolumeInfo dvi = edgeToDrbdVolumeMap.get(edge); if (dvi != null && dvi.isConnected(getRunMode()) && !dvi.isSplitBrain()) { return super.getEdgePickedPaint(edge); } else { return Tools.getDefaultColor("DrbdGraph.EdgeDrawPaintDisconnectedBrighter"); } } /** Returns id that is used for saving of the vertex positions to a file. */ @Override protected String getId(final Info i) { final Vertex v = getVertex(i); String hiId = ""; if (v != null) { final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi != null) { hiId = hi.getId(); } } return "dr=" + hiId + i.getId(); } @Override protected int getDefaultVertexWidth(final Vertex v) { if (isVertexBlockDevice(v)) { return VERTEX_SIZE_BD; } else { return VERTEX_SIZE_HOST; } } @Override protected int getDefaultVertexHeight(final Vertex v) { if (isVertexBlockDevice(v)) { return VERTEX_HEIGHT; } else { return HOST_VERTEX_HEIGHT; } } /** * Returns how much of the disk is used. * -1 for not used or not applicable. */ protected int getUsed(final Vertex v) { if (isVertexBlockDevice(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi == null) { return 0; } return bdi.getUsed(); } final HostDrbdInfo hi = vertexToHostMap.get(v); if (hi == null) { return 0; } else { return hi.getUsed(); } } /** This method draws how much of the vertex is used for something. */ @Override protected void drawInside(final Vertex v, final Graphics2D g2d, final double x, final double y, final Shape shape) { final double used = getUsed(v); final float height = (float) shape.getBounds().getHeight(); final float width = (float) shape.getBounds().getWidth(); if (isVertexBlockDevice(v)) { final BlockDevInfo bdi = (BlockDevInfo) getInfo(v); if (bdi != null && bdi.getBlockDevice().isDrbdMetaDisk()) { final Color[] colors = {null, null}; colors[1] = getVertexFillColor(blockDeviceToVertexMap.get( bdi.getBlockDevice().getMetaDiskOfBlockDevices().get(0))); drawInsideVertex(g2d, v, colors, x, y, height, width); } } else { final HostDrbdInfo hi = (HostDrbdInfo) getInfo(v); if (hi != null) { drawInsideVertex(g2d, v, hi.getHost().getDrbdColors(), x, y, height, width); } } final Application.RunMode runMode = getRunMode(); if (used > 0) { /** Show how much is used. */ final double freeWidth = width * (100 - used) / 100; g2d.setColor(new Color(255, 255, 255, 220)); g2d.fillRect((int) (x + width - freeWidth), (int) (y), (int) (freeWidth), (int) (height)); } if (isPicked(v)) { if (Application.isTest(runMode)) { g2d.setColor(Color.RED); } else { g2d.setColor(Color.BLACK); } } else { boolean pickedResource = false; if (Application.isTest(runMode)) { lockGraph(); for (final Edge e : getGraph().getInEdges(v)) { if (isPicked(e)) { pickedResource = true; break; } } if (!pickedResource) { for (final Edge e : getGraph().getOutEdges(v)) { if (isPicked(e)) { pickedResource = true; break; } } } unlockGraph(); } if (pickedResource) { g2d.setColor(Color.RED); } else { g2d.setColor(Color.WHITE); } } g2d.setStroke(new BasicStroke(1.5f)); g2d.draw(shape); } @Override protected boolean showHollowArrow(final Edge e) { final VolumeInfo dvi = edgeToDrbdVolumeMap.get(e); if (dvi == null) { return false; } return !dvi.isConnected(getRunMode()); } /** Select multiple elements. */ @Override protected void multiSelection() { final List<Info> selectedInfos = new ArrayList<Info>(); for (final Vertex v : getPickedVertices()) { final Info i = getInfo(v); if (i != null) { selectedInfos.add(i); } } multiSelectionInfo = multiSelectionInfoProvider.get(); multiSelectionInfo.init(selectedInfos, getClusterBrowser()); getClusterBrowser().setRightComponentInView(multiSelectionInfo); } public Map<VolumeInfo, Edge> getDrbdVolumeToEdgeMap() { return drbdVolumeToEdgeMap; } }