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.
- 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
|