package monitoring import ( "fmt" "os" "path/filepath" "strconv" "strings" "time" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) // OperationsCLI provides command-line interface for operations management type OperationsCLI struct { manager *OperationsManager configPath string logger *logrus.Logger } // NewOperationsCLI creates a new operations CLI func NewOperationsCLI(configPath string, logger *logrus.Logger) *OperationsCLI { return &OperationsCLI{ configPath: configPath, logger: logger, } } // CreateRootCommand creates the root operations command func (cli *OperationsCLI) CreateRootCommand() *cobra.Command { rootCmd := &cobra.Command{ Use: "operations", Short: "Debian Forge Operations Management", Long: "Manage backup, recovery, and testing operations for Debian Forge", PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return cli.initializeManager() }, } // Add subcommands rootCmd.AddCommand(cli.createBackupCommand()) rootCmd.AddCommand(cli.createRecoveryCommand()) rootCmd.AddCommand(cli.createTestingCommand()) rootCmd.AddCommand(cli.createConfigCommand()) rootCmd.AddCommand(cli.createStatusCommand()) return rootCmd } // initializeManager initializes the operations manager func (cli *OperationsCLI) initializeManager() error { // Load configuration config, err := LoadOperationsConfig(cli.configPath) if err != nil { return fmt.Errorf("failed to load configuration: %w", err) } // Validate configuration configManager := &OperationsConfigManager{configPath: cli.configPath, config: config} if err := configManager.ValidateConfig(); err != nil { return fmt.Errorf("configuration validation failed: %w", err) } // Create operations manager cli.manager = NewOperationsManager(config, cli.logger) return nil } // createBackupCommand creates the backup command func (cli *OperationsCLI) createBackupCommand() *cobra.Command { backupCmd := &cobra.Command{ Use: "backup", Short: "Manage backup operations", Long: "Create, list, and manage backup operations", } // Create backup subcommand createCmd := &cobra.Command{ Use: "create [strategy]", Short: "Create a new backup", Long: "Create a new backup using the specified strategy", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return cli.createBackup(args[0]) }, } // List backups subcommand listCmd := &cobra.Command{ Use: "list", Short: "List available backups", Long: "List all available backup strategies and recent backups", RunE: func(cmd *cobra.Command, args []string) error { return cli.listBackups() }, } // Schedule backup subcommand scheduleCmd := &cobra.Command{ Use: "schedule [schedule]", Short: "Schedule a backup", Long: "Schedule a backup using the specified schedule", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return cli.scheduleBackup(args[0]) }, } backupCmd.AddCommand(createCmd, listCmd, scheduleCmd) return backupCmd } // createRecoveryCommand creates the recovery command func (cli *OperationsCLI) createRecoveryCommand() *cobra.Command { recoveryCmd := &cobra.Command{ Use: "recovery", Short: "Manage recovery operations", Long: "Execute recovery plans and manage recovery procedures", } // Execute recovery subcommand executeCmd := &cobra.Command{ Use: "execute [plan] [backup]", Short: "Execute a recovery plan", Long: "Execute a recovery plan using the specified backup", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { return cli.executeRecovery(args[0], args[1]) }, } // List recovery plans subcommand listCmd := &cobra.Command{ Use: "list", Short: "List recovery plans", Long: "List all available recovery plans", RunE: func(cmd *cobra.Command, args []string) error { return cli.listRecoveryPlans() }, } // Show recovery procedure subcommand showCmd := &cobra.Command{ Use: "show [procedure]", Short: "Show recovery procedure details", Long: "Show detailed information about a recovery procedure", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return cli.showRecoveryProcedure(args[0]) }, } recoveryCmd.AddCommand(executeCmd, listCmd, showCmd) return recoveryCmd } // createTestingCommand creates the testing command func (cli *OperationsCLI) createTestingCommand() *cobra.Command { testingCmd := &cobra.Command{ Use: "testing", Short: "Manage recovery testing", Long: "Run and manage recovery testing scenarios", } // Run test subcommand runCmd := &cobra.Command{ Use: "run [scenario]", Short: "Run a test scenario", Long: "Run a recovery test scenario", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return cli.runTest(args[0]) }, } // List test scenarios subcommand listCmd := &cobra.Command{ Use: "list", Short: "List test scenarios", Long: "List all available test scenarios", RunE: func(cmd *cobra.Command, args []string) error { return cli.listTestScenarios() }, } // Show test results subcommand resultsCmd := &cobra.Command{ Use: "results [test-id]", Short: "Show test results", Long: "Show results for a specific test", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return cli.showTestResults(args[0]) }, } testingCmd.AddCommand(runCmd, listCmd, resultsCmd) return testingCmd } // createConfigCommand creates the configuration command func (cli *OperationsCLI) createConfigCommand() *cobra.Command { configCmd := &cobra.Command{ Use: "config", Short: "Manage operations configuration", Long: "View and modify operations configuration", } // Show configuration subcommand showCmd := &cobra.Command{ Use: "show", Short: "Show current configuration", Long: "Show current operations configuration", RunE: func(cmd *cobra.Command, args []string) error { return cli.showConfig() }, } // Update configuration subcommand updateCmd := &cobra.Command{ Use: "update [key] [value]", Short: "Update configuration", Long: "Update a configuration value", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { return cli.updateConfig(args[0], args[1]) }, } // Validate configuration subcommand validateCmd := &cobra.Command{ Use: "validate", Short: "Validate configuration", Long: "Validate current configuration", RunE: func(cmd *cobra.Command, args []string) error { return cli.validateConfig() }, } configCmd.AddCommand(showCmd, updateCmd, validateCmd) return configCmd } // createStatusCommand creates the status command func (cli *OperationsCLI) createStatusCommand() *cobra.Command { statusCmd := &cobra.Command{ Use: "status", Short: "Show operations status", Long: "Show current status of operations systems", RunE: func(cmd *cobra.Command, args []string) error { return cli.showStatus() }, } return statusCmd } // Backup operations func (cli *OperationsCLI) createBackup(strategyID string) error { cli.logger.Infof("Creating backup using strategy: %s", strategyID) job, err := cli.manager.backup.CreateBackup(strategyID) if err != nil { return fmt.Errorf("backup creation failed: %w", err) } fmt.Printf("Backup created successfully:\n") fmt.Printf(" ID: %s\n", job.ID) fmt.Printf(" Strategy: %s\n", job.StrategyID) fmt.Printf(" Status: %s\n", job.Status) fmt.Printf(" Size: %d bytes\n", job.Size) fmt.Printf(" Duration: %v\n", job.Duration) fmt.Printf(" Path: %s\n", job.Path) if job.Checksum != "" { fmt.Printf(" Checksum: %s\n", job.Checksum) } return nil } func (cli *OperationsCLI) listBackups() error { fmt.Printf("Available Backup Strategies:\n") fmt.Printf("============================\n") for id, strategy := range cli.manager.backup.strategies { fmt.Printf(" %s:\n", id) fmt.Printf(" Name: %s\n", strategy.Name) fmt.Printf(" Description: %s\n", strategy.Description) fmt.Printf(" Type: %s\n", strategy.Type) fmt.Printf(" Enabled: %t\n", strategy.Enabled) fmt.Printf(" Compression: %t\n", strategy.Compression) fmt.Printf(" Encryption: %t\n", strategy.Encryption) fmt.Printf(" Paths: %v\n", strategy.Paths) fmt.Printf(" Exclude: %v\n", strategy.Exclude) fmt.Printf("\n") } fmt.Printf("Backup Schedules:\n") fmt.Printf("=================\n") for id, schedule := range cli.manager.backup.schedules { fmt.Printf(" %s:\n", id) fmt.Printf(" Name: %s\n", schedule.Name) fmt.Printf(" Description: %s\n", schedule.Description) fmt.Printf(" Type: %s\n", schedule.Type) fmt.Printf(" Interval: %v\n", schedule.Interval) fmt.Printf(" Enabled: %t\n", schedule.Enabled) fmt.Printf(" Next Run: %v\n", schedule.NextRun) fmt.Printf("\n") } return nil } func (cli *OperationsCLI) scheduleBackup(scheduleID string) error { schedule, exists := cli.manager.backup.schedules[scheduleID] if !exists { return fmt.Errorf("backup schedule not found: %s", scheduleID) } if !schedule.Enabled { return fmt.Errorf("backup schedule is disabled: %s", scheduleID) } fmt.Printf("Scheduling backup for: %s\n", schedule.Name) fmt.Printf(" Type: %s\n", schedule.Type) fmt.Printf(" Interval: %v\n", schedule.Interval) fmt.Printf(" Next Run: %v\n", schedule.NextRun) // In production, this would actually schedule the backup cli.logger.Infof("Backup scheduled for: %s", scheduleID) return nil } // Recovery operations func (cli *OperationsCLI) executeRecovery(planID string, backupID string) error { cli.logger.Infof("Executing recovery plan: %s with backup: %s", planID, backupID) if err := cli.manager.recovery.ExecuteRecovery(planID, backupID); err != nil { return fmt.Errorf("recovery execution failed: %w", err) } fmt.Printf("Recovery plan executed successfully: %s\n", planID) return nil } func (cli *OperationsCLI) listRecoveryPlans() error { fmt.Printf("Available Recovery Plans:\n") fmt.Printf("=========================\n") for id, plan := range cli.manager.recovery.plans { fmt.Printf(" %s:\n", id) fmt.Printf(" Name: %s\n", plan.Name) fmt.Printf(" Description: %s\n", plan.Description) fmt.Printf(" Priority: %s\n", plan.Priority) fmt.Printf(" RTO: %v\n", plan.RTO) fmt.Printf(" RPO: %v\n", plan.RPO) fmt.Printf(" Enabled: %t\n", plan.Enabled) fmt.Printf(" Procedures: %v\n", plan.Procedures) fmt.Printf("\n") } return nil } func (cli *OperationsCLI) showRecoveryProcedure(procedureID string) error { procedure, exists := cli.manager.recovery.procedures[procedureID] if !exists { return fmt.Errorf("recovery procedure not found: %s", procedureID) } fmt.Printf("Recovery Procedure: %s\n", procedure.Name) fmt.Printf("=====================\n") fmt.Printf(" ID: %s\n", procedure.ID) fmt.Printf(" Description: %s\n", procedure.Description) fmt.Printf(" Type: %s\n", procedure.Type) fmt.Printf(" Risk Level: %s\n", procedure.RiskLevel) fmt.Printf(" Estimated Time: %v\n", procedure.EstimatedTime) fmt.Printf(" Enabled: %t\n", procedure.Enabled) fmt.Printf(" Prerequisites: %v\n", procedure.Prerequisites) fmt.Printf("\n Steps:\n") for i, step := range procedure.Steps { fmt.Printf(" %d. %s\n", i+1, step.Name) fmt.Printf(" Description: %s\n", step.Description) fmt.Printf(" Command: %s %v\n", step.Command, step.Args) fmt.Printf(" Timeout: %v\n", step.Timeout) if step.Rollback != "" { fmt.Printf(" Rollback: %s\n", step.Rollback) } fmt.Printf("\n") } return nil } // Testing operations func (cli *OperationsCLI) runTest(scenarioID string) error { cli.logger.Infof("Running test scenario: %s", scenarioID) result, err := cli.manager.testing.RunTest(scenarioID) if err != nil { return fmt.Errorf("test execution failed: %w", err) } fmt.Printf("Test scenario completed successfully:\n") fmt.Printf(" ID: %s\n", result.ID) fmt.Printf(" Scenario: %s\n", result.ScenarioID) fmt.Printf(" Status: %s\n", result.Status) fmt.Printf(" Duration: %v\n", result.Duration) fmt.Printf(" Results: %v\n", result.Results) return nil } func (cli *OperationsCLI) listTestScenarios() error { fmt.Printf("Available Test Scenarios:\n") fmt.Printf("=========================\n") for id, scenario := range cli.manager.testing.scenarios { fmt.Printf(" %s:\n", id) fmt.Printf(" Name: %s\n", scenario.Name) fmt.Printf(" Description: %s\n", scenario.Description) fmt.Printf(" Type: %s\n", scenario.Type) fmt.Printf(" Enabled: %t\n", scenario.Enabled) fmt.Printf(" Steps: %d\n", len(scenario.Steps)) fmt.Printf(" Expected: %v\n", scenario.Expected) fmt.Printf("\n") } return nil } func (cli *OperationsCLI) showTestResults(testID string) error { result, exists := cli.manager.testing.results[testID] if !exists { return fmt.Errorf("test result not found: %s", testID) } fmt.Printf("Test Result: %s\n", testID) fmt.Printf("============\n") fmt.Printf(" Scenario: %s\n", result.ScenarioID) fmt.Printf(" Status: %s\n", result.Status) fmt.Printf(" Start Time: %v\n", result.StartTime) fmt.Printf(" End Time: %v\n", result.EndTime) fmt.Printf(" Duration: %v\n", result.Duration) if result.Error != "" { fmt.Printf(" Error: %s\n", result.Error) } fmt.Printf(" Results: %v\n", result.Results) fmt.Printf(" Metadata: %v\n", result.Metadata) return nil } // Configuration operations func (cli *OperationsCLI) showConfig() error { if cli.manager.config == nil { return fmt.Errorf("no configuration loaded") } fmt.Printf("Operations Configuration:\n") fmt.Printf("========================\n") fmt.Printf(" Enabled: %t\n", cli.manager.config.Enabled) fmt.Printf(" Backup Path: %s\n", cli.manager.config.BackupPath) fmt.Printf(" Recovery Path: %s\n", cli.manager.config.RecoveryPath) fmt.Printf(" Retention Days: %d\n", cli.manager.config.RetentionDays) fmt.Printf(" Compression: %t\n", cli.manager.config.Compression) fmt.Printf(" Encryption: %t\n", cli.manager.config.Encryption) if len(cli.manager.config.Metadata) > 0 { fmt.Printf(" Metadata:\n") for key, value := range cli.manager.config.Metadata { fmt.Printf(" %s: %s\n", key, value) } } return nil } func (cli *OperationsCLI) updateConfig(key string, value string) error { configManager := &OperationsConfigManager{configPath: cli.configPath, config: cli.manager.config} updates := make(map[string]interface{}) // Parse value based on key type switch key { case "enabled", "compression", "encryption": if boolVal, err := strconv.ParseBool(value); err == nil { updates[key] = boolVal } else { return fmt.Errorf("invalid boolean value for %s: %s", key, value) } case "retention_days": if intVal, err := strconv.Atoi(value); err == nil { updates[key] = intVal } else { return fmt.Errorf("invalid integer value for %s: %s", key, value) } case "backup_path", "recovery_path": updates[key] = value default: return fmt.Errorf("unknown configuration key: %s", key) } if err := configManager.UpdateConfig(updates); err != nil { return fmt.Errorf("failed to update configuration: %w", err) } fmt.Printf("Configuration updated: %s = %s\n", key, value) return nil } func (cli *OperationsCLI) validateConfig() error { configManager := &OperationsConfigManager{configPath: cli.configPath, config: cli.manager.config} if err := configManager.ValidateConfig(); err != nil { return fmt.Errorf("configuration validation failed: %w", err) } fmt.Printf("Configuration validation passed\n") return nil } // Status operations func (cli *OperationsCLI) showStatus() error { fmt.Printf("Operations System Status:\n") fmt.Printf("=========================\n") // Backup system status fmt.Printf("Backup System:\n") fmt.Printf(" Status: Active\n") fmt.Printf(" Strategies: %d\n", len(cli.manager.backup.strategies)) fmt.Printf(" Schedules: %d\n", len(cli.manager.backup.schedules)) fmt.Printf(" Storage Path: %s\n", cli.manager.backup.storage.path) // Recovery system status fmt.Printf("\nRecovery System:\n") fmt.Printf(" Status: Active\n") fmt.Printf(" Procedures: %d\n", len(cli.manager.recovery.procedures)) fmt.Printf(" Plans: %d\n", len(cli.manager.recovery.plans)) // Testing system status fmt.Printf("\nTesting System:\n") fmt.Printf(" Status: Active\n") fmt.Printf(" Scenarios: %d\n", len(cli.manager.testing.scenarios)) fmt.Printf(" Results: %d\n", len(cli.manager.testing.results)) // Data persistence status fmt.Printf("\nData Persistence:\n") fmt.Printf(" Status: Active\n") fmt.Printf(" Replication: %t\n", cli.manager.persistence.config.Replication) fmt.Printf(" Replica Count: %d\n", cli.manager.persistence.config.ReplicaCount) return nil }