knife-windows 0.5.14 → 0.5.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,34 +1,34 @@
1
- #
2
- # Author:: Chirag Jog (<chirag@clogeny.com>)
3
- # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'chef/knife'
20
- require 'chef/knife/winrm'
21
- require 'chef/knife/bootstrap_windows_ssh'
22
- require 'chef/knife/bootstrap_windows_winrm'
23
-
24
- class Chef
25
- class Knife
26
- class WindowsHelper < Knife
27
-
28
- banner "#{BootstrapWindowsWinrm.banner}\n" +
29
- "#{BootstrapWindowsSsh.banner}\n" +
30
- "#{Winrm.banner}"
31
- end
32
- end
33
- end
34
-
1
+ #
2
+ # Author:: Chirag Jog (<chirag@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+ require 'chef/knife/winrm'
21
+ require 'chef/knife/bootstrap_windows_ssh'
22
+ require 'chef/knife/bootstrap_windows_winrm'
23
+
24
+ class Chef
25
+ class Knife
26
+ class WindowsHelper < Knife
27
+
28
+ banner "#{BootstrapWindowsWinrm.banner}\n" +
29
+ "#{BootstrapWindowsSsh.banner}\n" +
30
+ "#{Winrm.banner}"
31
+ end
32
+ end
33
+ end
34
+
@@ -1,286 +1,286 @@
1
- #
2
- # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
- # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require 'chef/knife'
20
- require 'chef/knife/winrm_base'
21
-
22
- class Chef
23
- class Knife
24
- class Winrm < Knife
25
-
26
- include Chef::Knife::WinrmBase
27
-
28
- deps do
29
- require 'readline'
30
- require 'chef/search/query'
31
- require 'em-winrm'
32
- end
33
-
34
- attr_writer :password
35
-
36
- banner "knife winrm QUERY COMMAND (options)"
37
-
38
- option :attribute,
39
- :short => "-a ATTR",
40
- :long => "--attribute ATTR",
41
- :description => "The attribute to use for opening the connection - default is fqdn",
42
- :default => "fqdn"
43
-
44
- option :returns,
45
- :long => "--returns CODES",
46
- :description => "A comma delimited list of return codes which indicate success",
47
- :default => nil,
48
- :proc => Proc.new { |codes|
49
- Chef::Config[:knife][:returns] = codes.split(',').collect {|item| item.to_i} }
50
-
51
- option :manual,
52
- :short => "-m",
53
- :long => "--manual-list",
54
- :boolean => true,
55
- :description => "QUERY is a space separated list of servers",
56
- :default => false
57
-
58
- def session
59
- session_opts = {}
60
- session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
61
- @session ||= begin
62
- s = EventMachine::WinRM::Session.new(session_opts)
63
- s.on_output do |host, data|
64
- print_data(host, data)
65
- end
66
- s.on_error do |host, err|
67
- print_data(host, err, :red)
68
- end
69
- s.on_command_complete do |host|
70
- host = host == :all ? 'All Servers' : host
71
- Chef::Log.debug("command complete on #{host}")
72
- end
73
- s
74
- end
75
-
76
- end
77
-
78
- # TODO: Copied from Knife::Core:GenericPresenter. Should be extracted
79
- def extract_nested_value(data, nested_value_spec)
80
- nested_value_spec.split(".").each do |attr|
81
- if data.nil?
82
- nil # don't get no method error on nil
83
- elsif data.respond_to?(attr.to_sym)
84
- data = data.send(attr.to_sym)
85
- elsif data.respond_to?(:[])
86
- data = data[attr]
87
- else
88
- data = begin
89
- data.send(attr.to_sym)
90
- rescue NoMethodError
91
- nil
92
- end
93
- end
94
- end
95
- ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
96
- end
97
-
98
- def configure_session
99
- list = case config[:manual]
100
- when true
101
- @name_args[0].split(" ")
102
- when false
103
- r = Array.new
104
- q = Chef::Search::Query.new
105
- @action_nodes = q.search(:node, @name_args[0])[0]
106
- @action_nodes.each do |item|
107
- i = extract_nested_value(item, config[:attribute])
108
- r.push(i) unless i.nil?
109
- end
110
- r
111
- end
112
- if list.length == 0
113
- if @action_nodes.length == 0
114
- ui.fatal("No nodes returned from search!")
115
- else
116
- ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
117
- "but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
118
- "Try setting another attribute to open the connection using --attribute.")
119
- end
120
- exit 10
121
- end
122
- session_from_list(list)
123
- end
124
-
125
- def session_from_list(list)
126
- list.each do |item|
127
- Chef::Log.debug("Adding #{item}")
128
- session_opts = {}
129
- session_opts[:user] = config[:winrm_user] = Chef::Config[:knife][:winrm_user] || config[:winrm_user]
130
- session_opts[:password] = config[:winrm_password] = Chef::Config[:knife][:winrm_password] || config[:winrm_password]
131
- session_opts[:port] = Chef::Config[:knife][:winrm_port] || config[:winrm_port]
132
- session_opts[:keytab] = Chef::Config[:knife][:kerberos_keytab_file] if Chef::Config[:knife][:kerberos_keytab_file]
133
- session_opts[:realm] = Chef::Config[:knife][:kerberos_realm] if Chef::Config[:knife][:kerberos_realm]
134
- session_opts[:service] = Chef::Config[:knife][:kerberos_service] if Chef::Config[:knife][:kerberos_service]
135
- session_opts[:ca_trust_path] = Chef::Config[:knife][:ca_trust_file] if Chef::Config[:knife][:ca_trust_file]
136
- session_opts[:operation_timeout] = 1800 # 30 min OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
137
-
138
- ## If you have a \\ in your name you need to use NTLM domain authentication
139
- if session_opts[:user].split("\\").length.eql?(2)
140
- session_opts[:basic_auth_only] = false
141
- else
142
- session_opts[:basic_auth_only] = true
143
- end
144
-
145
- if config.keys.any? {|k| k.to_s =~ /kerberos/ }
146
- session_opts[:transport] = :kerberos
147
- session_opts[:basic_auth_only] = false
148
- else
149
- session_opts[:transport] = (Chef::Config[:knife][:winrm_transport] || config[:winrm_transport]).to_sym
150
- session_opts[:disable_sspi] = true
151
- if session_opts[:user] and
152
- (not session_opts[:password])
153
- session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
154
-
155
- end
156
- end
157
-
158
- session.use(item, session_opts)
159
-
160
- @longest = item.length if item.length > @longest
161
- end
162
- session
163
- end
164
-
165
- def print_data(host, data, color = :cyan)
166
- if data =~ /\n/
167
- data.split(/\n/).each { |d| print_data(host, d, color) }
168
- else
169
- padding = @longest - host.length
170
- print ui.color(host, color)
171
- padding.downto(0) { print " " }
172
- puts data.chomp
173
- end
174
- end
175
-
176
- def winrm_command(command, subsession=nil)
177
- subsession ||= session
178
- subsession.relay_command(command)
179
- end
180
-
181
- def get_password
182
- @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
183
- end
184
-
185
- # Present the prompt and read a single line from the console. It also
186
- # detects ^D and returns "exit" in that case. Adds the input to the
187
- # history, unless the input is empty. Loops repeatedly until a non-empty
188
- # line is input.
189
- def read_line
190
- loop do
191
- command = reader.readline("#{ui.color('knife-winrm>', :bold)} ", true)
192
-
193
- if command.nil?
194
- command = "exit"
195
- puts(command)
196
- else
197
- command.strip!
198
- end
199
-
200
- unless command.empty?
201
- return command
202
- end
203
- end
204
- end
205
-
206
- def reader
207
- Readline
208
- end
209
-
210
- def interactive
211
- puts "Connected to #{ui.list(session.servers.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
212
- puts
213
- puts "To run a command on a list of servers, do:"
214
- puts " on SERVER1 SERVER2 SERVER3; COMMAND"
215
- puts " Example: on latte foamy; echo foobar"
216
- puts
217
- puts "To exit interactive mode, use 'quit!'"
218
- puts
219
- while 1
220
- command = read_line
221
- case command
222
- when 'quit!'
223
- puts 'Bye!'
224
- session.close
225
- break
226
- when /^on (.+?); (.+)$/
227
- raw_list = $1.split(" ")
228
- server_list = Array.new
229
- session.servers.each do |session_server|
230
- server_list << session_server if raw_list.include?(session_server.host)
231
- end
232
- command = $2
233
- winrm_command(command, session.on(*server_list))
234
- else
235
- winrm_command(command)
236
- end
237
- end
238
- end
239
-
240
- def check_for_errors!(exit_codes)
241
-
242
- exit_codes.each do |host, value|
243
- unless Chef::Config[:knife][:returns].include? value.to_i
244
- @exit_code = 1
245
- ui.error "Failed to execute command on #{host} return code #{value}"
246
- end
247
- end
248
-
249
- end
250
-
251
- def run
252
- STDOUT.sync = STDERR.sync = true
253
-
254
- begin
255
- @longest = 0
256
-
257
- configure_session
258
-
259
- case @name_args[1]
260
- when "interactive"
261
- interactive
262
- else
263
- winrm_command(@name_args[1..-1].join(" "))
264
-
265
- if config[:returns]
266
- check_for_errors! session.exit_codes
267
- end
268
-
269
- session.close
270
- @exit_code || 0
271
- end
272
- rescue WinRM::WinRMHTTPTransportError => e
273
- case e.message
274
- when /401/
275
- ui.error "Failed to authenticate to #{@name_args[0].split(" ")} as #{config[:winrm_user]}"
276
- ui.info "Response: #{e.message}"
277
- else
278
- raise e
279
- end
280
- end
281
- end
282
-
283
- end
284
- end
285
- end
286
-
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+ require 'chef/knife/winrm_base'
21
+
22
+ class Chef
23
+ class Knife
24
+ class Winrm < Knife
25
+
26
+ include Chef::Knife::WinrmBase
27
+
28
+ deps do
29
+ require 'readline'
30
+ require 'chef/search/query'
31
+ require 'em-winrm'
32
+ end
33
+
34
+ attr_writer :password
35
+
36
+ banner "knife winrm QUERY COMMAND (options)"
37
+
38
+ option :attribute,
39
+ :short => "-a ATTR",
40
+ :long => "--attribute ATTR",
41
+ :description => "The attribute to use for opening the connection - default is fqdn",
42
+ :default => "fqdn"
43
+
44
+ option :returns,
45
+ :long => "--returns CODES",
46
+ :description => "A comma delimited list of return codes which indicate success",
47
+ :default => nil,
48
+ :proc => Proc.new { |codes|
49
+ Chef::Config[:knife][:returns] = codes.split(',').collect {|item| item.to_i} }
50
+
51
+ option :manual,
52
+ :short => "-m",
53
+ :long => "--manual-list",
54
+ :boolean => true,
55
+ :description => "QUERY is a space separated list of servers",
56
+ :default => false
57
+
58
+ def session
59
+ session_opts = {}
60
+ session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
61
+ @session ||= begin
62
+ s = EventMachine::WinRM::Session.new(session_opts)
63
+ s.on_output do |host, data|
64
+ print_data(host, data)
65
+ end
66
+ s.on_error do |host, err|
67
+ print_data(host, err, :red)
68
+ end
69
+ s.on_command_complete do |host|
70
+ host = host == :all ? 'All Servers' : host
71
+ Chef::Log.debug("command complete on #{host}")
72
+ end
73
+ s
74
+ end
75
+
76
+ end
77
+
78
+ # TODO: Copied from Knife::Core:GenericPresenter. Should be extracted
79
+ def extract_nested_value(data, nested_value_spec)
80
+ nested_value_spec.split(".").each do |attr|
81
+ if data.nil?
82
+ nil # don't get no method error on nil
83
+ elsif data.respond_to?(attr.to_sym)
84
+ data = data.send(attr.to_sym)
85
+ elsif data.respond_to?(:[])
86
+ data = data[attr]
87
+ else
88
+ data = begin
89
+ data.send(attr.to_sym)
90
+ rescue NoMethodError
91
+ nil
92
+ end
93
+ end
94
+ end
95
+ ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
96
+ end
97
+
98
+ def configure_session
99
+ list = case config[:manual]
100
+ when true
101
+ @name_args[0].split(" ")
102
+ when false
103
+ r = Array.new
104
+ q = Chef::Search::Query.new
105
+ @action_nodes = q.search(:node, @name_args[0])[0]
106
+ @action_nodes.each do |item|
107
+ i = extract_nested_value(item, config[:attribute])
108
+ r.push(i) unless i.nil?
109
+ end
110
+ r
111
+ end
112
+ if list.length == 0
113
+ if @action_nodes.length == 0
114
+ ui.fatal("No nodes returned from search!")
115
+ else
116
+ ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
117
+ "but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
118
+ "Try setting another attribute to open the connection using --attribute.")
119
+ end
120
+ exit 10
121
+ end
122
+ session_from_list(list)
123
+ end
124
+
125
+ def session_from_list(list)
126
+ list.each do |item|
127
+ Chef::Log.debug("Adding #{item}")
128
+ session_opts = {}
129
+ session_opts[:user] = config[:winrm_user] = Chef::Config[:knife][:winrm_user] || config[:winrm_user]
130
+ session_opts[:password] = config[:winrm_password] = Chef::Config[:knife][:winrm_password] || config[:winrm_password]
131
+ session_opts[:port] = Chef::Config[:knife][:winrm_port] || config[:winrm_port]
132
+ session_opts[:keytab] = Chef::Config[:knife][:kerberos_keytab_file] if Chef::Config[:knife][:kerberos_keytab_file]
133
+ session_opts[:realm] = Chef::Config[:knife][:kerberos_realm] if Chef::Config[:knife][:kerberos_realm]
134
+ session_opts[:service] = Chef::Config[:knife][:kerberos_service] if Chef::Config[:knife][:kerberos_service]
135
+ session_opts[:ca_trust_path] = Chef::Config[:knife][:ca_trust_file] if Chef::Config[:knife][:ca_trust_file]
136
+ session_opts[:operation_timeout] = 1800 # 30 min OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
137
+
138
+ ## If you have a \\ in your name you need to use NTLM domain authentication
139
+ if session_opts[:user].split("\\").length.eql?(2)
140
+ session_opts[:basic_auth_only] = false
141
+ else
142
+ session_opts[:basic_auth_only] = true
143
+ end
144
+
145
+ if config.keys.any? {|k| k.to_s =~ /kerberos/ }
146
+ session_opts[:transport] = :kerberos
147
+ session_opts[:basic_auth_only] = false
148
+ else
149
+ session_opts[:transport] = (Chef::Config[:knife][:winrm_transport] || config[:winrm_transport]).to_sym
150
+ session_opts[:disable_sspi] = true
151
+ if session_opts[:user] and
152
+ (not session_opts[:password])
153
+ session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
154
+
155
+ end
156
+ end
157
+
158
+ session.use(item, session_opts)
159
+
160
+ @longest = item.length if item.length > @longest
161
+ end
162
+ session
163
+ end
164
+
165
+ def print_data(host, data, color = :cyan)
166
+ if data =~ /\n/
167
+ data.split(/\n/).each { |d| print_data(host, d, color) }
168
+ else
169
+ padding = @longest - host.length
170
+ print ui.color(host, color)
171
+ padding.downto(0) { print " " }
172
+ puts data.chomp
173
+ end
174
+ end
175
+
176
+ def winrm_command(command, subsession=nil)
177
+ subsession ||= session
178
+ subsession.relay_command(command)
179
+ end
180
+
181
+ def get_password
182
+ @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
183
+ end
184
+
185
+ # Present the prompt and read a single line from the console. It also
186
+ # detects ^D and returns "exit" in that case. Adds the input to the
187
+ # history, unless the input is empty. Loops repeatedly until a non-empty
188
+ # line is input.
189
+ def read_line
190
+ loop do
191
+ command = reader.readline("#{ui.color('knife-winrm>', :bold)} ", true)
192
+
193
+ if command.nil?
194
+ command = "exit"
195
+ puts(command)
196
+ else
197
+ command.strip!
198
+ end
199
+
200
+ unless command.empty?
201
+ return command
202
+ end
203
+ end
204
+ end
205
+
206
+ def reader
207
+ Readline
208
+ end
209
+
210
+ def interactive
211
+ puts "Connected to #{ui.list(session.servers.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}"
212
+ puts
213
+ puts "To run a command on a list of servers, do:"
214
+ puts " on SERVER1 SERVER2 SERVER3; COMMAND"
215
+ puts " Example: on latte foamy; echo foobar"
216
+ puts
217
+ puts "To exit interactive mode, use 'quit!'"
218
+ puts
219
+ while 1
220
+ command = read_line
221
+ case command
222
+ when 'quit!'
223
+ puts 'Bye!'
224
+ session.close
225
+ break
226
+ when /^on (.+?); (.+)$/
227
+ raw_list = $1.split(" ")
228
+ server_list = Array.new
229
+ session.servers.each do |session_server|
230
+ server_list << session_server if raw_list.include?(session_server.host)
231
+ end
232
+ command = $2
233
+ winrm_command(command, session.on(*server_list))
234
+ else
235
+ winrm_command(command)
236
+ end
237
+ end
238
+ end
239
+
240
+ def check_for_errors!(exit_codes)
241
+
242
+ exit_codes.each do |host, value|
243
+ unless Chef::Config[:knife][:returns].include? value.to_i
244
+ @exit_code = 1
245
+ ui.error "Failed to execute command on #{host} return code #{value}"
246
+ end
247
+ end
248
+
249
+ end
250
+
251
+ def run
252
+ STDOUT.sync = STDERR.sync = true
253
+
254
+ begin
255
+ @longest = 0
256
+
257
+ configure_session
258
+
259
+ case @name_args[1]
260
+ when "interactive"
261
+ interactive
262
+ else
263
+ winrm_command(@name_args[1..-1].join(" "))
264
+
265
+ if config[:returns]
266
+ check_for_errors! session.exit_codes
267
+ end
268
+
269
+ session.close
270
+ @exit_code || 0
271
+ end
272
+ rescue WinRM::WinRMHTTPTransportError => e
273
+ case e.message
274
+ when /401/
275
+ ui.error "Failed to authenticate to #{@name_args[0].split(" ")} as #{config[:winrm_user]}"
276
+ ui.info "Response: #{e.message}"
277
+ else
278
+ raise e
279
+ end
280
+ end
281
+ end
282
+
283
+ end
284
+ end
285
+ end
286
+