Skip to content

Commit a965dbb

Browse files
Add a fboss2 config history command.
1 parent 0215db8 commit a965dbb

File tree

9 files changed

+476
-0
lines changed

9 files changed

+476
-0
lines changed

cmake/CliFboss2.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ add_library(fboss2_config_lib
575575
fboss/cli/fboss2/commands/config/CmdConfigAppliedInfo.cpp
576576
fboss/cli/fboss2/commands/config/CmdConfigReload.h
577577
fboss/cli/fboss2/commands/config/CmdConfigReload.cpp
578+
fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h
579+
fboss/cli/fboss2/commands/config/history/CmdConfigHistory.cpp
578580
fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h
579581
fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.cpp
580582
fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.h

cmake/CliFboss2Test.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
add_executable(fboss2_cmd_test
55
fboss/cli/fboss2/test/TestMain.cpp
66
fboss/cli/fboss2/test/CmdConfigAppliedInfoTest.cpp
7+
fboss/cli/fboss2/test/CmdConfigHistoryTest.cpp
78
fboss/cli/fboss2/test/CmdConfigReloadTest.cpp
89
fboss/cli/fboss2/test/CmdConfigSessionDiffTest.cpp
910
fboss/cli/fboss2/test/CmdConfigSessionTest.cpp

fboss/cli/fboss2/BUCK

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ cpp_library(
766766
"CmdListConfig.cpp",
767767
"commands/config/CmdConfigAppliedInfo.cpp",
768768
"commands/config/CmdConfigReload.cpp",
769+
"commands/config/history/CmdConfigHistory.cpp",
769770
"commands/config/rollback/CmdConfigRollback.cpp",
770771
"commands/config/session/CmdConfigSessionCommit.cpp",
771772
"commands/config/session/CmdConfigSessionDiff.cpp",
@@ -775,6 +776,7 @@ cpp_library(
775776
headers = [
776777
"commands/config/CmdConfigAppliedInfo.h",
777778
"commands/config/CmdConfigReload.h",
779+
"commands/config/history/CmdConfigHistory.h",
778780
"commands/config/rollback/CmdConfigRollback.h",
779781
"commands/config/session/CmdConfigSessionCommit.h",
780782
"commands/config/session/CmdConfigSessionDiff.h",

fboss/cli/fboss2/CmdHandlerImplConfig.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "fboss/cli/fboss2/commands/config/CmdConfigAppliedInfo.h"
1414
#include "fboss/cli/fboss2/commands/config/CmdConfigReload.h"
15+
#include "fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h"
1516
#include "fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h"
1617
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.h"
1718
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.h"
@@ -21,6 +22,7 @@ namespace facebook::fboss {
2122
template void
2223
CmdHandler<CmdConfigAppliedInfo, CmdConfigAppliedInfoTraits>::run();
2324
template void CmdHandler<CmdConfigReload, CmdConfigReloadTraits>::run();
25+
template void CmdHandler<CmdConfigHistory, CmdConfigHistoryTraits>::run();
2426
template void CmdHandler<CmdConfigRollback, CmdConfigRollbackTraits>::run();
2527
template void
2628
CmdHandler<CmdConfigSessionCommit, CmdConfigSessionCommitTraits>::run();

fboss/cli/fboss2/CmdListConfig.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "fboss/cli/fboss2/CmdHandler.h"
1414
#include "fboss/cli/fboss2/commands/config/CmdConfigAppliedInfo.h"
1515
#include "fboss/cli/fboss2/commands/config/CmdConfigReload.h"
16+
#include "fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h"
1617
#include "fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h"
1718
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.h"
1819
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.h"
@@ -27,6 +28,12 @@ const CommandTree& kConfigCommandTree() {
2728
commandHandler<CmdConfigAppliedInfo>,
2829
argTypeHandler<CmdConfigAppliedInfoTraits>},
2930

31+
{"config",
32+
"history",
33+
"Show history of committed config revisions",
34+
commandHandler<CmdConfigHistory>,
35+
argTypeHandler<CmdConfigHistoryTraits>},
36+
3037
{
3138
"config",
3239
"session",
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2004-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
#include "fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h"
12+
#include <fcntl.h>
13+
#include <pwd.h>
14+
#include <sys/stat.h>
15+
#include <cstdint>
16+
#include <ctime>
17+
#include <filesystem>
18+
#include <iomanip>
19+
#include <sstream>
20+
#include <vector>
21+
#include "fboss/cli/fboss2/session/ConfigSession.h"
22+
#include "fboss/cli/fboss2/utils/Table.h"
23+
24+
namespace fs = std::filesystem;
25+
26+
namespace facebook::fboss {
27+
28+
namespace {
29+
30+
struct RevisionInfo {
31+
int revisionNumber;
32+
std::string owner;
33+
int64_t commitTimeNsec; // Commit time in nanoseconds since epoch
34+
std::string filePath;
35+
};
36+
37+
// Get the username from a UID
38+
std::string getUsername(uid_t uid) {
39+
struct passwd* pw = getpwuid(uid);
40+
if (pw) {
41+
return std::string(pw->pw_name);
42+
}
43+
// If we can't resolve the username, return the UID as a string
44+
return "UID:" + std::to_string(uid);
45+
}
46+
47+
// Format time as a human-readable string with milliseconds
48+
std::string formatTime(int64_t timeNsec) {
49+
// Convert nanoseconds to seconds and remaining nanoseconds
50+
std::time_t timeSec = timeNsec / 1000000000;
51+
long nsec = timeNsec % 1000000000;
52+
53+
char buffer[100];
54+
struct tm* timeinfo = std::localtime(&timeSec);
55+
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
56+
57+
// Add milliseconds
58+
long milliseconds = nsec / 1000000;
59+
std::ostringstream oss;
60+
oss << buffer << '.' << std::setfill('0') << std::setw(3) << milliseconds;
61+
return oss.str();
62+
}
63+
64+
// Collect all revision files from the CLI config directory
65+
std::vector<RevisionInfo> collectRevisions(const std::string& cliConfigDir) {
66+
std::vector<RevisionInfo> revisions;
67+
68+
std::error_code ec;
69+
if (!fs::exists(cliConfigDir, ec) || !fs::is_directory(cliConfigDir, ec)) {
70+
// Directory doesn't exist or is not a directory
71+
return revisions;
72+
}
73+
74+
for (const auto& entry : fs::directory_iterator(cliConfigDir, ec)) {
75+
if (ec) {
76+
continue; // Skip entries we can't read
77+
}
78+
79+
if (!entry.is_regular_file(ec)) {
80+
continue; // Skip non-regular files
81+
}
82+
83+
std::string filename = entry.path().filename().string();
84+
int revNum = ConfigSession::extractRevisionNumber(filename);
85+
86+
if (revNum < 0) {
87+
continue; // Skip files that don't match our pattern
88+
}
89+
90+
// Get file metadata using statx to get birth time (creation time)
91+
struct statx stx;
92+
if (statx(
93+
AT_FDCWD, entry.path().c_str(), 0, STATX_BTIME | STATX_UID, &stx) !=
94+
0) {
95+
continue; // Skip if we can't get file stats
96+
}
97+
98+
RevisionInfo info;
99+
info.revisionNumber = revNum;
100+
info.owner = getUsername(stx.stx_uid);
101+
// Use birth time (creation time) if available, otherwise fall back to mtime
102+
if (stx.stx_mask & STATX_BTIME) {
103+
info.commitTimeNsec =
104+
static_cast<int64_t>(stx.stx_btime.tv_sec) * 1000000000 +
105+
stx.stx_btime.tv_nsec;
106+
} else {
107+
info.commitTimeNsec =
108+
static_cast<int64_t>(stx.stx_mtime.tv_sec) * 1000000000 +
109+
stx.stx_mtime.tv_nsec;
110+
}
111+
info.filePath = entry.path().string();
112+
113+
revisions.push_back(info);
114+
}
115+
116+
// Sort by revision number (ascending)
117+
std::sort(
118+
revisions.begin(),
119+
revisions.end(),
120+
[](const RevisionInfo& a, const RevisionInfo& b) {
121+
return a.revisionNumber < b.revisionNumber;
122+
});
123+
124+
return revisions;
125+
}
126+
127+
} // namespace
128+
129+
CmdConfigHistoryTraits::RetType CmdConfigHistory::queryClient(
130+
const HostInfo& hostInfo) {
131+
auto& session = ConfigSession::getInstance();
132+
const std::string cliConfigDir = session.getCliConfigDir();
133+
134+
auto revisions = collectRevisions(cliConfigDir);
135+
136+
if (revisions.empty()) {
137+
return "No config revisions found in " + cliConfigDir;
138+
}
139+
140+
// Build the table
141+
utils::Table table;
142+
table.setHeader({"Revision", "Owner", "Commit Time"});
143+
144+
for (const auto& rev : revisions) {
145+
table.addRow(
146+
{"r" + std::to_string(rev.revisionNumber),
147+
rev.owner,
148+
formatTime(rev.commitTimeNsec)});
149+
}
150+
151+
// Convert table to string
152+
std::ostringstream oss;
153+
oss << table;
154+
return oss.str();
155+
}
156+
157+
void CmdConfigHistory::printOutput(const RetType& tableOutput) {
158+
std::cout << tableOutput << std::endl;
159+
}
160+
161+
} // namespace facebook::fboss
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2004-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
#pragma once
12+
13+
#include "fboss/cli/fboss2/CmdHandler.h"
14+
#include "fboss/cli/fboss2/utils/CmdClientUtils.h"
15+
#include "fboss/cli/fboss2/utils/CmdUtils.h"
16+
17+
namespace facebook::fboss {
18+
19+
struct CmdConfigHistoryTraits : public WriteCommandTraits {
20+
static constexpr utils::ObjectArgTypeId ObjectArgTypeId =
21+
utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE;
22+
using ObjectArgType = std::monostate;
23+
using RetType = std::string;
24+
};
25+
26+
class CmdConfigHistory
27+
: public CmdHandler<CmdConfigHistory, CmdConfigHistoryTraits> {
28+
public:
29+
using ObjectArgType = CmdConfigHistoryTraits::ObjectArgType;
30+
using RetType = CmdConfigHistoryTraits::RetType;
31+
32+
RetType queryClient(const HostInfo& hostInfo);
33+
34+
void printOutput(const RetType& tableOutput);
35+
};
36+
37+
} // namespace facebook::fboss

fboss/cli/fboss2/test/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cpp_unittest(
5555
name = "cmd_test",
5656
srcs = [
5757
"CmdConfigAppliedInfoTest.cpp",
58+
"CmdConfigHistoryTest.cpp",
5859
"CmdConfigReloadTest.cpp",
5960
"CmdConfigSessionDiffTest.cpp",
6061
"CmdConfigSessionTest.cpp",

0 commit comments

Comments
 (0)