Add first version
[ric-plt/sdl.git] / src / cli / testconnectivitycommand.cpp
diff --git a/src/cli/testconnectivitycommand.cpp b/src/cli/testconnectivitycommand.cpp
new file mode 100644 (file)
index 0000000..7c4b2ad
--- /dev/null
@@ -0,0 +1,196 @@
+#include <ostream>
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <iostream>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <chrono>
+#include <arpa/inet.h>
+#include <sdl/asyncstorage.hpp>
+#include <boost/asio.hpp>
+#include <thread>
+#include "private/cli/commandmap.hpp"
+#include "private/configurationpaths.hpp"
+#include "private/createlogger.hpp"
+#include "private/engineimpl.hpp"
+#include "private/databaseconfigurationimpl.hpp"
+#include "private/configurationreader.hpp"
+#include "private/redis/databaseinfo.hpp"
+#include "private/asyncstorageimpl.hpp"
+#include "private/redis/asyncredisstorage.hpp"
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::cli;
+using namespace shareddatalayer::redis;
+
+namespace
+{
+    void handler(std::shared_ptr<shareddatalayer::AsyncStorage> sdl, boost::asio::posix::stream_descriptor& sd)
+    {
+        sdl->handleEvents();
+        sd.async_read_some(boost::asio::null_buffers(), std::bind(handler, sdl, std::ref(sd)));
+    }
+
+    std::shared_ptr<AsyncStorage> createStorage(const std::string& nsStr, std::ostream& out)
+    {
+        try
+        {
+               std::shared_ptr<AsyncStorage> sdl(AsyncStorage::create());
+            boost::asio::io_service ios;
+            boost::asio::posix::stream_descriptor sd(ios);
+            sd.assign(sdl->fd());
+            sd.async_read_some(boost::asio::null_buffers(), std::bind(handler, sdl, std::ref(sd)));
+            sdl->waitReadyAsync(nsStr, [&ios](const std::error_code& error)
+                                {
+                                    if (error)
+                                        std::cerr << "SDL waitReadyAsync failed. Error:\n" << error.message() << std::endl;
+                                    ios.stop();
+                                });
+            ios.run();
+            sd.release();
+            out << "Storage to namespace " << nsStr << " created." << std::endl;
+            return sdl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "Storage create failed: " << error.what() << std::endl;
+        }
+        return nullptr;
+    }
+
+    std::string getHosts(const DatabaseConfiguration::Addresses& databaseAddresses)
+    {
+        std::string hosts("");
+        for (auto i(databaseAddresses.begin()); i != databaseAddresses.end(); ++i)
+            hosts = hosts + i->getHost() + " ";
+        return hosts;
+    }
+
+    std::string getPorts(const DatabaseConfiguration::Addresses& databaseAddresses)
+    {
+        std::string ports("");
+        for (auto i(databaseAddresses.begin()); i != databaseAddresses.end(); ++i)
+            ports = ports + std::to_string(ntohs(i->getPort())) + " ";
+        return ports;
+    }
+
+    void PrintStaticConfiguration(std::ostream& out)
+    {
+        auto engine(std::make_shared<EngineImpl>());
+        DatabaseConfigurationImpl databaseConfigurationImpl;
+        ConfigurationReader configurationReader(createLogger(SDL_LOG_PREFIX));
+        configurationReader.readDatabaseConfiguration(databaseConfigurationImpl);
+        auto staticAddresses(databaseConfigurationImpl.getServerAddresses());
+        auto defaultAddresses(databaseConfigurationImpl.getDefaultServerAddresses());
+        auto staticDbType(databaseConfigurationImpl.getDbType());
+        if (!staticAddresses.empty())
+        {
+            out << "\nStatic Server Addresses:" << std::endl;
+            out << "Static Host: " << getHosts(staticAddresses) << std::endl;
+            out << "Static Port: " << getPorts(staticAddresses) << std::endl;
+            if (staticDbType == DatabaseConfiguration::DbType::REDIS_CLUSTER)
+                out << "Static DB type: redis-cluster" << std::endl;
+            else if (staticDbType == DatabaseConfiguration::DbType::REDIS_STANDALONE)
+                out << "Static DB type: redis-standalone" << std::endl;
+            else
+                out << "Static DB type not defined" << std::endl;
+        }
+        if (!defaultAddresses.empty() && staticAddresses.empty())
+        {
+            out << "\nDefault Server Addresses:" << std::endl;
+            out << "Default Host: " << getHosts(defaultAddresses) << std::endl;
+            out << "Default Port: " << getPorts(defaultAddresses) << std::endl;
+        }
+        const auto var(DB_HOST_ENV_VAR_NAME);
+        const auto conf(getenv(var));
+        if (conf != nullptr)
+            out << var  << ": " << conf << std::endl;
+    }
+
+    void PrintDatabaseInfo(const DatabaseInfo& databaseInfo, std::ostream& out)
+    {
+        out << "Used database configuration (databaseInfo):" << std::endl;
+        out << "Host: " << getHosts(databaseInfo.hosts) << std::endl;
+        out << "Port: " << getPorts(databaseInfo.hosts) << std::endl;
+        switch (databaseInfo.type)
+        {
+            case DatabaseInfo::Type::SINGLE:
+                out << "Database type: SINGLE" << std::endl;
+                break;
+            case DatabaseInfo::Type::REDUNDANT:
+                out << "Database type: REDUNDANT" << std::endl;
+                break;
+            case DatabaseInfo::Type::CLUSTER:
+                out << "Database type: CLUSTER" << std::endl;
+                break;
+        }
+        switch (databaseInfo.discovery)
+        {
+            case DatabaseInfo::Discovery::HIREDIS:
+                out << "Discovery type:: HIREDIS" << std::endl;
+                PrintStaticConfiguration(out);
+                break;
+        }
+    }
+
+    [[noreturn]] void timeoutThread(const int& timeout)
+    {
+        std::this_thread::sleep_for(std::chrono::seconds(timeout));
+        std::cerr << "Storage create timeout, aborting after " << timeout << " seconds"<< std::endl;
+        PrintStaticConfiguration(std::cerr);
+        std::exit(EXIT_FAILURE);
+    }
+
+    void setTimeout(const int& timeout)
+    {
+        if (timeout)
+        {
+            std::thread t(timeoutThread, timeout);
+            t.detach();
+        }
+    }
+
+    int TestConnectivityCommand(std::ostream& out,
+                                const boost::program_options::variables_map& map)
+    {
+        const auto ns(map["ns"].as<std::string>());
+        const auto timeout(map["timeout"].as<int>());
+        setTimeout(timeout);
+        auto sdl(createStorage(ns, out));
+        if (sdl != nullptr)
+        {
+            auto asyncStorageImpl(std::dynamic_pointer_cast<AsyncStorageImpl>(sdl));
+            if (asyncStorageImpl != nullptr)
+            {
+               AsyncStorage& operationalHandler(asyncStorageImpl->getOperationHandler(ns));
+               AsyncRedisStorage* redisStorage = dynamic_cast<AsyncRedisStorage*>(&operationalHandler);
+                if (redisStorage != nullptr)
+                {
+                       auto databaseinfo (redisStorage->getDatabaseInfo());
+                       PrintDatabaseInfo(databaseinfo, out);
+                }
+                else
+                {
+                       // @TODO Improve output for the case if dummy backend is used.
+                    out << "Cannot get AsyncRedisStorage." << std::endl;
+                    return EXIT_FAILURE;
+                }
+            }
+            else
+            {
+                out << "Cannot get AsyncStorageImpl." << std::endl;
+                return EXIT_FAILURE;
+            }
+        }
+        return EXIT_SUCCESS;
+    }
+}
+
+AUTO_REGISTER_COMMAND(std::bind(TestConnectivityCommand, std::placeholders::_1, std::placeholders::_3),
+                      "test-connectivity",
+                      "Test SDL backend connectivity",
+                      "Check that SDL database backend is available and show discovered redis host address and port",
+                      CommandMap::Category::UTIL, 30020,
+                      ("ns", boost::program_options::value<std::string>()->default_value("sdltoolns"), "Used namespace")
+                      ("timeout", boost::program_options::value<int>()->default_value(0), "Timeout (in seconds), Default is no timeout"));