326 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2018 MariaDB Corporation Ab
 | 
						|
 *
 | 
						|
 * Use of this software is governed by the Business Source License included
 | 
						|
 * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | 
						|
 *
 | 
						|
 * Change Date: 2023-01-01
 | 
						|
 *
 | 
						|
 * On the date above, in accordance with the Business Source License, use
 | 
						|
 * of this software will be governed by version 2 or later of the General
 | 
						|
 * Public License.
 | 
						|
 */
 | 
						|
 | 
						|
#include "../mariadbmon.hh"
 | 
						|
#include <algorithm>
 | 
						|
#include <iostream>
 | 
						|
#include <numeric>
 | 
						|
#include <set>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
#include <maxbase/log.hh>
 | 
						|
#include <maxbase/maxbase.hh>
 | 
						|
#include <maxbase/alloc.h>
 | 
						|
#include "../../../../core/internal/server.hh"
 | 
						|
 | 
						|
using std::string;
 | 
						|
using std::cout;
 | 
						|
using maxscale::MonitorServer;
 | 
						|
 | 
						|
// Maximum sizes for array types
 | 
						|
const int MAX_CYCLE_SIZE = 10;
 | 
						|
const int MAX_CYCLES = 5;
 | 
						|
const int MAX_EDGES = 20;
 | 
						|
 | 
						|
class MariaDBMonitor::Test
 | 
						|
{
 | 
						|
    // TODO: Now using C++11 even on Centos 6 so get rid of these
 | 
						|
    typedef struct
 | 
						|
    {
 | 
						|
        int members[MAX_CYCLE_SIZE];
 | 
						|
    } CycleMembers;
 | 
						|
 | 
						|
    typedef struct
 | 
						|
    {
 | 
						|
        CycleMembers cycles[MAX_CYCLES];
 | 
						|
    } CycleArray;
 | 
						|
 | 
						|
    typedef struct
 | 
						|
    {
 | 
						|
        int slave_id;
 | 
						|
        int master_id;
 | 
						|
    } Edge;
 | 
						|
 | 
						|
    typedef struct
 | 
						|
    {
 | 
						|
        Edge edges[MAX_EDGES];
 | 
						|
    } EdgeArray;
 | 
						|
 | 
						|
public:
 | 
						|
    Test(bool use_hostnames);
 | 
						|
    ~Test();
 | 
						|
    int run_tests();
 | 
						|
private:
 | 
						|
    MariaDBMonitor* m_monitor = NULL;
 | 
						|
    int             m_current_test = 0;
 | 
						|
    bool            m_use_hostnames = true;
 | 
						|
 | 
						|
    void init_servers(int count);
 | 
						|
    void clear_servers();
 | 
						|
    void add_replication(EdgeArray edges);
 | 
						|
    int  check_result_cycles(CycleArray expected_cycles);
 | 
						|
 | 
						|
    string         create_servername(int i);
 | 
						|
    string         create_hostname(int i);
 | 
						|
    MariaDBServer* get_server(int i);
 | 
						|
};
 | 
						|
 | 
						|
int main()
 | 
						|
{
 | 
						|
    maxbase::init();
 | 
						|
    maxbase::Log log;
 | 
						|
 | 
						|
    bool use_hostnames = true;
 | 
						|
    MariaDBMonitor::Test tester1(use_hostnames);
 | 
						|
    int rval = tester1.run_tests();
 | 
						|
 | 
						|
    use_hostnames = false;
 | 
						|
    MariaDBMonitor::Test tester2(use_hostnames);
 | 
						|
    rval += tester2.run_tests();
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
MariaDBMonitor::Test::Test(bool use_hostnames)
 | 
						|
    : m_monitor(new MariaDBMonitor("TestMonitor", MXS_MODULE_NAME))
 | 
						|
    , m_use_hostnames(use_hostnames)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
MariaDBMonitor::Test::~Test()
 | 
						|
{
 | 
						|
    delete m_monitor;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Runs all the tests
 | 
						|
 *
 | 
						|
 * @return Number of failures
 | 
						|
 */
 | 
						|
int MariaDBMonitor::Test::run_tests()
 | 
						|
{
 | 
						|
    std::vector<int> results;
 | 
						|
 | 
						|
    // Test 1: 1 server, no replication
 | 
						|
    init_servers(1);
 | 
						|
    // No edges, no cycles
 | 
						|
    results.push_back(check_result_cycles({}));
 | 
						|
 | 
						|
    // Test 2: 4 servers, two cycles with a connection between them
 | 
						|
    init_servers(4);
 | 
						|
    EdgeArray edges2 = {{{1, 2}, {2, 1}, {3, 2}, {3, 4}, {4, 3}}};
 | 
						|
    add_replication(edges2);
 | 
						|
    CycleArray expected_cycles2 = {{{{1, 2}}, {{3, 4}}}};
 | 
						|
    results.push_back(check_result_cycles(expected_cycles2));
 | 
						|
 | 
						|
    // Test 3: 6 servers, with one cycle
 | 
						|
    init_servers(6);
 | 
						|
    EdgeArray edges3 = {{{2, 1}, {3, 2}, {4, 3}, {2, 4}, {5, 1}, {6, 5}, {6, 4}}};
 | 
						|
    add_replication(edges3);
 | 
						|
    CycleArray expected_cycles3 = {{{{2, 3, 4}}}};
 | 
						|
    results.push_back(check_result_cycles(expected_cycles3));
 | 
						|
 | 
						|
    // Test 4: 10 servers, with a big cycle composed of two smaller ones plus non-cycle servers
 | 
						|
    init_servers(10);
 | 
						|
    EdgeArray edges4 =
 | 
						|
    {   {{1, 5}, {2, 1}, {2, 5}, {3, 1}, {3, 4}, {3, 10}, {4, 1}, {5, 6}, {6, 7}, {6, 4}, {7, 8},
 | 
						|
        {8, 6},
 | 
						|
        {9, 8}}};
 | 
						|
    add_replication(edges4);
 | 
						|
    CycleArray expected_cycles4 = {{{{1, 5, 6, 7, 8, 4}}}};
 | 
						|
    results.push_back(check_result_cycles(expected_cycles4));
 | 
						|
 | 
						|
    clear_servers();
 | 
						|
    // Return total error count
 | 
						|
    return std::accumulate(results.begin(), results.end(), 0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Add dummy servers, removing any existing ones.
 | 
						|
 *
 | 
						|
 * @param count How many servers to add. Server id:s will start from 1.
 | 
						|
 */
 | 
						|
void MariaDBMonitor::Test::init_servers(int count)
 | 
						|
{
 | 
						|
    clear_servers();
 | 
						|
    m_monitor->m_settings.shared.assume_unique_hostnames = m_use_hostnames;
 | 
						|
    mxb_assert(m_monitor->m_servers.empty() && m_monitor->m_servers_by_id.empty());
 | 
						|
 | 
						|
    for (int i = 1; i < count + 1; i++)
 | 
						|
    {
 | 
						|
        // Server contents mostly undefined
 | 
						|
        auto base_server = Server::create_test_server();
 | 
						|
        MonitorServer* mon_server = new MonitorServer(base_server, m_monitor->settings().disk_space_limits);
 | 
						|
        MariaDBServer* mariadb_server = new MariaDBServer(mon_server, i - 1, m_monitor->m_settings.shared);
 | 
						|
 | 
						|
        if (m_use_hostnames)
 | 
						|
        {
 | 
						|
            string hostname = create_hostname(i);
 | 
						|
            strcpy(base_server->address, hostname.c_str());
 | 
						|
            base_server->port = i;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            mariadb_server->m_server_id = i;
 | 
						|
        }
 | 
						|
 | 
						|
        m_monitor->m_servers.push_back(mariadb_server);
 | 
						|
        if (!m_use_hostnames)
 | 
						|
        {
 | 
						|
            m_monitor->m_servers_by_id[i] = mariadb_server;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    m_current_test++;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Clear dummy servers and free memory
 | 
						|
 */
 | 
						|
void MariaDBMonitor::Test::clear_servers()
 | 
						|
{
 | 
						|
    m_monitor->m_servers_by_id.clear();
 | 
						|
    for (MariaDBServer* server : m_monitor->m_servers)
 | 
						|
    {
 | 
						|
        delete server->m_server_base->server;
 | 
						|
        delete server->m_server_base;
 | 
						|
        delete server;
 | 
						|
    }
 | 
						|
    m_monitor->m_servers.clear();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Add replication from slave to master
 | 
						|
 *
 | 
						|
 * @param edges An array of pairs of server id:s designating replication
 | 
						|
 */
 | 
						|
void MariaDBMonitor::Test::add_replication(EdgeArray edges)
 | 
						|
{
 | 
						|
    for (auto i = 0; i < MAX_EDGES; i++)
 | 
						|
    {
 | 
						|
        auto slave_id = edges.edges[i].slave_id;
 | 
						|
        auto master_id = edges.edges[i].master_id;
 | 
						|
        if (slave_id == 0 || master_id == 0)
 | 
						|
        {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        SlaveStatus ss;
 | 
						|
        ss.slave_io_running = SlaveStatus::SLAVE_IO_YES;
 | 
						|
        ss.slave_sql_running = true;
 | 
						|
        if (m_use_hostnames)
 | 
						|
        {
 | 
						|
            ss.master_host = create_hostname(master_id);
 | 
						|
            ss.master_port = master_id;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            ss.master_server_id = master_id;
 | 
						|
            ss.seen_connected = true;
 | 
						|
        }
 | 
						|
 | 
						|
        MariaDBServer* slave = get_server(slave_id);
 | 
						|
        slave->m_slave_status.push_back(ss);
 | 
						|
    }
 | 
						|
 | 
						|
    m_monitor->build_replication_graph();
 | 
						|
    m_monitor->find_graph_cycles();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check that the nodes have cycles as is expected. Non-cycled nodes must have cycle = 0.
 | 
						|
 *
 | 
						|
 * @param expected_cycles An array of cycles. Each cycle is an array of server id:s designating the members
 | 
						|
 * @return Number of failures
 | 
						|
 */
 | 
						|
int MariaDBMonitor::Test::check_result_cycles(CycleArray expected_cycles)
 | 
						|
{
 | 
						|
    string test_name = "Test " + std::to_string(m_current_test) + " ("
 | 
						|
        + (m_use_hostnames ? "hostnames" : "server id:s") + "): ";
 | 
						|
    int errors = 0;
 | 
						|
 | 
						|
    // Copy the servers for later comparison.
 | 
						|
    std::set<MariaDBServer*> no_cycle_servers(m_monitor->m_servers.begin(), m_monitor->m_servers.end());
 | 
						|
    std::set<int> used_cycle_ids;
 | 
						|
    for (auto ind_cycles = 0; ind_cycles < MAX_CYCLES; ind_cycles++)
 | 
						|
    {
 | 
						|
        int cycle_id = NodeData::CYCLE_NONE;
 | 
						|
        CycleMembers cycle_member_ids = expected_cycles.cycles[ind_cycles];
 | 
						|
        for (auto ind_servers = 0; ind_servers < MAX_CYCLE_SIZE; ind_servers++)
 | 
						|
        {
 | 
						|
            auto search_id = cycle_member_ids.members[ind_servers];
 | 
						|
            if (search_id == 0)
 | 
						|
            {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            MariaDBServer* cycle_server = get_server(search_id);
 | 
						|
            if (cycle_server->m_node.cycle == NodeData::CYCLE_NONE)
 | 
						|
            {
 | 
						|
                cout << test_name << cycle_server->name() << " is not in a cycle when it should.\n";
 | 
						|
                errors++;
 | 
						|
            }
 | 
						|
            // If this is the first element, check what the cycle id should be for all members of the cycle.
 | 
						|
            else if (cycle_id == NodeData::CYCLE_NONE)
 | 
						|
            {
 | 
						|
                cycle_id = cycle_server->m_node.cycle;
 | 
						|
                if (used_cycle_ids.count(cycle_id) > 0)
 | 
						|
                {
 | 
						|
                    cout << test_name << cycle_server->name() << " is in unexpected cycle "
 | 
						|
                         << cycle_id << ".\n";
 | 
						|
                    errors++;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    used_cycle_ids.insert(cycle_id);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (cycle_server->m_node.cycle != cycle_id)
 | 
						|
            {
 | 
						|
                cout << test_name << cycle_server->name() << " is in cycle " << cycle_server->m_node.cycle
 | 
						|
                     << " when " << cycle_id << " was expected.\n";
 | 
						|
                errors++;
 | 
						|
            }
 | 
						|
            no_cycle_servers.erase(cycle_server);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Check that servers not in expected_cycles are not in a cycle
 | 
						|
    for (MariaDBServer* server : no_cycle_servers)
 | 
						|
    {
 | 
						|
        if (server->m_node.cycle != NodeData::CYCLE_NONE)
 | 
						|
        {
 | 
						|
            cout << server->name() << " is in cycle " << server->m_node.cycle << " when none was expected.\n";
 | 
						|
            errors++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return errors;
 | 
						|
}
 | 
						|
 | 
						|
MariaDBServer* MariaDBMonitor::Test::get_server(int i)
 | 
						|
{
 | 
						|
    auto rval = m_use_hostnames ? m_monitor->get_server(create_hostname(i), i) :
 | 
						|
        m_monitor->get_server(i);
 | 
						|
    mxb_assert(rval);
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
string MariaDBMonitor::Test::create_servername(int i)
 | 
						|
{
 | 
						|
    return "Server" + std::to_string(i);
 | 
						|
}
 | 
						|
 | 
						|
string MariaDBMonitor::Test::create_hostname(int i)
 | 
						|
{
 | 
						|
    return "hostname" + std::to_string(i) + ".mariadb.com";
 | 
						|
}
 |