Class: VSphereCloud::Resources::Datacenter

Inherits:
Object
  • Object
show all
Includes:
ObjectStringifier, VimSdk
Defined in:
lib/cloud/vsphere/resources/datacenter.rb

Defined Under Namespace

Classes: PersistentDiskIndex

Constant Summary

Constants included from VimSdk

VimSdk::BASE_VERSION, VimSdk::DYNAMIC_TYPES, VimSdk::SOAP_BODY_END, VimSdk::SOAP_BODY_START, VimSdk::SOAP_BODY_TAG, VimSdk::SOAP_END, VimSdk::SOAP_ENVELOPE_END, VimSdk::SOAP_ENVELOPE_START, VimSdk::SOAP_ENVELOPE_TAG, VimSdk::SOAP_FAULT_TAG, VimSdk::SOAP_HEADER_END, VimSdk::SOAP_HEADER_START, VimSdk::SOAP_HEADER_TAG, VimSdk::SOAP_NAMESPACE_MAP, VimSdk::SOAP_START, VimSdk::VERSION1, VimSdk::XMLNS_SOAPENC, VimSdk::XMLNS_SOAPENV, VimSdk::XMLNS_VMODL_BASE, VimSdk::XMLNS_XSD, VimSdk::XMLNS_XSI, VimSdk::XML_ENCODING, VimSdk::XML_HEADER

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ObjectStringifier

included

Constructor Details

#initialize(attrs) ⇒ Datacenter

Returns a new instance of Datacenter.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 12

def initialize(attrs)
  @client = attrs.fetch(:client)
  @use_sub_folder = attrs.fetch(:use_sub_folder)
  @vm_folder = attrs.fetch(:vm_folder)
  @template_folder = attrs.fetch(:template_folder)
  @name = attrs.fetch(:name)
  @disk_path = attrs.fetch(:disk_path)
  @ephemeral_pattern = attrs.fetch(:ephemeral_pattern)
  @persistent_pattern = attrs.fetch(:persistent_pattern)
  @clusters = attrs.fetch(:clusters)
  @logger = attrs.fetch(:logger)
  @mem_overcommit = attrs.fetch(:mem_overcommit)

  @cluster_provider = ClusterProvider.new(self, @client, @logger)
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



10
11
12
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 10

def config
  @config
end

#disk_pathObject (readonly)

Returns the value of attribute disk_path.



28
29
30
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 28

def disk_path
  @disk_path
end

#ephemeral_patternObject (readonly)

Returns the value of attribute ephemeral_pattern.



28
29
30
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 28

def ephemeral_pattern
  @ephemeral_pattern
end

#mem_overcommitObject (readonly)

Returns the value of attribute mem_overcommit.



28
29
30
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 28

def mem_overcommit
  @mem_overcommit
end

#nameObject (readonly)

Returns the value of attribute name.



28
29
30
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 28

def name
  @name
end

#persistent_patternObject (readonly)

Returns the value of attribute persistent_pattern.



28
29
30
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 28

def persistent_pattern
  @persistent_pattern
end

Instance Method Details

#all_datastoresObject



97
98
99
100
101
102
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 97

def all_datastores
  clusters.values.inject({}) do |acc, cluster|
    acc.merge!(cluster.all_datastores)
    acc
  end
end

#clustersObject



70
71
72
73
74
75
76
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 70

def clusters
  @logger.debug("All clusters provided: #{@clusters}")
  @clusters.keys.inject({}) do |acc, cluster_name|
    acc[cluster_name] = find_cluster(cluster_name)
    acc
  end
end

#create_disk(datastore, size_in_mb, type) ⇒ Object

TODO: do we care about datastore.allocate?



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 113

def create_disk(datastore, size_in_mb, type)
  disk_type = type

  if disk_type.nil?
    disk_type = Resources::PersistentDisk::DEFAULT_DISK_TYPE
  end

  unless Resources::PersistentDisk::SUPPORTED_DISK_TYPES.include?(disk_type)
    raise "Disk type: '#{disk_type}' is not supported"
  end

  disk_cid = "disk-#{SecureRandom.uuid}"
  @logger.debug("Creating disk '#{disk_cid}' in datastore '#{datastore.name}'")

  @client.create_disk(mob, datastore, disk_cid, @disk_path, size_in_mb, disk_type)
end

#ensure_disk_is_accessible_to_vm(disk, vm) ⇒ Object

TODO: do we care about datastore.allocate?



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 153

def ensure_disk_is_accessible_to_vm(disk, vm)
  disk_is_in_persistent_datastore = persistent_datastores.include?(disk.datastore.name)
  disk_is_in_accessible_datastore = vm.accessible_datastores.include?(disk.datastore.name)
  if disk_is_in_persistent_datastore && disk_is_in_accessible_datastore
    @logger.info("Disk #{disk.cid} found in an accessible, persistent datastore '#{disk.datastore.name}'")
    return disk
  end

  unless vm.accessible_datastores.include?(disk.datastore)
    destination_datastore = pick_persistent_datastore(disk.size_in_mb, vm.accessible_datastores)
    destination_path = path(destination_datastore.name, @disk_path, disk.cid)
    @logger.info("Moving #{disk.path} to #{destination_path}")
    @client.move_disk(mob, disk.path, mob, destination_path)
    @logger.info('Moved disk successfully')
    Resources::PersistentDisk.new(disk.cid, disk.size_in_mb, destination_datastore, @disk_path)
  end
end

#ephemeral_datastoresObject



83
84
85
86
87
88
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 83

def ephemeral_datastores
  clusters.values.inject({}) do |acc, cluster|
    acc.merge!(cluster.ephemeral_datastores)
    acc
  end
end

#find_cluster(cluster_name) ⇒ Object



78
79
80
81
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 78

def find_cluster(cluster_name)
  cluster_config = @clusters[cluster_name]
  @cluster_provider.find(cluster_name, cluster_config)
end

#find_disk(disk_cid) ⇒ Object

Raises:

  • (Bosh::Clouds::DiskNotFound.new(false))


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 130

def find_disk(disk_cid)
  @logger.debug("Looking for disk #{disk_cid} in datastores: #{persistent_datastores}")
  persistent_datastores.each do |_, datastore|
    disk = @client.find_disk(disk_cid, datastore, @disk_path)
    @logger.debug("disk #{disk_cid} found in: #{datastore}") unless disk.nil?
    return disk unless disk.nil?
  end

  other_datastores = all_datastores.reject{|datastore_name, _| persistent_datastores[datastore_name] }
  @logger.debug("disk #{disk_cid} not found in filtered persistent datastores, trying other datastores: #{other_datastores}")
  other_datastores.each do |_, datastore|
    disk = @client.find_disk(disk_cid, datastore, @disk_path)

    if !disk.nil?
      @logger.debug("disk #{disk_cid} found in: #{datastore}")
      return disk
    end
  end

  raise Bosh::Clouds::DiskNotFound.new(false), "Could not find disk with id '#{disk_cid}'"
end

#inspectObject



66
67
68
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 66

def inspect
  "<Datacenter: #{mob} / #{name}>"
end

#master_template_folderObject



62
63
64
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 62

def master_template_folder
  Folder.new(@template_folder, @logger, @client, name)
end

#master_vm_folderObject



49
50
51
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 49

def master_vm_folder
  Folder.new(@vm_folder, @logger, @client, name)
end

#mobObject



30
31
32
33
34
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 30

def mob
  mob = @client.find_by_inventory_path(name)
  raise "Datacenter '#{name}' not found" if mob.nil?
  mob
end

#move_disk(source_path, dest_path) ⇒ Object



230
231
232
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 230

def move_disk(source_path, dest_path)
  @client.move_disk(mob, source_path, mob, dest_path)
end

#persistent_datastoresObject



90
91
92
93
94
95
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 90

def persistent_datastores
  clusters.values.inject({}) do |acc, cluster|
    acc.merge!(cluster.persistent_datastores)
    acc
  end
end

#pick_cluster_for_vm(requested_memory_in_mb, requested_ephemeral_disk_size_in_mb, existing_persistent_disks) ⇒ Cluster

Find a cluster for a vm with the requested memory and ephemeral storage, attempting to allocate it near existing persistent disks.

Parameters:

  • requested_memory_in_mb (Integer)

    requested memory.

  • requested_ephemeral_disk_size_in_mb (Integer)

    requested ephemeral storage.

  • existing_persistent_disks (Array<Resources::Disk>)

    existing persistent disks, if any.

Returns:

  • (Cluster)

    selected cluster if the resources were placed successfully, otherwise raises.



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 178

def pick_cluster_for_vm(requested_memory_in_mb, requested_ephemeral_disk_size_in_mb, existing_persistent_disks)
  # calculate locality to prioritizing clusters that contain the most persistent data.
  cluster_objects = clusters.values
  persistent_disk_index = PersistentDiskIndex.new(cluster_objects, existing_persistent_disks)

  scored_clusters = cluster_objects.map do |cluster|
    persistent_disk_not_in_this_cluster = existing_persistent_disks.reject do |disk|
      persistent_disk_index.clusters_connected_to_disk(disk).include?(cluster)
    end

    score = Scorer.score(
      @logger,
      cluster,
      requested_memory_in_mb,
      requested_ephemeral_disk_size_in_mb,
      persistent_disk_not_in_this_cluster.map(&:size_in_mb)
    )

    [cluster, score]
  end

  acceptable_clusters = scored_clusters.select { |_, score| score > 0 }

  @logger.debug("Acceptable clusters: #{acceptable_clusters.inspect}")

  if acceptable_clusters.empty?
    total_persistent_size = existing_persistent_disks.map(&:size_in_mb).inject(0, :+)
    cluster_infos = cluster_objects.map { |cluster| cluster.describe }

    raise "Unable to allocate vm with #{requested_memory_in_mb}mb RAM, " +
        "#{requested_ephemeral_disk_size_in_mb / 1024}gb ephemeral disk, " +
        "and #{total_persistent_size / 1024}gb persistent disk from any cluster.\n#{cluster_infos.join(", ")}."
  end

  acceptable_clusters = acceptable_clusters.sort_by do |cluster, _score|
    persistent_disk_index.disks_connected_to_cluster(cluster).map(&:size_in_mb).inject(0, :+)
  end.reverse

  if acceptable_clusters.any? { |cluster, _| persistent_disk_index.disks_connected_to_cluster(cluster).any? }
    @logger.debug('Choosing cluster with the greatest available disk')
    selected_cluster, _ = acceptable_clusters.first
  else
    @logger.debug('Choosing cluster by weighted random')
    selected_cluster = Util.weighted_random(acceptable_clusters)
  end

  @logger.debug("Selected cluster '#{selected_cluster.name}'")

  selected_cluster.allocate(requested_memory_in_mb)
  selected_cluster
end

#pick_ephemeral_datastore(size, filter = nil) ⇒ Object



104
105
106
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 104

def pick_ephemeral_datastore(size, filter=nil)
  pick_datastore(:ephemeral, size, filter)
end

#pick_persistent_datastore(size, filter = nil) ⇒ Object



108
109
110
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 108

def pick_persistent_datastore(size, filter=nil)
  pick_datastore(:persistent, size, filter)
end

#template_folderObject



53
54
55
56
57
58
59
60
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 53

def template_folder
  if @use_sub_folder
    folder_path = [@template_folder, Bosh::Clouds::Config.uuid].join('/')
    Folder.new(folder_path, @logger, @client, name)
  else
    master_template_folder
  end
end

#vm_folderObject



36
37
38
39
40
41
42
43
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 36

def vm_folder
  if @use_sub_folder
    folder_path = [@vm_folder, Bosh::Clouds::Config.uuid].join('/')
    Folder.new(folder_path, @logger, @client, name)
  else
    master_vm_folder
  end
end

#vm_path(vm_cid) ⇒ Object



45
46
47
# File 'lib/cloud/vsphere/resources/datacenter.rb', line 45

def vm_path(vm_cid)
  [name, 'vm', vm_folder.path_components, vm_cid].join('/')
end