/* * Copyright (c) 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.util; import java.util.ArrayList; import java.util.List; /** * Implements a recursive descent parser for the drill-down text * Typical examples: * virtual-volume: dd_V01758738113-5-47ec7c4fa123_V11498275522-a-6b81e8590ae3_vol (cluster-1) distributed-device: dd_V01758738113-5-47ec7c4fa123_V11498275522-a-6b81e8590ae3 distributed-device-component: device_V01758738113-5-47ec7c4fa123 (cluster-1) extent: extent_V01758738113-5-47ec7c4fa123_1 storage-volume: V01758738113-5-47ec7c4fa123 (blocks: 0 - 524287) distributed-device-component: device_V11498275522-a-6b81e8590ae3 (cluster-2) extent: extent_V11498275522-a-6b81e8590ae3_1 storage-volume: V11498275522-a-6b81e8590ae3 (blocks: 0 - 524287) * virtual-volume: device_VAPM00144755987-c50c5200cd2_vol (cluster-1) local-device: device_VAPM00144755987-c50c5200cd2 (cluster-1) extent: extent_VAPM00144755987-c50c5200cd2_1 storage-volume: VAPM00144755987-c50c5200cd2 (blocks: 0 - 262399) * virtual-volume: device_VAPM00140801303-00218_vol (cluster-1) local-device: device_VAPM00140801303-00218 (cluster-1) local-device-component: device_VAPM00140801303-002182016Jul07_163513 extent: extent_VAPM00140801303-00218_1 storage-volume: VAPM00140801303-00218 (blocks: 0 - 262143) local-device-component: device_VAPM00140844981-00512 extent: extent_VAPM00140844981-00512_1 storage-volume: VAPM00140844981-00512 (blocks: 0 - 262143) */ public class VPlexDrillDownParser { public enum NodeType { VIRT("virtual-volume"), DIST("distributed-device:"), DISTCOMP("distributed-device-component:"), LOCAL("local-device:"), LOCALCOMP("local-device-component"), EXT("extent:"), SVOL("storage-volume:"); // The first part of the line matches "match" private String match; NodeType(String match) { this.match = match; } public String getMatch() { return match; } }; public class Node { private NodeType type; private String arg1; private String arg2; private List<Node> children = new ArrayList<Node>(); Node(NodeType type, String [] lineArgs) { this.type = type; if (lineArgs.length > 1) { setArg1(lineArgs[1]); } if (lineArgs.length > 2) { setArg2(lineArgs[2]); } } public String getArg1() { return arg1; } public void setArg1(String arg1) { this.arg1 = new String(arg1); } public String getArg2() { return arg2; } public void setArg2(String arg2) { this.arg2 = new String(arg2); if (this.arg2.startsWith("(") && this.arg2.endsWith(")")) { this.arg2 = this.arg2.substring(1, arg2.length()-1); } } public NodeType getType() { return type; } public List<Node> getChildren() { return children; } /** Returns all the nodes of a given type in the current subtree. * @param type NodeType to be matched * @return list of Nodes matching the requested type */ public List<Node> getNodesOfType(NodeType type) { List<Node> matchingNodes = new ArrayList<Node>(); getNodesOfType(matchingNodes, type); return matchingNodes; } void getNodesOfType(List<Node> matchingNodes, NodeType type) { if (this.getType() == type) { matchingNodes.add(this); } for (Node child : getChildren()) { child.getNodesOfType(matchingNodes, type); } } } private int currentLine = 0; // drill-down text split up by lines private String[] lines; // arguments within the current line private String[] lineargs; public VPlexDrillDownParser(String drillDownString) { if (drillDownString == null) { return; } lines = drillDownString.split("\n"); for (int i = 0; i < lines.length; i++) { lines[i] = lines[i].trim(); } } private String next() { currentLine++; return line(); } private String line() { if (currentLine < lines.length) { lineargs = lines[currentLine].split(" "); return lines[currentLine]; } else return null; } public Node parse() { // We don't care about virtual volume if present, skip it if (line().startsWith(NodeType.VIRT.getMatch())) { currentLine++; } if (line().startsWith(NodeType.DIST.getMatch())) { return dist(); } else if (line().startsWith(NodeType.LOCAL.getMatch())){ return local(); } return null; } /** * Parses a distributed node. arg1 is the device name, arg2 is the cluster. * @return Node of type DIST; line pointer at next line */ private Node dist() { Node node = new Node(NodeType.DIST, lineargs); next(); while(line() != null && line().startsWith(NodeType.DISTCOMP.getMatch())) { node.getChildren().add(distcomp()); } return node; } /** * Parses a distributed component. Used only for telling which cluster the * subtree will be for. arg1 is device name, arg2 is cluster * @return Node of type DISTCOMP; currentLine at next line */ private Node distcomp() { Node node = new Node(NodeType.DISTCOMP, lineargs); while (next() != null && line().startsWith(NodeType.EXT.getMatch())) { node.getChildren().add(extent()); } return node; } /** * Parses a local node. arg1 is device name, arg2 is cluster. * @return Node of type LOCAL; currentLine at next line */ private Node local() { Node node = new Node(NodeType.LOCAL, lineargs); while (next() != null && (line().startsWith(NodeType.LOCALCOMP.getMatch()) || line().startsWith(NodeType.EXT.getMatch()) ) ) { if (line().startsWith(NodeType.LOCALCOMP.getMatch())) { // We ignore local device components for now as they have no additional information next(); } node.getChildren().add(extent()); } return node; } /** * We don't care to receive EXTENT nodes.. pass through the underling SVOLS. * @return Node of type SVOL, or null; currentLine at returned node */ private Node extent() { if (next() != null && line().startsWith(NodeType.SVOL.getMatch())) { return svol(); } return null; } /** * Parses StorageVolume node. arg1 is volume name, arg2 is cluster. * @return Node of type SVOL; currentLine at returned node */ private Node svol() { Node node = new Node(NodeType.SVOL, lineargs); return node; } }