Add Sentinel configuration reading
[ric-plt/sdl.git] / src / cli / testconnectivitycommand.cpp
1 #include <ostream>
2 #include <cstdlib>
3 #include <fstream>
4 #include <string>
5 #include <iostream>
6 #include <boost/property_tree/ptree.hpp>
7 #include <boost/property_tree/json_parser.hpp>
8 #include <chrono>
9 #include <arpa/inet.h>
10 #include <sdl/asyncstorage.hpp>
11 #include <boost/asio.hpp>
12 #include <thread>
13 #include "private/cli/commandmap.hpp"
14 #include "private/configurationpaths.hpp"
15 #include "private/createlogger.hpp"
16 #include "private/engineimpl.hpp"
17 #include "private/databaseconfigurationimpl.hpp"
18 #include "private/configurationreader.hpp"
19 #include "private/redis/databaseinfo.hpp"
20 #include "private/asyncstorageimpl.hpp"
21 #include "private/redis/asyncredisstorage.hpp"
22
23 using namespace shareddatalayer;
24 using namespace shareddatalayer::cli;
25 using namespace shareddatalayer::redis;
26
27 namespace
28 {
29     void handler(std::shared_ptr<shareddatalayer::AsyncStorage> sdl, boost::asio::posix::stream_descriptor& sd)
30     {
31         sdl->handleEvents();
32         sd.async_read_some(boost::asio::null_buffers(), std::bind(handler, sdl, std::ref(sd)));
33     }
34
35     std::shared_ptr<AsyncStorage> createStorage(const std::string& nsStr, std::ostream& out)
36     {
37         try
38         {
39                 std::shared_ptr<AsyncStorage> sdl(AsyncStorage::create());
40             boost::asio::io_service ios;
41             boost::asio::posix::stream_descriptor sd(ios);
42             sd.assign(sdl->fd());
43             sd.async_read_some(boost::asio::null_buffers(), std::bind(handler, sdl, std::ref(sd)));
44             sdl->waitReadyAsync(nsStr, [&ios](const std::error_code& error)
45                                 {
46                                     if (error)
47                                         std::cerr << "SDL waitReadyAsync failed. Error:\n" << error.message() << std::endl;
48                                     ios.stop();
49                                 });
50             ios.run();
51             sd.release();
52             out << "Storage to namespace " << nsStr << " created." << std::endl;
53             return sdl;
54         }
55         catch (const shareddatalayer::Exception& error)
56         {
57             out << "Storage create failed: " << error.what() << std::endl;
58         }
59         return nullptr;
60     }
61
62     std::string getHosts(const DatabaseConfiguration::Addresses& databaseAddresses)
63     {
64         std::string hosts("");
65         for (auto i(databaseAddresses.begin()); i != databaseAddresses.end(); ++i)
66             hosts = hosts + i->getHost() + " ";
67         return hosts;
68     }
69
70     std::string getPorts(const DatabaseConfiguration::Addresses& databaseAddresses)
71     {
72         std::string ports("");
73         for (auto i(databaseAddresses.begin()); i != databaseAddresses.end(); ++i)
74             ports = ports + std::to_string(ntohs(i->getPort())) + " ";
75         return ports;
76     }
77
78     void PrintEnvironmentVariable(std::ostream& out, std::string name)
79     {
80         const auto var(name.c_str());
81         const auto conf(getenv(var));
82         if (conf != nullptr)
83             out << var  << ": " << conf << std::endl;
84     }
85
86     void PrintStaticConfiguration(std::ostream& out)
87     {
88         auto engine(std::make_shared<EngineImpl>());
89         DatabaseConfigurationImpl databaseConfigurationImpl;
90         ConfigurationReader configurationReader(createLogger(SDL_LOG_PREFIX));
91         configurationReader.readDatabaseConfiguration(databaseConfigurationImpl);
92         auto staticAddresses(databaseConfigurationImpl.getServerAddresses());
93         auto defaultAddresses(databaseConfigurationImpl.getDefaultServerAddresses());
94         auto staticDbType(databaseConfigurationImpl.getDbType());
95         if (!staticAddresses.empty())
96         {
97             out << "\nStatic Server Addresses:" << std::endl;
98             out << "Static Host: " << getHosts(staticAddresses) << std::endl;
99             out << "Static Port: " << getPorts(staticAddresses) << std::endl;
100             if (staticDbType == DatabaseConfiguration::DbType::REDIS_CLUSTER)
101                 out << "Static DB type: redis-cluster" << std::endl;
102             else if (staticDbType == DatabaseConfiguration::DbType::REDIS_STANDALONE)
103                 out << "Static DB type: redis-standalone" << std::endl;
104             else if (staticDbType == DatabaseConfiguration::DbType::REDIS_SENTINEL)
105                 out << "Static DB type: redis-sentinel" << std::endl;
106             else
107                 out << "Static DB type not defined" << std::endl;
108         }
109         if (!defaultAddresses.empty() && staticAddresses.empty())
110         {
111             out << "\nDefault Server Addresses:" << std::endl;
112             out << "Default Host: " << getHosts(defaultAddresses) << std::endl;
113             out << "Default Port: " << getPorts(defaultAddresses) << std::endl;
114         }
115         PrintEnvironmentVariable(out, DB_HOST_ENV_VAR_NAME);
116         PrintEnvironmentVariable(out, DB_PORT_ENV_VAR_NAME);
117         PrintEnvironmentVariable(out, SENTINEL_PORT_ENV_VAR_NAME);
118         PrintEnvironmentVariable(out, SENTINEL_MASTER_NAME_ENV_VAR_NAME);
119     }
120
121     void PrintDatabaseInfo(const DatabaseInfo& databaseInfo, std::ostream& out)
122     {
123         out << "Used database configuration (databaseInfo):" << std::endl;
124         out << "Host: " << getHosts(databaseInfo.hosts) << std::endl;
125         out << "Port: " << getPorts(databaseInfo.hosts) << std::endl;
126         switch (databaseInfo.type)
127         {
128             case DatabaseInfo::Type::SINGLE:
129                 out << "Database type: SINGLE" << std::endl;
130                 break;
131             case DatabaseInfo::Type::REDUNDANT:
132                 out << "Database type: REDUNDANT" << std::endl;
133                 break;
134             case DatabaseInfo::Type::CLUSTER:
135                 out << "Database type: CLUSTER" << std::endl;
136                 break;
137         }
138         switch (databaseInfo.discovery)
139         {
140             case DatabaseInfo::Discovery::HIREDIS:
141                 out << "Discovery type:: HIREDIS" << std::endl;
142                 PrintStaticConfiguration(out);
143                 break;
144             case DatabaseInfo::Discovery::SENTINEL:
145                 out << "Discovery type:: SENTINEL" << std::endl;
146                 PrintStaticConfiguration(out);
147                 break;
148         }
149     }
150
151     [[noreturn]] void timeoutThread(const int& timeout)
152     {
153         std::this_thread::sleep_for(std::chrono::seconds(timeout));
154         std::cerr << "Storage create timeout, aborting after " << timeout << " seconds"<< std::endl;
155         PrintStaticConfiguration(std::cerr);
156         std::exit(EXIT_FAILURE);
157     }
158
159     void setTimeout(const int& timeout)
160     {
161         if (timeout)
162         {
163             std::thread t(timeoutThread, timeout);
164             t.detach();
165         }
166     }
167
168     int TestConnectivityCommand(std::ostream& out,
169                                 const boost::program_options::variables_map& map)
170     {
171         const auto ns(map["ns"].as<std::string>());
172         const auto timeout(map["timeout"].as<int>());
173         setTimeout(timeout);
174         auto sdl(createStorage(ns, out));
175         if (sdl != nullptr)
176         {
177             auto asyncStorageImpl(std::dynamic_pointer_cast<AsyncStorageImpl>(sdl));
178             if (asyncStorageImpl != nullptr)
179             {
180                 AsyncStorage& operationalHandler(asyncStorageImpl->getOperationHandler(ns));
181                 AsyncRedisStorage* redisStorage = dynamic_cast<AsyncRedisStorage*>(&operationalHandler);
182                 if (redisStorage != nullptr)
183                 {
184                         auto databaseinfo (redisStorage->getDatabaseInfo());
185                         PrintDatabaseInfo(databaseinfo, out);
186                 }
187                 else
188                 {
189                         // @TODO Improve output for the case if dummy backend is used.
190                     out << "Cannot get AsyncRedisStorage." << std::endl;
191                     return EXIT_FAILURE;
192                 }
193             }
194             else
195             {
196                 out << "Cannot get AsyncStorageImpl." << std::endl;
197                 return EXIT_FAILURE;
198             }
199         }
200         return EXIT_SUCCESS;
201     }
202 }
203
204 AUTO_REGISTER_COMMAND(std::bind(TestConnectivityCommand, std::placeholders::_1, std::placeholders::_3),
205                       "test-connectivity",
206                       "Test SDL backend connectivity",
207                       "Check that SDL database backend is available and show discovered redis host address and port",
208                       CommandMap::Category::UTIL, 30020,
209                       ("ns", boost::program_options::value<std::string>()->default_value("sdltoolns"), "Used namespace")
210                       ("timeout", boost::program_options::value<int>()->default_value(0), "Timeout (in seconds), Default is no timeout"));