-
Notifications
You must be signed in to change notification settings - Fork 736
[cli] add enable/disable-zones commands #3926
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d3c57b4
1e46870
5419e19
b604cb3
4b1683a
59e33e7
d1678ab
c7e2314
be0a243
13b4e3d
73c4cf5
df16559
e1b367e
a0f8c93
3ecc556
76e053e
c319729
c2a338e
5a32132
406cd34
f83e49b
141fe74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| /* | ||
| * Copyright (C) Canonical, Ltd. | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation; version 3. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| */ | ||
|
|
||
| #include "disable_zones.h" | ||
|
|
||
| #include "animated_spinner.h" | ||
| #include "common_callbacks.h" | ||
| #include "common_cli.h" | ||
| #include "multipass/cli/argparser.h" | ||
| #include "multipass/cli/client_common.h" | ||
|
|
||
| #include <QCommandLineOption> | ||
|
|
||
| namespace multipass::cmd | ||
| { | ||
| ReturnCode DisableZones::run(ArgParser* parser) | ||
| { | ||
| if (const auto ret = parse_args(parser); ret != ParseCode::Ok) | ||
| return parser->returnCodeFrom(ret); | ||
|
|
||
| if (ask_for_confirmation) | ||
| { | ||
| if (!term->is_live()) | ||
| throw std::runtime_error{"Unable to query client for confirmation. Use '--force' to forcefully make " | ||
| "unavailable all instances in the specified zones."}; | ||
|
|
||
| if (!confirm()) | ||
| return ReturnCode::CommandFail; | ||
| } | ||
|
|
||
| AnimatedSpinner spinner{cout}; | ||
| const auto use_all_zones = request.zones().empty(); | ||
| const auto message = | ||
| use_all_zones ? "Disabling all zones" : fmt::format("Disabling {}", fmt::join(request.zones(), ", ")); | ||
| spinner.start(message); | ||
|
|
||
| const auto on_success = [&](const ZonesStateReply&) { | ||
| spinner.stop(); | ||
| const auto output_message = use_all_zones ? "All zones disabled" | ||
| : fmt::format("Zone{} disabled: {}", | ||
| request.zones_size() == 1 ? "" : "s", | ||
| fmt::join(request.zones(), ", ")); | ||
| cout << output_message << std::endl; | ||
| return Ok; | ||
| }; | ||
|
|
||
| const auto on_failure = [this, &spinner](const grpc::Status& status) { | ||
| spinner.stop(); | ||
| return standard_failure_handler_for(name(), cerr, status); | ||
| }; | ||
|
|
||
| const auto streaming_callback = make_logging_spinner_callback<ZonesStateRequest, ZonesStateReply>(spinner, cerr); | ||
|
|
||
| return dispatch(&RpcMethod::zones_state, request, on_success, on_failure, streaming_callback); | ||
| } | ||
|
|
||
| std::string DisableZones::name() const | ||
| { | ||
| return "disable-zones"; | ||
| } | ||
|
|
||
| QString DisableZones::short_help() const | ||
| { | ||
| return QStringLiteral("Make zones unavailable"); | ||
| } | ||
|
|
||
| QString DisableZones::description() const | ||
| { | ||
| return QStringLiteral("Makes the given availability zones unavailable. Instances therein are " | ||
| "forcefully switched off and remain unavailable until their zone is re-enabled " | ||
| "(simulating a loss of availability on a cloud provider)."); | ||
| } | ||
|
|
||
| ParseCode DisableZones::parse_args(ArgParser* parser) | ||
| { | ||
| parser->addPositionalArgument("zone", "Name of the zones to make unavailable", "<zone> [<zone> ...]"); | ||
ricab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| QCommandLineOption all_option(all_option_name, "Disable all zones"); | ||
| QCommandLineOption forceOption{"force", "Do not ask for confirmation"}; | ||
| parser->addOptions({all_option, forceOption}); | ||
|
|
||
| if (const auto status = parser->commandParse(this); status != ParseCode::Ok) | ||
| return status; | ||
|
|
||
| if (const auto status = check_for_name_and_all_option_conflict(parser, cerr); status != ParseCode::Ok) | ||
| return status; | ||
|
|
||
| request.set_available(false); | ||
| request.set_verbosity_level(parser->verbosityLevel()); | ||
|
|
||
| if (!parser->isSet(all_option_name)) | ||
| { | ||
| for (const auto& zone_name : parser->positionalArguments()) | ||
| request.add_zones(zone_name.toStdString()); | ||
| } | ||
|
|
||
| ask_for_confirmation = !parser->isSet(forceOption); | ||
|
|
||
| return ParseCode::Ok; | ||
| } | ||
|
|
||
| bool DisableZones::confirm() const | ||
| { | ||
| // joins zones by comma with an 'and' for the last one e.g. 'zone1, zone2 and zone3' | ||
| const auto format_zones = [this] { | ||
ricab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (request.zones().empty()) | ||
| return std::string("all zones"); | ||
| if (request.zones_size() == 1) | ||
| return request.zones(0); | ||
|
|
||
| const auto last_zone = request.zones_size() - 1; | ||
| return fmt::format("{} and {}", | ||
| fmt::join(request.zones().begin(), request.zones().begin() + last_zone, ", "), | ||
| request.zones(last_zone)); | ||
| }; | ||
| const auto message = "This operation will forcefully stop the VMs in " + format_zones() + | ||
| ". Are you sure you want to continue? (Yes/no)"; | ||
|
|
||
| const PlainPrompter prompter{term}; | ||
| auto answer = prompter.prompt(message); | ||
| while (!answer.empty() && !std::regex_match(answer, client::yes_answer) && | ||
| !std::regex_match(answer, client::no_answer)) | ||
| answer = prompter.prompt("Please answer (Yes/No)"); | ||
levkropp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return answer.empty() || std::regex_match(answer, client::yes_answer); | ||
| } | ||
| } // namespace multipass::cmd | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| /* | ||
| * Copyright (C) Canonical, Ltd. | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation; version 3. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| */ | ||
|
|
||
| #ifndef MULTIPASS_DISABLE_ZONES_H | ||
| #define MULTIPASS_DISABLE_ZONES_H | ||
|
|
||
| #include <multipass/cli/command.h> | ||
|
|
||
| namespace multipass::cmd | ||
| { | ||
| class DisableZones : public Command | ||
| { | ||
| public: | ||
| using Command::Command; | ||
| ReturnCode run(ArgParser* parser) override; | ||
| std::string name() const override; | ||
| QString short_help() const override; | ||
| QString description() const override; | ||
|
|
||
| private: | ||
| bool ask_for_confirmation = true; | ||
| ZonesStateRequest request{}; | ||
| ParseCode parse_args(ArgParser* parser); | ||
| bool confirm() const; | ||
| }; | ||
| } // namespace multipass::cmd | ||
| #endif // MULTIPASS_DISABLE_ZONES_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| /* | ||
| * Copyright (C) Canonical, Ltd. | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation; version 3. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| */ | ||
|
|
||
| #include "enable_zones.h" | ||
|
|
||
| #include "animated_spinner.h" | ||
| #include "common_callbacks.h" | ||
| #include "common_cli.h" | ||
| #include "multipass/cli/argparser.h" | ||
| #include "multipass/cli/client_common.h" | ||
|
|
||
| #include <QCommandLineOption> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comes with argparser.h above. |
||
|
|
||
| namespace multipass::cmd | ||
| { | ||
| ReturnCode EnableZones::run(ArgParser* parser) | ||
| { | ||
| if (const auto ret = parse_args(parser); ret != ParseCode::Ok) | ||
| return parser->returnCodeFrom(ret); | ||
|
|
||
| AnimatedSpinner spinner{cout}; | ||
| const auto use_all_zones = request.zones().empty(); | ||
| const auto message = | ||
| use_all_zones ? "Enabling all zones" : fmt::format("Enabling {}", fmt::join(request.zones(), ", ")); | ||
ricab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| spinner.start(message); | ||
|
|
||
| const auto on_success = [&](const ZonesStateReply&) { | ||
| spinner.stop(); | ||
| const auto output_message = use_all_zones ? "All zones enabled" | ||
| : fmt::format("Zone{} enabled: {}", | ||
| request.zones_size() == 1 ? "" : "s", | ||
| fmt::join(request.zones(), ", ")); | ||
| cout << output_message << std::endl; | ||
| return Ok; | ||
| }; | ||
|
|
||
| const auto on_failure = [this, &spinner](const grpc::Status& status) { | ||
| spinner.stop(); | ||
| return standard_failure_handler_for(name(), cerr, status); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I use a bad AZ, I get this error message: Could we have another PR to avoid "AZ" in messages to the user? Let's stick to "zone" as a single nomenclature. "Availability zone" in the help text and docs is fine though IMO. |
||
| }; | ||
|
|
||
| const auto streaming_callback = make_logging_spinner_callback<ZonesStateRequest, ZonesStateReply>(spinner, cerr); | ||
|
|
||
| return dispatch(&RpcMethod::zones_state, request, on_success, on_failure, streaming_callback); | ||
| } | ||
|
|
||
| std::string EnableZones::name() const | ||
| { | ||
| return "enable-zones"; | ||
| } | ||
|
|
||
| QString EnableZones::short_help() const | ||
| { | ||
| return QStringLiteral("Make zones available"); | ||
| } | ||
|
|
||
| QString EnableZones::description() const | ||
| { | ||
| return QStringLiteral("Makes the given availability zones available. Instances therein are started if they were " | ||
| "running when their zone was last disabled."); | ||
| } | ||
|
|
||
| ParseCode EnableZones::parse_args(ArgParser* parser) | ||
| { | ||
| parser->addPositionalArgument("zone", "Name of the zones to make available", "<zone> [<zone> ...]"); | ||
ricab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| QCommandLineOption all_option(all_option_name, "Enable all zones"); | ||
| parser->addOption(all_option); | ||
|
|
||
| if (const auto status = parser->commandParse(this); status != ParseCode::Ok) | ||
| return status; | ||
|
|
||
| if (const auto status = check_for_name_and_all_option_conflict(parser, cerr); status != ParseCode::Ok) | ||
ricab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return status; | ||
|
|
||
| request.set_available(true); | ||
| request.set_verbosity_level(parser->verbosityLevel()); | ||
|
|
||
| if (!parser->isSet(all_option_name)) | ||
| { | ||
| for (const auto& zone_name : parser->positionalArguments()) | ||
| request.add_zones(zone_name.toStdString()); | ||
| } | ||
|
|
||
| return ParseCode::Ok; | ||
| } | ||
| } // namespace multipass::cmd | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /* | ||
| * Copyright (C) Canonical, Ltd. | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation; version 3. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| */ | ||
|
|
||
| #ifndef MULTIPASS_ENABLE_ZONES_H | ||
| #define MULTIPASS_ENABLE_ZONES_H | ||
|
|
||
| #include <multipass/cli/command.h> | ||
|
|
||
| namespace multipass::cmd | ||
| { | ||
| class EnableZones : public Command | ||
| { | ||
| public: | ||
| using Command::Command; | ||
| ReturnCode run(ArgParser* parser) override; | ||
| std::string name() const override; | ||
| QString short_help() const override; | ||
| QString description() const override; | ||
|
|
||
| private: | ||
| ZonesStateRequest request{}; | ||
| ParseCode parse_args(ArgParser* parser); | ||
| }; | ||
| } // namespace multipass::cmd | ||
| #endif // MULTIPASS_ENABLE_ZONES_H |
Uh oh!
There was an error while loading. Please reload this page.