knife-windows 1.5.0 → 1.6.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +26 -26
  3. data/CHANGELOG.md +131 -121
  4. data/DOC_CHANGES.md +22 -14
  5. data/Gemfile +14 -13
  6. data/README.md +400 -392
  7. data/RELEASE_NOTES.md +2 -26
  8. data/appveyor.yml +39 -39
  9. data/ci.gemfile +16 -16
  10. data/knife-windows.gemspec +25 -25
  11. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +246 -233
  12. data/lib/chef/knife/bootstrap_windows_base.rb +443 -454
  13. data/lib/chef/knife/bootstrap_windows_ssh.rb +116 -115
  14. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -95
  15. data/lib/chef/knife/core/windows_bootstrap_context.rb +378 -378
  16. data/lib/chef/knife/knife_windows_base.rb +33 -33
  17. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  18. data/lib/chef/knife/windows_cert_install.rb +68 -68
  19. data/lib/chef/knife/windows_helper.rb +36 -36
  20. data/lib/chef/knife/windows_listener_create.rb +107 -107
  21. data/lib/chef/knife/winrm.rb +122 -122
  22. data/lib/chef/knife/winrm_base.rb +123 -117
  23. data/lib/chef/knife/winrm_knife_base.rb +306 -305
  24. data/lib/chef/knife/winrm_session.rb +97 -91
  25. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  26. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  27. data/lib/chef/knife/wsman_test.rb +118 -118
  28. data/lib/knife-windows/path_helper.rb +234 -234
  29. data/lib/knife-windows/version.rb +6 -6
  30. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +223 -223
  31. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +223 -223
  32. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +335 -335
  33. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +335 -335
  34. data/spec/dummy_winrm_connection.rb +21 -0
  35. data/spec/functional/bootstrap_download_spec.rb +236 -241
  36. data/spec/spec_helper.rb +94 -94
  37. data/spec/unit/knife/bootstrap_options_spec.rb +157 -155
  38. data/spec/unit/knife/bootstrap_template_spec.rb +98 -98
  39. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +423 -426
  40. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -177
  41. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  42. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  43. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  44. data/spec/unit/knife/winrm_session_spec.rb +71 -76
  45. data/spec/unit/knife/winrm_spec.rb +500 -508
  46. data/spec/unit/knife/wsman_test_spec.rb +209 -209
  47. metadata +16 -17
  48. data/spec/dummy_winrm_service.rb +0 -24
@@ -1,91 +1,97 @@
1
- #
2
- # Author:: Steven Murawski <smurawski@chef.io>
3
- # Copyright:: Copyright (c) 2015-2016 Chef Software, 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/application'
20
- require 'winrm'
21
-
22
- class Chef
23
- class Knife
24
- class WinrmSession
25
- attr_reader :host, :endpoint, :port, :output, :error, :exit_code
26
-
27
- def initialize(options)
28
- configure_proxy
29
-
30
- @host = options[:host]
31
- @port = options[:port]
32
- url = "#{options[:host]}:#{options[:port]}/wsman"
33
- scheme = options[:transport] == :ssl ? 'https' : 'http'
34
- @endpoint = "#{scheme}://#{url}"
35
-
36
- opts = Hash.new
37
- opts = {:user => options[:user], :pass => options[:password], :basic_auth_only => options[:basic_auth_only], :disable_sspi => options[:disable_sspi], :no_ssl_peer_verification => options[:no_ssl_peer_verification], :ssl_peer_fingerprint => options[:ssl_peer_fingerprint]}
38
- options[:transport] == :kerberos ? opts.merge!({:service => options[:service], :realm => options[:realm], :keytab => options[:keytab]}) : opts.merge!({:ca_trust_path => options[:ca_trust_path]})
39
-
40
- Chef::Log.debug("WinRM::WinRMWebService options: #{opts}")
41
- Chef::Log.debug("Endpoint: #{endpoint}")
42
- Chef::Log.debug("Transport: #{options[:transport]}")
43
-
44
- @winrm_session = WinRM::WinRMWebService.new(@endpoint, options[:transport], opts)
45
- transport = @winrm_session.instance_variable_get(:@xfer)
46
- http_client = transport.instance_variable_get(:@httpcli)
47
- Chef::HTTP::DefaultSSLPolicy.new(http_client.ssl_config).set_custom_certs
48
- @winrm_session.set_timeout(options[:operation_timeout]) if options[:operation_timeout]
49
- end
50
-
51
- def relay_command(command)
52
- remote_id = @winrm_session.open_shell
53
- command_id = @winrm_session.run_command(remote_id, command)
54
- Chef::Log.debug("#{@host}[#{remote_id}] => :run_command[#{command}]")
55
- session_result = get_output(remote_id, command_id)
56
- @winrm_session.cleanup_command(remote_id, command_id)
57
- Chef::Log.debug("#{@host}[#{remote_id}] => :command_cleanup[#{command}]")
58
- @exit_code = session_result[:exitcode]
59
- @winrm_session.close_shell(remote_id)
60
- Chef::Log.debug("#{@host}[#{remote_id}] => :shell_close")
61
- session_result
62
- end
63
-
64
- private
65
-
66
- def get_output(remote_id, command_id)
67
- @winrm_session.get_command_output(remote_id, command_id) do |out,error|
68
- print_data(@host, out) if out
69
- print_data(@host, error, :red) if error
70
- end
71
- end
72
-
73
- def print_data(host, data, color = :cyan)
74
- if data =~ /\n/
75
- data.split(/\n/).each { |d| print_data(host, d, color) }
76
- elsif !data.nil?
77
- print Chef::Knife::Winrm.ui.color(host, color)
78
- puts " #{data}"
79
- end
80
- end
81
-
82
- def configure_proxy
83
- if Chef::Config.respond_to?(:export_proxies)
84
- Chef::Config.export_proxies
85
- else
86
- Chef::Application.new.configure_proxy_environment_variables
87
- end
88
- end
89
- end
90
- end
91
- end
1
+ #
2
+ # Author:: Steven Murawski <smurawski@chef.io>
3
+ # Copyright:: Copyright (c) 2015-2016 Chef Software, 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/application'
20
+ require 'winrm'
21
+ require 'winrm-elevated'
22
+
23
+ class Chef
24
+ class Knife
25
+ class WinrmSession
26
+ attr_reader :host, :endpoint, :port, :output, :error, :exit_code
27
+
28
+ def initialize(options)
29
+ configure_proxy
30
+
31
+ @host = options[:host]
32
+ @port = options[:port]
33
+ @user = options[:user]
34
+ @shell = options[:shell]
35
+ url = "#{options[:host]}:#{options[:port]}/wsman"
36
+ scheme = options[:transport] == :ssl ? 'https' : 'http'
37
+ @endpoint = "#{scheme}://#{url}"
38
+
39
+ opts = Hash.new
40
+ opts = {
41
+ user: @user,
42
+ password: options[:password],
43
+ basic_auth_only: options[:basic_auth_only],
44
+ disable_sspi: options[:disable_sspi],
45
+ no_ssl_peer_verification: options[:no_ssl_peer_verification],
46
+ ssl_peer_fingerprint: options[:ssl_peer_fingerprint],
47
+ endpoint: endpoint,
48
+ transport: options[:transport]
49
+ }
50
+ options[:transport] == :kerberos ? opts.merge!({:service => options[:service], :realm => options[:realm]}) : opts.merge!({:ca_trust_path => options[:ca_trust_path]})
51
+ opts[:operation_timeout] = options[:operation_timeout] if options[:operation_timeout]
52
+ Chef::Log.debug("WinRM::WinRMWebService options: #{opts}")
53
+ Chef::Log.debug("Endpoint: #{endpoint}")
54
+ Chef::Log.debug("Transport: #{options[:transport]}")
55
+
56
+ @winrm_session = WinRM::Connection.new(opts)
57
+ @winrm_session.logger = Chef::Log
58
+
59
+ transport = @winrm_session.send(:transport)
60
+ http_client = transport.instance_variable_get(:@httpcli)
61
+ Chef::HTTP::DefaultSSLPolicy.new(http_client.ssl_config).set_custom_certs
62
+ end
63
+
64
+ def relay_command(command)
65
+ session_result = WinRM::Output.new
66
+ @winrm_session.shell(@shell) do |shell|
67
+ shell.username = @user.split("\\").last if shell.respond_to?(:username)
68
+ session_result = shell.run(command) do |stdout, stderr|
69
+ print_data(@host, stdout) if stdout
70
+ print_data(@host, stderr, :red) if stderr
71
+ end
72
+ end
73
+ @exit_code = session_result.exitcode
74
+ session_result
75
+ end
76
+
77
+ private
78
+
79
+ def print_data(host, data, color = :cyan)
80
+ if data =~ /\n/
81
+ data.split(/\n/).each { |d| print_data(host, d, color) }
82
+ elsif !data.nil?
83
+ print Chef::Knife::Winrm.ui.color(host, color)
84
+ puts " #{data}"
85
+ end
86
+ end
87
+
88
+ def configure_proxy
89
+ if Chef::Config.respond_to?(:export_proxies)
90
+ Chef::Config.export_proxies
91
+ else
92
+ Chef::Application.new.configure_proxy_environment_variables
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,47 +1,47 @@
1
- #
2
- # Author:: Steven Murawski (<smurawski@chef.io)
3
- # Copyright:: Copyright (c) 2015-2016 Chef Software, 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/encrypted_data_bag_item'
21
- require 'kconv'
22
-
23
- class Chef
24
- class Knife
25
- module WinrmSharedOptions
26
-
27
- # Shared command line options for knife winrm and knife wsman test
28
- def self.included(includer)
29
- includer.class_eval do
30
- option :manual,
31
- :short => "-m",
32
- :long => "--manual-list",
33
- :boolean => true,
34
- :description => "QUERY is a space separated list of servers",
35
- :default => false
36
-
37
- option :attribute,
38
- :short => "-a ATTR",
39
- :long => "--attribute ATTR",
40
- :description => "The attribute to use for opening the connection - default is fqdn",
41
- :default => "fqdn"
42
- end
43
- end
44
-
45
- end
46
- end
47
- end
1
+ #
2
+ # Author:: Steven Murawski (<smurawski@chef.io)
3
+ # Copyright:: Copyright (c) 2015-2016 Chef Software, 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/encrypted_data_bag_item'
21
+ require 'kconv'
22
+
23
+ class Chef
24
+ class Knife
25
+ module WinrmSharedOptions
26
+
27
+ # Shared command line options for knife winrm and knife wsman test
28
+ def self.included(includer)
29
+ includer.class_eval do
30
+ option :manual,
31
+ :short => "-m",
32
+ :long => "--manual-list",
33
+ :boolean => true,
34
+ :description => "QUERY is a space separated list of servers",
35
+ :default => false
36
+
37
+ option :attribute,
38
+ :short => "-a ATTR",
39
+ :long => "--attribute ATTR",
40
+ :description => "The attribute to use for opening the connection - default is fqdn",
41
+ :default => "fqdn"
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -1,44 +1,44 @@
1
- #
2
- # Author:: Steven Murawski (<smurawski@chef.io)
3
- # Copyright:: Copyright (c) 2015-2016 Chef Software, 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
- class Chef
20
- class Knife
21
- class WsmanEndpoint
22
- attr_accessor :host
23
- attr_accessor :wsman_port
24
- attr_accessor :wsman_url
25
- attr_accessor :product_version
26
- attr_accessor :protocol_version
27
- attr_accessor :product_vendor
28
- attr_accessor :response_status_code
29
- attr_accessor :error_message
30
-
31
- def initialize(name, port, url)
32
- @host = name
33
- @wsman_port = port
34
- @wsman_url = url
35
- end
36
-
37
- def to_hash
38
- hash = {}
39
- instance_variables.each {|var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
40
- hash
41
- end
42
- end
43
- end
44
- end
1
+ #
2
+ # Author:: Steven Murawski (<smurawski@chef.io)
3
+ # Copyright:: Copyright (c) 2015-2016 Chef Software, 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
+ class Chef
20
+ class Knife
21
+ class WsmanEndpoint
22
+ attr_accessor :host
23
+ attr_accessor :wsman_port
24
+ attr_accessor :wsman_url
25
+ attr_accessor :product_version
26
+ attr_accessor :protocol_version
27
+ attr_accessor :product_vendor
28
+ attr_accessor :response_status_code
29
+ attr_accessor :error_message
30
+
31
+ def initialize(name, port, url)
32
+ @host = name
33
+ @wsman_port = port
34
+ @wsman_url = url
35
+ end
36
+
37
+ def to_hash
38
+ hash = {}
39
+ instance_variables.each {|var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
40
+ hash
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,118 +1,118 @@
1
- #
2
- # Author:: Steven Murawski (<smurawski@chef.io>)
3
- # Copyright:: Copyright (c) 2015-2016 Chef Software, 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 'httpclient'
20
- require 'chef/knife'
21
- require 'chef/knife/winrm_knife_base'
22
- require 'chef/knife/wsman_endpoint'
23
-
24
- class Chef
25
- class Knife
26
- class WsmanTest < Knife
27
-
28
- include Chef::Knife::WinrmCommandSharedFunctions
29
-
30
- deps do
31
- require 'chef/search/query'
32
- end
33
-
34
- banner "knife wsman test QUERY (options)"
35
-
36
- def run
37
- # pass a dummy password to avoid prompt for password
38
- # but it does nothing
39
- @config[:winrm_password] = 'cute_little_kittens'
40
-
41
- configure_session
42
- verify_wsman_accessiblity_for_nodes
43
- end
44
-
45
- private
46
-
47
- def verify_wsman_accessiblity_for_nodes
48
- error_count = 0
49
- @winrm_sessions.each do |item|
50
- Chef::Log.debug("checking for WSMAN availability at #{item.endpoint}")
51
-
52
- ssl_error = nil
53
- begin
54
- response = post_identity_request(item.endpoint)
55
- ui.msg "Connected successfully to #{item.host} at #{item.endpoint}."
56
- rescue Exception => err
57
- end
58
-
59
- output_object = parse_response(item, response)
60
- output_object.error_message += "\r\nError returned from endpoint: #{err.message}" if err
61
-
62
- unless output_object.error_message.nil?
63
- ui.warn "Failed to connect to #{item.host} at #{item.endpoint}."
64
- if err && err.is_a?(OpenSSL::SSL::SSLError)
65
- ui.warn "Failure due to an issue with SSL; likely cause would be unsuccessful certificate verification."
66
- ui.warn "Either ensure your certificate is valid or use '--winrm-ssl-verify-mode verify_none' to ignore verification failures."
67
- end
68
- error_count += 1
69
- end
70
-
71
- if config[:verbosity] >= 1
72
- output(output_object)
73
- end
74
- end
75
- if error_count > 0
76
- ui.error "Failed to connect to #{error_count} nodes."
77
- exit 1
78
- end
79
- end
80
-
81
- def post_identity_request(endpoint)
82
- xml = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd"><s:Header/><s:Body><wsmid:Identify/></s:Body></s:Envelope>'
83
- header = {
84
- 'WSMANIDENTIFY' => 'unauthenticated',
85
- 'Content-Type' => 'application/soap+xml; charset=UTF-8'
86
- }
87
-
88
- client = HTTPClient.new
89
- Chef::HTTP::DefaultSSLPolicy.new(client.ssl_config).set_custom_certs
90
- client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE if resolve_no_ssl_peer_verification
91
- client.post(endpoint, xml, header)
92
- end
93
-
94
- def parse_response(node, response)
95
- output_object = Chef::Knife::WsmanEndpoint.new(node.host, node.port, node.endpoint)
96
- output_object.response_status_code = response.status_code unless response.nil?
97
-
98
- if response.nil? || response.status_code != 200
99
- output_object.error_message = "No valid WSMan endoint listening at #{node.endpoint}."
100
- else
101
- doc = REXML::Document.new(response.body)
102
- output_object.protocol_version = search_xpath(doc, "//wsmid:ProtocolVersion")
103
- output_object.product_version = search_xpath(doc, "//wsmid:ProductVersion")
104
- output_object.product_vendor = search_xpath(doc, "//wsmid:ProductVendor")
105
- if output_object.protocol_version.to_s.strip.length == 0
106
- output_object.error_message = "Endpoint #{node.endpoint} on #{node.host} does not appear to be a WSMAN endpoint. Response body was #{response.body}"
107
- end
108
- end
109
- output_object
110
- end
111
-
112
- def search_xpath(document, property_name)
113
- result = REXML::XPath.match(document, property_name)
114
- result[0].nil? ? '' : result[0].text
115
- end
116
- end
117
- end
118
- end
1
+ #
2
+ # Author:: Steven Murawski (<smurawski@chef.io>)
3
+ # Copyright:: Copyright (c) 2015-2016 Chef Software, 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 'httpclient'
20
+ require 'chef/knife'
21
+ require 'chef/knife/winrm_knife_base'
22
+ require 'chef/knife/wsman_endpoint'
23
+
24
+ class Chef
25
+ class Knife
26
+ class WsmanTest < Knife
27
+
28
+ include Chef::Knife::WinrmCommandSharedFunctions
29
+
30
+ deps do
31
+ require 'chef/search/query'
32
+ end
33
+
34
+ banner "knife wsman test QUERY (options)"
35
+
36
+ def run
37
+ # pass a dummy password to avoid prompt for password
38
+ # but it does nothing
39
+ @config[:winrm_password] = 'cute_little_kittens'
40
+
41
+ configure_session
42
+ verify_wsman_accessiblity_for_nodes
43
+ end
44
+
45
+ private
46
+
47
+ def verify_wsman_accessiblity_for_nodes
48
+ error_count = 0
49
+ @winrm_sessions.each do |item|
50
+ Chef::Log.debug("checking for WSMAN availability at #{item.endpoint}")
51
+
52
+ ssl_error = nil
53
+ begin
54
+ response = post_identity_request(item.endpoint)
55
+ ui.msg "Connected successfully to #{item.host} at #{item.endpoint}."
56
+ rescue Exception => err
57
+ end
58
+
59
+ output_object = parse_response(item, response)
60
+ output_object.error_message += "\r\nError returned from endpoint: #{err.message}" if err
61
+
62
+ unless output_object.error_message.nil?
63
+ ui.warn "Failed to connect to #{item.host} at #{item.endpoint}."
64
+ if err && err.is_a?(OpenSSL::SSL::SSLError)
65
+ ui.warn "Failure due to an issue with SSL; likely cause would be unsuccessful certificate verification."
66
+ ui.warn "Either ensure your certificate is valid or use '--winrm-ssl-verify-mode verify_none' to ignore verification failures."
67
+ end
68
+ error_count += 1
69
+ end
70
+
71
+ if config[:verbosity] >= 1
72
+ output(output_object)
73
+ end
74
+ end
75
+ if error_count > 0
76
+ ui.error "Failed to connect to #{error_count} nodes."
77
+ exit 1
78
+ end
79
+ end
80
+
81
+ def post_identity_request(endpoint)
82
+ xml = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd"><s:Header/><s:Body><wsmid:Identify/></s:Body></s:Envelope>'
83
+ header = {
84
+ 'WSMANIDENTIFY' => 'unauthenticated',
85
+ 'Content-Type' => 'application/soap+xml; charset=UTF-8'
86
+ }
87
+
88
+ client = HTTPClient.new
89
+ Chef::HTTP::DefaultSSLPolicy.new(client.ssl_config).set_custom_certs
90
+ client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE if resolve_no_ssl_peer_verification
91
+ client.post(endpoint, xml, header)
92
+ end
93
+
94
+ def parse_response(node, response)
95
+ output_object = Chef::Knife::WsmanEndpoint.new(node.host, node.port, node.endpoint)
96
+ output_object.response_status_code = response.status_code unless response.nil?
97
+
98
+ if response.nil? || response.status_code != 200
99
+ output_object.error_message = "No valid WSMan endoint listening at #{node.endpoint}."
100
+ else
101
+ doc = REXML::Document.new(response.body)
102
+ output_object.protocol_version = search_xpath(doc, "//wsmid:ProtocolVersion")
103
+ output_object.product_version = search_xpath(doc, "//wsmid:ProductVersion")
104
+ output_object.product_vendor = search_xpath(doc, "//wsmid:ProductVendor")
105
+ if output_object.protocol_version.to_s.strip.length == 0
106
+ output_object.error_message = "Endpoint #{node.endpoint} on #{node.host} does not appear to be a WSMAN endpoint. Response body was #{response.body}"
107
+ end
108
+ end
109
+ output_object
110
+ end
111
+
112
+ def search_xpath(document, property_name)
113
+ result = REXML::XPath.match(document, property_name)
114
+ result[0].nil? ? '' : result[0].text
115
+ end
116
+ end
117
+ end
118
+ end