bosh_vsphere_cpi 1.2865.0 → 1.2881.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ require 'ostruct'
2
+ require 'cloud/vsphere/resources/disk'
3
+
4
+ module VSphereCloud
5
+ class DiskProvider
6
+ def initialize(virtual_disk_manager, datacenter, resources, disk_path, client, logger)
7
+ @virtual_disk_manager = virtual_disk_manager
8
+ @datacenter = datacenter
9
+ @resources = resources
10
+ @disk_path = disk_path
11
+ @client = client
12
+ @logger = logger
13
+ end
14
+
15
+ def create(disk_size_in_mb)
16
+ datastore = find_datastore(disk_size_in_mb)
17
+ disk_cid = "disk-#{SecureRandom.uuid}"
18
+ @logger.debug("Creating disk '#{disk_cid}' in datastore '#{datastore.name}'")
19
+
20
+ disk_spec = VimSdk::Vim::VirtualDiskManager::FileBackedVirtualDiskSpec.new
21
+ disk_spec.disk_type = 'preallocated'
22
+ disk_spec.capacity_kb = disk_size_in_mb * 1024
23
+ disk_spec.adapter_type = 'lsiLogic'
24
+
25
+ disk_path = path(datastore, disk_cid)
26
+ create_parent_folder(disk_path)
27
+
28
+ task = @virtual_disk_manager.create_virtual_disk(
29
+ disk_path,
30
+ @datacenter.mob,
31
+ disk_spec
32
+ )
33
+ @client.wait_for_task(task)
34
+
35
+ Resources::Disk.new(disk_cid, disk_size_in_mb, datastore, disk_path)
36
+ end
37
+
38
+ def find_and_move(disk_cid, cluster, datacenter_name, accessible_datastores)
39
+ disk = find(disk_cid)
40
+ return disk if accessible_datastores.include?(disk.datastore.name)
41
+
42
+ destination_datastore = @resources.pick_persistent_datastore_in_cluster(cluster, disk.size_in_mb)
43
+ if destination_datastore.nil?
44
+ raise Bosh::Clouds::NoDiskSpace.new(true),
45
+ "Not enough persistent space on cluster '#{cluster.name}', requested disk size: #{disk.size_in_mb}Mb"
46
+ end
47
+
48
+ unless accessible_datastores.include?(destination_datastore.name)
49
+ raise "Datastore '#{destination_datastore.name}' is not accessible to cluster '#{cluster.name}'"
50
+ end
51
+
52
+ destination_path = path(destination_datastore, disk_cid)
53
+ @logger.info("Moving #{disk.path} to #{destination_path}")
54
+ create_parent_folder(destination_path)
55
+ @client.move_disk(datacenter_name, disk.path, datacenter_name, destination_path) #TODO: return the new disk
56
+ @logger.info('Moved disk successfully')
57
+ Resources::Disk.new(disk_cid, disk.size_in_mb, destination_datastore, destination_path)
58
+ end
59
+
60
+ def find(disk_cid)
61
+ @datacenter.persistent_datastores.each do |_, datastore|
62
+ disk = @client.find_disk(disk_cid, datastore, @disk_path)
63
+ return disk unless disk.nil?
64
+ end
65
+
66
+ raise Bosh::Clouds::DiskNotFound, "Could not find disk with id #{disk_cid}"
67
+ end
68
+
69
+ private
70
+
71
+ def path(datastore, disk_cid)
72
+ "[#{datastore.name}] #{@disk_path}/#{disk_cid}.vmdk"
73
+ end
74
+
75
+ def find_datastore(disk_size_in_mb)
76
+ datastore = @datacenter.pick_persistent_datastore(disk_size_in_mb)
77
+
78
+ if datastore.nil?
79
+ raise Bosh::Clouds::NoDiskSpace.new(true),
80
+ "Not enough persistent space #{disk_size_in_mb}"
81
+ end
82
+
83
+ datastore
84
+ end
85
+
86
+ def create_parent_folder(disk_path)
87
+ destination_folder = File.dirname(disk_path)
88
+ @client.create_datastore_folder(destination_folder, @datacenter.mob)
89
+ end
90
+ end
91
+ end
@@ -1,6 +1,8 @@
1
+ require 'cloud/vsphere/retry_block'
2
+
1
3
  module VSphereCloud
2
4
  class FileProvider
3
- include RetryBlock
5
+ include VSphereCloud::RetryBlock
4
6
 
5
7
  def initialize(rest_client, vcenter_host)
6
8
  @vcenter_host = vcenter_host
@@ -7,14 +7,25 @@ module VSphereCloud
7
7
  @drs_rules = drs_rules
8
8
  end
9
9
 
10
- def place(memory, ephemeral, persistent)
11
- datastore = @cluster.pick_ephemeral(ephemeral)
12
- if datastore
13
- @cluster.allocate(memory)
14
- datastore.allocate(ephemeral)
15
- return [@cluster, datastore]
10
+ def pick_cluster_for_vm(memory, ephemeral, persistent)
11
+ @cluster.allocate(memory)
12
+ @cluster
13
+ end
14
+
15
+ def pick_ephemeral_datastore(cluster, disk_size_in_mb)
16
+ datastore = cluster.pick_ephemeral(disk_size_in_mb)
17
+
18
+ if datastore.nil?
19
+ raise Bosh::Clouds::NoDiskSpace.new(
20
+ "Not enough ephemeral disk space (#{disk_size_in_mb}MB) in cluster #{cluster.name}")
16
21
  end
17
- raise "No available resources"
22
+
23
+ datastore.allocate(disk_size_in_mb)
24
+ datastore
25
+ end
26
+
27
+ def pick_persistent_datastore(_, _)
28
+ raise NotImplementedError
18
29
  end
19
30
  end
20
31
  end
@@ -19,22 +19,6 @@ module VSphereCloud
19
19
  # @return [ResourcePool] resource pool.
20
20
  attr_reader :resource_pool
21
21
 
22
- # @!attribute ephemeral_datastores
23
- # @return [Hash<String, Datastore>] ephemeral datastores.
24
- attr_reader :ephemeral_datastores
25
-
26
- # @!attribute persistent_datastores
27
- # @return [Hash<String, Datastore>] persistent datastores.
28
- attr_reader :persistent_datastores
29
-
30
- # @!attribute shared_datastores
31
- # @return [Hash<String, Datastore>] shared datastores.
32
- attr_reader :shared_datastores
33
-
34
- # @!attribute synced_free_memory
35
- # @return [Integer] cached memory utilization in MB.
36
- attr_accessor :synced_free_memory
37
-
38
22
  # @!attribute allocated_after_sync
39
23
  # @return [Integer] memory allocated since utilization sync in MB.
40
24
  attr_accessor :allocated_after_sync
@@ -58,19 +42,6 @@ module VSphereCloud
58
42
  @resource_pool = ResourcePool.new(cloud_config, cluster_config, properties["resourcePool"])
59
43
 
60
44
  @allocated_after_sync = 0
61
- @ephemeral_datastores = {}
62
- @persistent_datastores = {}
63
- @shared_datastores = {}
64
-
65
- setup_datastores
66
-
67
- # Have to use separate mechanisms for fetching utilization depending on
68
- # whether we're using resource pools or raw clusters.
69
- if @config.resource_pool.nil?
70
- fetch_cluster_utilization(properties['host'])
71
- else
72
- fetch_resource_pool_utilization
73
- end
74
45
  end
75
46
 
76
47
  # Returns the persistent datastore by name. This could be either from the
@@ -79,16 +50,23 @@ module VSphereCloud
79
50
  # @param [String] datastore_name name of the datastore.
80
51
  # @return [Datastore, nil] the requested persistent datastore.
81
52
  def persistent(datastore_name)
82
- @persistent_datastores[datastore_name] ||
83
- @shared_datastores[datastore_name]
53
+ persistent_datastores[datastore_name]
84
54
  end
85
55
 
86
56
  # @return [Integer] amount of free memory in the cluster
87
57
  def free_memory
88
- @synced_free_memory -
58
+ synced_free_memory -
89
59
  (@allocated_after_sync * cloud_config.mem_overcommit).to_i
90
60
  end
91
61
 
62
+ def total_free_ephemeral_disk_in_mb
63
+ ephemeral_datastores.values.map(&:free_space).inject(0, :+)
64
+ end
65
+
66
+ def total_free_persistent_disk_in_mb
67
+ persistent_datastores.values.map(&:free_space).inject(0, :+)
68
+ end
69
+
92
70
  # Marks the memory reservation against the cached utilization data.
93
71
  #
94
72
  # @param [Integer] memory size of memory reservation.
@@ -97,7 +75,6 @@ module VSphereCloud
97
75
  @allocated_after_sync += memory
98
76
  end
99
77
 
100
-
101
78
  # Picks the best datastore for the specified persistent disk.
102
79
  #
103
80
  # @param [Integer] size persistent disk size.
@@ -126,43 +103,22 @@ module VSphereCloud
126
103
  "<Cluster: #{mob} / #{config.name}>"
127
104
  end
128
105
 
129
- private
130
-
131
- attr_reader :cloud_config, :config, :client, :properties, :logger
132
-
133
- def setup_datastores
134
- datastores_properties = @client.cloud_searcher.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)
106
+ def ephemeral_datastores
107
+ @ephemeral_datastores ||= select_datastores(cloud_config.datacenter_datastore_pattern)
108
+ end
141
109
 
142
- if ephemeral && persistent && !cloud_config.datacenter_allow_mixed_datastores
143
- raise "Datastore patterns are not mutually exclusive: #{name}"
144
- end
110
+ def persistent_datastores
111
+ @persistent_datastores ||= select_datastores(cloud_config.datacenter_persistent_datastore_pattern)
112
+ end
145
113
 
146
- if ephemeral || persistent
147
- place_datastore(datastore_properties, ephemeral, persistent)
148
- end
149
- end
114
+ private
150
115
 
151
- logger.debug(
152
- "Datastores - ephemeral: #{@ephemeral_datastores.keys.inspect}, " +
153
- "persistent: #{@persistent_datastores.keys.inspect}, " +
154
- "shared: #{@shared_datastores.keys.inspect}.")
155
- end
116
+ attr_reader :cloud_config, :config, :client, :properties, :logger
156
117
 
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
118
+ def select_datastores(pattern)
119
+ @datastores ||= Datastore.build_from_client(@client, properties['datastore'])
120
+ matching_datastores = @datastores.select { |datastore| datastore.name =~ pattern }
121
+ matching_datastores.inject({}) { |h, datastore| h[datastore.name] = datastore; h }
166
122
  end
167
123
 
168
124
  # Picks the best datastore for the specified disk size and type.
@@ -177,24 +133,27 @@ module VSphereCloud
177
133
  def pick_store(size, type)
178
134
  weighted_datastores = []
179
135
  datastores =
180
- type == :persistent ? @persistent_datastores : @ephemeral_datastores
136
+ type == :persistent ? persistent_datastores : ephemeral_datastores
181
137
  datastores.each_value do |datastore|
182
- if datastore.free_space - size >= DISK_THRESHOLD
138
+ if datastore.free_space - size >= DISK_HEADROOM
183
139
  weighted_datastores << [datastore, datastore.free_space]
184
140
  end
185
141
  end
186
142
 
187
- if weighted_datastores.empty?
188
- @shared_datastores.each_value do |datastore|
189
- if datastore.free_space - size >= DISK_THRESHOLD
190
- weighted_datastores << [datastore, datastore.free_space]
191
- end
192
- end
193
- end
194
-
195
143
  Util.weighted_random(weighted_datastores)
196
144
  end
197
145
 
146
+ def synced_free_memory
147
+ return @synced_free_memory if @synced_free_memory
148
+ # Have to use separate mechanisms for fetching utilization depending on
149
+ # whether we're using resource pools or raw clusters.
150
+ if @config.resource_pool.nil?
151
+ @synced_free_memory = fetch_cluster_utilization(properties['host'])
152
+ else
153
+ @synced_free_memory = fetch_resource_pool_utilization
154
+ end
155
+ end
156
+
198
157
  # Fetches the raw cluster utilization from vSphere.
199
158
  #
200
159
  # First filter out any hosts that are in maintenance mode. Then aggregate
@@ -208,8 +167,8 @@ module VSphereCloud
208
167
  cluster_host_systems, Vim::HostSystem, HOST_PROPERTIES, ensure_all: true)
209
168
  active_host_mobs = select_active_host_mobs(hosts_properties)
210
169
 
211
- @synced_free_memory = 0
212
- return if active_host_mobs.empty?
170
+ synced_free_memory = 0
171
+ return synced_free_memory if active_host_mobs.empty?
213
172
 
214
173
  cluster_free_memory = 0
215
174
 
@@ -223,7 +182,7 @@ module VSphereCloud
223
182
  cluster_free_memory += free_memory
224
183
  end
225
184
 
226
- @synced_free_memory = cluster_free_memory / BYTES_IN_MB
185
+ cluster_free_memory / BYTES_IN_MB
227
186
  end
228
187
 
229
188
  # Filters out the hosts that are in maintenance mode.
@@ -254,12 +213,12 @@ module VSphereCloud
254
213
 
255
214
  if runtime_info.overall_status == "green"
256
215
  memory = runtime_info.memory
257
- @synced_free_memory = (memory.max_usage - memory.overall_usage) / BYTES_IN_MB
216
+ return (memory.max_usage - memory.overall_usage) / BYTES_IN_MB
258
217
  else
259
218
  logger.warn("Ignoring cluster: #{config.name} resource_pool: " +
260
219
  "#{resource_pool.mob} as its state is " +
261
220
  "unreliable: #{runtime_info.overall_status}")
262
- @synced_free_memory = 0
221
+ return 0
263
222
  end
264
223
  end
265
224
  end
@@ -7,6 +7,7 @@ module VSphereCloud
7
7
 
8
8
  def initialize(config)
9
9
  @config = config
10
+ @client = config.client
10
11
  end
11
12
 
12
13
  def mob
@@ -24,6 +25,10 @@ module VSphereCloud
24
25
  end
25
26
  end
26
27
 
28
+ def vm_path(vm_cid)
29
+ [name, 'vm', vm_folder.path_components, vm_cid].join('/')
30
+ end
31
+
27
32
  def master_vm_folder
28
33
  Folder.new(@config.datacenter_vm_folder, @config)
29
34
  end
@@ -57,22 +62,14 @@ module VSphereCloud
57
62
  @config.datacenter_persistent_datastore_pattern
58
63
  end
59
64
 
60
- def allow_mixed
61
- @config.datacenter_allow_mixed_datastores
62
- end
63
-
64
65
  def inspect
65
66
  "<Datacenter: #{mob} / #{name}>"
66
67
  end
67
68
 
68
69
  def clusters
69
- client = @config.client
70
- cluster_mobs = client.cloud_searcher.get_managed_objects(
71
- Vim::ClusterComputeResource, root: mob, include_name: true)
72
- cluster_mobs.delete_if { |name, _| !config.datacenter_clusters.has_key?(name) }
73
- cluster_mobs = Hash[*cluster_mobs.flatten]
70
+ cluster_mobs = Hash[*cluster_tuples.flatten]
74
71
 
75
- clusters_properties = client.cloud_searcher.get_properties(
72
+ clusters_properties = @client.cloud_searcher.get_properties(
76
73
  cluster_mobs.values, Vim::ClusterComputeResource,
77
74
  Cluster::PROPERTIES, :ensure_all => true)
78
75
 
@@ -89,6 +86,36 @@ module VSphereCloud
89
86
  end
90
87
  clusters
91
88
  end
89
+
90
+ def persistent_datastores
91
+ datastores = {}
92
+ clusters.each do |_, cluster|
93
+ cluster.persistent_datastores.each do |_, datastore|
94
+ datastores[datastore.name] = datastore
95
+ end
96
+ end
97
+ datastores
98
+ end
99
+
100
+ def pick_persistent_datastore(disk_size_in_mb)
101
+ weighted_datastores = []
102
+ persistent_datastores.each_value do |datastore|
103
+ if datastore.free_space - disk_size_in_mb >= DISK_HEADROOM
104
+ weighted_datastores << [datastore, datastore.free_space]
105
+ end
106
+ end
107
+
108
+ Util.weighted_random(weighted_datastores)
109
+ end
110
+
111
+ private
112
+
113
+ def cluster_tuples
114
+ cluster_tuples = @client.cloud_searcher.get_managed_objects(
115
+ Vim::ClusterComputeResource, root: mob, include_name: true)
116
+ cluster_tuples.delete_if { |name, _| !config.datacenter_clusters.has_key?(name) }
117
+ cluster_tuples
118
+ end
92
119
  end
93
120
  end
94
121
  end
@@ -1,18 +1,29 @@
1
1
  module VSphereCloud
2
2
  class Resources
3
-
4
- # Datastore resource.
5
3
  class Datastore
4
+ include VimSdk
6
5
  PROPERTIES = %w(summary.freeSpace summary.capacity name)
7
6
 
8
- # @!attribute mob
9
- # @return [Vim::Datastore] datastore vSphere MOB.
10
- attr_accessor :mob
7
+ def self.build_from_client(client, datastore_properties)
8
+ ds_properties_map = client.cloud_searcher.get_properties(datastore_properties, Vim::Datastore, Datastore::PROPERTIES)
9
+ ds_properties_map.values.map do |ds_properties|
10
+ Datastore.new(
11
+ ds_properties['name'],
12
+ ds_properties[:obj],
13
+ ds_properties['summary.capacity'].to_i / BYTES_IN_MB,
14
+ ds_properties['summary.freeSpace'].to_i / BYTES_IN_MB,
15
+ )
16
+ end
17
+ end
11
18
 
12
19
  # @!attribute name
13
20
  # @return [String] datastore name.
14
21
  attr_accessor :name
15
22
 
23
+ # @!attribute mob
24
+ # @return [Vim::Datastore] datastore vSphere MOB.
25
+ attr_accessor :mob
26
+
16
27
  # @!attribute total_space
17
28
  # @return [Integer] datastore capacity.
18
29
  attr_accessor :total_space
@@ -29,11 +40,11 @@ module VSphereCloud
29
40
  #
30
41
  # @param [Hash] properties prefetched vSphere properties to build the
31
42
  # model.
32
- def initialize(properties)
33
- @mob = properties[:obj]
34
- @name = properties["name"]
35
- @total_space = properties["summary.capacity"].to_i / BYTES_IN_MB
36
- @synced_free_space = properties["summary.freeSpace"].to_i / BYTES_IN_MB
43
+ def initialize(name, mob, total_space, synced_free_space)
44
+ @name = name
45
+ @mob = mob
46
+ @total_space = total_space
47
+ @synced_free_space = synced_free_space
37
48
  @allocated_after_sync = 0
38
49
  end
39
50
 
@@ -1,15 +1,15 @@
1
1
  module VSphereCloud
2
2
  class DiskConfig
3
- def initialize(datastore, filename, controller_key, size)
4
- @datastore = datastore
3
+ def initialize(datastore_name, filename, controller_key, size_in_mb)
4
+ @datastore_name = datastore_name
5
5
  @filename = filename
6
6
  @controller_key = controller_key
7
- @size = size
7
+ @size_in_mb = size_in_mb
8
8
  end
9
9
 
10
10
  def spec(options)
11
11
  backing_info = VimSdk::Vim::Vm::Device::VirtualDisk::FlatVer2BackingInfo.new
12
- backing_info.datastore = @datastore
12
+ backing_info.datastore = @datastore_name
13
13
  if options[:independent]
14
14
  backing_info.disk_mode = VimSdk::Vim::Vm::Device::VirtualDiskOption::DiskMode::INDEPENDENT_PERSISTENT
15
15
  else
@@ -21,7 +21,7 @@ module VSphereCloud
21
21
  virtual_disk.key = -1
22
22
  virtual_disk.controller_key = @controller_key
23
23
  virtual_disk.backing = backing_info
24
- virtual_disk.capacity_in_kb = @size * 1024
24
+ virtual_disk.capacity_in_kb = @size_in_mb * 1024
25
25
 
26
26
  device_config_spec = VimSdk::Vim::Vm::Device::VirtualDeviceSpec.new
27
27
  device_config_spec.device = virtual_disk
@@ -2,14 +2,14 @@ require 'cloud/vsphere/resources/disk/disk_config'
2
2
 
3
3
  module VSphereCloud
4
4
  class EphemeralDisk
5
- def initialize(size, folder_name, datastore)
5
+ def initialize(size_in_mb, folder_name, datastore)
6
6
  @folder_name = folder_name
7
7
  @datastore = datastore
8
- @size = size
8
+ @size_in_mb = size_in_mb
9
9
  end
10
10
 
11
11
  def create_spec(controller_key)
12
- DiskConfig.new(@datastore.mob, filename, controller_key, @size).spec(create: true)
12
+ DiskConfig.new(@datastore.mob, filename, controller_key, @size_in_mb).spec(create: true)
13
13
  end
14
14
 
15
15
  private
@@ -0,0 +1,19 @@
1
+ module VSphereCloud
2
+ class Resources
3
+ class Disk
4
+ attr_reader :datastore, :size_in_mb, :path, :cid
5
+
6
+ def initialize(cid, size_in_mb, datastore, path)
7
+ @cid = cid
8
+ @size_in_mb = size_in_mb
9
+ @datastore = datastore
10
+ @path = path
11
+ end
12
+
13
+ def attach_spec(controller_key)
14
+ DiskConfig.new(datastore.name, path, controller_key, size_in_mb).
15
+ spec(independent: true)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -3,32 +3,37 @@ module VSphereCloud
3
3
  class ResourcePool
4
4
  include VimSdk
5
5
 
6
- # @!attribute mob
7
- # @return [Vim::ResourcePool] resource pool vSphere MOB.
8
- attr_accessor :mob
9
-
10
6
  # Creates a new ResourcePool resource.
11
7
  #
12
8
  # @param [Cluster] cluster parent cluster.
13
9
  # @param [Vim::ResourcePool] root_resource_pool cluster's root resource
14
10
  # pool.
15
11
  def initialize(cloud_config, cluster_config, root_resource_pool)
16
- if cluster_config.resource_pool.nil?
17
- @mob = root_resource_pool
12
+ @cloud_config = cloud_config
13
+ @cluster_config = cluster_config
14
+ @root_resource_pool = root_resource_pool
15
+ end
16
+
17
+ def mob
18
+ return @mob if @mob
19
+
20
+ if @cluster_config.resource_pool.nil?
21
+ @mob = @root_resource_pool
18
22
  else
19
- client = cloud_config.client
20
- logger = cloud_config.logger
23
+ client = @cloud_config.client
24
+ logger = @cloud_config.logger
21
25
  @mob = client.cloud_searcher.get_managed_object(
22
- Vim::ResourcePool,
23
- :root => root_resource_pool,
24
- :name => cluster_config.resource_pool)
25
- logger.debug("Found requested resource pool: #@mob")
26
+ Vim::ResourcePool,
27
+ :root => @root_resource_pool,
28
+ :name => @cluster_config.resource_pool)
29
+ logger.debug("Found requested resource pool: #{@mob}")
26
30
  end
31
+ @mob
27
32
  end
28
33
 
29
34
  # @return [String] debug resource pool information.
30
35
  def inspect
31
- "<Resource Pool: #@mob>"
36
+ "<Resource Pool: #{mob}>"
32
37
  end
33
38
  end
34
39
  end