bosh_vsphere_cpi 0.4.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,294 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module VSphereCloud
4
+ class Resources
5
+
6
+ # Cluster resource.
7
+ class Cluster
8
+ include VimSdk
9
+
10
+ PROPERTIES = %w(name datastore resourcePool host)
11
+ HOST_PROPERTIES = %w(hardware.memorySize runtime.inMaintenanceMode)
12
+ HOST_COUNTERS = %w(cpu.usage.average mem.usage.average)
13
+
14
+ # @!attribute mob
15
+ # @return [Vim::ClusterComputeResource] cluster vSphere MOB.
16
+ attr_accessor :mob
17
+
18
+ # @!attribute config
19
+ # @return [ClusterConfig] cluster config.
20
+ attr_accessor :config
21
+
22
+ # @!attribute datacenter
23
+ # @return [Datacenter] parent datacenter.
24
+ attr_accessor :datacenter
25
+
26
+ # @!attribute resource_pool
27
+ # @return [ResourcePool] resource pool.
28
+ attr_accessor :resource_pool
29
+
30
+ # @!attribute ephemeral_datastores
31
+ # @return [Hash<String, Datastore>] ephemeral datastores.
32
+ attr_accessor :ephemeral_datastores
33
+
34
+ # @!attribute persistent_datastores
35
+ # @return [Hash<String, Datastore>] persistent datastores.
36
+ attr_accessor :persistent_datastores
37
+
38
+ # @!attribute shared_datastores
39
+ # @return [Hash<String, Datastore>] shared datastores.
40
+ attr_accessor :shared_datastores
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
49
+
50
+ # @!attribute synced_free_memory
51
+ # @return [Integer] cached memory utilization in MB.
52
+ attr_accessor :synced_free_memory
53
+
54
+ # @!attribute allocated_after_sync
55
+ # @return [Integer] memory allocated since utilization sync in MB.
56
+ attr_accessor :allocated_after_sync
57
+
58
+ # Creates a new Cluster resource from the specified datacenter, cluster
59
+ # configuration, and prefetched properties.
60
+ #
61
+ # @param [Datacenter] datacenter parent datacenter.
62
+ # @param [ClusterConfig] config cluster configuration as specified by the
63
+ # operator.
64
+ # @param [Hash] properties prefetched vSphere properties for the cluster.
65
+ def initialize(datacenter, config, properties)
66
+ @logger = Config.logger
67
+ @client = Config.client
68
+
69
+ @datacenter = datacenter
70
+ @config = config
71
+ @mob = properties[:obj]
72
+ @resource_pool = ResourcePool.new(self, properties["resourcePool"])
73
+
74
+ @allocated_after_sync = 0
75
+ @ephemeral_datastores = {}
76
+ @persistent_datastores = {}
77
+ @shared_datastores = {}
78
+
79
+ datastores_properties = @client.get_properties(
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}.")
108
+
109
+ # Have to use separate mechanisms for fetching utilization depending on
110
+ # whether we're using resource pools or raw clusters.
111
+ if @config.resource_pool.nil?
112
+ fetch_cluster_utilization(properties["host"])
113
+ else
114
+ fetch_res_pool_utilization
115
+ end
116
+ end
117
+
118
+ # Returns the persistent datastore by name. This could be either from the
119
+ # exclusive or shared datastore pools.
120
+ #
121
+ # @param [String] datastore_name name of the datastore.
122
+ # @return [Datastore, nil] the requested persistent datastore.
123
+ def persistent(datastore_name)
124
+ @persistent_datastores[datastore_name] ||
125
+ @shared_datastores[datastore_name]
126
+ end
127
+
128
+ # @return [Integer] amount of free memory in the cluster
129
+ def free_memory
130
+ @synced_free_memory -
131
+ (@allocated_after_sync * Config.mem_overcommit).to_i
132
+ end
133
+
134
+ # Picks the best datastore for the specified persistent disk.
135
+ #
136
+ # @param [Integer] size persistent disk size.
137
+ # @return [Datastore, nil] best datastore if available for the requested
138
+ # size.
139
+ def pick_persistent(size)
140
+ pick_store(size, :persistent)
141
+ end
142
+
143
+ # Picks the best datastore for the specified ephemeral disk.
144
+ #
145
+ # @param [Integer] size ephemeral disk size.
146
+ # @return [Datastore, nil] best datastore if available for the requested
147
+ # size.
148
+ def pick_ephemeral(size)
149
+ pick_store(size, :ephemeral)
150
+ end
151
+
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
+ # @return [String] cluster name.
161
+ def name
162
+ @config.name
163
+ end
164
+
165
+ # @return [String] debug cluster information.
166
+ def inspect
167
+ "<Cluster: #@mob / #{@config.name}>"
168
+ end
169
+
170
+ private
171
+
172
+ # Picks the best datastore for the specified disk size and type.
173
+ #
174
+ # First the exclusive datastore pool is used. If it's empty or doesn't
175
+ # have enough capacity then the shared pool will be used.
176
+ #
177
+ # @param [Integer] size disk size.
178
+ # @param [Symbol] type disk type.
179
+ # @return [Datastore, nil] best datastore if available for the requested
180
+ # size.
181
+ def pick_store(size, type)
182
+ weighted_datastores = []
183
+ datastores =
184
+ type == :persistent ? @persistent_datastores : @ephemeral_datastores
185
+ datastores.each_value do |datastore|
186
+ if datastore.free_space - size >= DISK_THRESHOLD
187
+ weighted_datastores << [datastore, datastore.free_space]
188
+ end
189
+ end
190
+
191
+ if weighted_datastores.empty?
192
+ @shared_datastores.each_value do |datastore|
193
+ if datastore.free_space - size >= DISK_THRESHOLD
194
+ weighted_datastores << [datastore, datastore.free_space]
195
+ end
196
+ end
197
+ end
198
+
199
+ Util.weighted_random(weighted_datastores)
200
+ end
201
+
202
+ # Fetches the raw cluster utilization from vSphere.
203
+ #
204
+ # First filter out any hosts that are in maintenance mode. Then aggregate
205
+ # individual host capacity and its utilization using the performance
206
+ # manager.
207
+ #
208
+ # @param [Array<Vim::HostSystem>] host_mobs cluster hosts.
209
+ # @return [void]
210
+ def fetch_cluster_utilization(host_mobs)
211
+ hosts_properties = @client.get_properties(
212
+ host_mobs, Vim::HostSystem, HOST_PROPERTIES, :ensure_all => true)
213
+ host_mobs = filter_inactive_hosts(hosts_properties)
214
+
215
+ if host_mobs.empty?
216
+ @idle_cpu = 0
217
+ @total_memory = 0
218
+ @synced_free_memory = 0
219
+ return
220
+ end
221
+
222
+ samples = 0
223
+ cluster_total_memory = 0
224
+ cluster_free_memory = 0
225
+ cluster_cpu_usage = 0
226
+
227
+ counters = @client.get_perf_counters(
228
+ host_mobs, HOST_COUNTERS, :max_sample => 5)
229
+ counters.each do |host_mob, counter|
230
+ host_properties = hosts_properties[host_mob]
231
+ total_memory = host_properties["hardware.memorySize"].to_i
232
+ percent_used = Util.average_csv(counter["mem.usage.average"]) / 10000
233
+ free_memory = ((1.0 - percent_used) * total_memory).to_i
234
+
235
+ samples += 1
236
+ cluster_total_memory += total_memory
237
+ cluster_free_memory += free_memory
238
+ cluster_cpu_usage +=
239
+ Util.average_csv(counter["cpu.usage.average"]) / 100
240
+ end
241
+
242
+ @idle_cpu = (100 - cluster_cpu_usage / samples) / 100
243
+ @total_memory = cluster_total_memory / BYTES_IN_MB
244
+ @synced_free_memory = cluster_free_memory / BYTES_IN_MB
245
+ end
246
+
247
+ # Fetches the resource pool utilization from vSphere.
248
+ #
249
+ # We can only rely on the vSphere data if the resource pool is healthy.
250
+ # Otherwise we mark the resources as unavailable.
251
+ #
252
+ # Unfortunately this method does not work for the root resource pool,
253
+ # so we can't use it for the raw clusters.
254
+ #
255
+ # @return [void]
256
+ def fetch_res_pool_utilization
257
+ properties = @client.get_properties(
258
+ @resource_pool.mob, Vim::ResourcePool, %w(summary))
259
+ if properties.nil?
260
+ raise "Failed to get utilization for resource pool #{resource_pool}"
261
+ end
262
+
263
+ runtime_info = properties["summary"].runtime
264
+ if runtime_info.overall_status == "green"
265
+ cpu = runtime_info.cpu
266
+ @idle_cpu = 1 - (cpu.overall_usage.to_f / cpu.max_usage)
267
+ memory = runtime_info.memory
268
+ @total_memory = memory.max_usage / BYTES_IN_MB
269
+ @synced_free_memory =
270
+ (memory.max_usage - memory.overall_usage) / BYTES_IN_MB
271
+ else
272
+ @logger.warn("Ignoring cluster: #{config.name} resource_pool: " +
273
+ "#{@resource_pool.mob} as its state is " +
274
+ "unreliable: #{runtime_info.overall_status}")
275
+ # resource pool is in an unreliable state
276
+ @idle_cpu = 0
277
+ @total_memory = 0
278
+ @synced_free_memory = 0
279
+ end
280
+ 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
+ end
293
+ end
294
+ end
@@ -0,0 +1,86 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module VSphereCloud
4
+ class Resources
5
+
6
+ # Datacenter resource.
7
+ class Datacenter
8
+ include VimSdk
9
+
10
+ # @!attribute mob
11
+ # @return [Vim::Datacenter] datacenter vSphere MOB.
12
+ attr_accessor :mob
13
+
14
+ # @!attribute clusters
15
+ # @return [Hash<String, Cluster>] hash of cluster names to clusters.
16
+ attr_accessor :clusters
17
+
18
+ # @!attribute vm_folder
19
+ # @return [Folder] inventory folder for VMs.
20
+ attr_accessor :vm_folder
21
+
22
+ # @!attribute template_folder
23
+ # @return [Folder] inventory folder for stemcells/templates.
24
+ attr_accessor :template_folder
25
+
26
+ # @!attribute config
27
+ # @return [DatacenterConfig] datacenter config.
28
+ attr_accessor :config
29
+
30
+ # Creates a new Datacenter resource from the operator provided datacenter
31
+ # configuration.
32
+ #
33
+ # This traverses the provided datacenter/resource pools/datastores and
34
+ # builds the underlying resources and utilization.
35
+ #
36
+ # @param [DatacenterConfig] config datacenter configuration.
37
+ def initialize(config)
38
+ client = Config.client
39
+ @config = config
40
+ @mob = client.find_by_inventory_path(name)
41
+ raise "Datacenter: #{name} not found" if @mob.nil?
42
+
43
+ @vm_folder = Folder.new(self, config.folders.vm,
44
+ config.folders.shared)
45
+ @template_folder = Folder.new(self, config.folders.template,
46
+ config.folders.shared)
47
+
48
+ cluster_mobs = client.get_managed_objects(
49
+ Vim::ClusterComputeResource, :root => @mob, :include_name => true)
50
+ cluster_mobs.delete_if { |name, _| !config.clusters.has_key?(name) }
51
+ cluster_mobs = Hash[*cluster_mobs.flatten]
52
+
53
+ clusters_properties = client.get_properties(
54
+ cluster_mobs.values, Vim::ClusterComputeResource,
55
+ Cluster::PROPERTIES, :ensure_all => true)
56
+
57
+ @clusters = {}
58
+ config.clusters.each do |name, cluster_config|
59
+ cluster_mob = cluster_mobs[name]
60
+ raise "Can't find cluster: #{name}" if cluster_mob.nil?
61
+ cluster_properties = clusters_properties[cluster_mob]
62
+ if cluster_properties.nil?
63
+ raise "Can't find properties for cluster: #{name}"
64
+ end
65
+ cluster = Cluster.new(self, cluster_config, cluster_properties)
66
+ @clusters[cluster.name] = cluster
67
+ end
68
+ end
69
+
70
+ # @return [String] datacenter name.
71
+ def name
72
+ @config.name
73
+ end
74
+
75
+ # @return [String] vCenter path/namespace for VMDKs.
76
+ def disk_path
77
+ @config.datastores.disk_path
78
+ end
79
+
80
+ # @return [String] debug datacenter information.
81
+ def inspect
82
+ "<Datacenter: #@mob / #{@config.name}>"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module VSphereCloud
4
+ class Resources
5
+
6
+ # Datastore resource.
7
+ class Datastore
8
+ PROPERTIES = %w(summary.freeSpace summary.capacity name)
9
+
10
+ # @!attribute mob
11
+ # @return [Vim::Datastore] datastore vSphere MOB.
12
+ attr_accessor :mob
13
+
14
+ # @!attribute name
15
+ # @return [String] datastore name.
16
+ attr_accessor :name
17
+
18
+ # @!attribute total_space
19
+ # @return [Integer] datastore capacity.
20
+ attr_accessor :total_space
21
+
22
+ # @!attribute synced_free_space
23
+ # @return [Integer] datastore free space when fetched from vSphere.
24
+ attr_accessor :synced_free_space
25
+
26
+ # @!attribute allocated_after_sync
27
+ # @return [Integer] allocated space since vSphere fetch.
28
+ attr_accessor :allocated_after_sync
29
+
30
+ # Creates a Datastore resource from the prefetched vSphere properties.
31
+ #
32
+ # @param [Hash] properties prefetched vSphere properties to build the
33
+ # model.
34
+ def initialize(properties)
35
+ @mob = properties[:obj]
36
+ @name = properties["name"]
37
+ @total_space = properties["summary.capacity"].to_i / BYTES_IN_MB
38
+ @synced_free_space = properties["summary.freeSpace"].to_i / BYTES_IN_MB
39
+ @allocated_after_sync = 0
40
+ end
41
+
42
+ # @return [Integer] free disk space available for allocation
43
+ def free_space
44
+ @synced_free_space - @allocated_after_sync
45
+ end
46
+
47
+ # Marks the disk space against the cached utilization data.
48
+ #
49
+ # @param [Integer] space requested disk space.
50
+ # @return [void]
51
+ def allocate(space)
52
+ @allocated_after_sync += space
53
+ end
54
+
55
+ # @return [String] debug datastore information.
56
+ def inspect
57
+ "<Datastore: #@mob / #@name>"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module VSphereCloud
4
+ class Resources
5
+
6
+ # Folder resource.
7
+ class Folder
8
+
9
+ # @!attribute mob
10
+ # @return [Vim::Folder] folder vSphere MOB.
11
+ attr_accessor :mob
12
+
13
+ # @!attribute name
14
+ # @return [String] folder name.
15
+ attr_accessor :name
16
+
17
+ # Creates a new Folder resource given the parent datacenter and folder
18
+ # name.
19
+ #
20
+ # @param [Datacenter] datacenter parent datacenter.
21
+ # @param [String] name folder name.
22
+ # @param [true, false] shared flag signaling to use the director guid as
23
+ # the namespace for multi tenancy.
24
+ def initialize(datacenter, name, shared)
25
+ client = Config.client
26
+ logger = Config.logger
27
+
28
+ folder = client.find_by_inventory_path([datacenter.name, "vm", name])
29
+ raise "Missing folder: #{name}" if folder.nil?
30
+
31
+ if shared
32
+ shared_folder = folder
33
+
34
+ uuid = Bosh::Clouds::Config.uuid
35
+ name = [name, uuid]
36
+
37
+ logger.debug("Search for folder #{name.join("/")}")
38
+ folder = client.find_by_inventory_path([datacenter.name, "vm", name])
39
+ if folder.nil?
40
+ logger.debug("Creating folder #{name.join("/")}")
41
+ folder = shared_folder.create_folder(uuid)
42
+ end
43
+ logger.debug("Found folder #{name.join("/")}: #{folder}")
44
+
45
+ @mob = folder
46
+ @name = name
47
+ else
48
+ @mob = folder
49
+ @name = name
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end