blob: 3c89e1eb32ece9b1e287b57832e8d97bbe13129e [file] [log] [blame]
Rolf Badorekef2bf512019-08-20 11:17:15 +03001#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
23using namespace shareddatalayer;
24using namespace shareddatalayer::cli;
25using namespace shareddatalayer::redis;
26
27namespace
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
Rolf Badorek2dcf9402019-10-01 18:33:58 +030078 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
Rolf Badorekef2bf512019-08-20 11:17:15 +030086 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;
Rolf Badorek2dcf9402019-10-01 18:33:58 +0300104 else if (staticDbType == DatabaseConfiguration::DbType::REDIS_SENTINEL)
105 out << "Static DB type: redis-sentinel" << std::endl;
Rolf Badorekef2bf512019-08-20 11:17:15 +0300106 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 }
Rolf Badorek2dcf9402019-10-01 18:33:58 +0300115 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);
Rolf Badorekef2bf512019-08-20 11:17:15 +0300119 }
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;
Rolf Badorek8324d022019-09-17 16:47:20 +0300144 case DatabaseInfo::Discovery::SENTINEL:
145 out << "Discovery type:: SENTINEL" << std::endl;
146 PrintStaticConfiguration(out);
147 break;
Rolf Badorekef2bf512019-08-20 11:17:15 +0300148 }
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
204AUTO_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"));