knife-windows 1.0.0.rc.1 → 1.0.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -5
  3. data/.travis.yml +20 -20
  4. data/CHANGELOG.md +75 -74
  5. data/DOC_CHANGES.md +323 -323
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +393 -292
  9. data/RELEASE_NOTES.md +79 -74
  10. data/Rakefile +21 -16
  11. data/appveyor.yml +42 -42
  12. data/ci.gemfile +15 -15
  13. data/features/knife_help.feature +20 -20
  14. data/features/support/env.rb +5 -5
  15. data/knife-windows.gemspec +28 -28
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -241
  17. data/lib/chef/knife/bootstrap_windows_base.rb +388 -368
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +110 -110
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -113
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +361 -362
  21. data/lib/chef/knife/knife_windows_base.rb +33 -0
  22. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  23. data/lib/chef/knife/windows_cert_install.rb +68 -68
  24. data/lib/chef/knife/windows_helper.rb +36 -36
  25. data/lib/chef/knife/windows_listener_create.rb +107 -107
  26. data/lib/chef/knife/winrm.rb +212 -191
  27. data/lib/chef/knife/winrm_base.rb +118 -125
  28. data/lib/chef/knife/winrm_knife_base.rb +218 -201
  29. data/lib/chef/knife/winrm_session.rb +80 -71
  30. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  31. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  32. data/lib/chef/knife/wsman_test.rb +96 -96
  33. data/lib/knife-windows/path_helper.rb +234 -234
  34. data/lib/knife-windows/version.rb +6 -6
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +217 -0
  36. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -0
  37. data/spec/assets/win_template_unrendered.txt +246 -0
  38. data/spec/functional/bootstrap_download_spec.rb +216 -140
  39. data/spec/spec_helper.rb +87 -72
  40. data/spec/unit/knife/bootstrap_options_spec.rb +146 -146
  41. data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
  42. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +240 -161
  43. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +151 -101
  44. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  45. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  46. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  47. data/spec/unit/knife/winrm_session_spec.rb +55 -46
  48. data/spec/unit/knife/winrm_spec.rb +504 -376
  49. data/spec/unit/knife/wsman_test_spec.rb +175 -175
  50. metadata +28 -8
@@ -1,72 +1,81 @@
1
- #
2
- # Author:: Steven Murawski <smurawski@chef.io>
3
- # Copyright:: Copyright (c) 2015 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 'winrm'
20
-
21
- class Chef
22
- class Knife
23
- class WinrmSession
24
- attr_reader :host, :endpoint, :port, :output, :error, :exit_code
25
- def initialize(options)
26
- @host = options[:host]
27
- @port = options[:port]
28
- url = "#{options[:host]}:#{options[:port]}/wsman"
29
- scheme = options[:transport] == :ssl ? 'https' : 'http'
30
- @endpoint = "#{scheme}://#{url}"
31
- opts = Hash.new
32
- 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]}
33
-
34
- options[:transport] == :kerberos ? opts.merge!({:service => options[:service], :realm => options[:realm], :keytab => options[:keytab]}) : opts.merge!({:ca_trust_path => options[:ca_trust_path]})
35
-
36
- Chef::Log.debug("WinRM::WinRMWebService options: #{opts}")
37
- Chef::Log.debug("Endpoint: #{endpoint}")
38
- Chef::Log.debug("Transport: #{options[:transport]}")
39
- @winrm_session = WinRM::WinRMWebService.new(@endpoint, options[:transport], opts)
40
- @winrm_session.set_timeout(options[:operation_timeout]) if options[:operation_timeout]
41
- end
42
-
43
- def relay_command(command)
44
- remote_id = @winrm_session.open_shell
45
- command_id = @winrm_session.run_command(remote_id, command)
46
- Chef::Log.debug("#{@host}[#{remote_id}] => :run_command[#{command}]")
47
- session_result = get_output(remote_id, command_id)
48
- @winrm_session.cleanup_command(remote_id, command_id)
49
- Chef::Log.debug("#{@host}[#{remote_id}] => :command_cleanup[#{command}]")
50
- @exit_code = session_result[:exitcode]
51
- @winrm_session.close_shell(remote_id)
52
- Chef::Log.debug("#{@host}[#{remote_id}] => :shell_close")
53
- end
54
-
55
- def get_output(remote_id, command_id)
56
- @winrm_session.get_command_output(remote_id, command_id) do |out,error|
57
- print_data(@host, out) if out
58
- print_data(@host, error, :red) if error
59
- end
60
- end
61
-
62
- def print_data(host, data, color = :cyan)
63
- if data =~ /\n/
64
- data.split(/\n/).each { |d| print_data(host, d, color) }
65
- elsif !data.nil?
66
- print Chef::Knife::Winrm.ui.color(host, color)
67
- puts " #{data}"
68
- end
69
- end
70
- end
71
- end
1
+ #
2
+ # Author:: Steven Murawski <smurawski@chef.io>
3
+ # Copyright:: Copyright (c) 2015 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 'winrm'
20
+
21
+ class Chef
22
+ class Knife
23
+ class WinrmSession
24
+ attr_reader :host, :endpoint, :port, :output, :error, :exit_code
25
+
26
+ def initialize(options)
27
+ @host = options[:host]
28
+ @port = options[:port]
29
+ url = "#{options[:host]}:#{options[:port]}/wsman"
30
+ scheme = options[:transport] == :ssl ? 'https' : 'http'
31
+ @endpoint = "#{scheme}://#{url}"
32
+
33
+ opts = Hash.new
34
+ 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]}
35
+ options[:transport] == :kerberos ? opts.merge!({:service => options[:service], :realm => options[:realm], :keytab => options[:keytab]}) : opts.merge!({:ca_trust_path => options[:ca_trust_path]})
36
+
37
+ Chef::Log.debug("WinRM::WinRMWebService options: #{opts}")
38
+ Chef::Log.debug("Endpoint: #{endpoint}")
39
+ Chef::Log.debug("Transport: #{options[:transport]}")
40
+
41
+ WinrmSession.load_windows_specific_gems if options[:transport] == :sspinegotiate
42
+ @winrm_session = WinRM::WinRMWebService.new(@endpoint, options[:transport], opts)
43
+ @winrm_session.set_timeout(options[:operation_timeout]) if options[:operation_timeout]
44
+ end
45
+
46
+ def relay_command(command)
47
+ remote_id = @winrm_session.open_shell
48
+ command_id = @winrm_session.run_command(remote_id, command)
49
+ Chef::Log.debug("#{@host}[#{remote_id}] => :run_command[#{command}]")
50
+ session_result = get_output(remote_id, command_id)
51
+ @winrm_session.cleanup_command(remote_id, command_id)
52
+ Chef::Log.debug("#{@host}[#{remote_id}] => :command_cleanup[#{command}]")
53
+ @exit_code = session_result[:exitcode]
54
+ @winrm_session.close_shell(remote_id)
55
+ Chef::Log.debug("#{@host}[#{remote_id}] => :shell_close")
56
+ end
57
+
58
+ def get_output(remote_id, command_id)
59
+ @winrm_session.get_command_output(remote_id, command_id) do |out,error|
60
+ print_data(@host, out) if out
61
+ print_data(@host, error, :red) if error
62
+ end
63
+ end
64
+
65
+ def print_data(host, data, color = :cyan)
66
+ if data =~ /\n/
67
+ data.split(/\n/).each { |d| print_data(host, d, color) }
68
+ elsif !data.nil?
69
+ print Chef::Knife::Winrm.ui.color(host, color)
70
+ puts " #{data}"
71
+ end
72
+ end
73
+
74
+ def self.load_windows_specific_gems
75
+ #checking for windows in case testing on linux
76
+ require 'winrm-s'
77
+ Chef::Log.debug("Applied 'winrm-s' monkey patch and trying WinRM communication with 'sspinegotiate'")
78
+ end
79
+ end
80
+ end
72
81
  end
@@ -1,47 +1,47 @@
1
- #
2
- # Author:: Steven Murawski (<smurawski@chef.io)
3
- # Copyright:: Copyright (c) 2015 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 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 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 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,96 +1,96 @@
1
- #
2
- # Author:: Steven Murawski (<smurawski@chef.io>)
3
- # Copyright:: Copyright (c) 2015 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 'nokogiri'
21
- require 'chef/knife'
22
- require 'chef/knife/winrm_knife_base'
23
- require 'chef/knife/wsman_endpoint'
24
- require 'pry'
25
-
26
- class Chef
27
- class Knife
28
- class WsmanTest < Knife
29
-
30
- include Chef::Knife::WinrmCommandSharedFunctions
31
-
32
- deps do
33
- require 'chef/search/query'
34
- end
35
-
36
- banner "knife wsman test QUERY (options)"
37
-
38
- def run
39
- @config[:winrm_authentication_protocol] = 'basic'
40
- configure_session
41
- verify_wsman_accessiblity_for_nodes
42
- end
43
-
44
- def verify_wsman_accessiblity_for_nodes
45
- error_count = 0
46
- @winrm_sessions.each do |item|
47
- Chef::Log.debug("checking for WSMAN availability at #{item.endpoint}")
48
-
49
- 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>'
50
- header = {
51
- 'WSMANIDENTIFY' => 'unauthenticated',
52
- 'Content-Type' => 'application/soap+xml; charset=UTF-8'
53
- }
54
- output_object = Chef::Knife::WsmanEndpoint.new(item.host, item.port, item.endpoint)
55
- error_message = nil
56
- begin
57
- client = HTTPClient.new
58
- response = client.post(item.endpoint, xml, header)
59
- rescue Exception => e
60
- error_message = e.message
61
- else
62
- ui.msg "Connected successfully to #{item.host} at #{item.endpoint}."
63
- output_object.response_status_code = response.status_code
64
- end
65
-
66
- if response.nil? || output_object.response_status_code != 200
67
- error_message = "No valid WSMan endoint listening at #{item.endpoint}."
68
- else
69
- doc = Nokogiri::XML response.body
70
- namespace = 'http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd'
71
- output_object.protocol_version = doc.xpath('//wsmid:ProtocolVersion', 'wsmid' => namespace).text
72
- output_object.product_version = doc.xpath('//wsmid:ProductVersion', 'wsmid' => namespace).text
73
- output_object.product_vendor = doc.xpath('//wsmid:ProductVendor', 'wsmid' => namespace).text
74
- if output_object.protocol_version.to_s.strip.length == 0
75
- error_message = "Endpoint #{item.endpoint} on #{item.host} does not appear to be a WSMAN endpoint."
76
- end
77
- end
78
-
79
- unless error_message.nil?
80
- ui.warn "Failed to connect to #{item.host} at #{item.endpoint}."
81
- output_object.error_message = error_message
82
- error_count += 1
83
- end
84
-
85
- if config[:verbosity] >= 1
86
- output(output_object)
87
- end
88
- end
89
- if error_count > 0
90
- ui.error "Failed to connect to #{error_count} nodes."
91
- exit 1
92
- end
93
- end
94
- end
95
- end
96
- end
1
+ #
2
+ # Author:: Steven Murawski (<smurawski@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 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 'nokogiri'
21
+ require 'chef/knife'
22
+ require 'chef/knife/winrm_knife_base'
23
+ require 'chef/knife/wsman_endpoint'
24
+ require 'pry'
25
+
26
+ class Chef
27
+ class Knife
28
+ class WsmanTest < Knife
29
+
30
+ include Chef::Knife::WinrmCommandSharedFunctions
31
+
32
+ deps do
33
+ require 'chef/search/query'
34
+ end
35
+
36
+ banner "knife wsman test QUERY (options)"
37
+
38
+ def run
39
+ @config[:winrm_authentication_protocol] = 'basic'
40
+ configure_session
41
+ verify_wsman_accessiblity_for_nodes
42
+ end
43
+
44
+ def verify_wsman_accessiblity_for_nodes
45
+ error_count = 0
46
+ @winrm_sessions.each do |item|
47
+ Chef::Log.debug("checking for WSMAN availability at #{item.endpoint}")
48
+
49
+ 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>'
50
+ header = {
51
+ 'WSMANIDENTIFY' => 'unauthenticated',
52
+ 'Content-Type' => 'application/soap+xml; charset=UTF-8'
53
+ }
54
+ output_object = Chef::Knife::WsmanEndpoint.new(item.host, item.port, item.endpoint)
55
+ error_message = nil
56
+ begin
57
+ client = HTTPClient.new
58
+ response = client.post(item.endpoint, xml, header)
59
+ rescue Exception => e
60
+ error_message = e.message
61
+ else
62
+ ui.msg "Connected successfully to #{item.host} at #{item.endpoint}."
63
+ output_object.response_status_code = response.status_code
64
+ end
65
+
66
+ if response.nil? || output_object.response_status_code != 200
67
+ error_message = "No valid WSMan endoint listening at #{item.endpoint}."
68
+ else
69
+ doc = Nokogiri::XML response.body
70
+ namespace = 'http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd'
71
+ output_object.protocol_version = doc.xpath('//wsmid:ProtocolVersion', 'wsmid' => namespace).text
72
+ output_object.product_version = doc.xpath('//wsmid:ProductVersion', 'wsmid' => namespace).text
73
+ output_object.product_vendor = doc.xpath('//wsmid:ProductVendor', 'wsmid' => namespace).text
74
+ if output_object.protocol_version.to_s.strip.length == 0
75
+ error_message = "Endpoint #{item.endpoint} on #{item.host} does not appear to be a WSMAN endpoint."
76
+ end
77
+ end
78
+
79
+ unless error_message.nil?
80
+ ui.warn "Failed to connect to #{item.host} at #{item.endpoint}."
81
+ output_object.error_message = error_message
82
+ error_count += 1
83
+ end
84
+
85
+ if config[:verbosity] >= 1
86
+ output(output_object)
87
+ end
88
+ end
89
+ if error_count > 0
90
+ ui.error "Failed to connect to #{error_count} nodes."
91
+ exit 1
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end