bosh_vsphere_cpi 0.4.9 → 0.5.0
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.
- data/lib/cloud/vsphere/client.rb +3 -2
- data/lib/cloud/vsphere/cloud.rb +84 -95
- data/lib/cloud/vsphere/config.rb +254 -0
- data/lib/cloud/vsphere/resources.rb +164 -514
- data/lib/cloud/vsphere/resources/cluster.rb +294 -0
- data/lib/cloud/vsphere/resources/datacenter.rb +86 -0
- data/lib/cloud/vsphere/resources/datastore.rb +61 -0
- data/lib/cloud/vsphere/resources/folder.rb +54 -0
- data/lib/cloud/vsphere/resources/resource_pool.rb +39 -0
- data/lib/cloud/vsphere/resources/scorer.rb +130 -0
- data/lib/cloud/vsphere/resources/util.rb +44 -0
- data/lib/cloud/vsphere/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/cloud/vsphere/resources/cluster_spec.rb +383 -0
- data/spec/unit/cloud/vsphere/resources/datacenter_spec.rb +72 -0
- data/spec/unit/cloud/vsphere/resources/datastore_spec.rb +43 -0
- data/spec/unit/cloud/vsphere/resources/folder_spec.rb +63 -0
- data/spec/unit/cloud/vsphere/resources/resource_pool_spec.rb +42 -0
- data/spec/unit/cloud/vsphere/resources/scorer_spec.rb +73 -0
- data/spec/unit/cloud/vsphere/resources/util_spec.rb +35 -0
- data/spec/unit/cloud/vsphere/resources_spec.rb +216 -0
- metadata +48 -15
- data/spec/unit/vsphere_resource_spec.rb +0 -274
@@ -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
|