/**
* 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.Iterator;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Datacenter in an Ambry cluster. A Datacenter must be uniquely identifiable by its name. A Datacenter is the primary
* unit at which Ambry hardware is organized (see {@link HardwareLayout})). A Datacenter has zero or more {@link
* DataNode}s.
*
* This class is meant to be used within the {@link StaticClusterManager}.
*/
class Datacenter {
private final HardwareLayout hardwareLayout;
private final String name;
private final ArrayList<DataNode> dataNodes;
private final long rawCapacityInBytes;
private boolean rackAware = false;
private Logger logger = LoggerFactory.getLogger(getClass());
Datacenter(HardwareLayout hardwareLayout, JSONObject jsonObject, ClusterMapConfig clusterMapConfig)
throws JSONException {
if (logger.isTraceEnabled()) {
logger.trace("Datacenter " + jsonObject.toString());
}
this.hardwareLayout = hardwareLayout;
this.name = jsonObject.getString("name");
this.dataNodes = new ArrayList<DataNode>(jsonObject.getJSONArray("dataNodes").length());
for (int i = 0; i < jsonObject.getJSONArray("dataNodes").length(); ++i) {
this.dataNodes.add(new DataNode(this, jsonObject.getJSONArray("dataNodes").getJSONObject(i), clusterMapConfig));
}
this.rawCapacityInBytes = calculateRawCapacityInBytes();
validate();
}
HardwareLayout getHardwareLayout() {
return hardwareLayout;
}
String getName() {
return name;
}
long getRawCapacityInBytes() {
return rawCapacityInBytes;
}
private long calculateRawCapacityInBytes() {
long capacityInBytes = 0;
for (DataNode dataNode : dataNodes) {
capacityInBytes += dataNode.getRawCapacityInBytes();
}
return capacityInBytes;
}
List<DataNode> getDataNodes() {
return dataNodes;
}
/**
* Returns {@code true} if all nodes in the datacenter have rack IDs
* @return {@code true} if all nodes in the datacenter have rack IDs, {@code false} otherwise
*/
boolean isRackAware() {
return rackAware;
}
protected void validateHardwareLayout() {
if (hardwareLayout == null) {
throw new IllegalStateException("HardwareLayout cannot be null");
}
}
protected void validateName() {
if (name == null) {
throw new IllegalStateException("Datacenter name cannot be null.");
} else if (name.length() == 0) {
throw new IllegalStateException("Datacenter name cannot be zero length.");
}
}
/**
* A datacenter can be marked as rack-aware if all nodes have defined rack IDs. This method throws an exception
* if some nodes have rack IDs and some do not. It also sets the {@code rackAware} flag to {@code true} if all
* nodes have rack IDs.
*
* @throws IllegalStateException if some nodes have defined rack IDs and some do not.
*/
private void validateRackAwareness() {
if (dataNodes.size() > 0) {
Iterator<DataNode> dataNodeIter = dataNodes.iterator();
boolean hasRackId = (dataNodeIter.next().getRackId() >= 0);
while (dataNodeIter.hasNext()) {
if (hasRackId != (dataNodeIter.next().getRackId() >= 0)) {
throw new IllegalStateException(
"dataNodes in datacenter: " + name + " must all have defined rack IDs or none at all");
}
}
this.rackAware = hasRackId;
}
}
protected void validate() {
logger.trace("begin validate.");
validateHardwareLayout();
validateName();
validateRackAwareness();
logger.trace("complete validate.");
}
JSONObject toJSONObject() throws JSONException {
JSONObject jsonObject = new JSONObject().put("name", name).put("dataNodes", new JSONArray());
for (DataNode dataNode : dataNodes) {
jsonObject.accumulate("dataNodes", dataNode.toJSONObject());
}
return jsonObject;
}
@Override
public String toString() {
return "Datacenter[" + getName() + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Datacenter that = (Datacenter) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}