summaryrefslogtreecommitdiffstats
path: root/vdslib/src/tests/state/clusterstatetest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vdslib/src/tests/state/clusterstatetest.cpp')
-rw-r--r--vdslib/src/tests/state/clusterstatetest.cpp389
1 files changed, 389 insertions, 0 deletions
diff --git a/vdslib/src/tests/state/clusterstatetest.cpp b/vdslib/src/tests/state/clusterstatetest.cpp
new file mode 100644
index 00000000000..c2abfef8403
--- /dev/null
+++ b/vdslib/src/tests/state/clusterstatetest.cpp
@@ -0,0 +1,389 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/vdslib/state/clusterstate.h>
+
+#include <list>
+#include <vespa/document/bucket/bucketidfactory.h>
+#include <cmath>
+#include <vespa/vdslib/state/random.h>
+#include <vespa/vdstestlib/cppunit/macros.h>
+
+using vespalib::string;
+
+namespace storage {
+namespace lib {
+
+struct ClusterStateTest : public CppUnit::TestFixture {
+
+ void testBasicFunctionality();
+ void testErrorBehaviour();
+ void testBackwardsCompability();
+ void testDetailed();
+ void testParseFailure();
+ void testParseFailureGroups();
+
+ void testDiff();
+
+ CPPUNIT_TEST_SUITE(ClusterStateTest);
+ CPPUNIT_TEST(testBasicFunctionality);
+ CPPUNIT_TEST(testErrorBehaviour);
+ CPPUNIT_TEST(testBackwardsCompability);
+ CPPUNIT_TEST(testDetailed);
+
+ // Ideal state tests.
+ CPPUNIT_TEST(testParseFailure);
+ CPPUNIT_TEST(testParseFailureGroups);
+ CPPUNIT_TEST(testDiff);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ClusterStateTest);
+
+void
+ClusterStateTest::testDiff() {
+ ClusterState state1("distributor:9 storage:4");
+ ClusterState state2("distributor:7 storage:6");
+ ClusterState state3("distributor:9 storage:2");
+ CPPUNIT_ASSERT_EQUAL(
+ std::string("storage [4: d to u, 5: d to u] "
+ "distributor [7: u to d, 8: u to d]"),
+ state1.getTextualDifference(state2));
+ CPPUNIT_ASSERT_EQUAL(
+ std::string("storage [2: u to d, 3: u to d, 4: u to d, 5: u to d] "
+ "distributor [7: d to u, 8: d to u]"),
+ state2.getTextualDifference(state3));
+}
+
+
+#define VERIFY3(source, result, type, typestr) { \
+ vespalib::asciistream ost; \
+ try{ \
+ state->serialize(ost, type); \
+ } catch (std::exception& e) { \
+ CPPUNIT_FAIL("Failed to serialize system state " \
+ + state->toString(true) + " in " + std::string(typestr) \
+ + " format: " + std::string(e.what())); \
+ } \
+ CPPUNIT_ASSERT_EQUAL_MSG(vespalib::string(state->toString(true)), \
+ vespalib::string(typestr) + " \"" + vespalib::string(result) + "\"", \
+ vespalib::string(typestr) + " \"" + ost.str() + "\""); \
+}
+
+#define VERIFY2(serialized, result, testOld, testNew) { \
+ std::unique_ptr<ClusterState> state; \
+ try{ \
+ state.reset(new ClusterState(serialized)); \
+ } catch (std::exception& e) { \
+ CPPUNIT_FAIL("Failed to parse '" + std::string(serialized) \
+ + "': " + e.what()); \
+ } \
+ if (testOld) VERIFY3(serialized, result, true, "Old") \
+ if (testNew) VERIFY3(serialized, result, false, "New") \
+}
+
+#define VERIFYSAMEOLD(serialized) VERIFY2(serialized, serialized, true, false)
+#define VERIFYOLD(serialized, result) VERIFY2(serialized, result, true, false)
+#define VERIFYSAMENEW(serialized) VERIFY2(serialized, serialized, false, true)
+#define VERIFYNEW(serialized, result) VERIFY2(serialized, result, false, true)
+#define VERIFYSAME(serialized) VERIFY2(serialized, serialized, true, true)
+#define VERIFY(serialized, result) VERIFY2(serialized, result, true, true)
+
+#define VERIFY_FAIL(serialized, error) { \
+ try{ \
+ ClusterState state(serialized); \
+ CPPUNIT_FAIL("Parsing the state '" + std::string(serialized) \
+ + "' is supposed to fail."); \
+ } catch (vespalib::Exception& e) { \
+ CPPUNIT_ASSERT_MATCH_REGEX(error, e.getMessage()); \
+ } \
+}
+
+void
+ClusterStateTest::testBasicFunctionality()
+{
+ // Version is default and should not be written
+ VERIFYNEW("version:0", "");
+ VERIFYNEW("version:1", "version:1");
+
+ // Cluster state up is default and should not be written
+ VERIFYNEW("cluster:u", "");
+ VERIFYSAMENEW("cluster:d");
+ VERIFYSAMENEW("cluster:i");
+ VERIFYSAMENEW("cluster:s");
+
+ // No need to write node counts if no nodes exist.
+ VERIFYNEW("cluster:d distributor:0 storage:0", "cluster:d");
+
+ // Test legal distributor states
+ VERIFYNEW("distributor:10 .1.s:i .2.s:u .3.s:s .4.s:d",
+ "distributor:10 .1.s:i .1.i:0 .3.s:s .4.s:d");
+
+ // Test legal storage states
+ VERIFYNEW("storage:10 .1.s:i .2.s:u .3.s:d .4.s:m .5.s:r",
+ "storage:10 .1.s:i .1.i:0 .3.s:d .4.s:m .5.s:r");
+
+ // Test legal disk states
+ VERIFYNEW("storage:10 .1.d:4 .1.d.0.s:u .1.d.1.s:d",
+ "storage:10 .1.d:4 .1.d.1.s:d");
+
+ // Test other disk properties
+ VERIFYSAMENEW("storage:10 .1.d:4 .1.d.0.c:1.4");
+
+ // Test other distributor node propertise
+ // (Messages is excluded from system states to not make them too long as
+ // most nodes have no use for them)
+ VERIFYNEW("distributor:9 .7.m:foo\\x20bar", "distributor:9");
+ VERIFYSAMENEW("distributor:4 .2.s:m");
+
+ // Test other storage node propertise
+ // (Messages is excluded from system states to not make them too long as
+ // most nodes have no use for them)
+ VERIFYNEW("storage:9 .3.c:2.3 .4.r:8 .7.m:foo\\x20bar",
+ "storage:9 .3.c:2.3 .4.r:8");
+
+ // Test that messages are kept in verbose mode, even if last index
+ {
+ ClusterState state("storage:5 .4.s:d .4.m:Foo\\x20bar");
+ const NodeState& ns(state.getNodeState(Node(NodeType::STORAGE, 4)));
+ CPPUNIT_ASSERT_EQUAL(string("Foo bar"), ns.getDescription());
+ }
+
+ ClusterState state;
+ state.setClusterState(State::UP);
+ state.setNodeState(Node(NodeType::DISTRIBUTOR, 3),
+ NodeState(NodeType::DISTRIBUTOR, State::UP));
+ CPPUNIT_ASSERT_EQUAL(std::string("distributor:4 .0.s:d .1.s:d .2.s:d"),
+ state.toString(false));
+ state.setNodeState(Node(NodeType::DISTRIBUTOR, 1),
+ NodeState(NodeType::DISTRIBUTOR, State::UP));
+ CPPUNIT_ASSERT_EQUAL(std::string("distributor:4 .0.s:d .2.s:d"),
+ state.toString(false));
+ state.setNodeState(Node(NodeType::DISTRIBUTOR, 3),
+ NodeState(NodeType::DISTRIBUTOR, State::DOWN));
+ CPPUNIT_ASSERT_EQUAL(std::string("distributor:2 .0.s:d"),
+ state.toString(false));
+ state.setNodeState(Node(NodeType::DISTRIBUTOR, 4),
+ NodeState(NodeType::DISTRIBUTOR, State::UP));
+ CPPUNIT_ASSERT_EQUAL(std::string("distributor:5 .0.s:d .2.s:d .3.s:d"),
+ state.toString(false));
+}
+
+void
+ClusterStateTest::testErrorBehaviour()
+{
+ // Keys with invalid values
+
+ // Index out of range
+ VERIFY_FAIL("storage:5 distributor:4 .4.s:s",
+ "Cannot index distributor node 4 of 4");
+ VERIFY_FAIL("distributor:5 storage:4 .4.s:s",
+ "Cannot index storage node 4 of 4");
+
+ // Test illegal cluster states
+ VERIFY_FAIL("cluster:m", "Maintenance is not a legal cluster state");
+ VERIFY_FAIL("cluster:r", "Retired is not a legal cluster state");
+
+ // Test illegal distributor states
+// Currently set to legal
+// VERIFY_FAIL("distributor:4 .2.s:r",
+// "Retired is not a legal distributor state");
+
+ // Test illegal storage states
+ VERIFY_FAIL("storage:4 .2.d:2 .2.d.5.s:d", "Cannot index disk 5 of 2");
+
+ // Test blatantly illegal values for known attributes:
+ VERIFY_FAIL("distributor:4 .2.s:z", "Unknown state z given.*");
+ VERIFY_FAIL("distributor:4 .2.i:foobar",
+ ".*Init progress must be a floating point number from .*");
+ VERIFY_FAIL("storage:4 .2.d:foobar", "Invalid disk count 'foobar'. Need.*");
+ VERIFY_FAIL("storage:4 .2.d:2 .2.d.1.s:foobar",
+ "Unknown state foobar given.*");
+ VERIFY_FAIL("storage:4 .2.d:2 .2.d.1.c:foobar",
+ "Illegal disk capacity 'foobar'. Capacity must be a .*");
+ VERIFY_FAIL("storage:4 .2.d:2 .2.d.a.s:d",
+ "Invalid disk index 'a'. Need a positive integer .*");
+
+ // Lacking absolute path first
+ VERIFY_FAIL(".2.s:d distributor:4", "The first path in system state.*");
+
+ // Unknown tokens
+ VERIFYNEW("distributor:4 .2.d:2", "distributor:4");
+ VERIFYNEW("distributor:4 .2.d:2 .2.d:2", "distributor:4");
+ VERIFYNEW("distributor:4 .2.c:1.2 .3.r:2.0", "distributor:4");
+ VERIFYNEW("distributor:4 .2:foo storage:5 .4:d", "distributor:4 storage:5");
+ VERIFYNEW("ballalaika:true distributor:4 .2.urk:oj .2.z:foo .2.s:s "
+ ".2.j:foo storage:10 .3.d:4 .3.d.2.a:boo .3.s:s",
+ "distributor:4 .2.s:s storage:10 .3.s:s .3.d:4");
+}
+
+void
+ClusterStateTest::testBackwardsCompability()
+{
+ // 4.1 and older nodes do not support some features, and the java parser
+ // do not allow unknown elements as it was supposed to do, thus we should
+ // avoid using new features when talking to 4.1 nodes.
+
+ // - 4.1 nodes should not see new cluster, version, initializing and
+ // description tags.
+ VERIFYOLD("version:4 cluster:i storage:2 .0.s:i .0.i:0.5 .1.m:foobar",
+ "distributor:0 storage:2 .0.s:i");
+
+ // - 4.1 nodes have only one disk property being state, so in 4.1, a
+ // disk state is typically set as .4.d.2:d while in new format it
+ // specifies that this is the state .4.d.2.s:d
+ VERIFYSAMEOLD("distributor:0 storage:3 .2.d:10 .2.d.4:d");
+ VERIFYOLD("distributor:0 storage:3 .2.d:10 .2.d.4.s:d",
+ "distributor:0 storage:3 .2.d:10 .2.d.4:d");
+
+ // - 4.1 nodes should always have distributor and storage tags with counts.
+ VERIFYOLD("storage:4", "distributor:0 storage:4");
+ VERIFYOLD("distributor:4", "distributor:4 storage:0");
+
+ // - 4.1 nodes should not see the state stopping
+ VERIFYOLD("storage:4 .2.s:s", "distributor:0 storage:4 .2.s:d");
+
+}
+
+void
+ClusterStateTest::testDetailed()
+{
+ ClusterState state(
+ "version:314 cluster:i "
+ "distributor:8 .1.s:i .3.s:i .3.i:0.5 .5.s:d .7.m:foo\\x20bar "
+ "storage:10 .2.d:16 .2.d.3:d .4.s:d .5.c:1.3 .5.r:4"
+ " .6.m:bar\\tfoo .7.s:m .8.d:10 .8.d.4.c:0.6 .8.d.4.m:small"
+ );
+ CPPUNIT_ASSERT_EQUAL(314u, state.getVersion());
+ CPPUNIT_ASSERT_EQUAL(State::INITIALIZING, state.getClusterState());
+ CPPUNIT_ASSERT_EQUAL(uint16_t(8),state.getNodeCount(NodeType::DISTRIBUTOR));
+ CPPUNIT_ASSERT_EQUAL(uint16_t(10),state.getNodeCount(NodeType::STORAGE));
+
+ // Testing distributor node states
+ for (uint16_t i = 0; i <= 20; ++i) {
+ const NodeState& ns(state.getNodeState(Node(NodeType::DISTRIBUTOR, i)));
+ // Test node states
+ if (i == 1 || i == 3) {
+ CPPUNIT_ASSERT_EQUAL(State::INITIALIZING, ns.getState());
+ } else if (i == 5 || i >= 8) {
+ CPPUNIT_ASSERT_EQUAL(State::DOWN, ns.getState());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(State::UP, ns.getState());
+ }
+ // Test initialize progress
+ if (i == 1) {
+ CPPUNIT_ASSERT_EQUAL(vespalib::Double(0.0), ns.getInitProgress());
+ } else if (i == 3) {
+ CPPUNIT_ASSERT_EQUAL(vespalib::Double(0.5), ns.getInitProgress());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(vespalib::Double(0.0), ns.getInitProgress());
+ }
+ // Test message
+ if (i == 7) {
+ CPPUNIT_ASSERT_EQUAL(string("foo bar"), ns.getDescription());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(string(""), ns.getDescription());
+ }
+ }
+
+ // Testing storage node states
+ for (uint16_t i = 0; i <= 20; ++i) {
+ const NodeState& ns(state.getNodeState(Node(NodeType::STORAGE, i)));
+ // Test node states
+ if (i == 4 || i >= 10) {
+ CPPUNIT_ASSERT_EQUAL(State::DOWN, ns.getState());
+ } else if (i == 7) {
+ CPPUNIT_ASSERT_EQUAL(State::MAINTENANCE, ns.getState());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(State::UP, ns.getState());
+ }
+ // Test disk states
+ if (i == 2) {
+ CPPUNIT_ASSERT_EQUAL(uint16_t(16), ns.getDiskCount());
+ } else if (i == 8) {
+ CPPUNIT_ASSERT_EQUAL(uint16_t(10), ns.getDiskCount());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(uint16_t(0), ns.getDiskCount());
+ }
+ if (i == 2) {
+ for (uint16_t j = 0; j < 16; ++j) {
+ if (j == 3) {
+ CPPUNIT_ASSERT_EQUAL(State::DOWN,
+ ns.getDiskState(j).getState());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(State::UP,
+ ns.getDiskState(j).getState());
+ }
+ }
+ } else if (i == 8) {
+ for (uint16_t j = 0; j < 10; ++j) {
+ if (j == 4) {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 0.6, ns.getDiskState(j).getCapacity().getValue(), 0.0001);
+ CPPUNIT_ASSERT_EQUAL(
+ string("small"),
+ ns.getDiskState(j).getDescription());
+ } else {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 1.0, ns.getDiskState(j).getCapacity().getValue(), 0.0001);
+ CPPUNIT_ASSERT_EQUAL(
+ string(""),
+ ns.getDiskState(j).getDescription());
+ }
+ }
+ }
+ // Test message
+ if (i == 6) {
+ CPPUNIT_ASSERT_EQUAL(string("bar\tfoo"), ns.getDescription());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(string(""), ns.getDescription());
+ }
+ // Test reliability
+ if (i == 5) {
+ CPPUNIT_ASSERT_EQUAL(uint16_t(4), ns.getReliability());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(uint16_t(1), ns.getReliability());
+ }
+ // Test capacity
+ if (i == 5) {
+ CPPUNIT_ASSERT_EQUAL(vespalib::Double(1.3), ns.getCapacity());
+ } else {
+ CPPUNIT_ASSERT_EQUAL(vespalib::Double(1.0), ns.getCapacity());
+ }
+ }
+
+}
+
+void
+ClusterStateTest::testParseFailure()
+{
+ try {
+ ClusterState state("storage");
+ CPPUNIT_ASSERT(false);
+ } catch (vespalib::Exception& e) {
+ }
+
+ try {
+ ClusterState state("");
+ } catch (vespalib::Exception& e) {
+ CPPUNIT_ASSERT(false);
+ }
+
+ try {
+ ClusterState state(".her:tull");
+ CPPUNIT_ASSERT(false);
+ } catch (vespalib::Exception& e) {
+ }
+}
+
+void
+ClusterStateTest::testParseFailureGroups()
+{
+ try {
+ ClusterState state(")");
+ CPPUNIT_ASSERT(false);
+ } catch (vespalib::Exception& e) {
+ }
+}
+
+} // lib
+} // storage