bosh_vsphere_cpi 1.1868.0 → 1.1975.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cloud/vsphere/client.rb +3 -3
- data/lib/cloud/vsphere/cloud.rb +31 -110
- data/lib/cloud/vsphere/cluster_config.rb +15 -0
- data/lib/cloud/vsphere/config.rb +140 -198
- data/lib/cloud/vsphere/fixed_cluster_placer.rb +17 -0
- data/lib/cloud/vsphere/resources.rb +20 -22
- data/lib/cloud/vsphere/resources/cluster.rb +98 -125
- data/lib/cloud/vsphere/resources/datacenter.rb +51 -61
- data/lib/cloud/vsphere/resources/datastore.rb +1 -3
- data/lib/cloud/vsphere/resources/folder.rb +41 -41
- data/lib/cloud/vsphere/resources/resource_pool.rb +6 -6
- data/lib/cloud/vsphere/resources/scorer.rb +2 -2
- data/lib/cloud/vsphere/version.rb +1 -1
- data/lib/cloud/vsphere/vm_creator.rb +116 -0
- data/lib/cloud/vsphere/vm_creator_builder.rb +17 -0
- metadata +12 -8
@@ -0,0 +1,17 @@
|
|
1
|
+
module VSphereCloud
|
2
|
+
class FixedClusterPlacer
|
3
|
+
def initialize(cluster)
|
4
|
+
@cluster = cluster
|
5
|
+
end
|
6
|
+
|
7
|
+
def place(memory, ephemeral, persistent)
|
8
|
+
datastore = @cluster.pick_ephemeral(ephemeral)
|
9
|
+
if datastore
|
10
|
+
@cluster.allocate(memory)
|
11
|
+
datastore.allocate(ephemeral)
|
12
|
+
return [@cluster, datastore]
|
13
|
+
end
|
14
|
+
raise "No available resources"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,18 +1,15 @@
|
|
1
|
-
|
1
|
+
require 'cloud/vsphere/resources/datacenter'
|
2
2
|
|
3
3
|
module VSphereCloud
|
4
|
-
|
5
|
-
# Resources model.
|
6
4
|
class Resources
|
7
5
|
MEMORY_THRESHOLD = 128
|
8
6
|
DISK_THRESHOLD = 1024
|
9
7
|
STALE_TIMEOUT = 60
|
10
8
|
BYTES_IN_MB = 1024 * 1024
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
@logger = Config.logger
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
@logger = config.logger
|
16
13
|
@last_update = 0
|
17
14
|
@lock = Monitor.new
|
18
15
|
end
|
@@ -100,7 +97,7 @@ module VSphereCloud
|
|
100
97
|
locality.each do |cluster, _|
|
101
98
|
persistent_sizes = persistent_sizes_for_cluster(cluster, persistent)
|
102
99
|
|
103
|
-
scorer = Scorer.new(cluster, memory, ephemeral, persistent_sizes)
|
100
|
+
scorer = Scorer.new(@config, cluster, memory, ephemeral, persistent_sizes)
|
104
101
|
if scorer.score > 0
|
105
102
|
datastore = cluster.pick_ephemeral(ephemeral)
|
106
103
|
if datastore
|
@@ -113,23 +110,23 @@ module VSphereCloud
|
|
113
110
|
|
114
111
|
unless locality.empty?
|
115
112
|
@logger.debug("Ignoring datastore locality as we could not find " +
|
116
|
-
|
113
|
+
"any resources near disks: #{persistent.inspect}")
|
117
114
|
end
|
118
115
|
|
119
116
|
weighted_clusters = []
|
120
|
-
datacenters.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
end
|
117
|
+
datacenter = datacenters.first.last
|
118
|
+
datacenter.clusters.each_value do |cluster|
|
119
|
+
persistent_sizes = persistent_sizes_for_cluster(cluster, persistent)
|
120
|
+
scorer = Scorer.new(@config, cluster, memory, ephemeral, persistent_sizes)
|
121
|
+
score = scorer.score
|
122
|
+
@logger.debug("Score: #{cluster.name}: #{score}")
|
123
|
+
weighted_clusters << [cluster, score] if score > 0
|
128
124
|
end
|
129
125
|
|
130
126
|
raise "No available resources" if weighted_clusters.empty?
|
131
127
|
|
132
128
|
cluster = Util.weighted_random(weighted_clusters)
|
129
|
+
|
133
130
|
datastore = cluster.pick_ephemeral(ephemeral)
|
134
131
|
|
135
132
|
if datastore
|
@@ -144,13 +141,14 @@ module VSphereCloud
|
|
144
141
|
|
145
142
|
private
|
146
143
|
|
144
|
+
attr_reader :config
|
145
|
+
|
147
146
|
# Updates the resource models from vSphere.
|
148
147
|
# @return [void]
|
149
148
|
def update
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
149
|
+
#datacenter_config = config.vcenter_datacenter
|
150
|
+
datacenter = Datacenter.new(config)
|
151
|
+
@datacenters = { datacenter.name => datacenter }
|
154
152
|
@last_update = Time.now.to_i
|
155
153
|
end
|
156
154
|
|
@@ -212,7 +210,7 @@ module VSphereCloud
|
|
212
210
|
# @return [Array<Hash>] filtered out disk specs.
|
213
211
|
def persistent_sizes_for_cluster(cluster, disks)
|
214
212
|
disks.select { |disk| disk[:cluster] != cluster }.
|
215
|
-
|
213
|
+
collect { |disk| disk[:size] }
|
216
214
|
end
|
217
215
|
end
|
218
216
|
end
|
@@ -1,51 +1,35 @@
|
|
1
|
-
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
-
|
3
1
|
module VSphereCloud
|
4
2
|
class Resources
|
5
|
-
|
6
|
-
# Cluster resource.
|
7
3
|
class Cluster
|
8
4
|
include VimSdk
|
9
5
|
|
10
6
|
PROPERTIES = %w(name datastore resourcePool host)
|
11
7
|
HOST_PROPERTIES = %w(hardware.memorySize runtime.inMaintenanceMode)
|
12
|
-
HOST_COUNTERS = %w(
|
8
|
+
HOST_COUNTERS = %w(mem.usage.average)
|
13
9
|
|
14
10
|
# @!attribute mob
|
15
11
|
# @return [Vim::ClusterComputeResource] cluster vSphere MOB.
|
16
|
-
|
17
|
-
|
18
|
-
# @!attribute config
|
19
|
-
# @return [ClusterConfig] cluster config.
|
20
|
-
attr_accessor :config
|
12
|
+
attr_reader :mob
|
21
13
|
|
22
14
|
# @!attribute datacenter
|
23
15
|
# @return [Datacenter] parent datacenter.
|
24
|
-
|
16
|
+
attr_reader :datacenter
|
25
17
|
|
26
18
|
# @!attribute resource_pool
|
27
19
|
# @return [ResourcePool] resource pool.
|
28
|
-
|
20
|
+
attr_reader :resource_pool
|
29
21
|
|
30
22
|
# @!attribute ephemeral_datastores
|
31
23
|
# @return [Hash<String, Datastore>] ephemeral datastores.
|
32
|
-
|
24
|
+
attr_reader :ephemeral_datastores
|
33
25
|
|
34
26
|
# @!attribute persistent_datastores
|
35
27
|
# @return [Hash<String, Datastore>] persistent datastores.
|
36
|
-
|
28
|
+
attr_reader :persistent_datastores
|
37
29
|
|
38
30
|
# @!attribute shared_datastores
|
39
31
|
# @return [Hash<String, Datastore>] shared datastores.
|
40
|
-
|
41
|
-
|
42
|
-
# @!attribute idle_cpu
|
43
|
-
# @return [Float] idle cpu ratio.
|
44
|
-
attr_accessor :idle_cpu
|
45
|
-
|
46
|
-
# @!attribute total_memory
|
47
|
-
# @return [Integer] memory capacity in MB.
|
48
|
-
attr_accessor :total_memory
|
32
|
+
attr_reader :shared_datastores
|
49
33
|
|
50
34
|
# @!attribute synced_free_memory
|
51
35
|
# @return [Integer] cached memory utilization in MB.
|
@@ -58,60 +42,34 @@ module VSphereCloud
|
|
58
42
|
# Creates a new Cluster resource from the specified datacenter, cluster
|
59
43
|
# configuration, and prefetched properties.
|
60
44
|
#
|
61
|
-
# @param [
|
45
|
+
# @param [CloudConfig] VSphereCloud::Config
|
62
46
|
# @param [ClusterConfig] config cluster configuration as specified by the
|
63
47
|
# operator.
|
64
48
|
# @param [Hash] properties prefetched vSphere properties for the cluster.
|
65
|
-
def initialize(datacenter,
|
66
|
-
@logger = Config.logger
|
67
|
-
@client = Config.client
|
68
|
-
|
49
|
+
def initialize(datacenter, cloud_config, cluster_config, properties)
|
69
50
|
@datacenter = datacenter
|
70
|
-
@
|
71
|
-
@
|
72
|
-
@
|
51
|
+
@logger = cloud_config.logger
|
52
|
+
@client = cloud_config.client
|
53
|
+
@properties = properties
|
54
|
+
|
55
|
+
@config = cluster_config
|
56
|
+
@cloud_config = cloud_config
|
57
|
+
@mob = properties['obj']
|
58
|
+
@resource_pool = ResourcePool.new(cloud_config, cluster_config, properties["resourcePool"])
|
73
59
|
|
74
60
|
@allocated_after_sync = 0
|
75
61
|
@ephemeral_datastores = {}
|
76
62
|
@persistent_datastores = {}
|
77
63
|
@shared_datastores = {}
|
78
64
|
|
79
|
-
|
80
|
-
properties["datastore"], Vim::Datastore, Datastore::PROPERTIES)
|
81
|
-
datastores_properties.each_value do |datastore_properties|
|
82
|
-
name = datastore_properties["name"]
|
83
|
-
datastore_config = datacenter.config.datastores
|
84
|
-
ephemeral = !!(name =~ datastore_config.ephemeral_pattern)
|
85
|
-
persistent = !!(name =~ datastore_config.persistent_pattern)
|
86
|
-
|
87
|
-
if ephemeral && persistent &&
|
88
|
-
!datastore_config.allow_mixed
|
89
|
-
raise "Datastore patterns are not mutually exclusive: #{name}"
|
90
|
-
end
|
91
|
-
|
92
|
-
if ephemeral || persistent
|
93
|
-
datastore = Datastore.new(datastore_properties)
|
94
|
-
if ephemeral && persistent
|
95
|
-
@shared_datastores[datastore.name] = datastore
|
96
|
-
elsif ephemeral
|
97
|
-
@ephemeral_datastores[datastore.name] = datastore
|
98
|
-
else
|
99
|
-
@persistent_datastores[datastore.name] = datastore
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
@logger.debug(
|
105
|
-
"Datastores - ephemeral: #{@ephemeral_datastores.keys.inspect}, " +
|
106
|
-
"persistent: #{@persistent_datastores.keys.inspect}, " +
|
107
|
-
"shared: #{@shared_datastores.keys.inspect}.")
|
65
|
+
setup_datastores
|
108
66
|
|
109
67
|
# Have to use separate mechanisms for fetching utilization depending on
|
110
68
|
# whether we're using resource pools or raw clusters.
|
111
69
|
if @config.resource_pool.nil?
|
112
|
-
fetch_cluster_utilization(properties[
|
70
|
+
fetch_cluster_utilization(properties['host'])
|
113
71
|
else
|
114
|
-
|
72
|
+
fetch_resource_pool_utilization
|
115
73
|
end
|
116
74
|
end
|
117
75
|
|
@@ -122,15 +80,24 @@ module VSphereCloud
|
|
122
80
|
# @return [Datastore, nil] the requested persistent datastore.
|
123
81
|
def persistent(datastore_name)
|
124
82
|
@persistent_datastores[datastore_name] ||
|
125
|
-
|
83
|
+
@shared_datastores[datastore_name]
|
126
84
|
end
|
127
85
|
|
128
86
|
# @return [Integer] amount of free memory in the cluster
|
129
87
|
def free_memory
|
130
88
|
@synced_free_memory -
|
131
|
-
|
89
|
+
(@allocated_after_sync * cloud_config.mem_overcommit).to_i
|
132
90
|
end
|
133
91
|
|
92
|
+
# Marks the memory reservation against the cached utilization data.
|
93
|
+
#
|
94
|
+
# @param [Integer] memory size of memory reservation.
|
95
|
+
# @return [void]
|
96
|
+
def allocate(memory)
|
97
|
+
@allocated_after_sync += memory
|
98
|
+
end
|
99
|
+
|
100
|
+
|
134
101
|
# Picks the best datastore for the specified persistent disk.
|
135
102
|
#
|
136
103
|
# @param [Integer] size persistent disk size.
|
@@ -149,26 +116,55 @@ module VSphereCloud
|
|
149
116
|
pick_store(size, :ephemeral)
|
150
117
|
end
|
151
118
|
|
152
|
-
# Marks the memory reservation against the cached utilization data.
|
153
|
-
#
|
154
|
-
# @param [Integer] memory size of memory reservation.
|
155
|
-
# @return [void]
|
156
|
-
def allocate(memory)
|
157
|
-
@allocated_after_sync += memory
|
158
|
-
end
|
159
|
-
|
160
119
|
# @return [String] cluster name.
|
161
120
|
def name
|
162
|
-
|
121
|
+
config.name
|
163
122
|
end
|
164
123
|
|
165
124
|
# @return [String] debug cluster information.
|
166
125
|
def inspect
|
167
|
-
"<Cluster:
|
126
|
+
"<Cluster: #{mob} / #{config.name}>"
|
168
127
|
end
|
169
128
|
|
170
129
|
private
|
171
130
|
|
131
|
+
attr_reader :cloud_config, :config, :client, :properties, :logger
|
132
|
+
|
133
|
+
def setup_datastores
|
134
|
+
datastores_properties = client.get_properties(properties['datastore'], Vim::Datastore, Datastore::PROPERTIES)
|
135
|
+
|
136
|
+
datastores_properties.each_value do |datastore_properties|
|
137
|
+
name = datastore_properties["name"]
|
138
|
+
|
139
|
+
ephemeral = !!(name =~ cloud_config.datacenter_datastore_pattern)
|
140
|
+
persistent = !!(name =~ cloud_config.datacenter_persistent_datastore_pattern)
|
141
|
+
|
142
|
+
if ephemeral && persistent && !cloud_config.datacenter_allow_mixed_datastores
|
143
|
+
raise "Datastore patterns are not mutually exclusive: #{name}"
|
144
|
+
end
|
145
|
+
|
146
|
+
if ephemeral || persistent
|
147
|
+
place_datastore(datastore_properties, ephemeral, persistent)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
logger.debug(
|
152
|
+
"Datastores - ephemeral: #{@ephemeral_datastores.keys.inspect}, " +
|
153
|
+
"persistent: #{@persistent_datastores.keys.inspect}, " +
|
154
|
+
"shared: #{@shared_datastores.keys.inspect}.")
|
155
|
+
end
|
156
|
+
|
157
|
+
def place_datastore(datastore_properties, ephemeral, persistent)
|
158
|
+
datastore = Datastore.new(datastore_properties)
|
159
|
+
if ephemeral && persistent
|
160
|
+
@shared_datastores[datastore.name] = datastore
|
161
|
+
elsif ephemeral
|
162
|
+
@ephemeral_datastores[datastore.name] = datastore
|
163
|
+
else
|
164
|
+
@persistent_datastores[datastore.name] = datastore
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
172
168
|
# Picks the best datastore for the specified disk size and type.
|
173
169
|
#
|
174
170
|
# First the exclusive datastore pool is used. If it's empty or doesn't
|
@@ -181,7 +177,7 @@ module VSphereCloud
|
|
181
177
|
def pick_store(size, type)
|
182
178
|
weighted_datastores = []
|
183
179
|
datastores =
|
184
|
-
|
180
|
+
type == :persistent ? @persistent_datastores : @ephemeral_datastores
|
185
181
|
datastores.each_value do |datastore|
|
186
182
|
if datastore.free_space - size >= DISK_THRESHOLD
|
187
183
|
weighted_datastores << [datastore, datastore.free_space]
|
@@ -205,45 +201,42 @@ module VSphereCloud
|
|
205
201
|
# individual host capacity and its utilization using the performance
|
206
202
|
# manager.
|
207
203
|
#
|
208
|
-
# @param [Array<Vim::HostSystem>]
|
204
|
+
# @param [Array<Vim::HostSystem>] cluster_host_systems cluster hosts.
|
209
205
|
# @return [void]
|
210
|
-
def fetch_cluster_utilization(
|
206
|
+
def fetch_cluster_utilization(cluster_host_systems)
|
211
207
|
hosts_properties = @client.get_properties(
|
212
|
-
|
213
|
-
|
208
|
+
cluster_host_systems, Vim::HostSystem, HOST_PROPERTIES, ensure_all: true)
|
209
|
+
active_host_mobs = select_active_host_mobs(hosts_properties)
|
214
210
|
|
215
|
-
|
216
|
-
|
217
|
-
@total_memory = 0
|
218
|
-
@synced_free_memory = 0
|
219
|
-
return
|
220
|
-
end
|
211
|
+
@synced_free_memory = 0
|
212
|
+
return if active_host_mobs.empty?
|
221
213
|
|
222
|
-
samples = 0
|
223
|
-
cluster_total_memory = 0
|
224
214
|
cluster_free_memory = 0
|
225
|
-
cluster_cpu_usage = 0
|
226
215
|
|
227
|
-
counters = @client.get_perf_counters(
|
228
|
-
host_mobs, HOST_COUNTERS, :max_sample => 5)
|
216
|
+
counters = @client.get_perf_counters(active_host_mobs, HOST_COUNTERS, max_sample: 5)
|
229
217
|
counters.each do |host_mob, counter|
|
230
218
|
host_properties = hosts_properties[host_mob]
|
231
219
|
total_memory = host_properties["hardware.memorySize"].to_i
|
232
220
|
percent_used = Util.average_csv(counter["mem.usage.average"]) / 10000
|
233
221
|
free_memory = ((1.0 - percent_used) * total_memory).to_i
|
234
222
|
|
235
|
-
samples += 1
|
236
|
-
cluster_total_memory += total_memory
|
237
223
|
cluster_free_memory += free_memory
|
238
|
-
cluster_cpu_usage +=
|
239
|
-
Util.average_csv(counter["cpu.usage.average"]) / 100
|
240
224
|
end
|
241
225
|
|
242
|
-
@idle_cpu = (100 - cluster_cpu_usage / samples) / 100
|
243
|
-
@total_memory = cluster_total_memory / BYTES_IN_MB
|
244
226
|
@synced_free_memory = cluster_free_memory / BYTES_IN_MB
|
245
227
|
end
|
246
228
|
|
229
|
+
# Filters out the hosts that are in maintenance mode.
|
230
|
+
#
|
231
|
+
# @param [Hash] host_properties host properties that already fetched
|
232
|
+
# inMaintenanceMode from vSphere.
|
233
|
+
# @return [Array<Vim::HostSystem>] list of hosts that are active
|
234
|
+
def select_active_host_mobs(host_properties)
|
235
|
+
host_properties.values.
|
236
|
+
select { |p| p['runtime.inMaintenanceMode'] != 'true' }.
|
237
|
+
collect { |p| p['obj'] }
|
238
|
+
end
|
239
|
+
|
247
240
|
# Fetches the resource pool utilization from vSphere.
|
248
241
|
#
|
249
242
|
# We can only rely on the vSphere data if the resource pool is healthy.
|
@@ -253,42 +246,22 @@ module VSphereCloud
|
|
253
246
|
# so we can't use it for the raw clusters.
|
254
247
|
#
|
255
248
|
# @return [void]
|
256
|
-
def
|
257
|
-
properties = @client.get_properties(
|
258
|
-
|
259
|
-
if properties.nil?
|
260
|
-
raise "Failed to get utilization for resource pool #{resource_pool}"
|
261
|
-
end
|
249
|
+
def fetch_resource_pool_utilization
|
250
|
+
properties = @client.get_properties(resource_pool.mob, Vim::ResourcePool, 'summary')
|
251
|
+
raise "Failed to get utilization for resource pool #{resource_pool}" if properties.nil?
|
262
252
|
|
263
253
|
runtime_info = properties["summary"].runtime
|
254
|
+
|
264
255
|
if runtime_info.overall_status == "green"
|
265
|
-
cpu = runtime_info.cpu
|
266
|
-
@idle_cpu = 1 - (cpu.overall_usage.to_f / cpu.max_usage)
|
267
256
|
memory = runtime_info.memory
|
268
|
-
@
|
269
|
-
@synced_free_memory =
|
270
|
-
(memory.max_usage - memory.overall_usage) / BYTES_IN_MB
|
257
|
+
@synced_free_memory = (memory.max_usage - memory.overall_usage) / BYTES_IN_MB
|
271
258
|
else
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
# resource pool is in an unreliable state
|
276
|
-
@idle_cpu = 0
|
277
|
-
@total_memory = 0
|
259
|
+
logger.warn("Ignoring cluster: #{config.name} resource_pool: " +
|
260
|
+
"#{resource_pool.mob} as its state is " +
|
261
|
+
"unreliable: #{runtime_info.overall_status}")
|
278
262
|
@synced_free_memory = 0
|
279
263
|
end
|
280
264
|
end
|
281
|
-
|
282
|
-
# Filters out the hosts that are in maintenance mode.
|
283
|
-
#
|
284
|
-
# @param [Hash] host_properties host properties that already fetched
|
285
|
-
# inMaintenanceMode from vSphere.
|
286
|
-
# @return [Array<Vim::HostSystem>] list of hosts that are active
|
287
|
-
def filter_inactive_hosts(host_properties)
|
288
|
-
host_properties.values.
|
289
|
-
select { |p| p["runtime.inMaintenanceMode"] != "true" }.
|
290
|
-
collect { |p| p[:obj] }
|
291
|
-
end
|
292
265
|
end
|
293
266
|
end
|
294
|
-
end
|
267
|
+
end
|