Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cmake/CliFboss2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -575,8 +575,14 @@ add_library(fboss2_config_lib
fboss/cli/fboss2/commands/config/CmdConfigAppliedInfo.cpp
fboss/cli/fboss2/commands/config/CmdConfigReload.h
fboss/cli/fboss2/commands/config/CmdConfigReload.cpp
fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h
fboss/cli/fboss2/commands/config/history/CmdConfigHistory.cpp
fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h
fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.cpp
fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.h
fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.cpp
fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.h
fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.cpp
fboss/cli/fboss2/session/ConfigSession.h
fboss/cli/fboss2/session/ConfigSession.cpp
fboss/cli/fboss2/CmdListConfig.cpp
Expand Down
2 changes: 2 additions & 0 deletions cmake/CliFboss2Test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
add_executable(fboss2_cmd_test
fboss/cli/fboss2/test/TestMain.cpp
fboss/cli/fboss2/test/CmdConfigAppliedInfoTest.cpp
fboss/cli/fboss2/test/CmdConfigHistoryTest.cpp
fboss/cli/fboss2/test/CmdConfigReloadTest.cpp
fboss/cli/fboss2/test/CmdConfigSessionDiffTest.cpp
fboss/cli/fboss2/test/CmdConfigSessionTest.cpp
fboss/cli/fboss2/test/CmdSetPortStateTest.cpp
fboss/cli/fboss2/test/CmdShowAclTest.cpp
Expand Down
6 changes: 6 additions & 0 deletions fboss/cli/fboss2/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -771,13 +771,19 @@ cpp_library(
"CmdListConfig.cpp",
"commands/config/CmdConfigAppliedInfo.cpp",
"commands/config/CmdConfigReload.cpp",
"commands/config/history/CmdConfigHistory.cpp",
"commands/config/rollback/CmdConfigRollback.cpp",
"commands/config/session/CmdConfigSessionCommit.cpp",
"commands/config/session/CmdConfigSessionDiff.cpp",
"session/ConfigSession.cpp",
],
headers = [
"commands/config/CmdConfigAppliedInfo.h",
"commands/config/CmdConfigReload.h",
"commands/config/history/CmdConfigHistory.h",
"commands/config/rollback/CmdConfigRollback.h",
"commands/config/session/CmdConfigSessionCommit.h",
"commands/config/session/CmdConfigSessionDiff.h",
"session/ConfigSession.h",
],
exported_deps = [
Expand Down
7 changes: 7 additions & 0 deletions fboss/cli/fboss2/CmdHandlerImplConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,21 @@

#include "fboss/cli/fboss2/commands/config/CmdConfigAppliedInfo.h"
#include "fboss/cli/fboss2/commands/config/CmdConfigReload.h"
#include "fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h"
#include "fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h"
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.h"
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.h"

namespace facebook::fboss {

template void
CmdHandler<CmdConfigAppliedInfo, CmdConfigAppliedInfoTraits>::run();
template void CmdHandler<CmdConfigReload, CmdConfigReloadTraits>::run();
template void CmdHandler<CmdConfigHistory, CmdConfigHistoryTraits>::run();
template void CmdHandler<CmdConfigRollback, CmdConfigRollbackTraits>::run();
template void
CmdHandler<CmdConfigSessionCommit, CmdConfigSessionCommitTraits>::run();
template void
CmdHandler<CmdConfigSessionDiff, CmdConfigSessionDiffTraits>::run();

} // namespace facebook::fboss
31 changes: 26 additions & 5 deletions fboss/cli/fboss2/CmdListConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
#include "fboss/cli/fboss2/CmdHandler.h"
#include "fboss/cli/fboss2/commands/config/CmdConfigAppliedInfo.h"
#include "fboss/cli/fboss2/commands/config/CmdConfigReload.h"
#include "fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h"
#include "fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h"
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.h"
#include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.h"

namespace facebook::fboss {

Expand All @@ -25,23 +28,41 @@ const CommandTree& kConfigCommandTree() {
commandHandler<CmdConfigAppliedInfo>,
argTypeHandler<CmdConfigAppliedInfoTraits>},

{"config",
"history",
"Show history of committed config revisions",
commandHandler<CmdConfigHistory>,
argTypeHandler<CmdConfigHistoryTraits>},

{
"config",
"session",
"Manage config session",
{{
"commit",
"Commit the current config session",
commandHandler<CmdConfigSessionCommit>,
argTypeHandler<CmdConfigSessionCommitTraits>,
}},
"commit",
"Commit the current config session",
commandHandler<CmdConfigSessionCommit>,
argTypeHandler<CmdConfigSessionCommitTraits>,
},
{
"diff",
"Show diff between configs (session vs live, session vs revision, or revision vs revision)",
commandHandler<CmdConfigSessionDiff>,
argTypeHandler<CmdConfigSessionDiffTraits>,
}},
},

{"config",
"reload",
"Reload agent configuration",
commandHandler<CmdConfigReload>,
argTypeHandler<CmdConfigReloadTraits>},

{"config",
"rollback",
"Rollback to a previous config revision",
commandHandler<CmdConfigRollback>,
argTypeHandler<CmdConfigRollbackTraits>},
};
sort(root.begin(), root.end());
return root;
Expand Down
4 changes: 4 additions & 0 deletions fboss/cli/fboss2/CmdSubcommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ CLI::App* CmdSubcommands::addCommand(
case utils::ObjectArgTypeId::OBJECT_ARG_TYPE_FAN_PWM:
subCmd->add_option("pwm", args, "Fan PWM (0..100) or 'disable'");
break;
case utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_REVISION_LIST:
subCmd->add_option(
"revisions", args, "Revision(s) in the form 'rN' or 'current'");
break;
case utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_UNINITIALIZE:
case utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE:
break;
Expand Down
161 changes: 161 additions & 0 deletions fboss/cli/fboss2/commands/config/history/CmdConfigHistory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

#include "fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h"
#include <fcntl.h>
#include <pwd.h>
#include <sys/stat.h>
#include <cstdint>
#include <ctime>
#include <filesystem>
#include <iomanip>
#include <sstream>
#include <vector>
#include "fboss/cli/fboss2/session/ConfigSession.h"
#include "fboss/cli/fboss2/utils/Table.h"

namespace fs = std::filesystem;

namespace facebook::fboss {

namespace {

struct RevisionInfo {
int revisionNumber;
std::string owner;
int64_t commitTimeNsec; // Commit time in nanoseconds since epoch
std::string filePath;
};

// Get the username from a UID
std::string getUsername(uid_t uid) {
struct passwd* pw = getpwuid(uid);
if (pw) {
return std::string(pw->pw_name);
}
// If we can't resolve the username, return the UID as a string
return "UID:" + std::to_string(uid);
}

// Format time as a human-readable string with milliseconds
std::string formatTime(int64_t timeNsec) {
// Convert nanoseconds to seconds and remaining nanoseconds
std::time_t timeSec = timeNsec / 1000000000;
long nsec = timeNsec % 1000000000;

char buffer[100];
struct tm* timeinfo = std::localtime(&timeSec);
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);

// Add milliseconds
long milliseconds = nsec / 1000000;
std::ostringstream oss;
oss << buffer << '.' << std::setfill('0') << std::setw(3) << milliseconds;
return oss.str();
}

// Collect all revision files from the CLI config directory
std::vector<RevisionInfo> collectRevisions(const std::string& cliConfigDir) {
std::vector<RevisionInfo> revisions;

std::error_code ec;
if (!fs::exists(cliConfigDir, ec) || !fs::is_directory(cliConfigDir, ec)) {
// Directory doesn't exist or is not a directory
return revisions;
}

for (const auto& entry : fs::directory_iterator(cliConfigDir, ec)) {
if (ec) {
continue; // Skip entries we can't read
}

if (!entry.is_regular_file(ec)) {
continue; // Skip non-regular files
}

std::string filename = entry.path().filename().string();
int revNum = ConfigSession::extractRevisionNumber(filename);

if (revNum < 0) {
continue; // Skip files that don't match our pattern
}

// Get file metadata using statx to get birth time (creation time)
struct statx stx;
if (statx(
AT_FDCWD, entry.path().c_str(), 0, STATX_BTIME | STATX_UID, &stx) !=
0) {
continue; // Skip if we can't get file stats
}

RevisionInfo info;
info.revisionNumber = revNum;
info.owner = getUsername(stx.stx_uid);
// Use birth time (creation time) if available, otherwise fall back to mtime
if (stx.stx_mask & STATX_BTIME) {
info.commitTimeNsec =
static_cast<int64_t>(stx.stx_btime.tv_sec) * 1000000000 +
stx.stx_btime.tv_nsec;
} else {
info.commitTimeNsec =
static_cast<int64_t>(stx.stx_mtime.tv_sec) * 1000000000 +
stx.stx_mtime.tv_nsec;
}
info.filePath = entry.path().string();

revisions.push_back(info);
}

// Sort by revision number (ascending)
std::sort(
revisions.begin(),
revisions.end(),
[](const RevisionInfo& a, const RevisionInfo& b) {
return a.revisionNumber < b.revisionNumber;
});

return revisions;
}

} // namespace

CmdConfigHistoryTraits::RetType CmdConfigHistory::queryClient(
const HostInfo& hostInfo) {
auto& session = ConfigSession::getInstance();
const std::string cliConfigDir = session.getCliConfigDir();

auto revisions = collectRevisions(cliConfigDir);

if (revisions.empty()) {
return "No config revisions found in " + cliConfigDir;
}

// Build the table
utils::Table table;
table.setHeader({"Revision", "Owner", "Commit Time"});

for (const auto& rev : revisions) {
table.addRow(
{"r" + std::to_string(rev.revisionNumber),
rev.owner,
formatTime(rev.commitTimeNsec)});
}

// Convert table to string
std::ostringstream oss;
oss << table;
return oss.str();
}

void CmdConfigHistory::printOutput(const RetType& tableOutput) {
std::cout << tableOutput << std::endl;
}

} // namespace facebook::fboss
37 changes: 37 additions & 0 deletions fboss/cli/fboss2/commands/config/history/CmdConfigHistory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

#pragma once

#include "fboss/cli/fboss2/CmdHandler.h"
#include "fboss/cli/fboss2/utils/CmdClientUtils.h"
#include "fboss/cli/fboss2/utils/CmdUtils.h"

namespace facebook::fboss {

struct CmdConfigHistoryTraits : public WriteCommandTraits {
static constexpr utils::ObjectArgTypeId ObjectArgTypeId =
utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE;
using ObjectArgType = std::monostate;
using RetType = std::string;
};

class CmdConfigHistory
: public CmdHandler<CmdConfigHistory, CmdConfigHistoryTraits> {
public:
using ObjectArgType = CmdConfigHistoryTraits::ObjectArgType;
using RetType = CmdConfigHistoryTraits::RetType;

RetType queryClient(const HostInfo& hostInfo);

void printOutput(const RetType& tableOutput);
};

} // namespace facebook::fboss
57 changes: 57 additions & 0 deletions fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

#include "fboss/cli/fboss2/commands/config/rollback/CmdConfigRollback.h"
#include "fboss/cli/fboss2/session/ConfigSession.h"

namespace facebook::fboss {

CmdConfigRollbackTraits::RetType CmdConfigRollback::queryClient(
const HostInfo& hostInfo,
const utils::RevisionList& revisions) {
auto& session = ConfigSession::getInstance();

// Validate arguments
if (revisions.size() > 1) {
throw std::invalid_argument(
"Too many arguments. Expected 0 or 1 revision specifier.");
}

if (!revisions.empty() && revisions[0] == "current") {
throw std::invalid_argument(
"Cannot rollback to 'current'. Please specify a revision number like 'r42'.");
}

try {
int newRevision;
if (revisions.empty()) {
// No revision specified - rollback to previous revision
newRevision = session.rollback(hostInfo);
} else {
// Specific revision specified
newRevision = session.rollback(hostInfo, revisions[0]);
}
if (newRevision) {
return "Successfully rolled back to r" + std::to_string(newRevision) +
" and config reloaded.";
} else {
return "Failed to create a new revision after rollback.";
}
} catch (const std::exception& ex) {
throw std::runtime_error(
"Failed to rollback config: " + std::string(ex.what()));
}
}

void CmdConfigRollback::printOutput(const RetType& logMsg) {
std::cout << logMsg << std::endl;
}

} // namespace facebook::fboss
Loading
Loading