knife-vcenter 5.0.5 → 5.1.1
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.
- checksums.yaml +4 -4
- data/lib/chef/knife/cloud/ssh_bootstrap_protocol_patch.rb +139 -0
- data/lib/chef/knife/cloud/vcenter_service.rb +62 -22
- data/lib/chef/knife/cloud/vcenter_service_helpers.rb +4 -4
- data/lib/chef/knife/cloud/vcenter_service_options.rb +1 -1
- data/lib/chef/knife/cloud/winrm_bootstrap_protocol_patch.rb +81 -0
- data/lib/chef/knife/vcenter_cluster_list.rb +1 -1
- data/lib/chef/knife/vcenter_datacenter_list.rb +1 -1
- data/lib/chef/knife/vcenter_host_list.rb +1 -1
- data/lib/chef/knife/vcenter_vm_clone.rb +37 -1
- data/lib/chef/knife/vcenter_vm_create.rb +1 -1
- data/lib/chef/knife/vcenter_vm_delete.rb +1 -1
- data/lib/chef/knife/vcenter_vm_list.rb +1 -1
- data/lib/chef/knife/vcenter_vm_show.rb +1 -1
- data/lib/knife-vcenter/version.rb +2 -2
- data/lib/support/clone_vm.rb +48 -11
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5deb02621e85d8a3bbd047ac7a93b51c7e39c3c5320738c7f33fb939e648b200
|
|
4
|
+
data.tar.gz: e2fb1fdb8a4cbcb68211ced8e143ccb671b08bfdb9214c13439d60cc82391d77
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4aa67a6997a6a79c8fd46d02a4aafcc9fd375cbb0778e37f4c8b7d9919326196346c6020e6979008596841737fdd470d1e7729dcf1493cfafb42ce7f10cfbba
|
|
7
|
+
data.tar.gz: 5a9f4a0a63469a836196374ec2fca3bcb1287c17e4bd0751ddf4446147bb3214c278b20b9aaa484d3ec6d5e2e36b7ee623f95fae53b42b36393cd5ce747e810c
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
#
|
|
3
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
|
+
# License:: Apache License, Version 2.0
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
require "chef/knife"
|
|
21
|
+
require "chef/knife/cloud/chefbootstrap/ssh_bootstrap_protocol"
|
|
22
|
+
|
|
23
|
+
class Chef
|
|
24
|
+
# The main knife class
|
|
25
|
+
class Knife
|
|
26
|
+
# The main cloud class from knife-cloud
|
|
27
|
+
class Cloud
|
|
28
|
+
# Patches SshBootstrapProtocol: upstream's wait_for_server_ready never
|
|
29
|
+
# falls back to port 22 when --connection-port/--ssh-port aren't set,
|
|
30
|
+
# so it probes TCPSocket.new(host, nil) and hangs forever. This adds
|
|
31
|
+
# the missing default, treats EADDRNOTAVAIL as a transient/retryable
|
|
32
|
+
# error, and adds backoff plus periodic status output while waiting.
|
|
33
|
+
class SshBootstrapProtocol
|
|
34
|
+
# Maximum backoff between probe attempts, in seconds.
|
|
35
|
+
SSH_PROBE_MAX_BACKOFF = 20
|
|
36
|
+
# Minimum interval between status updates surfaced to the user, in seconds.
|
|
37
|
+
SSH_PROBE_STATUS_INTERVAL = 15
|
|
38
|
+
# Standard SSH port used when neither --connection-port nor --ssh-port is given.
|
|
39
|
+
DEFAULT_SSH_PORT = 22
|
|
40
|
+
|
|
41
|
+
# Waits until sshd is reachable on the bootstrap IP before returning.
|
|
42
|
+
# Falls back to the standard SSH port 22 when neither
|
|
43
|
+
# --connection-port nor --ssh-port is configured (see class comment).
|
|
44
|
+
#
|
|
45
|
+
# @return [void]
|
|
46
|
+
def wait_for_server_ready
|
|
47
|
+
print "\n#{ui.color("Waiting for sshd to host (#{config[:bootstrap_ip_address]})", :magenta)}"
|
|
48
|
+
|
|
49
|
+
ssh_gateway = get_ssh_gateway_for(config[:bootstrap_ip_address])
|
|
50
|
+
ssh_port = config[:connection_port] || config[:ssh_port] || DEFAULT_SSH_PORT
|
|
51
|
+
|
|
52
|
+
if ssh_gateway
|
|
53
|
+
print(".") until tunnel_test_ssh(ssh_gateway, config[:bootstrap_ip_address]) do
|
|
54
|
+
@initial_sleep_delay = !!config[:subnet_id] ? 40 : 10
|
|
55
|
+
sleep @initial_sleep_delay
|
|
56
|
+
puts("done")
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
print(".") until tcp_test_ssh(config[:bootstrap_ip_address], ssh_port) do
|
|
60
|
+
@initial_sleep_delay = !!config[:subnet_id] ? 40 : 10
|
|
61
|
+
sleep @initial_sleep_delay
|
|
62
|
+
puts("done")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Tests whether sshd is accepting connections on the given host/port
|
|
68
|
+
#
|
|
69
|
+
# @param [String] hostname The host to test the SSH connection against
|
|
70
|
+
# @param [Integer] ssh_port The port to test the SSH connection against
|
|
71
|
+
# @return [Boolean] true if sshd is accepting connections, false otherwise
|
|
72
|
+
def tcp_test_ssh(hostname, ssh_port)
|
|
73
|
+
tcp_socket = TCPSocket.new(hostname, ssh_port)
|
|
74
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
|
75
|
+
if readable
|
|
76
|
+
ssh_banner = tcp_socket.gets
|
|
77
|
+
if ssh_banner.nil? || ssh_banner.empty?
|
|
78
|
+
record_ssh_probe_failure(hostname, "TCP connected but received no SSH banner yet")
|
|
79
|
+
false
|
|
80
|
+
else
|
|
81
|
+
Chef::Log.debug("ssh accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
|
82
|
+
yield
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
record_ssh_probe_failure(hostname, "TCP connect did not become readable within 5s")
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
rescue Errno::EPERM, Errno::ETIMEDOUT => e
|
|
90
|
+
Chef::Log.debug("ssh timed out: #{hostname}")
|
|
91
|
+
record_ssh_probe_failure(hostname, e)
|
|
92
|
+
false
|
|
93
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::EADDRNOTAVAIL, IOError => e
|
|
94
|
+
Chef::Log.debug("ssh failed to connect: #{hostname}")
|
|
95
|
+
record_ssh_probe_failure(hostname, e)
|
|
96
|
+
backoff_before_next_ssh_probe
|
|
97
|
+
false
|
|
98
|
+
rescue Errno::ECONNRESET => e
|
|
99
|
+
Chef::Log.debug("ssh reset its connection: #{hostname}")
|
|
100
|
+
record_ssh_probe_failure(hostname, e)
|
|
101
|
+
backoff_before_next_ssh_probe
|
|
102
|
+
false
|
|
103
|
+
ensure
|
|
104
|
+
tcp_socket && tcp_socket.close
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
# Records a failed probe attempt and surfaces a throttled status line.
|
|
110
|
+
#
|
|
111
|
+
# @param [String] hostname The host being probed
|
|
112
|
+
# @param [String, Exception] reason Why this attempt failed
|
|
113
|
+
# @return [void]
|
|
114
|
+
def record_ssh_probe_failure(hostname, reason)
|
|
115
|
+
@ssh_probe_started_at ||= Time.now
|
|
116
|
+
@ssh_probe_attempts = (@ssh_probe_attempts || 0) + 1
|
|
117
|
+
|
|
118
|
+
detail = reason.is_a?(Exception) ? "#{reason.class}: #{reason.message}" : reason
|
|
119
|
+
elapsed = Time.now - @ssh_probe_started_at
|
|
120
|
+
Chef::Log.debug(format("SSH readiness probe attempt #%d for %s failed after %.1fs total: %s", @ssh_probe_attempts, hostname, elapsed, detail))
|
|
121
|
+
|
|
122
|
+
return if @ssh_probe_last_status_at && (Time.now - @ssh_probe_last_status_at) < SSH_PROBE_STATUS_INTERVAL
|
|
123
|
+
|
|
124
|
+
@ssh_probe_last_status_at = Time.now
|
|
125
|
+
ui.warn(format("\nStill waiting for sshd on %s (attempt #%d, %ds elapsed) - last result: %s", hostname, @ssh_probe_attempts, elapsed.to_i, detail))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Sleeps a capped exponential backoff (with jitter) before the next probe.
|
|
129
|
+
#
|
|
130
|
+
# @return [void]
|
|
131
|
+
def backoff_before_next_ssh_probe
|
|
132
|
+
attempt = @ssh_probe_attempts || 1
|
|
133
|
+
base_delay = [2 * (2**(attempt - 1)), SSH_PROBE_MAX_BACKOFF].min
|
|
134
|
+
sleep(base_delay + rand * 0.5)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -25,7 +25,13 @@ require_relative "../../../support/clone_vm"
|
|
|
25
25
|
require "uri" unless defined?(URI)
|
|
26
26
|
require "json" unless defined?(JSON)
|
|
27
27
|
require "ostruct" unless defined?(OpenStruct)
|
|
28
|
-
require "vsphere-automation-cis"
|
|
28
|
+
require "vsphere-automation-cis/api/session_api"
|
|
29
|
+
require "vsphere-automation-cis/models/cis_session_create_result"
|
|
30
|
+
require "vsphere-automation-cis/models/vapi_std_errors_unauthenticated"
|
|
31
|
+
require "vsphere-automation-cis/models/vapi_std_errors_unauthenticated_error"
|
|
32
|
+
require "vsphere-automation-cis/models/vapi_std_errors_service_unavailable"
|
|
33
|
+
require "vsphere-automation-cis/models/vapi_std_errors_service_unavailable_error"
|
|
34
|
+
require "vsphere-automation-cis/models/vapi_std_localizable_message"
|
|
29
35
|
require "vsphere-automation-vcenter"
|
|
30
36
|
require "set" unless defined?(Set)
|
|
31
37
|
|
|
@@ -41,35 +47,34 @@ class Chef
|
|
|
41
47
|
attr_reader :api_client, :session_api, :session_id
|
|
42
48
|
attr_reader :connection_options, :ipaddress
|
|
43
49
|
|
|
44
|
-
def initialize(
|
|
45
|
-
super(
|
|
50
|
+
def initialize(config: nil, host: nil, username: nil, password: nil, verify_ssl: true, logs: false)
|
|
51
|
+
super(config: config)
|
|
46
52
|
|
|
47
53
|
configuration = VSphereAutomation::Configuration.new.tap do |c|
|
|
48
|
-
c.host =
|
|
49
|
-
c.username =
|
|
50
|
-
c.password =
|
|
54
|
+
c.host = host
|
|
55
|
+
c.username = username
|
|
56
|
+
c.password = password
|
|
51
57
|
c.scheme = "https"
|
|
52
|
-
c.verify_ssl =
|
|
53
|
-
c.verify_ssl_host =
|
|
58
|
+
c.verify_ssl = verify_ssl
|
|
59
|
+
c.verify_ssl_host = verify_ssl
|
|
54
60
|
end
|
|
55
61
|
|
|
56
|
-
Base.log.warn("SSL Verification is turned OFF") if
|
|
62
|
+
Base.log.warn("SSL Verification is turned OFF") if logs && !verify_ssl
|
|
57
63
|
|
|
58
64
|
@api_client = VSphereAutomation::ApiClient.new(configuration)
|
|
59
65
|
api_client.default_headers["Authorization"] = configuration.basic_auth_token
|
|
60
66
|
|
|
61
|
-
session_api = VSphereAutomation::CIS::SessionApi.new(api_client)
|
|
62
|
-
session_id = session_api.create("").value
|
|
63
|
-
|
|
64
|
-
api_client.default_headers["vmware-api-session-id"] = session_id
|
|
65
|
-
|
|
66
67
|
# Set the class properties for the rbvmomi connections
|
|
67
68
|
@connection_options = {
|
|
68
|
-
user:
|
|
69
|
-
password:
|
|
70
|
-
insecure:
|
|
71
|
-
host:
|
|
69
|
+
user: username,
|
|
70
|
+
password: password,
|
|
71
|
+
insecure: verify_ssl ? false : true,
|
|
72
|
+
host: host,
|
|
72
73
|
}
|
|
74
|
+
|
|
75
|
+
@session_id = create_api_session_id
|
|
76
|
+
|
|
77
|
+
api_client.default_headers["vmware-api-session-id"] = @session_id
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
# Creates the server
|
|
@@ -130,6 +135,11 @@ class Chef
|
|
|
130
135
|
rescue StandardError => e
|
|
131
136
|
puts e.message
|
|
132
137
|
end
|
|
138
|
+
|
|
139
|
+
# vm_api.create only returns the new VM's ID as a raw String, not
|
|
140
|
+
# a summary object with .vm/.name/.power_state (unlike "clone"
|
|
141
|
+
# above), so fetch the full summary the same way.
|
|
142
|
+
get_server(options[:name])
|
|
133
143
|
end
|
|
134
144
|
end
|
|
135
145
|
|
|
@@ -137,6 +147,7 @@ class Chef
|
|
|
137
147
|
#
|
|
138
148
|
def list_servers
|
|
139
149
|
vms = VSphereAutomation::VCenter::VMApi.new(api_client).list.value
|
|
150
|
+
vms = validate_list_response!(vms, "virtual machines")
|
|
140
151
|
|
|
141
152
|
# list_resource_command uses .send(:name) syntax, so convert to OpenStruct to keep it compatible
|
|
142
153
|
vms.map { |vmsummary| OpenStruct.new(vmsummary.to_hash) }
|
|
@@ -145,19 +156,22 @@ class Chef
|
|
|
145
156
|
# Return a list of the hosts in the vCenter
|
|
146
157
|
#
|
|
147
158
|
def list_hosts
|
|
148
|
-
VSphereAutomation::VCenter::HostApi.new(api_client).list.value
|
|
159
|
+
hosts = VSphereAutomation::VCenter::HostApi.new(api_client).list.value
|
|
160
|
+
validate_list_response!(hosts, "hosts")
|
|
149
161
|
end
|
|
150
162
|
|
|
151
163
|
# Return a list of the datacenters in the vCenter
|
|
152
164
|
#
|
|
153
165
|
def list_datacenters
|
|
154
|
-
VSphereAutomation::VCenter::DatacenterApi.new(api_client).list.value
|
|
166
|
+
datacenters = VSphereAutomation::VCenter::DatacenterApi.new(api_client).list.value
|
|
167
|
+
validate_list_response!(datacenters, "datacenters")
|
|
155
168
|
end
|
|
156
169
|
|
|
157
170
|
# Return a list of the clusters in the vCenter
|
|
158
171
|
#
|
|
159
172
|
def list_clusters
|
|
160
|
-
VSphereAutomation::VCenter::ClusterApi.new(api_client).list.value
|
|
173
|
+
clusters = VSphereAutomation::VCenter::ClusterApi.new(api_client).list.value
|
|
174
|
+
validate_list_response!(clusters, "clusters")
|
|
161
175
|
end
|
|
162
176
|
|
|
163
177
|
# Checks to see if the datacenter exists in the vCenter
|
|
@@ -300,6 +314,32 @@ class Chef
|
|
|
300
314
|
|
|
301
315
|
private
|
|
302
316
|
|
|
317
|
+
def create_api_session_id
|
|
318
|
+
session_api = VSphereAutomation::CIS::SessionApi.new(api_client)
|
|
319
|
+
session_response = session_api.create("")
|
|
320
|
+
|
|
321
|
+
if session_response.respond_to?(:value) && session_response.value.is_a?(String) && !session_response.value.empty?
|
|
322
|
+
return session_response.value
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
response_type = session_response.class.name
|
|
326
|
+
if response_type.end_with?("VapiStdErrorsUnauthenticatedError") || response_type.end_with?("VapiStdErrorsUnauthenticated")
|
|
327
|
+
raise format("Authentication to vCenter failed for user %s on host %s", connection_options[:user], connection_options[:host])
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
raise format("Unable to establish a vCenter session on host %s: %s", connection_options[:host], response_type)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def validate_list_response!(resources, resource_type)
|
|
334
|
+
return resources if resources.is_a?(Array)
|
|
335
|
+
|
|
336
|
+
if resources.class.name.end_with?("VapiStdErrorsUnauthenticated")
|
|
337
|
+
raise format("Authentication to vCenter failed for user %s on host %s", connection_options[:user], connection_options[:host])
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
raise format("Unexpected vCenter response while listing %s: %s", resource_type, resources.class.name)
|
|
341
|
+
end
|
|
342
|
+
|
|
303
343
|
def cleanup
|
|
304
344
|
session_svc.delete unless session_id.nil?
|
|
305
345
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c)
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -32,9 +32,9 @@ class Chef
|
|
|
32
32
|
#
|
|
33
33
|
def create_service_instance
|
|
34
34
|
Chef::Knife::Cloud::VcenterService.new(username: config[:vcenter_username],
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
password: config[:vcenter_password],
|
|
36
|
+
host: config[:vcenter_host],
|
|
37
|
+
verify_ssl: verify_ssl?)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Do we have valid SSL?
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
#
|
|
3
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
|
+
# License:: Apache License, Version 2.0
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
# knife-cloud's WinrmBootstrapProtocol does
|
|
21
|
+
# `require "winrm" unless defined?(WinRM::Connection)`. The upstream "winrm"
|
|
22
|
+
# gem is unmaintained and not a declared dependency of this plugin, so this
|
|
23
|
+
# raises a LoadError on every --connection-protocol winrm bootstrap. The
|
|
24
|
+
# actively maintained fork "chef-winrm" defines the same WinRM::Connection
|
|
25
|
+
# class, satisfying the guard, so we require it here first (falling back to
|
|
26
|
+
# "winrm" directly in case a future environment has the real gem installed).
|
|
27
|
+
begin
|
|
28
|
+
require "chef-winrm" unless defined?(WinRM::Connection)
|
|
29
|
+
rescue LoadError
|
|
30
|
+
require "winrm" unless defined?(WinRM::Connection)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
require "chef/knife"
|
|
34
|
+
require "chef/knife/cloud/chefbootstrap/winrm_bootstrap_protocol"
|
|
35
|
+
|
|
36
|
+
class Chef
|
|
37
|
+
# The main knife class
|
|
38
|
+
class Knife
|
|
39
|
+
# The main cloud class from knife-cloud
|
|
40
|
+
class Cloud
|
|
41
|
+
# Patches WinrmBootstrapProtocol: upstream's wait_for_server_ready has
|
|
42
|
+
# no port default at all (unlike SSH, which falls back to --ssh-port),
|
|
43
|
+
# so it probes TCPSocket.new(host, nil) and hangs forever. This adds
|
|
44
|
+
# a default of 5985 (or 5986 with --winrm-ssl) and treats
|
|
45
|
+
# EADDRNOTAVAIL as a transient/retryable error.
|
|
46
|
+
class WinrmBootstrapProtocol
|
|
47
|
+
# Standard WinRM ports used when --connection-port isn't given.
|
|
48
|
+
DEFAULT_WINRM_PORT = 5985
|
|
49
|
+
DEFAULT_WINRM_SSL_PORT = 5986
|
|
50
|
+
|
|
51
|
+
def wait_for_server_ready
|
|
52
|
+
print "\n#{ui.color("Waiting for winrm to host (#{@config[:bootstrap_ip_address]})", :magenta)}"
|
|
53
|
+
|
|
54
|
+
winrm_port = @config[:connection_port] || (@config[:winrm_ssl] ? DEFAULT_WINRM_SSL_PORT : DEFAULT_WINRM_PORT)
|
|
55
|
+
|
|
56
|
+
print(".") until tcp_test_winrm(@config[:bootstrap_ip_address], winrm_port) do
|
|
57
|
+
sleep @initial_sleep_delay ||= 10
|
|
58
|
+
puts("done")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def tcp_test_winrm(hostname, port)
|
|
63
|
+
tcp_socket = TCPSocket.new(hostname, port)
|
|
64
|
+
true
|
|
65
|
+
rescue SocketError
|
|
66
|
+
sleep 2
|
|
67
|
+
false
|
|
68
|
+
rescue Errno::ETIMEDOUT
|
|
69
|
+
false
|
|
70
|
+
rescue Errno::EPERM
|
|
71
|
+
false
|
|
72
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::EADDRNOTAVAIL
|
|
73
|
+
sleep 2
|
|
74
|
+
false
|
|
75
|
+
ensure
|
|
76
|
+
tcp_socket && tcp_socket.close
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#
|
|
4
4
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
5
|
-
# Copyright:: Copyright (c)
|
|
5
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
6
6
|
# License:: Apache License, Version 2.0
|
|
7
7
|
#
|
|
8
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -38,6 +38,8 @@ class Chef
|
|
|
38
38
|
# lazy load this file as it includes vmware deps that we only want at plugin runtime
|
|
39
39
|
deps do
|
|
40
40
|
require_relative "cloud/vcenter_service"
|
|
41
|
+
require_relative "cloud/ssh_bootstrap_protocol_patch"
|
|
42
|
+
require_relative "cloud/winrm_bootstrap_protocol_patch"
|
|
41
43
|
include VcenterServiceHelpers
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -116,6 +118,40 @@ class Chef
|
|
|
116
118
|
|
|
117
119
|
ipaddress.nil? ? server.name : ipaddress
|
|
118
120
|
end
|
|
121
|
+
|
|
122
|
+
# Retry bootstrap when local networking has a transient source-address failure.
|
|
123
|
+
# This keeps a successful clone from failing permanently on a temporary route/interface flap.
|
|
124
|
+
def after_exec_command
|
|
125
|
+
bootstrap_attempt = 0
|
|
126
|
+
max_bootstrap_retries = 3
|
|
127
|
+
|
|
128
|
+
begin
|
|
129
|
+
bootstrap
|
|
130
|
+
rescue Errno::EADDRNOTAVAIL => e
|
|
131
|
+
bootstrap_attempt += 1
|
|
132
|
+
|
|
133
|
+
if bootstrap_attempt <= max_bootstrap_retries
|
|
134
|
+
retry_wait = bootstrap_attempt * 5
|
|
135
|
+
ui.warn(format("Transient local networking error while bootstrapping (%s). Retrying in %d seconds (attempt %d/%d).", e.message, retry_wait, bootstrap_attempt, max_bootstrap_retries))
|
|
136
|
+
sleep retry_wait
|
|
137
|
+
retry
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
error_message = format("Bootstrap failed after %d retries due to local networking error: %s", max_bootstrap_retries, e.message)
|
|
141
|
+
ui.fatal(error_message)
|
|
142
|
+
cleanup_on_failure
|
|
143
|
+
raise e, error_message
|
|
144
|
+
rescue CloudExceptions::BootstrapError => e
|
|
145
|
+
ui.fatal(e.message)
|
|
146
|
+
cleanup_on_failure
|
|
147
|
+
raise e
|
|
148
|
+
rescue StandardError => e
|
|
149
|
+
error_message = format("Check if --connection-protocol and --image-os-type is correct. %s", e.message)
|
|
150
|
+
ui.fatal(error_message)
|
|
151
|
+
cleanup_on_failure
|
|
152
|
+
raise e, error_message
|
|
153
|
+
end
|
|
154
|
+
end
|
|
119
155
|
end
|
|
120
156
|
end
|
|
121
157
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c)
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c)
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
# Provisions machines in vCenter
|
|
21
21
|
module KnifeVcenter
|
|
22
22
|
# The version of this amazing Gem, you should <3 it.
|
|
23
|
-
VERSION = "5.
|
|
23
|
+
VERSION = "5.1.1"
|
|
24
24
|
end
|
data/lib/support/clone_vm.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
#
|
|
3
3
|
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
4
|
-
# Copyright:: Copyright (c) 2017-
|
|
4
|
+
# Copyright:: Copyright (c) 2017-2024 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -45,8 +45,8 @@ class Support
|
|
|
45
45
|
relocate_spec.pool = options[:resource_pool]
|
|
46
46
|
|
|
47
47
|
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(location: relocate_spec,
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
powerOn: options[:poweron],
|
|
49
|
+
template: false)
|
|
50
50
|
|
|
51
51
|
# Set the folder to use
|
|
52
52
|
dest_folder = options[:folder].nil? ? src_vm.parent : options[:folder][:id]
|
|
@@ -57,19 +57,56 @@ class Support
|
|
|
57
57
|
task.wait_for_completion
|
|
58
58
|
|
|
59
59
|
# get the IP address of the machine for bootstrapping
|
|
60
|
-
|
|
61
|
-
name = options[:folder].nil? ? options[:name] : format("%s/%s", options[:folder][:name], options[:name])
|
|
62
|
-
new_vm = dc.find_vm(name)
|
|
60
|
+
new_vm = locate_new_vm(dc)
|
|
63
61
|
|
|
64
62
|
if new_vm.nil?
|
|
65
|
-
puts format("Unable to find machine: %s", name)
|
|
63
|
+
puts format("Unable to find machine: %s", options[:name])
|
|
66
64
|
else
|
|
67
65
|
puts "Waiting for network interfaces to become available..."
|
|
68
|
-
|
|
69
|
-
new_vm.guest.net[0].ipConfig.ipAddress.detect do |addr|
|
|
70
|
-
addr.origin != "linklayer"
|
|
71
|
-
end.ipAddress
|
|
66
|
+
bootstrap_ip_for_vm(new_vm)
|
|
72
67
|
end
|
|
73
68
|
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def locate_new_vm(datacenter)
|
|
73
|
+
vm_name = options[:name]
|
|
74
|
+
|
|
75
|
+
if options[:folder]
|
|
76
|
+
folder_vm_name = format("%s/%s", options[:folder][:name], vm_name)
|
|
77
|
+
vm = datacenter.find_vm(folder_vm_name)
|
|
78
|
+
return vm unless vm.nil?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
datacenter.find_vm(vm_name)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def bootstrap_ip_for_vm(vm, timeout: 300, interval: 2)
|
|
85
|
+
deadline = Time.now + timeout
|
|
86
|
+
|
|
87
|
+
loop do
|
|
88
|
+
ipaddress = extract_bootstrap_ip(vm)
|
|
89
|
+
return ipaddress unless ipaddress.nil?
|
|
90
|
+
|
|
91
|
+
return nil if Time.now >= deadline
|
|
92
|
+
|
|
93
|
+
sleep interval
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def extract_bootstrap_ip(vm)
|
|
98
|
+
guest = vm.guest
|
|
99
|
+
|
|
100
|
+
addresses = guest.net.to_a.flat_map do |nic|
|
|
101
|
+
nic.ipConfig&.ipAddress.to_a.map(&:ipAddress)
|
|
102
|
+
end.compact
|
|
103
|
+
|
|
104
|
+
ipv4 = addresses.find { |addr| addr.include?(".") && !addr.start_with?("169.254.") }
|
|
105
|
+
return ipv4 unless ipv4.nil?
|
|
106
|
+
|
|
107
|
+
return nil if guest.ipAddress.nil? || !guest.ipAddress.include?(".")
|
|
108
|
+
|
|
109
|
+
guest.ipAddress.start_with?("169.254.") ? nil : guest.ipAddress
|
|
110
|
+
end
|
|
74
111
|
end
|
|
75
112
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: knife-vcenter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chef Partner Engineering
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-08-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: chef
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '18.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '18.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: knife-cloud
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,9 +94,11 @@ extensions: []
|
|
|
94
94
|
extra_rdoc_files: []
|
|
95
95
|
files:
|
|
96
96
|
- LICENSE
|
|
97
|
+
- lib/chef/knife/cloud/ssh_bootstrap_protocol_patch.rb
|
|
97
98
|
- lib/chef/knife/cloud/vcenter_service.rb
|
|
98
99
|
- lib/chef/knife/cloud/vcenter_service_helpers.rb
|
|
99
100
|
- lib/chef/knife/cloud/vcenter_service_options.rb
|
|
101
|
+
- lib/chef/knife/cloud/winrm_bootstrap_protocol_patch.rb
|
|
100
102
|
- lib/chef/knife/vcenter_cluster_list.rb
|
|
101
103
|
- lib/chef/knife/vcenter_datacenter_list.rb
|
|
102
104
|
- lib/chef/knife/vcenter_host_list.rb
|
|
@@ -119,14 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
119
121
|
requirements:
|
|
120
122
|
- - ">="
|
|
121
123
|
- !ruby/object:Gem::Version
|
|
122
|
-
version: '
|
|
124
|
+
version: '3.1'
|
|
123
125
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
126
|
requirements:
|
|
125
127
|
- - ">="
|
|
126
128
|
- !ruby/object:Gem::Version
|
|
127
129
|
version: '0'
|
|
128
130
|
requirements: []
|
|
129
|
-
rubygems_version: 3.
|
|
131
|
+
rubygems_version: 3.3.27
|
|
130
132
|
signing_key:
|
|
131
133
|
specification_version: 4
|
|
132
134
|
summary: Knife plugin to VMware vCenter.
|