Skip to content
87 changes: 87 additions & 0 deletions cortex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5588,6 +5588,29 @@ def main():
help="Enable verbose output",
)

# Netplan Validator
netplan_parser = subparsers.add_parser(
"netplan", help="Validate and manage Netplan network configuration"
)
netplan_parser.add_argument(
"action", choices=["validate", "diff", "apply", "dry-run"], help="Action to perform"
)
netplan_parser.add_argument(
"config_file", nargs="?", help="Path to netplan config file (auto-detects if not provided)"
)
netplan_parser.add_argument(
"--new-config", help="Path to new config file for diff/apply operations"
)
netplan_parser.add_argument(
"--timeout",
type=int,
default=60,
help="Auto-revert timeout in seconds for dry-run mode (default: 60)",
)
netplan_parser.add_argument(
"-v", "--verbose", action="store_true", help="Enable verbose output"
)
Comment on lines +5591 to +5612
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add the new netplan command to the rich help table for discoverability.
Line 5591 introduces the command, but show_rich_help() still won’t list it, so users running cortex with no args won’t see it.

🤖 Prompt for AI Agents
In `@cortex/cli.py` around lines 5591 - 5612, The new "netplan" subparser is not
added to the discoverable help table, so update show_rich_help() to include an
entry for the netplan command (matching the subparser created in the Netplan
Validator block: the "netplan" parser with actions
["validate","diff","apply","dry-run"] and options like "--new-config" and
"--timeout"); locate the code that builds the rich help table (function
show_rich_help or the structure where other commands are registered) and add a
row/entry for "netplan" with a short description like "Validate and manage
Netplan network configuration" and note key flags (e.g., --new-config,
--timeout, -v/--verbose) to ensure it appears when users run cortex with no
args.


args = parser.parse_args()

# Configure logging based on parsed arguments
Expand Down Expand Up @@ -5808,6 +5831,70 @@ def main():
action=getattr(args, "action", "check"),
verbose=getattr(args, "verbose", False),
)
elif args.command == "netplan":
from cortex.netplan_validator import NetplanValidator

try:
validator = NetplanValidator(args.config_file)

if args.action == "validate":
# Validate configuration
result = validator.validate_file()
validator.print_validation_results(result)
return 0 if result.is_valid else 1

elif args.action == "diff":
# Show diff between current and new config
if not args.new_config:
console.print("[red]Error: --new-config required for diff[/red]")
return 1
validator.show_diff(args.new_config)
return 0

elif args.action == "apply":
# Apply new configuration
if not args.new_config:
console.print("[red]Error: --new-config required for apply[/red]")
return 1

# Show diff first
validator.show_diff(args.new_config)
console.print()

# Confirm with user
from rich.prompt import Confirm

if not Confirm.ask("[yellow]Apply this configuration?[/yellow]"):
console.print("[yellow]Cancelled[/yellow]")
return 0

success, message = validator.apply_config(args.new_config)
if success:
console.print(f"[green]✓[/green] {message}")
return 0
else:
console.print(f"[red]✗[/red] {message}")
return 1

elif args.action == "dry-run":
# Dry-run with auto-revert
if not args.new_config:
console.print("[red]Error: --new-config required for dry-run[/red]")
return 1

confirmed = validator.dry_run_with_revert(args.new_config, args.timeout)
return 0 if confirmed else 1
Comment on lines +5879 to +5886
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Validate --timeout to avoid non-positive values.
Line 5832 passes args.timeout directly; zero/negative values can trigger immediate revert or errors. Add a small guard before invoking the dry-run.

🔧 Suggested fix
                 elif args.action == "dry-run":
                     # Dry-run with auto-revert
                     if not args.new_config:
                         console.print("[red]Error: --new-config required for dry-run[/red]")
                         return 1

+                    if args.timeout <= 0:
+                        console.print("[red]Error: --timeout must be a positive integer[/red]")
+                        return 1
+
                     confirmed = validator.dry_run_with_revert(args.new_config, args.timeout)
                     return 0 if confirmed else 1
🤖 Prompt for AI Agents
In `@cortex/cli.py` around lines 5826 - 5833, The dry-run branch calls
validator.dry_run_with_revert(args.new_config, args.timeout) without validating
args.timeout; add a guard that ensures args.timeout is a positive number (e.g.,
int(args.timeout) > 0) before calling it, printing an error and returning 1 if
it's zero/negative or not an integer, and optionally normalize None to a
sensible default timeout value; update the "dry-run" handling around
args.new_config/args.timeout and the call to validator.dry_run_with_revert to
use the validated/normalized timeout.


except FileNotFoundError as e:
console.print(f"[red]Error:[/red] {str(e)}")
return 1
except Exception as e:
console.print(f"[red]Unexpected error:[/red] {str(e)}")
if args.verbose:
import traceback

traceback.print_exc()
return 1
else:
parser.print_help()
return 1
Expand Down
Loading