//------------------------------------------------------------------------------
// File: RoutingTests.cc
// Author: Elvin-Alin Sindrilaru - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2018 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "gtest/gtest.h"
#include "mgm/RouteEndpoint.hh"
#include "mgm/PathRouting.hh"
//------------------------------------------------------------------------------
// Test basic RouteEndpoint construction and parsing
//------------------------------------------------------------------------------
TEST(Routing, Construction)
{
using eos::mgm::RouteEndpoint;
using eos::mgm::PathRouting;
eos::mgm::PathRouting route(std::chrono::seconds(0));
std::vector inputs {
"eos-dummy1.cern.ch:1094:8000",
"eos-dummy2.cern.ch:2094:9000",
"eos-dummy3.cern.ch:3094:1000",
"eos-dummy4.cern.ch:4094:11000"};
{
// Check parsing and equality operators
RouteEndpoint endpoint1, endpoint2;
ASSERT_FALSE(endpoint1.ParseFromString("wrong.cern.ch"));
ASSERT_FALSE(endpoint1.ParseFromString("wrong.cern.ch:94"));
ASSERT_FALSE(endpoint1.ParseFromString("wrong.cern.ch:94:number"));
ASSERT_FALSE(endpoint1.ParseFromString("wrong.cern.ch:number:number"));
ASSERT_FALSE(endpoint1.ParseFromString("*hostwrong.cern.ch:1094:8000"));
ASSERT_TRUE(endpoint1.ParseFromString(inputs[0]));
ASSERT_TRUE(endpoint2.ParseFromString(inputs[1]));
ASSERT_NE(endpoint1, endpoint2);
ASSERT_TRUE(endpoint2.ParseFromString(inputs[0]));
ASSERT_EQ(endpoint1, endpoint2);
}
for (const auto& input : inputs) {
RouteEndpoint endpoint;
ASSERT_TRUE(endpoint.ParseFromString(input));
ASSERT_TRUE(route.Add("/eos/", std::move(endpoint)));
RouteEndpoint endpoint1;
ASSERT_TRUE(endpoint1.ParseFromString(input));
ASSERT_FALSE(route.Add("/eos/", std::move(endpoint1)));
}
ASSERT_TRUE(route.Remove("/eos/"));
ASSERT_FALSE(route.Remove("/eos/unknown/dir/"));
route.Clear();
}
//------------------------------------------------------------------------------
// Test routing functionality
//------------------------------------------------------------------------------
TEST(Routing, Functionality)
{
using eos::mgm::RouteEndpoint;
using eos::mgm::PathRouting;
// Routing without async updates
eos::mgm::PathRouting route(std::chrono::seconds(0));
std::vector inputs {
"eos-dummy1.cern.ch:1094:8000",
"eos-dummy2.cern.ch:2094:9000",
"eos-dummy3.cern.ch:3094:10000",
"eos-dummy4.cern.ch:4094:11000"};
int count = 0;
// Add several routes to test out the routing
for (const auto& input : inputs) {
RouteEndpoint endpoint;
endpoint.mIsOnline.store(true);
ASSERT_TRUE(endpoint.ParseFromString(input));
ASSERT_TRUE(route.Add("/eos/dir" + std::to_string(++count) + "/",
std::move(endpoint)));
}
eos::common::VirtualIdentity vid = eos::common::VirtualIdentity::Root();
std::string stat_info;
std::string host;
int port;
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("/", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("/unknown", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("/eos/", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("/", "&mgm.fsid=3452&mgm.fid=0e98cc49&mgm.localprefix=/data13",
vid, host, port, stat_info));
// Test http/https redirection
vid.prot = "http";
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/dir1/", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/dir1", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(host == "eos-dummy1.cern.ch");
ASSERT_TRUE(port == 8000);
vid.prot = "https";
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/dir1", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(host == "eos-dummy1.cern.ch");
ASSERT_TRUE(port == 8000);
// Test xrd redirection
vid.prot = "";
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/dir2", nullptr, vid, host, port, stat_info));
ASSERT_TRUE(host == "eos-dummy2.cern.ch");
ASSERT_TRUE(port == 2094);
// Test redirection given a longer path
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/dir3/subdir1/subdir2", nullptr, vid, host,
port, stat_info));
ASSERT_TRUE(host == "eos-dummy3.cern.ch");
ASSERT_TRUE(port == 3094);
// Put all endpoints as offline and not master to trigger stall response
for (const auto& input : inputs) {
RouteEndpoint endpoint;
endpoint.mIsOnline.store(false);
endpoint.mIsMaster.store(false);
ASSERT_TRUE(endpoint.ParseFromString(input));
ASSERT_TRUE(route.Add("/eos/dir/multi_ep/", std::move(endpoint)));
}
ASSERT_TRUE(PathRouting::Status::STALL ==
route.Reroute("/eos/dir/multi_ep/", nullptr, vid, host, port,
stat_info));
// Add online endpoint to trigger rerouting
RouteEndpoint endpoint;
endpoint.mIsOnline.store(true);
endpoint.mIsMaster.store(true);
ASSERT_TRUE(endpoint.ParseFromString("eos-dummy5.cern.ch:5094:12000"));
ASSERT_TRUE(route.Add("/eos/dir/multi_ep/", std::move(endpoint)));
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/dir/multi_ep/", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-dummy5.cern.ch", host.c_str());
ASSERT_EQ(5094, port);
// Assert empty routing
route.Clear();
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("/eos/dir1", nullptr, vid, host, port, stat_info));
std::string listing = "not-empty";
route.GetListing("", listing);
ASSERT_STREQ("", listing.c_str());
}
//------------------------------------------------------------------------------
// Test routing functionality
//------------------------------------------------------------------------------
TEST(Routing, SpecialPaths)
{
using eos::mgm::RouteEndpoint;
using eos::mgm::PathRouting;
eos::mgm::PathRouting route(std::chrono::seconds(0));
eos::common::VirtualIdentity vid = eos::common::VirtualIdentity::Root();
std::string stat_info;
std::string host;
int port;
RouteEndpoint endpoint1;
endpoint1.mIsOnline.store(true);
ASSERT_TRUE(endpoint1.ParseFromString("eos-instance.cern.ch:1094:8000"));
ASSERT_TRUE(route.Add("/eos/instance/", std::move(endpoint1)));
RouteEndpoint endpoint2;
endpoint2.mIsOnline.store(true);
ASSERT_TRUE(endpoint2.ParseFromString("eos-specific.cern.ch:1094:8000"));
ASSERT_TRUE(route.Add("/eos/instance/a/atest/", std::move(endpoint2)));
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/instance/a/atest/.", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-specific.cern.ch", host.c_str());
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/instance/a/atest/subdir/.", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-specific.cern.ch", host.c_str());
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/instance/a/./atest/", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-specific.cern.ch", host.c_str());
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/instance/a/atest/subdir/..", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-specific.cern.ch", host.c_str());
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/instance/a/atest/..", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-instance.cern.ch", host.c_str());
ASSERT_TRUE(PathRouting::Status::REROUTE ==
route.Reroute("/eos/instance/a/../atest/..", nullptr, vid, host, port,
stat_info));
ASSERT_STREQ("eos-instance.cern.ch", host.c_str());
ASSERT_TRUE(PathRouting::Status::NOROUTING ==
route.Reroute("/eos/instance/../a/atest/", nullptr, vid, host, port,
stat_info));
route.Clear();
}