/* * Copyright 2014 Alexey Plotnik * * 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. * See the License for the specific language governing permissions and * limitations under the License. */ package org.stem.domain.topology; import com.twitter.crunch.*; import org.stem.exceptions.TopologyException; import org.stem.utils.TopologyUtils; import java.util.*; import static org.stem.utils.TopologyUtils.addChild; class CrushAdapter implements AlgorithmAdapter<Long, Long, Topology.Node, Node, Topology.ReplicaSet, List<Node>, Topology, Node> { @Override public Long convertBucket(Long src) { return src; } private List<Long> convertBucket(List<Long> src) { List<Long> result = new ArrayList<>(src.size()); for (Long orig : src) { result.add(convertBucket(orig)); } return result; } @Override public Node convertNode(Topology.Node src) { Node node = new Node(); node.setName(src.id.toString()); node.setWeight(100); node.setType(NodeType.fromClass(src.getClass()).code); node.setSelection(Node.Selection.STRAW); node.setChildren(new ArrayList<Node>()); return node; } @Override public List<Node> convertReplicaSet(Topology.ReplicaSet src) { List<Node> nodes = new ArrayList<>(src.disks.size()); for (Topology.Disk disk : src.disks) { nodes.add(convertNode(disk)); } return nodes; } @Override public Node convertTopology(Topology src) { Node root = TopologyUtils.createRoot("ROOT"); for (Topology.Datacenter datacenter : src.dataCenters()) { Node datacenterNode = addChild(convertNode(datacenter), root); for (Topology.Rack rack : datacenter.racks()) { Node rackNode = addChild(convertNode(rack), datacenterNode); for (Topology.StorageNode storageNode : rack.storageNodes()) { Node storageNodeNode = addChild(convertNode(storageNode), rackNode); for (Topology.Disk disk : storageNode.disks()) { addChild(convertNode(disk), storageNodeNode); } } } } return root; } @Override public Map<Long, Topology.ReplicaSet> computeMapping(List<Long> buckets, int rf, Topology topology) { PlacementRules rules = new RackIsolationPlacementRules(); MappingFunction function = new SimpleCRUSHMapping(rf, rules); Map<Long, List<Node>> crushMapping = function.computeMapping(convertBucket(buckets), convertTopology(topology)); Map<Long, Topology.ReplicaSet> dataMapping = new HashMap<>(crushMapping.size()); for (Map.Entry<Long, List<Node>> entry : crushMapping.entrySet()) { Long bucket = entry.getKey(); List<Node> replicas = entry.getValue(); Topology.ReplicaSet replicaSet = buildReplicaSet(replicas, topology, rf); dataMapping.put(bucket, replicaSet); // TODO: convert bucket type from CRUSH to Application level } return dataMapping; } private static Topology.ReplicaSet buildReplicaSet(List<Node> disks, Topology topology, int rf) { List<Topology.Disk> replicas = new ArrayList<>(disks.size()); for (Node diskNode : disks) { UUID uuid = uuid(diskNode.getName()); Topology.Disk disk = topology.findDisk(uuid); if (null == disk) throw new TopologyException("Disk not found: " + uuid); replicas.add(disk); } if (replicas.size() != rf) throw new TopologyException(String.format("Inconsistent number of replicas (%s) for RF=%s", replicas.size(), rf)); return new Topology.ReplicaSet(replicas); } private static UUID uuid(String uuid) { return UUID.fromString(uuid); } public static final class CrushNode extends Node { // Stub in favour of Node class which has too generic name } private static enum NodeType { DC(Topology.Datacenter.class, 1), RACK(Topology.Rack.class, 2), STORAGE(Topology.StorageNode.class, 4), DISK(Topology.Disk.class, 5); private static Map<Class<? extends Topology.Node>, NodeType> values = new HashMap<>(); static { for (NodeType val : NodeType.values()) { values.put(val.clazz, val); } } public static NodeType fromClass(Class<? extends Topology.Node> clazz) { NodeType type = values.get(clazz); if (null == type) throw new IllegalStateException(String.format("Unknown node type for clazz \"%s\"", clazz.getCanonicalName())); return type; } final Class<? extends Topology.Node> clazz; final int code; NodeType(Class<? extends Topology.Node> clazz, int code) { this.clazz = clazz; this.code = code; } } }