/** * Copyright 2016 LinkedIn Corp. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ package com.github.ambry.clustermap; import com.github.ambry.config.ClusterMapConfig; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An Ambry hardwareLayout consists of a set of {@link Datacenter}s. */ class HardwareLayout { private final String clusterName; private final long version; private final ArrayList<Datacenter> datacenters; private final long rawCapacityInBytes; private final long dataNodeCount; private final long diskCount; private final Map<HardwareState, Long> dataNodeInHardStateCount; private final Map<HardwareState, Long> diskInHardStateCount; private Logger logger = LoggerFactory.getLogger(getClass()); public HardwareLayout(JSONObject jsonObject, ClusterMapConfig clusterMapConfig) throws JSONException { if (logger.isTraceEnabled()) { logger.trace("HardwareLayout " + jsonObject.toString()); } this.clusterName = jsonObject.getString("clusterName"); this.version = jsonObject.getLong("version"); this.datacenters = new ArrayList<Datacenter>(jsonObject.getJSONArray("datacenters").length()); for (int i = 0; i < jsonObject.getJSONArray("datacenters").length(); ++i) { this.datacenters.add(i, new Datacenter(this, jsonObject.getJSONArray("datacenters").getJSONObject(i), clusterMapConfig)); } this.rawCapacityInBytes = calculateRawCapacityInBytes(); this.dataNodeCount = calculateDataNodeCount(); this.diskCount = calculateDiskCount(); this.dataNodeInHardStateCount = calculateDataNodeInHardStateCount(); this.diskInHardStateCount = calculateDiskInHardStateCount(); validate(); } public String getClusterName() { return clusterName; } public long getVersion() { return version; } public List<Datacenter> getDatacenters() { return datacenters; } public long getRawCapacityInBytes() { return rawCapacityInBytes; } private long calculateRawCapacityInBytes() { long capacityInBytes = 0; for (Datacenter datacenter : datacenters) { capacityInBytes += datacenter.getRawCapacityInBytes(); } return capacityInBytes; } public long getDatacenterCount() { return datacenters.size(); } public long getDataNodeCount() { return dataNodeCount; } private long calculateDataNodeCount() { long count = 0; for (Datacenter datacenter : datacenters) { count += datacenter.getDataNodes().size(); } return count; } public long getDiskCount() { return diskCount; } private long calculateDiskCount() { long count = 0; for (Datacenter datacenter : datacenters) { for (DataNode dataNode : datacenter.getDataNodes()) { count += dataNode.getDisks().size(); } } return count; } public long getDataNodeInHardStateCount(HardwareState hardwareState) { return dataNodeInHardStateCount.get(hardwareState); } private Map<HardwareState, Long> calculateDataNodeInHardStateCount() { Map<HardwareState, Long> dataNodeInStateCount = new HashMap<HardwareState, Long>(); for (HardwareState hardwareState : HardwareState.values()) { dataNodeInStateCount.put(hardwareState, new Long(0)); } for (Datacenter datacenter : datacenters) { for (DataNode dataNode : datacenter.getDataNodes()) { dataNodeInStateCount.put(dataNode.getState(), dataNodeInStateCount.get(dataNode.getState()) + 1); } } return dataNodeInStateCount; } public long calculateUnavailableDataNodeCount() { long count = 0; for (Datacenter datacenter : datacenters) { for (DataNode dataNode : datacenter.getDataNodes()) { if (dataNode.isDown()) { count++; } } } return count; } public long getDiskInHardStateCount(HardwareState hardwareState) { return diskInHardStateCount.get(hardwareState); } private Map<HardwareState, Long> calculateDiskInHardStateCount() { Map<HardwareState, Long> diskInStateCount = new HashMap<HardwareState, Long>(); for (HardwareState hardwareState : HardwareState.values()) { diskInStateCount.put(hardwareState, new Long(0)); } for (Datacenter datacenter : datacenters) { for (DataNode dataNode : datacenter.getDataNodes()) { for (Disk disk : dataNode.getDisks()) { diskInStateCount.put(disk.getState(), diskInStateCount.get(disk.getState()) + 1); } } } return diskInStateCount; } public long calculateUnavailableDiskCount() { long count = 0; for (Datacenter datacenter : datacenters) { for (DataNode dataNode : datacenter.getDataNodes()) { for (Disk disk : dataNode.getDisks()) { if (disk.isDown()) { count++; } } } } return count; } /** * Finds Datacenter by name * * @param datacenterName name of datacenter to be found * @return Datacenter or null if not found. */ public Datacenter findDatacenter(String datacenterName) { for (Datacenter datacenter : datacenters) { if (datacenter.getName().compareToIgnoreCase(datacenterName) == 0) { return datacenter; } } return null; } /** * Finds DataNode by hostname and port. Note that hostname is converted to canonical hostname for comparison. * * @param hostname of datanode * @param port of datanode * @return DataNode or null if not found. */ public DataNode findDataNode(String hostname, int port) { String canonicalHostname = ClusterMapUtils.getFullyQualifiedDomainName(hostname); logger.trace("host to find host {} port {}", canonicalHostname, port); for (Datacenter datacenter : datacenters) { logger.trace("datacenter {}", datacenter.getName()); for (DataNode dataNode : datacenter.getDataNodes()) { if (dataNode.getHostname().equals(canonicalHostname) && (dataNode.getPort() == port)) { return dataNode; } } } return null; } /** * Finds Disk by hostname, port, and mount path. * * @param hostname of datanode * @param port of datanode * @param mountPath of disk * @return Disk or null if not found. */ public Disk findDisk(String hostname, int port, String mountPath) { DataNode dataNode = findDataNode(hostname, port); if (dataNode != null) { for (Disk disk : dataNode.getDisks()) { if (disk.getMountPath().equals(mountPath)) { return disk; } } } return null; } protected void validateClusterName() { if (clusterName == null) { throw new IllegalStateException("HardwareLayout clusterName cannot be null."); } else if (clusterName.length() == 0) { throw new IllegalStateException("HardwareLayout clusterName cannot be zero length."); } } // Validate each hardware component (Datacenter, DataNode, and Disk) are unique protected void validateUniqueness() throws IllegalStateException { logger.trace("begin validateUniqueness."); HashSet<Datacenter> datacenterSet = new HashSet<Datacenter>(); HashSet<DataNode> dataNodeSet = new HashSet<DataNode>(); HashSet<Disk> diskSet = new HashSet<Disk>(); for (Datacenter datacenter : datacenters) { if (!datacenterSet.add(datacenter)) { throw new IllegalStateException("Duplicate Datacenter detected: " + datacenter.toString()); } for (DataNode dataNode : datacenter.getDataNodes()) { if (!dataNodeSet.add(dataNode)) { throw new IllegalStateException("Duplicate DataNode detected: " + dataNode.toString()); } for (Disk disk : dataNode.getDisks()) { if (!diskSet.add(disk)) { throw new IllegalStateException("Duplicate Disk detected: " + disk.toString()); } } } } logger.trace("complete validateUniqueness."); } protected void validate() { logger.trace("begin validate."); validateClusterName(); validateUniqueness(); logger.trace("complete validate."); } public JSONObject toJSONObject() throws JSONException { JSONObject jsonObject = new JSONObject().put("clusterName", clusterName).put("version", version).put("datacenters", new JSONArray()); for (Datacenter datacenter : datacenters) { jsonObject.accumulate("datacenters", datacenter.toJSONObject()); } return jsonObject; } @Override public String toString() { try { return toJSONObject().toString(2); } catch (JSONException e) { logger.error("JSONException caught in toString: {}", e.getCause()); } return null; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } HardwareLayout that = (HardwareLayout) o; if (!clusterName.equals(that.clusterName)) { return false; } return datacenters.equals(that.datacenters); } }