Skip to content

Commit 575d211

Browse files
Add a fboss2 config history command.
1 parent 5d3de93 commit 575d211

File tree

9 files changed

+477
-0
lines changed

9 files changed

+477
-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
@@ -771,6 +771,7 @@ cpp_library(
771771
"CmdListConfig.cpp",
772772
"commands/config/CmdConfigAppliedInfo.cpp",
773773
"commands/config/CmdConfigReload.cpp",
774+
"commands/config/history/CmdConfigHistory.cpp",
774775
"commands/config/rollback/CmdConfigRollback.cpp",
775776
"commands/config/session/CmdConfigSessionCommit.cpp",
776777
"commands/config/session/CmdConfigSessionDiff.cpp",
@@ -779,6 +780,7 @@ cpp_library(
779780
headers = [
780781
"commands/config/CmdConfigAppliedInfo.h",
781782
"commands/config/CmdConfigReload.h",
783+
"commands/config/history/CmdConfigHistory.h",
782784
"commands/config/rollback/CmdConfigRollback.h",
783785
"commands/config/session/CmdConfigSessionCommit.h",
784786
"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: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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+
tm timeinfo{};
55+
localtime_r(&timeSec, &timeinfo);
56+
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &timeinfo);
57+
58+
// Add milliseconds
59+
long milliseconds = nsec / 1000000;
60+
std::ostringstream oss;
61+
oss << buffer << '.' << std::setfill('0') << std::setw(3) << milliseconds;
62+
return oss.str();
63+
}
64+
65+
// Collect all revision files from the CLI config directory
66+
std::vector<RevisionInfo> collectRevisions(const std::string& cliConfigDir) {
67+
std::vector<RevisionInfo> revisions;
68+
69+
std::error_code ec;
70+
if (!fs::exists(cliConfigDir, ec) || !fs::is_directory(cliConfigDir, ec)) {
71+
// Directory doesn't exist or is not a directory
72+
return revisions;
73+
}
74+
75+
for (const auto& entry : fs::directory_iterator(cliConfigDir, ec)) {
76+
if (ec) {
77+
continue; // Skip entries we can't read
78+
}
79+
80+
if (!entry.is_regular_file(ec)) {
81+
continue; // Skip non-regular files
82+
}
83+
84+
std::string filename = entry.path().filename().string();
85+
int revNum = ConfigSession::extractRevisionNumber(filename);
86+
87+
if (revNum < 0) {
88+
continue; // Skip files that don't match our pattern
89+
}
90+
91+
// Get file metadata using statx to get birth time (creation time)
92+
struct statx stx;
93+
if (statx(
94+
AT_FDCWD, entry.path().c_str(), 0, STATX_BTIME | STATX_UID, &stx) !=
95+
0) {
96+
continue; // Skip if we can't get file stats
97+
}
98+
99+
RevisionInfo info;
100+
info.revisionNumber = revNum;
101+
info.owner = getUsername(stx.stx_uid);
102+
// Use birth time (creation time) if available, otherwise fall back to mtime
103+
if (stx.stx_mask & STATX_BTIME) {
104+
info.commitTimeNsec =
105+
static_cast<int64_t>(stx.stx_btime.tv_sec) * 1000000000 +
106+
stx.stx_btime.tv_nsec;
107+
} else {
108+
info.commitTimeNsec =
109+
static_cast<int64_t>(stx.stx_mtime.tv_sec) * 1000000000 +
110+
stx.stx_mtime.tv_nsec;
111+
}
112+
info.filePath = entry.path().string();
113+
114+
revisions.push_back(info);
115+
}
116+
117+
// Sort by revision number (ascending)
118+
std::sort(
119+
revisions.begin(),
120+
revisions.end(),
121+
[](const RevisionInfo& a, const RevisionInfo& b) {
122+
return a.revisionNumber < b.revisionNumber;
123+
});
124+
125+
return revisions;
126+
}
127+
128+
} // namespace
129+
130+
CmdConfigHistoryTraits::RetType CmdConfigHistory::queryClient(
131+
const HostInfo& hostInfo) {
132+
auto& session = ConfigSession::getInstance();
133+
const std::string cliConfigDir = session.getCliConfigDir();
134+
135+
auto revisions = collectRevisions(cliConfigDir);
136+
137+
if (revisions.empty()) {
138+
return "No config revisions found in " + cliConfigDir;
139+
}
140+
141+
// Build the table
142+
utils::Table table;
143+
table.setHeader({"Revision", "Owner", "Commit Time"});
144+
145+
for (const auto& rev : revisions) {
146+
table.addRow(
147+
{"r" + std::to_string(rev.revisionNumber),
148+
rev.owner,
149+
formatTime(rev.commitTimeNsec)});
150+
}
151+
152+
// Convert table to string
153+
std::ostringstream oss;
154+
oss << table;
155+
return oss.str();
156+
}
157+
158+
void CmdConfigHistory::printOutput(const RetType& tableOutput) {
159+
std::cout << tableOutput << std::endl;
160+
}
161+
162+
} // 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
@@ -56,6 +56,7 @@ cpp_unittest(
5656
name = "cmd_test",
5757
srcs = [
5858
"CmdConfigAppliedInfoTest.cpp",
59+
"CmdConfigHistoryTest.cpp",
5960
"CmdConfigReloadTest.cpp",
6061
"CmdConfigSessionDiffTest.cpp",
6162
"CmdConfigSessionTest.cpp",

0 commit comments

Comments
 (0)