diff --git a/hadoop-hdds/common/pom.xml b/hadoop-hdds/common/pom.xml
index 2385a9ec0c..5d1bb52844 100644
--- a/hadoop-hdds/common/pom.xml
+++ b/hadoop-hdds/common/pom.xml
@@ -137,6 +137,11 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
opentracing-util
0.31.0
+
+ org.yaml
+ snakeyaml
+ 1.16
+
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
index b097321507..2c267fb176 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
@@ -368,6 +368,8 @@ public final class ScmConfigKeys {
"hdds.scm.http.kerberos.keytab";
// Network topology
+ public static final String OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE_TYPE =
+ "ozone.scm.network.topology.schema.file.type";
public static final String OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE =
"ozone.scm.network.topology.schema.file";
public static final String OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE_DEFAULT =
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchema.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchema.java
index 8c289f7617..47e5de880d 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchema.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchema.java
@@ -19,6 +19,8 @@
import org.apache.hadoop.HadoopIllegalArgumentException;
+import java.util.List;
+
/**
* Network topology schema to housekeeper relevant information.
*/
@@ -59,13 +61,15 @@ public static LayerType getType(String typeStr) {
}
// default cost
- private final int cost;
+ private int cost;
// layer Type, mandatory property
- private final LayerType type;
+ private LayerType type;
// default name, can be null or ""
- private final String defaultName;
+ private String defaultName;
// layer prefix, can be null or ""
- private final String prefix;
+ private String prefix;
+ // sublayer
+ private List sublayer;
/**
* Builder for NodeSchema.
@@ -123,6 +127,14 @@ public NodeSchema(LayerType type, int cost, String prefix,
this.defaultName = defaultName;
}
+ /**
+ * Constructor. This constructor is only used when build NodeSchema from
+ * YAML file.
+ */
+ public NodeSchema() {
+ this.type = LayerType.INNER_NODE;
+ }
+
public boolean matchPrefix(String name) {
if (name == null || name.isEmpty() || prefix == null || prefix.isEmpty()) {
return false;
@@ -134,15 +146,38 @@ public LayerType getType() {
return this.type;
}
+ public void setType(LayerType type) {
+ this.type = type;
+ }
+
public String getPrefix() {
return this.prefix;
}
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
public String getDefaultName() {
return this.defaultName;
}
+ public void setDefaultName(String name) {
+ this.defaultName = name;
+ }
+
public int getCost() {
return this.cost;
}
+ public void setCost(int cost) {
+ this.cost = cost;
+ }
+
+ public void setSublayer(List sublayer) {
+ this.sublayer = sublayer;
+ }
+
+ public List getSublayer() {
+ return sublayer;
+ }
}
\ No newline at end of file
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaLoader.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaLoader.java
index 9125fb7b8c..32d7f16a99 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaLoader.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaLoader.java
@@ -30,6 +30,7 @@
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,6 +38,7 @@
import java.util.Map;
import org.apache.hadoop.hdds.scm.net.NodeSchema.LayerType;
+import org.yaml.snakeyaml.Yaml;
/**
* A Network topology layer schema loading tool that loads user defined network
@@ -95,7 +97,7 @@ public List getSchemaList() {
* @param schemaFilePath path of schema file
* @return all valid node schemas defined in schema file
*/
- public NodeSchemaLoadResult loadSchemaFromFile(String schemaFilePath)
+ public NodeSchemaLoadResult loadSchemaFromXml(String schemaFilePath)
throws IllegalArgumentException {
try {
File schemaFile = new File(schemaFilePath);
@@ -165,6 +167,88 @@ private NodeSchemaLoadResult loadSchema(File schemaFile) throws
return schemaList;
}
+ /**
+ * Load user defined network layer schemas from a YAML configuration file.
+ * @param schemaFilePath path of schema file
+ * @return all valid node schemas defined in schema file
+ */
+ public NodeSchemaLoadResult loadSchemaFromYaml(String schemaFilePath)
+ throws IllegalArgumentException {
+ try {
+ File schemaFile = new File(schemaFilePath);
+ if (!schemaFile.exists()) {
+ String msg = "Network topology layer schema file " + schemaFilePath +
+ " is not found.";
+ LOG.warn(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ return loadSchemaFromYaml(schemaFile);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Fail to load network topology node"
+ + " schema file: " + schemaFilePath + " , error:"
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Load network topology layer schemas from a YAML configuration file.
+ * @param schemaFile schema file
+ * @return all valid node schemas defined in schema file
+ * @throws ParserConfigurationException ParserConfigurationException happen
+ * @throws IOException no such schema file
+ * @throws SAXException xml file has some invalid elements
+ * @throws IllegalArgumentException xml file content is logically invalid
+ */
+ private NodeSchemaLoadResult loadSchemaFromYaml(File schemaFile) {
+ LOG.info("Loading network topology layer schema file {}", schemaFile);
+ NodeSchemaLoadResult finalSchema;
+
+ try {
+ Yaml yaml = new Yaml();
+ NodeSchema nodeTree;
+
+ try (FileInputStream fileInputStream = new FileInputStream(schemaFile)) {
+ nodeTree = yaml.loadAs(fileInputStream, NodeSchema.class);
+ }
+ List schemaList = new ArrayList<>();
+ if (nodeTree.getType() != LayerType.ROOT) {
+ throw new IllegalArgumentException("First layer is not a ROOT node."
+ + " schema file: " + schemaFile.getAbsolutePath());
+ }
+ schemaList.add(nodeTree);
+ if (nodeTree.getSublayer() != null) {
+ nodeTree = nodeTree.getSublayer().get(0);
+ }
+
+ while (nodeTree != null) {
+ if (nodeTree.getType() == LayerType.LEAF_NODE
+ && nodeTree.getSublayer() != null) {
+ throw new IllegalArgumentException("Leaf node in the middle of path."
+ + " schema file: " + schemaFile.getAbsolutePath());
+ }
+ if (nodeTree.getType() == LayerType.ROOT) {
+ throw new IllegalArgumentException("Multiple root nodes are defined."
+ + " schema file: " + schemaFile.getAbsolutePath());
+ }
+ schemaList.add(nodeTree);
+ if (nodeTree.getSublayer() != null) {
+ nodeTree = nodeTree.getSublayer().get(0);
+ } else {
+ break;
+ }
+ }
+ finalSchema = new NodeSchemaLoadResult(schemaList, true);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Fail to load network topology node"
+ + " schema file: " + schemaFile.getAbsolutePath() + " , error:"
+ + e.getMessage());
+ }
+
+ return finalSchema;
+ }
+
/**
* Load layoutVersion from root element in the XML configuration file.
* @param root root element
diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaManager.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaManager.java
index 8f2fac75f5..8e5d935e61 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaManager.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/net/NodeSchemaManager.java
@@ -59,13 +59,20 @@ public void init(Configuration conf) {
/**
* Load schemas from network topology schema configuration file
*/
+ String schemaFileType = conf.get(
+ ScmConfigKeys.OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE_TYPE);
+
String schemaFile = conf.get(
ScmConfigKeys.OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE,
ScmConfigKeys.OZONE_SCM_NETWORK_TOPOLOGY_SCHEMA_FILE_DEFAULT);
NodeSchemaLoadResult result;
try {
- result = NodeSchemaLoader.getInstance().loadSchemaFromFile(schemaFile);
+ if (schemaFileType.toLowerCase().compareTo("yaml") == 0) {
+ result = NodeSchemaLoader.getInstance().loadSchemaFromYaml(schemaFile);
+ } else {
+ result = NodeSchemaLoader.getInstance().loadSchemaFromXml(schemaFile);
+ }
allSchema = result.getSchemaList();
enforcePrefix = result.isEnforePrefix();
maxLevel = allSchema.size();
diff --git a/hadoop-hdds/common/src/main/resources/network-topology-default.yaml b/hadoop-hdds/common/src/main/resources/network-topology-default.yaml
new file mode 100644
index 0000000000..561869fb43
--- /dev/null
+++ b/hadoop-hdds/common/src/main/resources/network-topology-default.yaml
@@ -0,0 +1,61 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+---
+# Cost: The cost of crossing this layer.
+# The value should be positive integer or 0. This field is optional.
+# When it's not defined, it's value is default "1".
+cost: 1
+
+# The prefix of this layer.
+# If the prefix is "dc", then every name in this layer should start with "dc",
+# such as "dc1", "dc2".
+# Note that unlike XML schema, the prefix must be specified explicitly if the type is InnerNode.
+prefix: /
+
+# Layer type, optional field, default value InnerNode.
+# Current value range : {ROOT, INNER_NODE, LEAF_NODE}
+type: ROOT
+
+# Layer name
+defaultName: root
+
+# Sub layer
+# The sub layer property defines as a list which can reflect a node tree, though
+# in schema template it always has only one child.
+sublayer:
+ -
+ cost: 1
+ prefix: dc
+ defaultName: datacenter
+ type: INNER_NODE
+ sublayer:
+ -
+ cost: 1
+ prefix: rack
+ defaultName: rack
+ type: INNER_NODE
+ sublayer:
+ -
+ cost: 1
+ prefix: ng
+ defaultName: nodegroup
+ type: INNER_NODE
+ sublayer:
+ -
+ defaultName: node
+ type: LEAF_NODE
+ prefix: node
+...
\ No newline at end of file
diff --git a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestNodeSchemaLoader.java b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestNodeSchemaLoader.java
index 6d9057cb55..30799b1099 100644
--- a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestNodeSchemaLoader.java
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestNodeSchemaLoader.java
@@ -44,7 +44,7 @@ public TestNodeSchemaLoader(String schemaFile, String errMsg) {
try {
String filePath = classLoader.getResource(
"./networkTopologyTestFiles/" + schemaFile).getPath();
- NodeSchemaLoader.getInstance().loadSchemaFromFile(filePath);
+ NodeSchemaLoader.getInstance().loadSchemaFromXml(filePath);
fail("expect exceptions");
} catch (Throwable e) {
assertTrue(e.getMessage().contains(errMsg));
@@ -83,7 +83,7 @@ public void testGood() {
try {
String filePath = classLoader.getResource(
"./networkTopologyTestFiles/good.xml").getPath();
- NodeSchemaLoader.getInstance().loadSchemaFromFile(filePath);
+ NodeSchemaLoader.getInstance().loadSchemaFromXml(filePath);
} catch (Throwable e) {
fail("should succeed");
}
@@ -94,7 +94,7 @@ public void testNotExist() {
String filePath = classLoader.getResource(
"./networkTopologyTestFiles/good.xml").getPath() + ".backup";
try {
- NodeSchemaLoader.getInstance().loadSchemaFromFile(filePath);
+ NodeSchemaLoader.getInstance().loadSchemaFromXml(filePath);
fail("should fail");
} catch (Throwable e) {
assertTrue(e.getMessage().contains("file " + filePath + " is not found"));
diff --git a/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestYamlSchemaLoader.java b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestYamlSchemaLoader.java
new file mode 100644
index 0000000000..580a7fb485
--- /dev/null
+++ b/hadoop-hdds/common/src/test/java/org/apache/hadoop/hdds/scm/net/TestYamlSchemaLoader.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.hadoop.hdds.scm.net;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/** Test the node schema loader. */
+@RunWith(Parameterized.class)
+public class TestYamlSchemaLoader {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(TestYamlSchemaLoader.class);
+ private ClassLoader classLoader =
+ Thread.currentThread().getContextClassLoader();
+
+ public TestYamlSchemaLoader(String schemaFile, String errMsg) {
+ try {
+ String filePath = classLoader.getResource(
+ "./networkTopologyTestFiles/" + schemaFile).getPath();
+ NodeSchemaLoader.getInstance().loadSchemaFromYaml(filePath);
+ fail("expect exceptions");
+ } catch (Throwable e) {
+ assertTrue(e.getMessage().contains(errMsg));
+ }
+ }
+
+ @Rule
+ public Timeout testTimeout = new Timeout(30000);
+
+ @Parameters
+ public static Collection