_admin_socket是CephContext类的一个属性,在实例化CephContext时,初始化该变量。在rgw中他代表一个线程,提供了一些额外的command line API。
他定义了一些命令行指令:
//在CephContext构造函数中注册以下指令
_admin_hook = new CephContextHook(this);
_admin_socket->register_command("perfcounters_dump", "perfcounters_dump", _admin_hook, "");
_admin_socket->register_command("1", "1", _admin_hook, "");
_admin_socket->register_command("perf dump", "perf dump name=logger,type=CephString,req=false name=counter,type=CephString,req=false", _admin_hook, "dump perfcounters value");
_admin_socket->register_command("perfcounters_schema", "perfcounters_schema", _admin_hook, "");
_admin_socket->register_command("2", "2", _admin_hook, "");
_admin_socket->register_command("perf schema", "perf schema", _admin_hook, "dump perfcounters schema");
_admin_socket->register_command("perf reset", "perf reset name=var,type=CephString", _admin_hook, "perf reset : perf reset all or one perfcounter name" );
_admin_socket->register_command("config show", "config show", _admin_hook, "dump current config settings");
_admin_socket->register_command("config set", "config set name=var,type=CephString name=val,type=CephString,n=N", _admin_hook, "config set [ ...]: set a config variable" );
_admin_socket->register_command("config get", "config get name=var,type=CephString", _admin_hook, "config get : get the config value" );
_admin_socket->register_command("config diff",
"config diff", _admin_hook,
"dump diff of current config and default config");
_admin_socket->register_command("log flush", "log flush", _admin_hook, "flush log entries to log file");
_admin_socket->register_command("log dump", "log dump", _admin_hook, "dump recent log entries to log file");
_admin_socket->register_command("log reopen", "log reopen", _admin_hook, "reopen log file");
//以下是在adminSocket::init()中注册:
m_version_hook = new VersionHook;
register_command("0", "0", m_version_hook, "");
register_command("version", "version", m_version_hook, "get ceph version");
register_command("git_version", "git_version", m_version_hook, "get git sha1");
m_help_hook = new HelpHook(this);
register_command("help", "help", m_help_hook, "list available commands");
m_getdescs_hook = new GetdescsHook(this);
register_command("get_command_descriptions", "get_command_descriptions",
m_getdescs_hook, "list available commands");
以上注册的方法在CephContextHook类中有实现,通过注册回调的方式提供。
_admin_socket是类AdminSocket类的实例,该线程的构建过程如下:
1. 为admin 线程的创建做一些初始化工作:
bool AdminSocket::init(const std::string &path)
{
ldout(m_cct, 5) << "init " << path << dendl;
/* Set up things for the new thread */
std::string err;
int pipe_rd = -1, pipe_wr = -1;
//为线程管理(关闭该线程)创建入口管道
err = create_shutdown_pipe(&pipe_rd, &pipe_wr);
if (!err.empty()) {
lderr(m_cct) << "AdminSocketConfigObs::init: error: " << err << dendl;
return false;
}
int sock_fd;
//创建admin socket并监听指定端口
err = bind_and_listen(path, &sock_fd);
if (!err.empty()) {
lderr(m_cct) << "AdminSocketConfigObs::init: failed: " << err << dendl;
close(pipe_rd);
close(pipe_wr);
return false;
}
/* Create new thread */
m_sock_fd = sock_fd;
m_shutdown_rd_fd = pipe_rd;
m_shutdown_wr_fd = pipe_wr;
m_path = path;
m_version_hook = new VersionHook;
register_command("0", "0", m_version_hook, "");
register_command("version", "version", m_version_hook, "get ceph version");
register_command("git_version", "git_version", m_version_hook, "get git sha1");
m_help_hook = new HelpHook(this);
register_command("help", "help", m_help_hook, "list available commands");
m_getdescs_hook = new GetdescsHook(this);
register_command("get_command_descriptions", "get_command_descriptions",
m_getdescs_hook, "list available commands");
create();
add_cleanup_file(m_path.c_str());
return true;
}
2.创建、绑定、监听admin socket
std::string AdminSocket::bind_and_listen(const std::string &sock_path, int *fd)
{
ldout(m_cct, 5) << "bind_and_listen " << sock_path << dendl;
//1.创建sockaddr_un对象。
struct sockaddr_un address;
if (sock_path.size() > sizeof(address.sun_path) - 1) {
ostringstream oss;
oss << "AdminSocket::bind_and_listen: "
<< "The UNIX domain socket path " << sock_path << " is too long! The "
<< "maximum length on this system is "
<< (sizeof(address.sun_path) - 1);
return oss.str();
}
//2.创建unix domain socket
int sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
int err = errno;
ostringstream oss;
oss << "AdminSocket::bind_and_listen: "
<< "failed to create socket: " << cpp_strerror(err);
return oss.str();
}
//2.设置socket的属性
int r = fcntl(sock_fd, F_SETFD, FD_CLOEXEC);
if (r < 0) {
r = errno;
VOID_TEMP_FAILURE_RETRY(::close(sock_fd));
ostringstream oss;
oss << "AdminSocket::bind_and_listen: failed to fcntl on socket: " << cpp_strerror(r);
return oss.str();
}
//清空address实例,并初始化sum_family属性
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
//把sock_path按照指定格式赋值给address.sum_path。
snprintf(address.sun_path, sizeof(address.sun_path),
"%s", sock_path.c_str());
//3.把sock_fd与address绑定。
if (::bind(sock_fd, (struct sockaddr*)&address,
sizeof(struct sockaddr_un)) != 0) {
int err = errno;
//判断该sokcet path是否已被使用。
if (err == EADDRINUSE) {
AdminSocketClient client(sock_path);
bool ok;
client.ping(&ok);
if (ok) {
ldout(m_cct, 20) << "socket " << sock_path << " is in use" << dendl;
err = EEXIST;
} else {
ldout(m_cct, 20) << "unlink stale file " << sock_path << dendl;
VOID_TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));
if (::bind(sock_fd, (struct sockaddr*)&address,
sizeof(struct sockaddr_un)) == 0) {
err = 0;
} else {
err = errno;
}
}
}
if (err != 0) {
ostringstream oss;
oss << "AdminSocket::bind_and_listen: "
<< "failed to bind the UNIX domain socket to '" << sock_path
<< "': " << cpp_strerror(err);
close(sock_fd);
return oss.str();
}
}
//4.socket监听指定端口5
if (listen(sock_fd, 5) != 0) {
int err = errno;
ostringstream oss;
oss << "AdminSocket::bind_and_listen: "
<< "failed to listen to socket: " << cpp_strerror(err);
close(sock_fd);
VOID_TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));
return oss.str();
}
*fd = sock_fd;
return "";
}
该线程的主要逻辑
void* AdminSocket::entry()
{
ldout(m_cct, 5) << "entry start" << dendl;
while (true) {
struct pollfd fds[2];
memset(fds, 0, sizeof(fds));
fds[0].fd = m_sock_fd;
fds[0].events = POLLIN | POLLRDBAND;
fds[1].fd = m_shutdown_rd_fd;
fds[1].events = POLLIN | POLLRDBAND;
int ret = poll(fds, 2, -1);
if (ret < 0) {
int err = errno;
if (err == EINTR) {
continue;
}
lderr(m_cct) << "AdminSocket: poll(2) error: '"
<< cpp_strerror(err) << dendl;
return PFL_FAIL;
}
//接收指令,并找到与之对对应的函数,执行。
if (fds[0].revents & POLLIN) {
// Send out some data
do_accept();
}
//关闭当前线程
if (fds[1].revents & POLLIN) {
// Parent wants us to shut down
return PFL_SUCCESS;
}
}
ldout(m_cct, 5) << "entry exit" << dendl;
}
3.接收请求并处理
bool AdminSocket::do_accept()
{
struct sockaddr_un address;
socklen_t address_length = sizeof(address);
ldout(m_cct, 30) << "AdminSocket: calling accept" << dendl;
int connection_fd = accept(m_sock_fd, (struct sockaddr*) &address,
&address_length);
ldout(m_cct, 30) << "AdminSocket: finished accept" << dendl;
if (connection_fd < 0) {
int err = errno;
lderr(m_cct) << "AdminSocket: do_accept error: '"
<< cpp_strerror(err) << dendl;
return false;
}
char cmd[1024];
int pos = 0;
string c;
while (1) {
int ret = safe_read(connection_fd, &cmd[pos], 1);
if (ret <= 0) {
lderr(m_cct) << "AdminSocket: error reading request code: "
<< cpp_strerror(ret) << dendl;
close(connection_fd);
return false;
}
//ldout(m_cct, 0) << "AdminSocket read byte " << (int)cmd[pos] << " pos " << pos << dendl;
if (cmd[0] == '\0') {
// old protocol: __be32
if (pos == 3 && cmd[0] == '\0') {
switch (cmd[3]) {
case 0:
c = "0";
break;
case 1:
c = "perfcounters_dump";
break;
case 2:
c = "perfcounters_schema";
break;
default:
c = "foo";
break;
}
break;
}
} else {
// new protocol: null or \n terminated string
if (cmd[pos] == '\n' || cmd[pos] == '\0') {
cmd[pos] = '\0';
c = cmd;
break;
}
}
pos++;
}
bool rval = false;
map<string, cmd_vartype> cmdmap;
string format;
vector<string> cmdvec;
stringstream errss;
cmdvec.push_back(cmd);
if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) {
ldout(m_cct, 0) << "AdminSocket: " << errss.rdbuf() << dendl;
return false;
}
cmd_getval(m_cct, cmdmap, "format", format);
if (format != "json" && format != "json-pretty" &&
format != "xml" && format != "xml-pretty")
format = "json-pretty";
cmd_getval(m_cct, cmdmap, "prefix", c);
m_lock.Lock();
map<string,AdminSocketHook*>::iterator p;
string match = c;
while (match.size()) {
p = m_hooks.find(match);
if (p != m_hooks.end())
break;
// drop right-most word
size_t pos = match.rfind(' ');
if (pos == std::string::npos) {
match.clear(); // we fail
break;
} else {
match.resize(pos);
}
}
bufferlist out;
if (p == m_hooks.end()) {
lderr(m_cct) << "AdminSocket: request '" << c << "' not defined" << dendl;
} else {
string args;
if (match != c)
args = c.substr(match.length() + 1);
bool success = p->second->call(match, cmdmap, format, out);
if (!success) {
ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args
<< "' to " << p->second << " failed" << dendl;
out.append("failed");
} else {
ldout(m_cct, 5) << "AdminSocket: request '" << match << "' '" << args
<< "' to " << p->second
<< " returned " << out.length() << " bytes" << dendl;
}
uint32_t len = htonl(out.length());
int ret = safe_write(connection_fd, &len, sizeof(len));
if (ret < 0) {
lderr(m_cct) << "AdminSocket: error writing response length "
<< cpp_strerror(ret) << dendl;
} else {
if (out.write_fd(connection_fd) >= 0)
rval = true;
}
}
m_lock.Unlock();
VOID_TEMP_FAILURE_RETRY(close(connection_fd));
return rval;
}
回调被注册的回调函数,AdminSocketHook是一个类,其中定义了一个钩子,如果要提供在admin 线程中扩展新的方法,也可以通过派生AdminSocketHook的子类,来实现,其中一定要实现父类中的call(…)方法即可。
AdminSocketHook类:
class AdminSocketHook {
public:
virtual bool call(std::string command, cmdmap_t &cmdmap, std::string format,
bufferlist& out) = 0;
virtual ~AdminSocketHook() {}
};
派生的子类CephContextHook:
class CephContextHook : public AdminSocketHook {
CephContext *m_cct;
public:
CephContextHook(CephContext *cct) : m_cct(cct) {}
bool call(std::string command, cmdmap_t& cmdmap, std::string format,
bufferlist& out) {
m_cct->do_command(command, cmdmap, format, &out);
return true;
}
};
void CephContext::do_command(std::string command, cmdmap_t& cmdmap,
std::string format, bufferlist *out)
{
Formatter *f = Formatter::create(format, "json-pretty", "json-pretty");
stringstream ss;
for (cmdmap_t::iterator it = cmdmap.begin(); it != cmdmap.end(); ++it) {
if (it->first != "prefix") {
ss << it->first << ":" << cmd_vartype_stringify(it->second) << " ";
}
}
lgeneric_dout(this, 1) << "do_command '" << command << "' '"
<< ss.str() << dendl;
if (command == "perfcounters_dump" || command == "1" ||
command == "perf dump") {
std::string logger;
std::string counter;
cmd_getval(this, cmdmap, "logger", logger);
cmd_getval(this, cmdmap, "counter", counter);
_perf_counters_collection->dump_formatted(f, false, logger, counter);
}
else if (command == "perfcounters_schema" || command == "2" ||
command == "perf schema") {
_perf_counters_collection->dump_formatted(f, true);
}
else if (command == "perf reset") {
std::string var;
if (!cmd_getval(this, cmdmap, "var", var)) {
f->dump_string("error", "syntax error: 'perf reset '");
} else {
if(!_perf_counters_collection->reset(var))
f->dump_stream("error") << "Not find: " << var;
}
}
else {
string section = command;
boost::replace_all(section, " ", "_");
f->open_object_section(section.c_str());
if (command == "config show") {
_conf->show_config(f);
}
else if (command == "config set") {
std::string var;
std::vector<std::string> val;
if (!(cmd_getval(this, cmdmap, "var", var)) ||
!(cmd_getval(this, cmdmap, "val", val))) {
f->dump_string("error", "syntax error: 'config set '" );
} else {
// val may be multiple words
string valstr = str_join(val, " ");
int r = _conf->set_val(var.c_str(), valstr.c_str());
if (r < 0) {
f->dump_stream("error") << "error setting '" << var << "' to '" << valstr << "': " << cpp_strerror(r);
} else {
ostringstream ss;
_conf->apply_changes(&ss);
f->dump_string("success", ss.str());
}
}
} else if (command == "config get") {
std::string var;
if (!cmd_getval(this, cmdmap, "var", var)) {
f->dump_string("error", "syntax error: 'config get '");
} else {
char buf[4096];
memset(buf, 0, sizeof(buf));
char *tmp = buf;
int r = _conf->get_val(var.c_str(), &tmp, sizeof(buf));
if (r < 0) {
f->dump_stream("error") << "error getting '" << var << "': " << cpp_strerror(r);
} else {
f->dump_string(var.c_str(), buf);
}
}
} else if (command == "config diff") {
md_config_t def_conf;
def_conf.set_val("cluster", _conf->cluster);
def_conf.name = _conf->name;
def_conf.set_val("host", _conf->host);
def_conf.apply_changes(NULL);
map<string,pair<string,string> > diff;
set<string> unknown;
def_conf.diff(_conf, &diff, &unknown);
f->open_object_section("diff");
f->open_object_section("current");
for (map<string,pair<string,string> >::iterator p = diff.begin();
p != diff.end(); ++p) {
f->dump_string(p->first.c_str(), p->second.second);
}
f->close_section(); // current
f->open_object_section("defaults");
for (map<string,pair<string,string> >::iterator p = diff.begin();
p != diff.end(); ++p) {
f->dump_string(p->first.c_str(), p->second.first);
}
f->close_section(); // defaults
f->close_section(); // diff
f->open_array_section("unknown");
for (set<string>::iterator p = unknown.begin();
p != unknown.end(); ++p) {
f->dump_string("option", *p);
}
f->close_section(); // unknown
} else if (command == "log flush") {
_log->flush();
}
else if (command == "log dump") {
_log->dump_recent();
}
else if (command == "log reopen") {
_log->reopen_log_file();
}
else {
assert(0 == "registered under wrong command?");
}
f->close_section();
}
f->flush(*out);
delete f;
lgeneric_dout(this, 1) << "do_command '" << command << "' '" << ss.str()
<< "result is " << out->length() << " bytes" << dendl;
}
派生的子类HelpHook:
class HelpHook : public AdminSocketHook {
AdminSocket *m_as;
public:
HelpHook(AdminSocket *as) : m_as(as) {}
bool call(string command, cmdmap_t &cmdmap, string format, bufferlist& out) {
Formatter *f = Formatter::create(format, "json-pretty", "json-pretty");
f->open_object_section("help");
for (map<string,string>::iterator p = m_as->m_help.begin();
p != m_as->m_help.end();
++p) {
if (p->second.length())
f->dump_string(p->first.c_str(), p->second);
}
f->close_section();
ostringstream ss;
f->flush(ss);
out.append(ss.str());
delete f;
return true;
}
};
派生类GetdescsHook :
class GetdescsHook : public AdminSocketHook {
AdminSocket *m_as;
public:
GetdescsHook(AdminSocket *as) : m_as(as) {}
bool call(string command, cmdmap_t &cmdmap, string format, bufferlist& out) {
int cmdnum = 0;
JSONFormatter jf(false);
jf.open_object_section("command_descriptions");
for (map<string,string>::iterator p = m_as->m_descs.begin();
p != m_as->m_descs.end();
++p) {
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
dump_cmd_and_help_to_json(&jf,
secname.str().c_str(),
p->second.c_str(),
m_as->m_help[p->first]);
cmdnum++;
}
jf.close_section(); // command_descriptions
ostringstream ss;
jf.flush(ss);
out.append(ss.str());
return true;
}
};