Add parallelism
This commit is contained in:
@ -142,6 +142,7 @@ types:
|
|||||||
- **`--password`**: SSH password
|
- **`--password`**: SSH password
|
||||||
- **`--key-file`**: SSH private key file path
|
- **`--key-file`**: SSH private key file path
|
||||||
- **`--port`**: SSH port (default: `22`)
|
- **`--port`**: SSH port (default: `22`)
|
||||||
|
- **`--parallel`**: Maximum number of devices to process in parallel (default: `10`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@ -160,6 +161,9 @@ ipng-router-backup --yaml config.yaml --output-dir /backup/network
|
|||||||
|
|
||||||
# With password authentication
|
# With password authentication
|
||||||
ipng-router-backup --yaml config.yaml --password mypassword
|
ipng-router-backup --yaml config.yaml --password mypassword
|
||||||
|
|
||||||
|
# Process more devices in parallel
|
||||||
|
ipng-router-backup --yaml config.yaml --parallel 20
|
||||||
```
|
```
|
||||||
|
|
||||||
## SSH Authentication
|
## SSH Authentication
|
||||||
|
@ -35,6 +35,9 @@ SSH port number (default: 22)
|
|||||||
.BR --host " \fIHOSTNAME\fR"
|
.BR --host " \fIHOSTNAME\fR"
|
||||||
Specific host(s) or glob patterns to process (can be repeated, processes all if not specified)
|
Specific host(s) or glob patterns to process (can be repeated, processes all if not specified)
|
||||||
.TP
|
.TP
|
||||||
|
.BR --parallel " \fINUMBER\fR"
|
||||||
|
Maximum number of devices to process in parallel (default: 10)
|
||||||
|
.TP
|
||||||
.BR --help
|
.BR --help
|
||||||
Show help message
|
Show help message
|
||||||
.SH CONFIGURATION
|
.SH CONFIGURATION
|
||||||
@ -98,6 +101,11 @@ Process hosts matching patterns:
|
|||||||
.EX
|
.EX
|
||||||
ipng-router-backup --yaml config.yaml --host "asw*" --host "*switch*"
|
ipng-router-backup --yaml config.yaml --host "asw*" --host "*switch*"
|
||||||
.EE
|
.EE
|
||||||
|
.TP
|
||||||
|
Process devices in parallel:
|
||||||
|
.EX
|
||||||
|
ipng-router-backup --yaml config.yaml --parallel 20
|
||||||
|
.EE
|
||||||
.SH FILES
|
.SH FILES
|
||||||
.TP
|
.TP
|
||||||
.I /etc/ipng-router-backup/config.yaml.example
|
.I /etc/ipng-router-backup/config.yaml.example
|
||||||
|
92
src/main.go
92
src/main.go
@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -21,6 +22,29 @@ const Version = "1.2.4"
|
|||||||
|
|
||||||
// SSH helper functions are now in ssh.go
|
// SSH helper functions are now in ssh.go
|
||||||
|
|
||||||
|
// processDevice handles backup processing for a single device
|
||||||
|
func processDevice(hostname string, deviceConfig Device, commands []string, excludePatterns []string, password, keyFile string, port int, outputDir string) bool {
|
||||||
|
// Create backup instance
|
||||||
|
backup := NewRouterBackup(hostname, deviceConfig.Address, deviceConfig.User, password, keyFile, port)
|
||||||
|
|
||||||
|
// Connect and backup
|
||||||
|
if err := backup.Connect(); err != nil {
|
||||||
|
fmt.Printf("%s: Failed to connect: %v\n", hostname, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
err := backup.BackupCommands(commands, excludePatterns, outputDir)
|
||||||
|
backup.Disconnect()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s: Backup failed: %v\n", hostname, err)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s: Backup completed\n", hostname)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var yamlFiles []string
|
var yamlFiles []string
|
||||||
var password string
|
var password string
|
||||||
@ -28,6 +52,7 @@ func main() {
|
|||||||
var port int
|
var port int
|
||||||
var outputDir string
|
var outputDir string
|
||||||
var hostFilter []string
|
var hostFilter []string
|
||||||
|
var parallel int
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "ipng-router-backup",
|
Use: "ipng-router-backup",
|
||||||
@ -93,12 +118,41 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
successCount := 0
|
|
||||||
totalCount := len(devicesToProcess)
|
totalCount := len(devicesToProcess)
|
||||||
|
|
||||||
for hostname, deviceConfig := range devicesToProcess {
|
// Create channels for work distribution and result collection
|
||||||
fmt.Printf("\n%s: Processing device (type: %s)\n", hostname, deviceConfig.Type)
|
type DeviceWork struct {
|
||||||
|
hostname string
|
||||||
|
deviceConfig Device
|
||||||
|
commands []string
|
||||||
|
excludePatterns []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceResult struct {
|
||||||
|
hostname string
|
||||||
|
success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
workChan := make(chan DeviceWork, totalCount)
|
||||||
|
resultChan := make(chan DeviceResult, totalCount)
|
||||||
|
|
||||||
|
// Start worker pool
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < parallel; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for work := range workChan {
|
||||||
|
fmt.Printf("%s: Processing device (type: %s)\n", work.hostname, work.deviceConfig.Type)
|
||||||
|
|
||||||
|
success := processDevice(work.hostname, work.deviceConfig, work.commands, work.excludePatterns, password, keyFile, port, outputDir)
|
||||||
|
resultChan <- DeviceResult{hostname: work.hostname, success: success}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue all work
|
||||||
|
for hostname, deviceConfig := range devicesToProcess {
|
||||||
user := deviceConfig.User
|
user := deviceConfig.User
|
||||||
commands := deviceConfig.Commands
|
commands := deviceConfig.Commands
|
||||||
deviceType := deviceConfig.Type
|
deviceType := deviceConfig.Type
|
||||||
@ -122,27 +176,30 @@ func main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create backup instance
|
workChan <- DeviceWork{
|
||||||
backup := NewRouterBackup(hostname, deviceConfig.Address, user, password, keyFile, port)
|
hostname: hostname,
|
||||||
|
deviceConfig: deviceConfig,
|
||||||
// Connect and backup
|
commands: commands,
|
||||||
if err := backup.Connect(); err != nil {
|
excludePatterns: excludePatterns,
|
||||||
fmt.Printf("%s: Failed to connect: %v\n", hostname, err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
close(workChan)
|
||||||
|
|
||||||
err = backup.BackupCommands(commands, excludePatterns, outputDir)
|
// Wait for all workers to finish
|
||||||
backup.Disconnect()
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(resultChan)
|
||||||
|
}()
|
||||||
|
|
||||||
if err != nil {
|
// Collect results
|
||||||
fmt.Printf("%s: Backup failed: %v\n", hostname, err)
|
successCount := 0
|
||||||
} else {
|
for result := range resultChan {
|
||||||
fmt.Printf("%s: Backup completed\n", hostname)
|
if result.success {
|
||||||
successCount++
|
successCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\nOverall summary: %d/%d devices processed successfully\n", successCount, totalCount)
|
fmt.Printf("Overall summary: %d/%d devices processed successfully\n", successCount, totalCount)
|
||||||
|
|
||||||
// Set exit code based on results
|
// Set exit code based on results
|
||||||
if successCount == 0 {
|
if successCount == 0 {
|
||||||
@ -160,6 +217,7 @@ func main() {
|
|||||||
rootCmd.Flags().IntVar(&port, "port", 22, "SSH port")
|
rootCmd.Flags().IntVar(&port, "port", 22, "SSH port")
|
||||||
rootCmd.Flags().StringVar(&outputDir, "output-dir", "/tmp", "Output directory for command output files")
|
rootCmd.Flags().StringVar(&outputDir, "output-dir", "/tmp", "Output directory for command output files")
|
||||||
rootCmd.Flags().StringSliceVar(&hostFilter, "host", []string{}, "Specific host(s) to process (can be repeated, processes all if not specified)")
|
rootCmd.Flags().StringSliceVar(&hostFilter, "host", []string{}, "Specific host(s) to process (can be repeated, processes all if not specified)")
|
||||||
|
rootCmd.Flags().IntVar(¶llel, "parallel", 10, "Maximum number of devices to process in parallel")
|
||||||
|
|
||||||
rootCmd.MarkFlagRequired("yaml")
|
rootCmd.MarkFlagRequired("yaml")
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user