@@ -3,9 +3,12 @@ package cmd
3
3
import (
4
4
"fmt"
5
5
"io/ioutil"
6
+ "os"
6
7
"strings"
7
8
"sync"
8
9
10
+ "gopkg.in/ini.v1"
11
+
9
12
"github.com/fatih/color"
10
13
"github.com/songrgg/llsh/pkg/helper"
11
14
"github.com/songrgg/llsh/pkg/ssh"
@@ -15,6 +18,8 @@ import (
15
18
type options struct {
16
19
Command string
17
20
Hosts string
21
+ HostFile string
22
+ Section string
18
23
User string
19
24
UseSSHCommand bool
20
25
UsePassword bool
@@ -43,6 +48,10 @@ func NewLLSHCommand() *cobra.Command {
43
48
44
49
cmds .PersistentFlags ().StringVar (& options .Hosts , "hosts" , "" ,
45
50
"The host names for remote server, split by comma, for example, host1,host2,host3" )
51
+ cmds .PersistentFlags ().StringVar (& options .HostFile , "host_file" , "" ,
52
+ "The path of host file, the content should be hosts separated by new line, if specified, hosts will be omitted" )
53
+ cmds .PersistentFlags ().StringVar (& options .Section , "section" , "" ,
54
+ "The section of the host_file, default to all hosts when not specified or empty" )
46
55
cmds .PersistentFlags ().StringVarP (& options .User , "user" , "u" , "" ,
47
56
"The username for login user" )
48
57
cmds .PersistentFlags ().StringVarP (& options .Command , "command" , "c" , "" ,
@@ -86,10 +95,55 @@ func (o *options) authMethods() []ssh.AuthMethod {
86
95
return authMethods
87
96
}
88
97
98
+ func (o * options ) hosts () ([]string , error ) {
99
+ if o .HostFile != "" {
100
+ //cfg, err := ini.Load(o.HostFile)
101
+ cfg , err := ini .LoadSources (ini.LoadOptions {SkipUnrecognizableLines : true }, o .HostFile )
102
+ if err != nil {
103
+ return nil , err
104
+ }
105
+
106
+ cfg , err = ini .LoadSources (ini.LoadOptions {
107
+ UnparseableSections : cfg .SectionStrings (),
108
+ }, o .HostFile )
109
+ if err != nil {
110
+ return nil , err
111
+ }
112
+
113
+ // Set to all roles which is the ini `DEFAULT` section.
114
+ var hosts []string
115
+ var sections []* ini.Section
116
+ if o .Section == "" {
117
+ sections = cfg .Sections ()
118
+ } else {
119
+ sec , err := cfg .GetSection (o .Section )
120
+ if err != nil {
121
+ return nil , err
122
+ }
123
+ sections = append (sections , sec )
124
+ }
125
+
126
+ for _ , sec := range sections {
127
+ for _ , host := range strings .Split (string (sec .Body ()), "\n " ) {
128
+ if h := strings .Trim (host , " " ); h != "" {
129
+ hosts = append (hosts , h )
130
+ }
131
+ }
132
+ }
133
+ return hosts , nil
134
+ }
135
+ return strings .Split (o .Hosts , "," ), nil
136
+ }
137
+
89
138
func (o * options ) run () {
90
139
authMethods := o .authMethods ()
91
140
92
- hosts := strings .Split (o .Hosts , "," )
141
+ hosts , err := o .hosts ()
142
+ if err != nil {
143
+ color .Red ("Invalid hosts: %v" , err )
144
+ os .Exit (1 )
145
+ }
146
+
93
147
var wg sync.WaitGroup
94
148
var results []sshResult
95
149
var lock = sync.Mutex {}
@@ -134,7 +188,7 @@ func (o *options) run() {
134
188
}
135
189
136
190
func (o * options ) validate () {
137
- if o .Hosts == "" {
191
+ if o .HostFile == "" && o . Hosts == "" {
138
192
helper .Errorlq ("hosts can't be empty" )
139
193
}
140
194
0 commit comments