mirror of
https://github.com/CHN-beta/hpcstat.git
synced 2024-10-22 20:18:44 +08:00
完成登陆登出
This commit is contained in:
parent
5e02dfec1a
commit
2d9c3cab76
2
.clangd
2
.clangd
@ -1,3 +1,3 @@
|
|||||||
CompileFlags:
|
CompileFlags:
|
||||||
Add: [ -Wall, -Wextra, -std=c++23 ]
|
Add: [ -Wall, -Wextra, -std=c++23 ]
|
||||||
Compiler: gcc
|
Compiler: g++
|
||||||
|
@ -3,10 +3,6 @@ project(hpcstat VERSION 0.0.0 LANGUAGES CXX)
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY CXX_STANDARD 23)
|
|
||||||
set_property(GLOBAL PROPERTY CXX_STANDARD_REQUIRED ON)
|
|
||||||
set_property(GLOBAL PROPERTY CXX_EXTENSIONS OFF)
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
message("Setting build type to 'Release' as none was specified.")
|
message("Setting build type to 'Release' as none was specified.")
|
||||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
||||||
@ -18,6 +14,7 @@ find_package(Boost REQUIRED COMPONENTS headers filesystem)
|
|||||||
find_package(zxorm REQUIRED)
|
find_package(zxorm REQUIRED)
|
||||||
|
|
||||||
add_executable(hpcstat src/main.cpp)
|
add_executable(hpcstat src/main.cpp)
|
||||||
|
target_compile_features(hpcstat PUBLIC cxx_std_23)
|
||||||
target_link_libraries(hpcstat PRIVATE fmt::fmt Boost::headers Boost::filesystem zxorm::zxorm)
|
target_link_libraries(hpcstat PRIVATE fmt::fmt Boost::headers Boost::filesystem zxorm::zxorm)
|
||||||
|
|
||||||
install(TARGETS hpcstat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS hpcstat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
{
|
{
|
||||||
name = "hpcstat";
|
name = "hpcstat";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
buildInputs = with pkgs.pkgsStatic; [ boost fmt sqlitecpp ];
|
buildInputs = with pkgs.pkgsStatic; [ boost fmt localPackages.zxorm ];
|
||||||
nativeBuildInputs = with pkgs; [ cmake pkg-config ];
|
nativeBuildInputs = with pkgs; [ cmake pkg-config ];
|
||||||
postInstall = "cp ${openssh}/bin/ssh-add $out/bin";
|
postInstall = "cp ${openssh}/bin/ssh-add $out/bin";
|
||||||
};
|
};
|
||||||
|
158
src/main.cpp
158
src/main.cpp
@ -18,18 +18,27 @@ using namespace std::literals;
|
|||||||
// ssh fingerprint -> username
|
// ssh fingerprint -> username
|
||||||
const std::map<std::string, std::string> Username
|
const std::map<std::string, std::string> Username
|
||||||
{
|
{
|
||||||
{ "LNoYfq/SM7l8sFAy325WpC+li+kZl3jwST7TmP72Tz8", "Haonan Chen" }
|
{ "LNoYfq/SM7l8sFAy325WpC+li+kZl3jwST7TmP72Tz8", "Haonan Chen" },
|
||||||
|
{ "VJT5wgkb2RcIeVNTA+/NKxokctbYnJ/KgH6IxrKqIGE", "Bin Gong" },
|
||||||
|
{ "umC3/RB1vS8TQBHsY3IzhOiyqVrOSw2fB3rIpDQSmf4", "Leilei Xiang" },
|
||||||
|
{ "fdq5k13N2DAzIK/2a1Mm4/ZVsDUgT623TSOXsVswxT8", "Junqi Yao" },
|
||||||
|
{ "8USxEYi8ePPpLhk5FYBo2udT7/NFmEe8c2+oQajGXzA", "Enming Zhang" },
|
||||||
|
{ "7bmG24muNsaAZkCy7mQ9Nf2HuNafmvUO+Hf1bId9zts", "Yaping Wu" },
|
||||||
|
{ "dtx0QxdgFrXn2SYxtIRz43jIAH6rLgJidSdTvuTuews", "Jing Li" },
|
||||||
|
{ "8crUO9u4JiVqw3COyjXfzZe87s6XZFhvi0LaY0Mv6bg", "Huahan Zhan" },
|
||||||
|
{ "QkmIYw7rmDEAP+LDWxm6L2/XLnAqTwRUB7B0pxYlOUs", "Na Gao" }
|
||||||
};
|
};
|
||||||
|
|
||||||
// program path, set at start of main, e.g. /gpfs01/.../bin/hpcstat
|
// program path, set at start of main, e.g. /gpfs01/.../bin/hpcstat
|
||||||
std::filesystem::path Program;
|
std::filesystem::path Program;
|
||||||
|
|
||||||
// run a program, wait until it exit, return its stdout and stderr if it return 0, otherwise nullopt
|
// run a program, wait until it exit, return its stdout if it return 0, otherwise nullopt
|
||||||
std::optional<std::string> exec(boost::filesystem::path program, std::vector<std::string> args)
|
std::optional<std::string> exec(boost::filesystem::path program, std::vector<std::string> args)
|
||||||
{
|
{
|
||||||
namespace bp = boost::process;
|
namespace bp = boost::process;
|
||||||
bp::ipstream output;
|
bp::ipstream output;
|
||||||
auto process = bp::child(program, bp::args(args), bp::std_out > output, bp::std_err > bp::null);
|
auto process = bp::child
|
||||||
|
(program, bp::args(args), bp::std_out > output, bp::std_err > stderr, bp::std_in < bp::null);
|
||||||
process.wait();
|
process.wait();
|
||||||
if (process.exit_code() != 0) return std::nullopt;
|
if (process.exit_code() != 0) return std::nullopt;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@ -38,11 +47,12 @@ std::optional<std::string> exec(boost::filesystem::path program, std::vector<std
|
|||||||
}
|
}
|
||||||
|
|
||||||
// detect ssh fingerprint using ssh-add
|
// detect ssh fingerprint using ssh-add
|
||||||
|
// always assume sha256 fingerprint
|
||||||
std::optional<std::vector<std::string>> fingerprints()
|
std::optional<std::vector<std::string>> fingerprints()
|
||||||
{
|
{
|
||||||
auto output =
|
auto output =
|
||||||
exec(Program.replace_filename("ssh-add"), { "-l" });
|
exec(Program.replace_filename("ssh-add"), { "-l" });
|
||||||
if (!output) return std::nullopt;
|
if (!output) { std::cerr << "Failed to get ssh fingerprints\n"; return std::nullopt; }
|
||||||
auto fingerprint = output->substr(0, 47);
|
auto fingerprint = output->substr(0, 47);
|
||||||
// search for all strings that match the fingerprint pattern: sha256:...
|
// search for all strings that match the fingerprint pattern: sha256:...
|
||||||
std::regex pattern(R"r(\b(?:sha|SHA)256:([0-9A-Za-z+/=]{43})\b)r");
|
std::regex pattern(R"r(\b(?:sha|SHA)256:([0-9A-Za-z+/=]{43})\b)r");
|
||||||
@ -60,17 +70,16 @@ std::optional<std::pair<std::string, std::string>> authenticated()
|
|||||||
if (!fps) return std::nullopt;
|
if (!fps) return std::nullopt;
|
||||||
for (auto& fp : *fps)
|
for (auto& fp : *fps)
|
||||||
if (Username.contains(fp)) return std::make_pair(fp, Username.at(fp));
|
if (Username.contains(fp)) return std::make_pair(fp, Username.at(fp));
|
||||||
|
std::cerr << fmt::format("No valid fingerprint found, available fingerprints: {}\n", *fps);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the database
|
// initialize the database
|
||||||
// std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
||||||
struct LoginData
|
struct LoginData
|
||||||
{
|
{
|
||||||
unsigned Id = 0;
|
unsigned Id = 0; long Time = 0;
|
||||||
long Time = 0;
|
std::string Key, SessionId;
|
||||||
std::string Key;
|
std::optional<std::string> Subaccount;
|
||||||
std::string SessionId;
|
|
||||||
bool Interactive;
|
bool Interactive;
|
||||||
};
|
};
|
||||||
using LoginTable = zxorm::Table
|
using LoginTable = zxorm::Table
|
||||||
@ -79,115 +88,114 @@ using LoginTable = zxorm::Table
|
|||||||
zxorm::Column<"id", &LoginData::Id, zxorm::PrimaryKey<>>,
|
zxorm::Column<"id", &LoginData::Id, zxorm::PrimaryKey<>>,
|
||||||
zxorm::Column<"time", &LoginData::Time>,
|
zxorm::Column<"time", &LoginData::Time>,
|
||||||
zxorm::Column<"key", &LoginData::Key>,
|
zxorm::Column<"key", &LoginData::Key>,
|
||||||
zxorm::Column<"session_id", &LoginData::SessionId>
|
zxorm::Column<"session_id", &LoginData::SessionId>,
|
||||||
|
zxorm::Column<"sub_account", &LoginData::Subaccount>,
|
||||||
|
zxorm::Column<"interactive", &LoginData::Interactive>
|
||||||
>;
|
>;
|
||||||
struct LogoutData
|
struct LogoutData { unsigned Id = 0; long Time = 0; std::string SessionId; };
|
||||||
{
|
|
||||||
unsigned Id = 0;
|
|
||||||
long Time = 0;
|
|
||||||
std::string SessionId;
|
|
||||||
};
|
|
||||||
using LogoutTable = zxorm::Table
|
using LogoutTable = zxorm::Table
|
||||||
<
|
<
|
||||||
"logout", LogoutData,
|
"logout", LogoutData,
|
||||||
zxorm::Column<"id", &LogoutData::Id, zxorm::PrimaryKey<>>,
|
zxorm::Column<"id", &LogoutData::Id, zxorm::PrimaryKey<>>,
|
||||||
zxorm::Column<"time", &LogoutData::Time>,
|
zxorm::Column<"time", &LogoutData::Time>, zxorm::Column<"sessionid", &LogoutData::SessionId>
|
||||||
zxorm::Column<"session_id", &LogoutData::SessionId>
|
>;
|
||||||
|
struct SubmitJobData
|
||||||
|
{ unsigned Id = 0; long Time = 0; int JobId; std::string Key, SessionId, Subaccount, SubmitDir, JobCommand; };
|
||||||
|
using SubmitJobTable = zxorm::Table
|
||||||
|
<
|
||||||
|
"submitjob", SubmitJobData,
|
||||||
|
zxorm::Column<"id", &SubmitJobData::Id, zxorm::PrimaryKey<>>,
|
||||||
|
zxorm::Column<"time", &SubmitJobData::Time>,
|
||||||
|
zxorm::Column<"job_id", &SubmitJobData::JobId>,
|
||||||
|
zxorm::Column<"key", &SubmitJobData::Key>,
|
||||||
|
zxorm::Column<"session_id", &SubmitJobData::SessionId>,
|
||||||
|
zxorm::Column<"sub_account", &SubmitJobData::Subaccount>,
|
||||||
|
zxorm::Column<"submit_dir", &SubmitJobData::SubmitDir>,
|
||||||
|
zxorm::Column<"job_command", &SubmitJobData::JobCommand>
|
||||||
|
>;
|
||||||
|
struct FinishJobData { unsigned Id = 0; long Time = 0; int JobId; std::string JobResult; double CpuTime; };
|
||||||
|
using FinishJobTable = zxorm::Table
|
||||||
|
<
|
||||||
|
"finishjob", FinishJobData,
|
||||||
|
zxorm::Column<"id", &FinishJobData::Id, zxorm::PrimaryKey<>>,
|
||||||
|
zxorm::Column<"time", &FinishJobData::Time>,
|
||||||
|
zxorm::Column<"job_id", &FinishJobData::JobId>,
|
||||||
|
zxorm::Column<"job_result", &FinishJobData::JobResult>,
|
||||||
|
zxorm::Column<"cpu_time", &FinishJobData::CpuTime>
|
||||||
|
>;
|
||||||
|
struct QueryJobData { unsigned Id = 0; int JobId; };
|
||||||
|
using QueryJobTable = zxorm::Table
|
||||||
|
<
|
||||||
|
"queryjob", QueryJobData,
|
||||||
|
zxorm::Column<"id", &QueryJobData::Id, zxorm::PrimaryKey<>>, zxorm::Column<"job_id", &QueryJobData::JobId>
|
||||||
>;
|
>;
|
||||||
// struct SubmitData
|
|
||||||
// {
|
|
||||||
// unsigned Id;
|
|
||||||
// long Time;
|
|
||||||
// std::string Key;
|
|
||||||
// std::string SessionId;
|
|
||||||
// unsigned JobId;
|
|
||||||
// std::string JobWorkdir;
|
|
||||||
// std::string JobCommand;
|
|
||||||
// };
|
|
||||||
// struct FinishData
|
|
||||||
// {
|
|
||||||
// unsigned Id;
|
|
||||||
// long Time;
|
|
||||||
// unsigned JobId;
|
|
||||||
// std::string JobResult;
|
|
||||||
// double CpuTime;
|
|
||||||
// };
|
|
||||||
void initdb()
|
void initdb()
|
||||||
{
|
{
|
||||||
auto dbfile = Program.replace_filename("hpcstat.db").string();
|
auto dbfile = Program.replace_filename("hpcstat.db").string();
|
||||||
zxorm::Connection<LoginTable, LogoutTable> conn(dbfile.c_str());
|
zxorm::Connection<LoginTable, LogoutTable, SubmitJobTable, FinishJobTable, QueryJobTable>
|
||||||
|
conn(dbfile.c_str());
|
||||||
conn.create_tables();
|
conn.create_tables();
|
||||||
}
|
}
|
||||||
void writedb(auto value)
|
void writedb(auto value)
|
||||||
{
|
{
|
||||||
auto dbfile = Program.replace_filename("hpcstat.db").string();
|
auto dbfile = Program.replace_filename("hpcstat.db").string();
|
||||||
zxorm::Connection<LoginTable, LogoutTable> conn(dbfile.c_str());
|
zxorm::Connection<LoginTable, LogoutTable, SubmitJobTable, FinishJobTable, QueryJobTable>
|
||||||
|
conn(dbfile.c_str());
|
||||||
value.Time = std::chrono::duration_cast<std::chrono::seconds>
|
value.Time = std::chrono::duration_cast<std::chrono::seconds>
|
||||||
(std::chrono::system_clock::now().time_since_epoch()).count();
|
(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
conn.insert_record(value);
|
conn.insert_record(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool interactive()
|
bool interactive() { return isatty(fileno(stdin)); }
|
||||||
{
|
|
||||||
return isatty(fileno(stdin));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get value of XDG_SESSION_ID
|
// get value of XDG_SESSION_ID
|
||||||
std::optional<std::string> session_id()
|
std::optional<std::string> session_id()
|
||||||
{
|
{
|
||||||
auto value = std::getenv("XDG_SESSION_ID");
|
if (auto value = std::getenv("XDG_SESSION_ID"); !value)
|
||||||
if (!value) return std::nullopt; else return value;
|
{ std::cerr << "Failed to get session id\n"; return std::nullopt; }
|
||||||
|
else return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get value of HPCSTAT_SUBACCOUNT
|
||||||
|
std::optional<std::string> subaccount()
|
||||||
|
{ if (auto value = std::getenv("HPCSTAT_SUBACCOUNT"); value) return value; else return std::nullopt; }
|
||||||
|
|
||||||
int main(int argc, const char** argv)
|
int main(int argc, const char** argv)
|
||||||
{
|
{
|
||||||
std::vector<std::string> args(argv, argv + argc);
|
std::vector<std::string> args(argv, argv + argc);
|
||||||
Program = args[0];
|
Program = args[0];
|
||||||
|
|
||||||
if (args.size() == 1)
|
if (args.size() == 1) { std::cout << "Usage: hpcstat initdb|login|logout|submitjob|finishjob\n"; return 1; }
|
||||||
{
|
|
||||||
std::cout << "Usage: hpcstat initdb|login|logout|submitjob|finishjob\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (args[1] == "initdb")
|
else if (args[1] == "initdb")
|
||||||
initdb();
|
initdb();
|
||||||
else if (args[1] == "login")
|
else if (args[1] == "login")
|
||||||
{
|
{
|
||||||
auto key = authenticated();
|
if (auto key = authenticated(); !key) return 1;
|
||||||
if (!key)
|
else if (auto session = session_id(); !session) return 1;
|
||||||
{
|
else writedb(LoginData
|
||||||
auto fps = fingerprints();
|
{.Key = key->first, .SessionId = *session, .Subaccount = subaccount(), .Interactive = interactive()});
|
||||||
if (!fingerprints())
|
|
||||||
std::cerr << "Failed to get ssh fingerprints\n";
|
|
||||||
else
|
|
||||||
std::cerr << fmt::format("No valid fingerprint found, available fingerprints: {}\n", *fps);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
auto session = session_id();
|
|
||||||
if (!session)
|
|
||||||
{
|
|
||||||
std::cerr << "Failed to get session id\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
writedb(LoginData{.Key = key->first, .SessionId = *session, .Interactive = interactive()});
|
|
||||||
}
|
}
|
||||||
else if (args[1] == "logout")
|
else if (args[1] == "logout")
|
||||||
{
|
{
|
||||||
auto session = session_id();
|
if (auto session = session_id(); !session) return 1;
|
||||||
if (!session)
|
else writedb(LogoutData{.SessionId = *session});
|
||||||
{
|
|
||||||
std::cerr << "Failed to get session id\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
writedb(LogoutData{.SessionId = *session});
|
|
||||||
}
|
}
|
||||||
else if (args[1] == "submitjob")
|
else if (args[1] == "submitjob")
|
||||||
{
|
{
|
||||||
// TODO
|
if (args.size() < 4) { std::cerr << "Usage: hpcstat submitjob <jobid> <submitdir> <jobcommand>\n"; return 1; }
|
||||||
|
if (auto key = authenticated(); !key) return 1;
|
||||||
|
else if (auto session = session_id(); !session) return 1;
|
||||||
|
else writedb(SubmitJobData
|
||||||
|
{
|
||||||
|
.JobId = std::stoi(args[2]), .Key = key->first, .SessionId = *session,
|
||||||
|
.SubmitDir = std::filesystem::current_path().string(),
|
||||||
|
.JobCommand = [&]
|
||||||
|
{ std::stringstream ss; for (int i = 3; i < args.size(); i++) ss << args[i] << " "; return ss.str(); }()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if (args[1] == "finishjob")
|
else if (args[1] == "finishjob")
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user