Add more helpers.
GetNode, GetNodeOrDie, GetChannelIndex, GetNodeIndex. All useful when
reading multi-node log files.
Change-Id: I8a2cb88a820ac8baf6cf637ea654dc39e9a03855
diff --git a/aos/configuration.cc b/aos/configuration.cc
index 095add5..c4f139c 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -470,6 +470,18 @@
}
}
+size_t ChannelIndex(const Configuration *configuration,
+ const Channel *channel) {
+ CHECK(configuration->channels() != nullptr) << ": No channels";
+
+ auto c = std::find(configuration->channels()->begin(),
+ configuration->channels()->end(), channel);
+ CHECK(c != configuration->channels()->end())
+ << ": Channel pointer not found in configuration()->channels()";
+
+ return std::distance(configuration->channels()->begin(), c);
+}
+
std::string CleanedChannelToString(const Channel *channel) {
FlatbufferDetachedBuffer<Channel> cleaned_channel = CopyFlatBuffer(channel);
cleaned_channel.mutable_message()->clear_schema();
@@ -605,6 +617,17 @@
return nullptr;
}
+const Node *GetNode(const Configuration *config, const Node *node) {
+ if (!MultiNode(config)) {
+ CHECK(node == nullptr) << ": Provided a node in a single node world.";
+ return nullptr;
+ } else {
+ CHECK(node != nullptr);
+ CHECK(node->has_name());
+ return GetNode(config, node->name()->string_view());
+ }
+}
+
const Node *GetNode(const Configuration *config, std::string_view name) {
CHECK(config->has_nodes())
<< ": Asking for a node from a single node configuration.";
@@ -617,6 +640,44 @@
return nullptr;
}
+const Node *GetNodeOrDie(const Configuration *config, const Node *node) {
+ if (!MultiNode(config)) {
+ CHECK(node == nullptr) << ": Provided a node in a single node world.";
+ return nullptr;
+ } else {
+ const Node *config_node = GetNode(config, node);
+ if (config_node == nullptr) {
+ LOG(FATAL) << "Couldn't find node matching " << FlatbufferToJson(node);
+ }
+ return config_node;
+ }
+}
+
+int GetNodeIndex(const Configuration *config, const Node *node) {
+ CHECK(config->has_nodes())
+ << ": Asking for a node from a single node configuration.";
+ int node_index = 0;
+ for (const Node *iterated_node : *config->nodes()) {
+ if (iterated_node == node) {
+ return node_index;
+ }
+ ++node_index;
+ }
+ LOG(FATAL) << "Node not found in the configuration.";
+}
+
+std::vector<const Node *> GetNodes(const Configuration *config) {
+ std::vector<const Node *> nodes;
+ if (configuration::MultiNode(config)) {
+ for (const Node *node : *config->nodes()) {
+ nodes.emplace_back(node);
+ }
+ } else {
+ nodes.emplace_back(nullptr);
+ }
+ return nodes;
+}
+
bool MultiNode(const Configuration *config) { return config->has_nodes(); }
bool ChannelIsSendableOnNode(const Channel *channel, const Node *node) {
diff --git a/aos/configuration.h b/aos/configuration.h
index 88687ad..a9fbfe3 100644
--- a/aos/configuration.h
+++ b/aos/configuration.h
@@ -60,15 +60,29 @@
channel->type()->string_view(), application_name, node);
}
+// Returns the channel index (or dies) of channel in the provided config.
+size_t ChannelIndex(const Configuration *config, const Channel *channel);
+
// Returns the Node out of the config with the matching name, or nullptr if it
// can't be found.
const Node *GetNode(const Configuration *config, std::string_view name);
+const Node *GetNode(const Configuration *config, const Node *node);
+// Returns a matching node, or nullptr if the provided node is nullptr and we
+// are in a single node world.
+const Node *GetNodeOrDie(const Configuration *config, const Node *node);
// Returns the Node out of the configuration which matches our hostname.
// CHECKs if it can't be found.
const Node *GetMyNode(const Configuration *config);
const Node *GetNodeFromHostname(const Configuration *config,
std::string_view name);
+// Returns a vector of the nodes in the config. (nullptr is considered the node
+// in a single node world.)
+std::vector<const Node *> GetNodes(const Configuration *config);
+
+// Returns the node index for a node. Note: node needs to exist inside config.
+int GetNodeIndex(const Configuration *config, const Node *node);
+
// Returns true if we are running in a multinode configuration.
bool MultiNode(const Configuration *config);
diff --git a/aos/configuration_test.cc b/aos/configuration_test.cc
index b8fc5b6..70515fd 100644
--- a/aos/configuration_test.cc
+++ b/aos/configuration_test.cc
@@ -39,6 +39,16 @@
FlatbufferToJson(config, true));
}
+// Tests that we can get back a ChannelIndex.
+TEST_F(ConfigurationTest, ChannelIndex) {
+ FlatbufferDetachedBuffer<Configuration> config =
+ ReadConfig("aos/testdata/config1.json");
+
+ EXPECT_EQ(
+ ChannelIndex(&config.message(), config.message().channels()->Get(1u)),
+ 1u);
+}
+
// Tests that we can read and merge a multinode configuration.
TEST_F(ConfigurationTest, ConfigMergeMultinode) {
FlatbufferDetachedBuffer<Configuration> config =
@@ -599,6 +609,63 @@
::testing::ElementsAreArray({"pi1"}));
}
+// Tests that we can pull out all the nodes.
+TEST_F(ConfigurationTest, GetNodes) {
+ {
+ FlatbufferDetachedBuffer<Configuration> config =
+ ReadConfig("aos/testdata/good_multinode.json");
+ const Node *pi1 = GetNode(&config.message(), "pi1");
+ const Node *pi2 = GetNode(&config.message(), "pi2");
+
+ EXPECT_THAT(GetNodes(&config.message()), ::testing::ElementsAre(pi1, pi2));
+ }
+
+ {
+ FlatbufferDetachedBuffer<Configuration> config =
+ ReadConfig("aos/testdata/config1.json");
+ EXPECT_THAT(GetNodes(&config.message()), ::testing::ElementsAre(nullptr));
+ }
+}
+
+// Tests that we can extract a node index from a config.
+TEST_F(ConfigurationTest, GetNodeIndex) {
+ FlatbufferDetachedBuffer<Configuration> config =
+ ReadConfig("aos/testdata/good_multinode.json");
+ const Node *pi1 = GetNode(&config.message(), "pi1");
+ const Node *pi2 = GetNode(&config.message(), "pi2");
+
+ EXPECT_EQ(GetNodeIndex(&config.message(), pi1), 0);
+ EXPECT_EQ(GetNodeIndex(&config.message(), pi2), 1);
+}
+
+// Tests that GetNodeOrDie handles both single and multi-node worlds and returns
+// valid nodes.
+TEST_F(ConfigurationDeathTest, GetNodeOrDie) {
+ FlatbufferDetachedBuffer<Configuration> config =
+ ReadConfig("aos/testdata/good_multinode.json");
+ FlatbufferDetachedBuffer<Configuration> config2 =
+ ReadConfig("aos/testdata/good_multinode.json");
+ {
+ // Simple case, nullptr -> nullptr
+ FlatbufferDetachedBuffer<Configuration> single_node_config =
+ ReadConfig("aos/testdata/config1.json");
+ EXPECT_EQ(nullptr, GetNodeOrDie(&single_node_config.message(), nullptr));
+
+ // Confirm that we die when a node is passed in.
+ EXPECT_DEATH(
+ {
+ GetNodeOrDie(&single_node_config.message(),
+ config.message().nodes()->Get(0));
+ },
+ "Provided a node in a single node world.");
+ }
+
+ const Node *pi1 = GetNode(&config.message(), "pi1");
+ // Now try a lookup using a node from a different instance of the config.
+ EXPECT_EQ(pi1,
+ GetNodeOrDie(&config.message(), config2.message().nodes()->Get(0)));
+}
+
} // namespace testing
} // namespace configuration
} // namespace aos
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index e368df2..c6d3755 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -71,14 +71,7 @@
}
int EventLoop::ChannelIndex(const Channel *channel) {
- CHECK(configuration_->channels() != nullptr) << ": No channels";
-
- auto c = std::find(configuration_->channels()->begin(),
- configuration_->channels()->end(), channel);
- CHECK(c != configuration_->channels()->end())
- << ": Channel pointer not found in configuration()->channels()";
-
- return std::distance(configuration()->channels()->begin(), c);
+ return configuration::ChannelIndex(configuration_, channel);
}
void EventLoop::NewSender(RawSender *sender) {