Skip to content

Commit 918d47c

Browse files
Update cvd stop to handle targeted instances
1 parent 48217be commit 918d47c

9 files changed

Lines changed: 91 additions & 8 deletions

File tree

base/cvd/cuttlefish/flag_parser/gflags_compat.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ Flag GflagsCompatFlag(const std::string& name,
422422
return GflagsCompatFlagImpl<std::string>(name, value, "");
423423
}
424424

425+
Flag GflagsCompatFlag(const std::string& name,
426+
std::vector<unsigned>& value) {
427+
return GflagsCompatFlagImpl<unsigned>(name, value, 0);
428+
}
429+
425430
Flag GflagsCompatFlag(const std::string& name, std::vector<bool>& value,
426431
const bool default_value) {
427432
return GflagsCompatFlagImpl(name, value, default_value);

base/cvd/cuttlefish/flag_parser/gflags_compat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Flag GflagsCompatFlag(const std::string& name, int32_t& value);
4040
Flag GflagsCompatFlag(const std::string& name, size_t& value);
4141
Flag GflagsCompatFlag(const std::string& name, bool& value);
4242
Flag GflagsCompatFlag(const std::string& name, std::vector<std::string>& value);
43+
Flag GflagsCompatFlag(const std::string& name, std::vector<unsigned>& value);
4344
Flag GflagsCompatFlag(const std::string& name, std::vector<bool>& value,
4445
bool default_value);
4546
// Indicates when to assign std::nullopt to the std::optional backing the flag.

base/cvd/cuttlefish/host/commands/cvd/cli/commands/stop.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "cuttlefish/host/commands/cvd/cli/types.h"
3535
#include "cuttlefish/host/commands/cvd/cli/utils.h"
3636
#include "cuttlefish/host/commands/cvd/instances/instance_manager.h"
37+
#include "cuttlefish/host/commands/cvd/instances/local_instance.h"
3738
#include "cuttlefish/host/libs/metrics/device_metrics_orchestration.h"
3839
#include "cuttlefish/result/result.h"
3940

@@ -94,10 +95,24 @@ Result<void> CvdStopCommandHandler::Handle(const CommandRequest& request) {
9495
if (flags.wait_for_launcher_secs > 0) {
9596
launcher_timeout.emplace(flags.wait_for_launcher_secs);
9697
}
98+
99+
std::vector<unsigned> instance_nums;
100+
if (request.Selectors().instance_names) {
101+
for (const auto& name : *request.Selectors().instance_names) {
102+
std::vector<LocalInstance> instances = group.FindByInstanceName(name);
103+
CF_EXPECTF(!instances.empty(), "Instance '{}' not found in group '{}'",
104+
name, group.GroupName());
105+
for (const auto& inst : instances) {
106+
instance_nums.push_back(inst.Id());
107+
}
108+
}
109+
}
110+
97111
Result<void> stop_outcome = instance_manager_.StopInstanceGroup(
98112
group, launcher_timeout,
99113
flags.clear_instance_dirs ? InstanceDirActionOnStop::Clear
100-
: InstanceDirActionOnStop::Keep);
114+
: InstanceDirActionOnStop::Keep,
115+
instance_nums);
101116

102117
GatherVmStopMetrics(group);
103118

base/cvd/cuttlefish/host/commands/cvd/instances/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ cf_cc_library(
241241
"//cuttlefish/result",
242242
"//libbase",
243243
"@abseil-cpp//absl/log",
244+
"@abseil-cpp//absl/strings",
244245
"@fmt",
245246
],
246247
)

base/cvd/cuttlefish/host/commands/cvd/instances/instance_manager.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,21 @@ Result<void> InstanceManager::UpdateInstanceGroup(
214214
Result<void> InstanceManager::StopInstanceGroup(
215215
LocalInstanceGroup& group,
216216
std::optional<std::chrono::seconds> launcher_timeout,
217-
InstanceDirActionOnStop instance_dir_action) {
217+
InstanceDirActionOnStop instance_dir_action,
218+
const std::vector<unsigned>& instance_nums) {
219+
// Validate that the requested instances actually belong to this group
220+
std::set<unsigned> valid_ids;
221+
for (const auto& instance : group.Instances()) {
222+
valid_ids.insert(instance.Id());
223+
}
224+
std::set<unsigned> ids_to_stop;
225+
for (unsigned num : instance_nums) {
226+
CF_EXPECTF(valid_ids.count(num) != 0,
227+
"Instance ID {} does not belong to group '{}'", num,
228+
group.GroupName());
229+
ids_to_stop.insert(num);
230+
}
231+
218232
const auto stop_bin = CF_EXPECT(StopBin(group.HostArtifactsPath()));
219233
const auto stop_bin_path = group.HostArtifactsPath() + "/bin/" + stop_bin;
220234
int wait_for_launcher_secs = 0;
@@ -227,13 +241,23 @@ Result<void> InstanceManager::StopInstanceGroup(
227241
.wait_for_launcher_secs = wait_for_launcher_secs,
228242
.clear_runtime_dirs =
229243
instance_dir_action == InstanceDirActionOnStop::Clear,
244+
.instance_nums = instance_nums,
230245
});
231246
if (!cmd_result.ok()) {
232247
LOG(WARNING)
233248
<< "Warning: error stopping instances for dir \"" + group.HomeDir() +
234249
"\".\nThis can happen if instances are already stopped.\n";
250+
return cmd_result;
251+
}
252+
if (instance_nums.empty()) {
253+
group.SetAllStates(cvd::INSTANCE_STATE_STOPPED);
254+
} else {
255+
for (auto& instance : group.Instances()) {
256+
if (ids_to_stop.count(instance.Id()) > 0) {
257+
instance.SetState(cvd::INSTANCE_STATE_STOPPED);
258+
}
259+
}
235260
}
236-
group.SetAllStates(cvd::INSTANCE_STATE_STOPPED);
237261
// TODO: b/471069557 - diagnose unused
238262
Result<void> unused = instance_db_.UpdateInstanceGroup(group);
239263
return {};

base/cvd/cuttlefish/host/commands/cvd/instances/instance_manager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ class InstanceManager {
8585
Result<void> StopInstanceGroup(
8686
LocalInstanceGroup& group,
8787
std::optional<std::chrono::seconds> launcher_timeout,
88-
InstanceDirActionOnStop instance_dir_action);
88+
InstanceDirActionOnStop instance_dir_action,
89+
const std::vector<unsigned>& instance_nums = {});
8990

9091
private:
9192
struct InternalInstanceDesc {

base/cvd/cuttlefish/host/commands/cvd/instances/stop.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <fmt/core.h>
3333
#include <fmt/ranges.h> // NOLINT(misc-include-cleaner): version difference
3434
#include "absl/log/log.h"
35+
#include "absl/strings/str_join.h"
3536

3637
#include "cuttlefish/common/libs/utils/contains.h"
3738
#include "cuttlefish/common/libs/utils/files.h"
@@ -266,6 +267,9 @@ Result<void> RunStopCvd(StopCvdParams params) {
266267
if (params.clear_runtime_dirs) {
267268
args.push_back("--clear_instance_dirs=true");
268269
}
270+
if (!params.instance_nums.empty()) {
271+
args.push_back(fmt::format("--instance_nums={}", absl::StrJoin(params.instance_nums, ",")));
272+
}
269273
Result<void> cmd_res = RunStopCvdCmd(stopper_path, stop_cvd_envs, args);
270274
if (cmd_res.ok()) {
271275
return {};

base/cvd/cuttlefish/host/commands/cvd/instances/stop.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct StopCvdParams{
3737
std::string home_dir;
3838
int wait_for_launcher_secs;
3939
bool clear_runtime_dirs;
40+
std::vector<unsigned> instance_nums;
4041
};
4142
Result<void> RunStopCvd(StopCvdParams params);
4243

base/cvd/cuttlefish/host/commands/stop/main.cc

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,14 @@ int StopInstance(const CuttlefishConfig& config,
159159
struct FlagVaules {
160160
std::int32_t wait_for_launcher;
161161
bool clear_instance_dirs;
162+
std::vector<unsigned> instance_nums;
162163
bool helpxml;
163164
};
164165

165166
FlagVaules GetFlagValues(int argc, char** argv) {
166167
std::int32_t wait_for_launcher = 5;
167168
bool clear_instance_dirs = false;
169+
std::vector<unsigned> instance_nums;
168170
std::vector<Flag> flags;
169171
flags.emplace_back(
170172
GflagsCompatFlag("wait_for_launcher", wait_for_launcher)
@@ -174,6 +176,9 @@ FlagVaules GetFlagValues(int argc, char** argv) {
174176
GflagsCompatFlag("clear_instance_dirs", clear_instance_dirs)
175177
.Help("If provided, deletes the instance dir after attempting to "
176178
"stop each instance."));
179+
flags.emplace_back(
180+
GflagsCompatFlag("instance_nums", instance_nums)
181+
.Help("Comma-separated list of instance numbers to stop."));
177182
flags.emplace_back(HelpFlag(flags));
178183
bool helpxml = false;
179184
flags.emplace_back(HelpXmlFlag(flags, std::cout, helpxml));
@@ -182,22 +187,48 @@ FlagVaules GetFlagValues(int argc, char** argv) {
182187
auto parse_res = ConsumeFlags(flags, args);
183188
CHECK(parse_res.ok() || helpxml) << "Could not process command line flags.";
184189

185-
return {wait_for_launcher, clear_instance_dirs, helpxml};
190+
return {wait_for_launcher, clear_instance_dirs, instance_nums, helpxml};
186191
}
187192

188193
int StopCvdMain(const std::int32_t wait_for_launcher,
189-
const bool clear_instance_dirs) {
194+
const bool clear_instance_dirs,
195+
const std::vector<unsigned>& instance_nums) {
190196
auto config = CuttlefishConfig::Get();
191197
if (!config) {
192198
LOG(ERROR) << "Failed to obtain config object";
193199
return FallBackStop(FallbackDirs());
194200
}
195201

202+
std::set<unsigned> instance_ids(instance_nums.begin(), instance_nums.end());
203+
196204
int exit_code = 0;
197205
auto instances = config->Instances();
206+
207+
if (!instance_ids.empty() && !instances.empty()) {
208+
unsigned first_instance_id;
209+
if (absl::SimpleAtoi(instances[0].id(), &first_instance_id)) {
210+
if (instance_ids.count(first_instance_id) > 0 &&
211+
instance_ids.size() < instances.size()) {
212+
LOG(ERROR) << "Stopping the first instance (ID: " << first_instance_id
213+
<< ") is not allowed while other instances are remaining. "
214+
<< "Stop the entire group instead.";
215+
return 1;
216+
}
217+
}
218+
}
219+
198220
std::vector<std::future<int>> exit_state_futures;
199221
exit_state_futures.reserve(instances.size());
200222
for (const auto& instance : instances) {
223+
unsigned id;
224+
if (!absl::SimpleAtoi(instance.id(), &id)) {
225+
LOG(ERROR) << "Failed to parse instance ID \"" << instance.id() << "\" as unsigned";
226+
exit_code |= 1;
227+
continue;
228+
}
229+
if (!instance_ids.empty() && instance_ids.count(id) == 0) {
230+
continue;
231+
}
201232
std::future<int> exit_code_from_thread = std::async(
202233
std::launch::async,
203234
[&instance, &config, &wait_for_launcher,
@@ -223,7 +254,7 @@ int StopCvdMain(const std::int32_t wait_for_launcher,
223254
} // namespace cuttlefish
224255

225256
int main(int argc, char** argv) {
226-
const auto [wait_for_launcher, clear_instance_dirs, helpxml] =
257+
const auto [wait_for_launcher, clear_instance_dirs, instance_nums, helpxml] =
227258
cuttlefish::GetFlagValues(argc, argv);
228259
cuttlefish::LogToStderr();
229260

@@ -244,5 +275,5 @@ int main(int argc, char** argv) {
244275
cuttlefish::MetricsReceiver::LogMetricsVMStop();
245276
}
246277

247-
return cuttlefish::StopCvdMain(wait_for_launcher, clear_instance_dirs);
278+
return cuttlefish::StopCvdMain(wait_for_launcher, clear_instance_dirs, instance_nums);
248279
}

0 commit comments

Comments
 (0)