# Boost::thread depends on Boost::atomic, so list it explicitly.
set(BOOST_COMPONENTS
- atomic thread system regex random program_options date_time iostreams)
+ atomic thread system regex random program_options date_time iostreams
+ filesystem)
set(BOOST_HEADER_COMPONENTS container)
if(WITH_MGR)
--- /dev/null
+roles:
+- [mon.a, mds.a, mgr.x, osd.0, osd.1, client.0]
+overrides:
+ ceph:
+ log-whitelist:
+ - MDS in read-only mode
+ - force file system read-only
+tasks:
+- install:
+- ceph:
+- rgw:
+ - client.0
+- exec:
+ client.0:
+ - ceph_test_admin_socket_output --all
)
endif(HAVE_BLKID)
+# ceph_test_admin_socket_output
+
+add_executable(ceph_test_admin_socket_output
+ test_admin_socket_output.cc
+ admin_socket_output.cc
+ admin_socket_output_tests.cc
+ )
+target_link_libraries(ceph_test_admin_socket_output
+ ceph-common
+ ${Boost_FILESYSTEM_LIBRARY}
+ )
+install(TARGETS
+ ceph_test_admin_socket_output
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+
#make check starts here
#following dependencies are run inside make check unit tests
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <iostream>
+#include <boost/filesystem/convenience.hpp> // For extension
+#include <boost/regex.hpp> // For regex, regex_search
+
+#include "common/admin_socket_client.h" // For AdminSocketClient
+#include "common/ceph_json.h" // For JSONParser, JSONObjIter
+#include "include/buffer.h" // For bufferlist
+
+#include "admin_socket_output.h"
+
+void AdminSocketOutput::add_target(const std::string& target) {
+ if (target == "all") {
+ add_target("osd");
+ add_target("mon");
+ add_target("mgr");
+ add_target("mds");
+ add_target("client");
+ return;
+ }
+ targets.insert(target);
+}
+
+void AdminSocketOutput::add_command(const std::string& target,
+ const std::string& command) {
+ auto seek = custom_commands.find(target);
+ if (seek != custom_commands.end()) {
+ seek->second.push_back(command);
+ } else {
+ std::vector<std::string> vec;
+ vec.push_back(command);
+ custom_commands.insert(std::make_pair(target, vec));
+ }
+
+}
+
+void AdminSocketOutput::add_test(const std::string &target,
+ const std::string &command,
+ bool (*test)(std::string &)) {
+ auto seek = tests.find(target);
+ if (seek != tests.end()) {
+ seek->second.push_back(std::make_pair(command, test));
+ } else {
+ std::vector<std::pair<std::string, bool (*)(std::string &)>> vec;
+ vec.push_back(std::make_pair(command, test));
+ tests.insert(std::make_pair(target, vec));
+ }
+}
+
+void AdminSocketOutput::postpone(const std::string &target,
+ const std::string& command) {
+ auto seek = postponed_commands.find(target);
+ if (seek != postponed_commands.end()) {
+ seek->second.push_back(command);
+ } else {
+ std::vector<string> vec;
+ vec.push_back(command);
+ postponed_commands.insert(std::make_pair(target, vec));
+ }
+}
+
+bool AdminSocketOutput::init_sockets() {
+ std::cout << "Initialising sockets" << std::endl;
+ for (const auto &x : bfs::directory_iterator(socketdir)) {
+ std::cout << x.path() << std::endl;
+ if (bfs::extension(x.path()) == ".asok") {
+ for (auto &target : targets) {
+ if (boost::regex_search(x.path().filename().string(),
+ boost::regex(prefix + target + R"(\..*\.asok)"))) {
+ std::cout << "Found " << target << " socket " << x.path()
+ << std::endl;
+ sockets.insert(std::make_pair(target, x.path().string()));
+ targets.erase(target);
+ }
+ }
+ if (targets.empty()) {
+ std::cout << "Found all required sockets" << std::endl;
+ break;
+ }
+ }
+ }
+
+ return !sockets.empty() && targets.empty();
+}
+
+std::pair<std::string, std::string>
+AdminSocketOutput::run_command(AdminSocketClient &client,
+ const std::string raw_command,
+ bool send_untouched) {
+ std::cout << "Sending command \"" << raw_command << "\"" << std::endl;
+ std::string command;
+ std::string output;
+ if (send_untouched) {
+ command = raw_command;
+ } else {
+ command = "{\"prefix\":\"" + raw_command + "\"}";
+ }
+ client.do_request(command, &output);
+ return std::make_pair(command, output);
+}
+
+bool AdminSocketOutput::gather_socket_output() {
+
+ std::cout << "Gathering socket output" << std::endl;
+ for (const auto& socket : sockets) {
+ std::string response;
+ AdminSocketClient client(socket.second);
+ std::cout << std::endl
+ << "Sending request to " << socket << std::endl
+ << std::endl;
+ client.do_request("{\"prefix\":\"help\"}", &response);
+ std::cout << response << '\n';
+
+ JSONParser parser;
+ bool ret = parser.parse(response.c_str(), response.size());
+ if (!ret) {
+ cerr << "parse error" << std::endl;
+ return false;
+ }
+
+ socket_results sresults;
+ JSONObjIter iter = parser.find_first();
+ const auto postponed_iter = postponed_commands.find(socket.first);
+ std::vector<std::string> postponed;
+ if (postponed_iter != postponed_commands.end()) {
+ postponed = postponed_iter->second;
+ }
+ std::cout << "Sending commands to " << socket.first << " socket"
+ << std::endl;
+ for (; !iter.end(); ++iter) {
+ if (std::find(postponed.begin(), postponed.end(), (*iter)->get_name())
+ != std::end(postponed)) {
+ std::cout << "Command \"" << (*iter)->get_name() << "\" postponed"
+ << std::endl;
+ continue;
+ }
+ sresults.insert(run_command(client, (*iter)->get_name()));
+ }
+
+ if (sresults.empty()) {
+ return false;
+ }
+
+ // Custom commands
+ const auto seek = custom_commands.find(socket.first);
+ if (seek != custom_commands.end()) {
+ std::cout << std::endl << "Sending custom commands:" << std::endl;
+ for (const auto& raw_command : seek->second) {
+ sresults.insert(run_command(client, raw_command, true));
+ }
+ }
+
+ // Postponed commands
+ if (!postponed.empty())
+ std::cout << std::endl << "Sending postponed commands" << std::endl;
+ for (const auto& command : postponed) {
+ sresults.insert(run_command(client, command));
+ }
+
+ results.insert(
+ std::pair<std::string, socket_results>(socket.first, sresults));
+
+ }
+
+ return true;
+}
+
+std::string AdminSocketOutput::get_result(const std::string target,
+ const std::string command) const {
+ const auto& target_results = results.find(target);
+ if (target_results == results.end())
+ return std::string("");
+ else {
+ const auto& result = target_results->second.find(command);
+ if (result == target_results->second.end())
+ return std::string("");
+ else
+ return result->second;
+ }
+}
+
+bool AdminSocketOutput::run_tests() const {
+ for (const auto& socket : sockets) {
+ const auto& seek = tests.find(socket.first);
+ if (seek != tests.end()) {
+ std::cout << std::endl;
+ std::cout << "Running tests for " << socket.first << " socket" << std::endl;
+ for (const auto& test : seek->second) {
+ auto result = get_result(socket.first, test.first);
+ if(result.empty()) {
+ std::cout << "Failed to find result for command: " << test.first << std::endl;
+ return false;
+ } else {
+ std::cout << "Running test for command: " << test.first << std::endl;
+ const auto& test_func = test.second;
+ bool res = test_func(result);
+ if (res == false)
+ return false;
+ else
+ std::cout << "Test passed" << std::endl;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void AdminSocketOutput::exec() {
+ ceph_assert(init_directories());
+ ceph_assert(init_sockets());
+ ceph_assert(gather_socket_output());
+ ceph_assert(run_tests());
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_ADMIN_SOCKET_OUTPUT_H
+#define CEPH_ADMIN_SOCKET_OUTPUT_H
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+
+#include <boost/filesystem/path.hpp> // For path
+#include <boost/filesystem/operations.hpp> // For exists, is_directory
+
+namespace bfs = boost::filesystem;
+
+using socket_results = std::map<std::string, std::string>;
+using test_functions =
+ std::vector<std::pair<std::string, bool (*)(std::string &)>>;
+
+class AdminSocketClient;
+
+class AdminSocketOutput {
+public:
+ AdminSocketOutput() {}
+
+ void add_target(const std::string &target);
+ void add_command(const std::string &target, const std::string &command);
+ void add_test(const std::string &target, const std::string &command,
+ bool (*test)(std::string &));
+ void postpone(const std::string &target, const std::string &command);
+
+ void exec();
+
+ void mod_for_vstart() {
+ socketdir = "./out";
+ prefix = "";
+ }
+
+private:
+ bool init_directories() const {
+ std::cout << "Checking " << socketdir << std::endl;
+ return exists(socketdir) && is_directory(socketdir);
+ }
+
+ bool init_sockets();
+ bool gather_socket_output();
+ std::string get_result(const std::string target, const std::string command) const;
+
+ std::pair<std::string, std::string>
+ run_command(AdminSocketClient &client, const std::string raw_command,
+ bool send_untouched = false);
+
+ bool run_tests() const;
+
+ std::set<std::string> targets;
+ std::map<std::string, std::string> sockets;
+ std::map<std::string, socket_results> results;
+ std::map<std::string, std::vector<std::string>> custom_commands;
+ std::map<std::string, std::vector<std::string>> postponed_commands;
+ std::map<std::string, test_functions> tests;
+
+ std::string prefix = "ceph-";
+ bfs::path socketdir = "/var/run/ceph";
+};
+
+#endif // CEPH_ADMIN_SOCKET_OUTPUT_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <string>
+#include <iostream>
+
+#include "common/ceph_json.h"
+
+// Test functions
+
+// Example test function
+/*
+bool test_config_get_admin_socket(std::string& output) {
+ return std::string::npos != output.find("admin_socket") &&
+ std::string::npos != output.rfind(".asok");
+}
+*/
+
+bool test_dump_pgstate_history(std::string &output) {
+ JSONParser parser;
+ bool ret = parser.parse(output.c_str(), output.size());
+ if (!ret) {
+ std::cerr << "test_dump_pgstate_history: parse error" << std::endl;
+ return false;
+ }
+
+ JSONObjIter iter = parser.find_first();
+ for (; !iter.end(); ++iter) {
+ if ((*iter)->get_name() == "pg") {
+ ret = !(*iter)->get_data().empty();
+ if (ret == false) {
+ std::cerr << "test_dump_pgstate_history: pg value empty, failing"
+ << std::endl;
+ std::cerr << "Dumping full output: " << std::endl;
+ std::cerr << output << std::endl;
+ break;
+ }
+ } else if ((*iter)->get_name() == "history") {
+ ret = std::string::npos != (*iter)->get_data().find("epoch") &&
+ std::string::npos != (*iter)->get_data().find("state") &&
+ std::string::npos != (*iter)->get_data().find("Initial") &&
+ std::string::npos != (*iter)->get_data().find("enter") &&
+ std::string::npos != (*iter)->get_data().find("exit");
+ if (ret == false) {
+ std::cerr << "test_dump_pgstate_history: Can't find expected values in "
+ "history object, failing"
+ << std::endl;
+ std::cerr << "Problem output was:" << std::endl;
+ std::cerr << (*iter)->get_data() << std::endl;
+ break;
+ }
+ }
+ }
+ return ret;
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#ifndef CEPH_ADMIN_SOCKET_OUTPUT_TESTS_H
+#define CEPH_ADMIN_SOCKET_OUTPUT_TESTS_H
+
+// Test function declarations, definitions in admin_socket_output_tests.cc
+
+// Example test function
+
+/*
+bool test_config_get_admin_socket(std::string& output);
+*/
+
+bool test_dump_pgstate_history(std::string& output);
+
+#endif // CEPH_ADMIN_SOCKET_OUTPUT_TESTS_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <algorithm> // for move
+#include <iostream> // for ostream
+#include <memory> // for unique_ptr
+#include <string> // for operator<<
+#include <vector> // for vector
+#include <boost/program_options/option.hpp> // for program_opt...
+#include <boost/program_options/options_description.hpp> // for options_des...
+#include <boost/program_options/parsers.hpp> // for basic_comma...
+#include <boost/program_options/variables_map.hpp> // for variables_map
+#include <boost/program_options/parsers.hpp> // for basic_comma...
+
+#include "admin_socket_output.h"
+#include "admin_socket_output_tests.h"
+
+namespace po = boost::program_options;
+
+void usage(po::options_description desc) {
+ std::cout << desc << std::endl;
+}
+
+void handle_unrecognised(std::vector<std::string>&& unrecognised) {
+ for (auto& un : unrecognised) {
+ std::cout << "Unrecognized Parameter: " << un << std::endl;
+ }
+}
+
+// Test functions:
+// See admin_socket_output_tests.h
+
+int main(int argc, char** argv) {
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help,h", "produce help message")
+ ("all", "implies"
+ " --osd"
+ " --mon"
+ " --mgr"
+ " --mds"
+ " --client"
+ " --vstart")
+ ("osd", "Test osd admin socket output")
+ ("mon", "Test mon admin socket output")
+ ("mgr", "Test mgr admin socket output")
+ ("mds", "Test mds admin socket output")
+ ("client", "Test client (includes rgw) admin socket output")
+ ("vstart", "Modify to run in vstart environment")
+ ;
+ auto parsed =
+ po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
+ po::variables_map vm;
+ po::store(parsed, vm);
+ po::notify(vm);
+
+ auto unrecognised = collect_unrecognized(parsed.options, po::include_positional);
+ if(!unrecognised.empty()) {
+ handle_unrecognised(std::move(unrecognised));
+ usage(desc);
+ return 1;
+ }
+ if (vm.count("help") || vm.empty()) {
+ usage(desc);
+ return 2;
+ }
+
+ std::unique_ptr<AdminSocketOutput> asockout(new AdminSocketOutput);
+
+ if (vm.count("vstart")) {
+ asockout->mod_for_vstart();
+ }
+
+ if(vm.count("all")) {
+ asockout->add_target("all");
+ } else {
+ if (vm.count("osd")) {
+ asockout->add_target("osd");
+ }
+ if (vm.count("mon")) {
+ asockout->add_target("mon");
+ }
+ if (vm.count("mgr")) {
+ asockout->add_target("mgr");
+ }
+ if (vm.count("mds")) {
+ asockout->add_target("mds");
+ }
+ if (vm.count("client")) {
+ asockout->add_target("client");
+ }
+ }
+
+ // Postpone commands that may affect later commands
+
+ asockout->postpone("mds", "force_readonly");
+
+ // Custom commands
+
+ //Example:
+ //asockout->add_command("osd", R"({"prefix":"config get", "var":"admin_socket"})");
+
+ // End custom commands
+
+ // Tests
+ //Example:
+ //asockout->add_test("osd", R"({"prefix":"config get", "var":"admin_socket"})", test_config_get_admin_socket);
+
+ asockout->add_test("osd", R"({"prefix":"dump_pgstate_history"})", test_dump_pgstate_history);
+
+ // End tests
+
+ asockout->exec();
+
+ return 0;
+}