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
data/lib/cloud/vsphere/client.rb
CHANGED
@@ -13,8 +13,9 @@ module VSphereCloud
|
|
13
13
|
|
14
14
|
def initialize(host, options = {})
|
15
15
|
http_client = HTTPClient.new
|
16
|
-
|
17
|
-
|
16
|
+
log_path = options["soap_log"]
|
17
|
+
if log_path
|
18
|
+
log_file = File.open(log_path, "w")
|
18
19
|
log_file.sync = true
|
19
20
|
http_client.debug_dev = log_file
|
20
21
|
end
|
data/lib/cloud/vsphere/cloud.rb
CHANGED
@@ -1,7 +1,18 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "membrane"
|
1
3
|
require "ruby_vim_sdk"
|
4
|
+
|
2
5
|
require "cloud/vsphere/client"
|
6
|
+
require "cloud/vsphere/config"
|
3
7
|
require "cloud/vsphere/lease_updater"
|
4
8
|
require "cloud/vsphere/resources"
|
9
|
+
require "cloud/vsphere/resources/cluster"
|
10
|
+
require "cloud/vsphere/resources/datacenter"
|
11
|
+
require "cloud/vsphere/resources/datastore"
|
12
|
+
require "cloud/vsphere/resources/folder"
|
13
|
+
require "cloud/vsphere/resources/resource_pool"
|
14
|
+
require "cloud/vsphere/resources/scorer"
|
15
|
+
require "cloud/vsphere/resources/util"
|
5
16
|
require "cloud/vsphere/models/disk"
|
6
17
|
|
7
18
|
module VSphereCloud
|
@@ -14,37 +25,17 @@ module VSphereCloud
|
|
14
25
|
attr_accessor :client
|
15
26
|
|
16
27
|
def initialize(options)
|
17
|
-
|
18
|
-
raise "Invalid number of VCenters" unless @vcenters.size == 1
|
19
|
-
@vcenter = @vcenters[0]
|
20
|
-
|
21
|
-
@logger = Bosh::Clouds::Config.logger
|
22
|
-
|
23
|
-
@agent_properties = options["agent"]
|
24
|
-
|
25
|
-
@client = Client.new("https://#{@vcenter["host"]}/sdk/vimService", options)
|
26
|
-
@client.login(@vcenter["user"], @vcenter["password"], "en")
|
27
|
-
|
28
|
-
@rest_client = HTTPClient.new
|
29
|
-
@rest_client.send_timeout = 14400 # 4 hours
|
30
|
-
@rest_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
31
|
-
|
32
|
-
# HACK: read the session from the SOAP client so we don't leak sessions when using the REST client
|
33
|
-
cookie_str = @client.stub.cookie
|
34
|
-
@rest_client.cookie_manager.parse(cookie_str, URI.parse("https://#{@vcenter["host"]}"))
|
35
|
-
|
36
|
-
mem_ratio = 1.0
|
37
|
-
if options["mem_overcommit_ratio"]
|
38
|
-
mem_ratio = options["mem_overcommit_ratio"].to_f
|
39
|
-
end
|
40
|
-
|
41
|
-
@resources = Resources.new(@client, @vcenter, mem_ratio)
|
28
|
+
Config.configure(options)
|
42
29
|
|
43
|
-
|
44
|
-
|
45
|
-
@
|
30
|
+
@logger = Config.logger
|
31
|
+
@client = Config.client
|
32
|
+
@rest_client = Config.rest_client
|
33
|
+
@resources = Resources.new
|
46
34
|
|
35
|
+
# Global lock
|
47
36
|
@lock = Mutex.new
|
37
|
+
|
38
|
+
# Resource locks
|
48
39
|
@locks = {}
|
49
40
|
@locks_mutex = Mutex.new
|
50
41
|
|
@@ -76,12 +67,13 @@ module VSphereCloud
|
|
76
67
|
@logger.info("Generated name: #{name}")
|
77
68
|
|
78
69
|
# TODO: make stemcell friendly version of the calls below
|
79
|
-
|
70
|
+
stemcell_size = File.size(image) / (1024 * 1024)
|
71
|
+
cluster, datastore = @resources.place(0, stemcell_size, [])
|
80
72
|
@logger.info("Deploying to: #{cluster.mob} / #{datastore.mob}")
|
81
73
|
|
82
|
-
import_spec_result = import_ovf(name, ovf_file, cluster.resource_pool, datastore.mob)
|
83
|
-
lease = obtain_nfc_lease(cluster.resource_pool, import_spec_result.import_spec,
|
84
|
-
cluster.datacenter.template_folder)
|
74
|
+
import_spec_result = import_ovf(name, ovf_file, cluster.resource_pool.mob, datastore.mob)
|
75
|
+
lease = obtain_nfc_lease(cluster.resource_pool.mob, import_spec_result.import_spec,
|
76
|
+
cluster.datacenter.template_folder.mob)
|
85
77
|
@logger.info("Waiting for NFC lease")
|
86
78
|
state = wait_for_nfc_lease(lease)
|
87
79
|
raise "Could not acquire HTTP NFC lease" unless state == Vim::HttpNfcLease::State::READY
|
@@ -115,7 +107,7 @@ module VSphereCloud
|
|
115
107
|
Bosh::ThreadPool.new(:max_threads => 32, :logger => @logger).wrap do |pool|
|
116
108
|
@resources.datacenters.each_value do |datacenter|
|
117
109
|
@logger.info("Looking for stemcell replicas in: #{datacenter.name}")
|
118
|
-
templates = client.get_property(datacenter.template_folder, Vim::Folder, "childEntity", :ensure_all => true)
|
110
|
+
templates = client.get_property(datacenter.template_folder.mob, Vim::Folder, "childEntity", :ensure_all => true)
|
119
111
|
template_properties = client.get_properties(templates, Vim::VirtualMachine, ["name"])
|
120
112
|
template_properties.each_value do |properties|
|
121
113
|
template_name = properties["name"].gsub("%2f", "/")
|
@@ -133,29 +125,53 @@ module VSphereCloud
|
|
133
125
|
end
|
134
126
|
end
|
135
127
|
|
136
|
-
def disk_spec(
|
128
|
+
def disk_spec(persistent_disks)
|
137
129
|
disks = []
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
130
|
+
if persistent_disks
|
131
|
+
persistent_disks.each do |disk_id|
|
132
|
+
disk = Models::Disk[disk_id]
|
133
|
+
disks << {
|
134
|
+
:size => disk.size,
|
135
|
+
:dc_name => disk.datacenter,
|
136
|
+
:ds_name => disk.datastore
|
137
|
+
}
|
138
|
+
end
|
144
139
|
end
|
145
140
|
disks
|
146
141
|
end
|
147
142
|
|
143
|
+
def stemcell_vm(name)
|
144
|
+
dc = @resources.datacenters.values.first
|
145
|
+
client.find_by_inventory_path(
|
146
|
+
[dc.name, "vm", dc.template_folder.name, name])
|
147
|
+
end
|
148
|
+
|
148
149
|
def create_vm(agent_id, stemcell, resource_pool, networks, disk_locality = nil, environment = nil)
|
149
150
|
with_thread_name("create_vm(#{agent_id}, ...)") do
|
150
151
|
memory = resource_pool["ram"]
|
151
152
|
disk = resource_pool["disk"]
|
152
153
|
cpu = resource_pool["cpu"]
|
153
154
|
|
154
|
-
|
155
|
-
|
155
|
+
# Make sure number of cores is a power of 2. kb.vmware.com/kb/2003484
|
156
|
+
if cpu & cpu - 1 != 0
|
157
|
+
raise "Number of vCPUs: #{cpu} is not a power of 2."
|
158
|
+
end
|
159
|
+
|
160
|
+
stemcell_vm = stemcell_vm(stemcell)
|
161
|
+
raise "Could not find stemcell: #{stemcell}" if stemcell_vm.nil?
|
162
|
+
|
163
|
+
stemcell_size = client.get_property(
|
164
|
+
stemcell_vm, Vim::VirtualMachine, "summary.storage.committed",
|
165
|
+
:ensure_all => true)
|
166
|
+
stemcell_size /= 1024 * 1024
|
167
|
+
|
168
|
+
disks = disk_spec(disk_locality)
|
169
|
+
# need to include swap and linked clone log
|
170
|
+
ephemeral = disk + memory + stemcell_size
|
171
|
+
cluster, datastore = @resources.place(memory, ephemeral, disks)
|
156
172
|
|
157
173
|
name = "vm-#{generate_unique_name}"
|
158
|
-
@logger.info("Creating vm
|
174
|
+
@logger.info("Creating vm: #{name} on #{cluster.mob} stored in #{datastore.mob}")
|
159
175
|
|
160
176
|
replicated_stemcell_vm = replicate_stemcell(cluster, datastore, stemcell)
|
161
177
|
replicated_stemcell_properties = client.get_properties(replicated_stemcell_vm, Vim::VirtualMachine,
|
@@ -194,7 +210,7 @@ module VSphereCloud
|
|
194
210
|
|
195
211
|
@logger.info("Cloning vm: #{replicated_stemcell_vm} to #{name}")
|
196
212
|
|
197
|
-
task = clone_vm(replicated_stemcell_vm, name, cluster.datacenter.vm_folder, cluster.resource_pool,
|
213
|
+
task = clone_vm(replicated_stemcell_vm, name, cluster.datacenter.vm_folder.mob, cluster.resource_pool.mob,
|
198
214
|
:datastore => datastore.mob, :linked => true, :snapshot => snapshot.current_snapshot,
|
199
215
|
:config => config)
|
200
216
|
vm = client.wait_for_task(task)
|
@@ -421,7 +437,8 @@ module VSphereCloud
|
|
421
437
|
|
422
438
|
def find_persistent_datastore(datacenter_name, host_info, disk_size)
|
423
439
|
# Find datastore
|
424
|
-
datastore = @resources.
|
440
|
+
datastore = @resources.place_persistent_datastore(
|
441
|
+
datacenter_name, host_info["cluster"], disk_size)
|
425
442
|
|
426
443
|
if datastore.nil?
|
427
444
|
raise Bosh::Clouds::NoDiskSpace.new(true), "Not enough persistent space on cluster #{host_info["cluster"]}, #{disk_size}"
|
@@ -447,21 +464,18 @@ module VSphereCloud
|
|
447
464
|
|
448
465
|
vm_properties = client.get_properties(vm, Vim::VirtualMachine, "config.hardware.device", :ensure_all => true)
|
449
466
|
host_info = get_vm_host_info(vm)
|
450
|
-
persistent_datastore = nil
|
451
467
|
|
452
468
|
create_disk = false
|
453
469
|
if disk.path
|
454
470
|
if disk.datacenter == datacenter_name &&
|
455
471
|
@resources.validate_persistent_datastore(datacenter_name, disk.datastore) &&
|
456
472
|
host_info["datastores"].include?(disk.datastore)
|
457
|
-
# Looks like we have a valid persistent data store
|
458
|
-
|
459
473
|
@logger.info("Disk already in the right datastore #{datacenter_name} #{disk.datastore}")
|
460
|
-
persistent_datastore = @resources.
|
461
|
-
|
474
|
+
persistent_datastore = @resources.persistent_datastore(
|
475
|
+
datacenter_name, host_info["cluster"], disk.datastore)
|
476
|
+
@logger.debug("Datastore: #{persistent_datastore}")
|
462
477
|
else
|
463
478
|
@logger.info("Disk needs to move from #{datacenter_name} #{disk.datastore}")
|
464
|
-
|
465
479
|
# Find the destination datastore
|
466
480
|
persistent_datastore = find_persistent_datastore(datacenter_name, host_info, disk.size)
|
467
481
|
|
@@ -473,7 +487,7 @@ module VSphereCloud
|
|
473
487
|
destination_path = "[#{persistent_datastore.name}] #{datacenter_disk_path}/#{disk.id}"
|
474
488
|
@logger.info("Moving #{disk.datacenter}/#{source_path} to #{datacenter_name}/#{destination_path}")
|
475
489
|
|
476
|
-
if
|
490
|
+
if Config.copy_disks
|
477
491
|
client.copy_disk(source_datacenter, source_path, datacenter, destination_path)
|
478
492
|
@logger.info("Copied disk successfully")
|
479
493
|
else
|
@@ -576,7 +590,7 @@ module VSphereCloud
|
|
576
590
|
disk = Models::Disk.new
|
577
591
|
disk.size = size
|
578
592
|
disk.save
|
579
|
-
@logger.info("Created disk: #{disk.
|
593
|
+
@logger.info("Created disk: #{disk.inspect}")
|
580
594
|
disk.id.to_s
|
581
595
|
end
|
582
596
|
end
|
@@ -605,23 +619,27 @@ module VSphereCloud
|
|
605
619
|
end
|
606
620
|
|
607
621
|
def get_vm_by_cid(vm_cid)
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
622
|
+
@resources.datacenters.each_value do |datacenter|
|
623
|
+
vm = client.find_by_inventory_path(
|
624
|
+
[datacenter.name, "vm", datacenter.vm_folder.name, vm_cid])
|
625
|
+
unless vm.nil?
|
626
|
+
return vm
|
627
|
+
end
|
628
|
+
end
|
629
|
+
raise Bosh::Clouds::VMNotFound, "VM `#{vm_cid}' not found"
|
613
630
|
end
|
614
631
|
|
615
632
|
def replicate_stemcell(cluster, datastore, stemcell)
|
633
|
+
# TODO: support more than a single datacenter
|
616
634
|
stemcell_vm = client.find_by_inventory_path([cluster.datacenter.name, "vm",
|
617
|
-
cluster.datacenter.
|
635
|
+
cluster.datacenter.template_folder.name, stemcell])
|
618
636
|
raise "Could not find stemcell: #{stemcell}" if stemcell_vm.nil?
|
619
637
|
stemcell_datastore = client.get_property(stemcell_vm, Vim::VirtualMachine, "datastore", :ensure_all => true)
|
620
638
|
|
621
639
|
if stemcell_datastore != datastore.mob
|
622
640
|
@logger.info("Stemcell lives on a different datastore, looking for a local copy of: #{stemcell}.")
|
623
641
|
local_stemcell_name = "#{stemcell} / #{datastore.mob.__mo_id__}"
|
624
|
-
local_stemcell_path = [cluster.datacenter.name, "vm", cluster.datacenter.
|
642
|
+
local_stemcell_path = [cluster.datacenter.name, "vm", cluster.datacenter.template_folder.name,
|
625
643
|
local_stemcell_name]
|
626
644
|
replicated_stemcell_vm = client.find_by_inventory_path(local_stemcell_path)
|
627
645
|
|
@@ -639,8 +657,8 @@ module VSphereCloud
|
|
639
657
|
replicated_stemcell_vm = client.find_by_inventory_path(local_stemcell_path)
|
640
658
|
if replicated_stemcell_vm.nil?
|
641
659
|
@logger.info("Replicating #{stemcell} (#{stemcell_vm}) to #{local_stemcell_name}")
|
642
|
-
task = clone_vm(stemcell_vm, local_stemcell_name, cluster.datacenter.template_folder,
|
643
|
-
cluster.resource_pool, :datastore => datastore.mob)
|
660
|
+
task = clone_vm(stemcell_vm, local_stemcell_name, cluster.datacenter.template_folder.mob,
|
661
|
+
cluster.resource_pool.mob, :datastore => datastore.mob)
|
644
662
|
replicated_stemcell_vm = client.wait_for_task(task)
|
645
663
|
@logger.info("Replicated #{stemcell} (#{stemcell_vm}) to " +
|
646
664
|
"#{local_stemcell_name} (#{replicated_stemcell_vm})")
|
@@ -711,7 +729,7 @@ module VSphereCloud
|
|
711
729
|
env["agent_id"] = agent_id
|
712
730
|
env["networks"] = networking_env
|
713
731
|
env["disks"] = disk_env
|
714
|
-
env.merge!(
|
732
|
+
env.merge!(Config.agent)
|
715
733
|
env
|
716
734
|
end
|
717
735
|
|
@@ -737,7 +755,7 @@ module VSphereCloud
|
|
737
755
|
end
|
738
756
|
end
|
739
757
|
|
740
|
-
{:datacenter => datacenter_name, :datastore =>datastore_name, :vm =>vm_name}
|
758
|
+
{:datacenter => datacenter_name, :datastore => datastore_name, :vm => vm_name}
|
741
759
|
end
|
742
760
|
|
743
761
|
def get_primary_datastore(devices)
|
@@ -826,7 +844,7 @@ module VSphereCloud
|
|
826
844
|
|
827
845
|
def fetch_file(datacenter_name, datastore_name, path)
|
828
846
|
retry_block do
|
829
|
-
url = "https://#{
|
847
|
+
url = "https://#{Config.vcenter.host}/folder/#{path}?dcPath=#{URI.escape(datacenter_name)}" +
|
830
848
|
"&dsName=#{URI.escape(datastore_name)}"
|
831
849
|
|
832
850
|
response = @rest_client.get(url)
|
@@ -843,7 +861,7 @@ module VSphereCloud
|
|
843
861
|
|
844
862
|
def upload_file(datacenter_name, datastore_name, path, contents)
|
845
863
|
retry_block do
|
846
|
-
url = "https://#{
|
864
|
+
url = "https://#{Config.vcenter.host}/folder/#{path}?dcPath=#{URI.escape(datacenter_name)}" +
|
847
865
|
"&dsName=#{URI.escape(datastore_name)}"
|
848
866
|
response = @rest_client.put(url, contents, {"Content-Type" => "application/octet-stream",
|
849
867
|
"Content-Length" => contents.length})
|
@@ -1052,34 +1070,5 @@ module VSphereCloud
|
|
1052
1070
|
sleep(1.0)
|
1053
1071
|
end
|
1054
1072
|
end
|
1055
|
-
|
1056
|
-
def delete_all_vms
|
1057
|
-
Bosh::ThreadPool.new(:max_threads => 32, :logger => @logger).wrap do |pool|
|
1058
|
-
index = 0
|
1059
|
-
|
1060
|
-
@resources.datacenters.each_value do |datacenter|
|
1061
|
-
vm_folder_path = [datacenter.name, "vm", datacenter.vm_folder_name]
|
1062
|
-
vm_folder = client.find_by_inventory_path(vm_folder_path)
|
1063
|
-
vms = client.get_managed_objects(Vim::VirtualMachine, :root => vm_folder)
|
1064
|
-
next if vms.empty?
|
1065
|
-
|
1066
|
-
vm_properties = client.get_properties(vms, Vim::VirtualMachine, ["name"])
|
1067
|
-
|
1068
|
-
vm_properties.each do |_, properties|
|
1069
|
-
pool.process do
|
1070
|
-
@lock.synchronize {index += 1}
|
1071
|
-
vm = properties["name"]
|
1072
|
-
@logger.debug("Deleting #{index}/#{vms.size}: #{vm}")
|
1073
|
-
begin
|
1074
|
-
delete_vm(vm)
|
1075
|
-
rescue Exception => e
|
1076
|
-
@logger.info("#{e} - #{e.backtrace.join("\n")}")
|
1077
|
-
end
|
1078
|
-
end
|
1079
|
-
end
|
1080
|
-
end
|
1081
|
-
end
|
1082
|
-
end
|
1083
|
-
|
1084
1073
|
end
|
1085
1074
|
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module VSphereCloud
|
4
|
+
|
5
|
+
# vSphere CPI Config
|
6
|
+
class Config
|
7
|
+
|
8
|
+
# Membrane schema for the provided config.
|
9
|
+
@schema = Membrane::SchemaParser.parse do
|
10
|
+
{
|
11
|
+
"agent" => dict(String, Object), # passthrough to the agent
|
12
|
+
optional("cpi_log") => String,
|
13
|
+
optional("soap_log") => String,
|
14
|
+
optional("mem_overcommit_ratio") => Numeric,
|
15
|
+
optional("copy_disks") => bool,
|
16
|
+
"vcenters" => [{
|
17
|
+
"host" => String,
|
18
|
+
"user" => String,
|
19
|
+
"password" => String,
|
20
|
+
"datacenters" => [{
|
21
|
+
"name" => String,
|
22
|
+
"vm_folder" => String,
|
23
|
+
"template_folder" => String,
|
24
|
+
optional("use_sub_folder") => bool,
|
25
|
+
"disk_path" => String,
|
26
|
+
"datastore_pattern" => String,
|
27
|
+
"persistent_datastore_pattern" => String,
|
28
|
+
optional("allow_mixed_datastores") => bool,
|
29
|
+
"clusters" => [enum(String,
|
30
|
+
dict(String, {"resource_pool" => String}))]
|
31
|
+
}]
|
32
|
+
}]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
# vCenter config.
|
37
|
+
class VCenterConfig
|
38
|
+
|
39
|
+
# @!attribute host
|
40
|
+
# @return [String] vCenter host.
|
41
|
+
attr_accessor :host
|
42
|
+
|
43
|
+
# @!attribute user
|
44
|
+
# @return [String] vCenter user.
|
45
|
+
attr_accessor :user
|
46
|
+
|
47
|
+
# @!attribute password
|
48
|
+
# @return [String] vCenter password.
|
49
|
+
attr_accessor :password
|
50
|
+
|
51
|
+
# @!attribute datacenters
|
52
|
+
# @return [Hash<String, DatacenterConfig>] child datacenters.
|
53
|
+
attr_accessor :datacenters
|
54
|
+
|
55
|
+
# Creates a new vCenter Config model from parsed YAML.
|
56
|
+
#
|
57
|
+
# @param [Hash] config parsed YAML.
|
58
|
+
def initialize(config)
|
59
|
+
@host = config["host"]
|
60
|
+
@user = config["user"]
|
61
|
+
@password = config["password"]
|
62
|
+
@datacenters = {}
|
63
|
+
|
64
|
+
# TODO: current limitation to support a single vDC.
|
65
|
+
# Primarily due to stemcell replication logic calling CloneVM_Task which
|
66
|
+
# does not copy across datacenters. Should use CopyDatastoreFile_Task /
|
67
|
+
# CopyVirtualDisk_Task.
|
68
|
+
unless config["datacenters"].size == 1
|
69
|
+
raise "vSphere CPI only supports a single datacenter."
|
70
|
+
end
|
71
|
+
|
72
|
+
config["datacenters"].each do |dc|
|
73
|
+
dc_config = DatacenterConfig.new(dc)
|
74
|
+
@datacenters[dc_config.name] = dc_config
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Folder config.
|
80
|
+
class FolderConfig
|
81
|
+
# @!attribute vm
|
82
|
+
# @return [String] vm folder path.
|
83
|
+
attr_accessor :vm
|
84
|
+
|
85
|
+
# @!attribute template
|
86
|
+
# @return [String] template/stemcell folder path.
|
87
|
+
attr_accessor :template
|
88
|
+
|
89
|
+
# @!attribute shared
|
90
|
+
# @return [true, false] boolean indicating shared folders, so an
|
91
|
+
# additional namespace should be used.
|
92
|
+
attr_accessor :shared
|
93
|
+
end
|
94
|
+
|
95
|
+
# Datastore config.
|
96
|
+
class DatastoreConfig
|
97
|
+
|
98
|
+
# @!attribute ephemeral_pattern
|
99
|
+
# @return [Regexp] regexp pattern for ephemeral datastores.
|
100
|
+
attr_accessor :ephemeral_pattern
|
101
|
+
|
102
|
+
# @!attribute persistent_pattern
|
103
|
+
# @return [Regexp] regexp pattern for persistent datastores.
|
104
|
+
attr_accessor :persistent_pattern
|
105
|
+
|
106
|
+
# @!attribute disk_path
|
107
|
+
# @return [String] VMDK datastore path.
|
108
|
+
attr_accessor :disk_path
|
109
|
+
|
110
|
+
# @!attribute allow_mixed
|
111
|
+
# @return [true, false] boolean indicating whether persistent and
|
112
|
+
# ephemeral datastores can overlap..
|
113
|
+
attr_accessor :allow_mixed
|
114
|
+
end
|
115
|
+
|
116
|
+
# Datacenter config.
|
117
|
+
class DatacenterConfig
|
118
|
+
|
119
|
+
# @!attribute name
|
120
|
+
# @return [String] datacenter name.
|
121
|
+
attr_accessor :name
|
122
|
+
|
123
|
+
# @!attribute folders
|
124
|
+
# @return [FolderConfig] folder config.
|
125
|
+
attr_accessor :folders
|
126
|
+
|
127
|
+
# @!attribute datastores
|
128
|
+
# @return [DatastoreConfig] datastore config.
|
129
|
+
attr_accessor :datastores
|
130
|
+
|
131
|
+
# @!attribute clusters
|
132
|
+
# @return [Hash<String, ClusterConfig>] child clusters.
|
133
|
+
attr_accessor :clusters
|
134
|
+
|
135
|
+
# Creates a new Datacenter Config model from parsed YAML.
|
136
|
+
#
|
137
|
+
# @param [Hash] config parsed YAML.
|
138
|
+
def initialize(config)
|
139
|
+
@name = config["name"]
|
140
|
+
|
141
|
+
@folders = FolderConfig.new
|
142
|
+
@folders.template = config["template_folder"]
|
143
|
+
@folders.vm = config["vm_folder"]
|
144
|
+
@folders.shared = !!config["use_sub_folder"]
|
145
|
+
|
146
|
+
@datastores = DatastoreConfig.new
|
147
|
+
@datastores.ephemeral_pattern = Regexp.new(config["datastore_pattern"])
|
148
|
+
@datastores.persistent_pattern = Regexp.new(
|
149
|
+
config["persistent_datastore_pattern"])
|
150
|
+
@datastores.disk_path = config["disk_path"]
|
151
|
+
@datastores.allow_mixed = !!config["allow_mixed_datastores"]
|
152
|
+
|
153
|
+
@clusters = {}
|
154
|
+
config["clusters"].each do |cluster|
|
155
|
+
cluster_config = ClusterConfig.new(cluster)
|
156
|
+
@clusters[cluster_config.name] = cluster_config
|
157
|
+
end
|
158
|
+
|
159
|
+
if @clusters.any? { |_, cluster| cluster.resource_pool }
|
160
|
+
@folders.shared = true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Cluster config.
|
166
|
+
class ClusterConfig
|
167
|
+
|
168
|
+
# @!attribute name
|
169
|
+
# @return [String] cluster name.
|
170
|
+
attr_accessor :name
|
171
|
+
|
172
|
+
# @!attribute resource_pool
|
173
|
+
# @return [String?] optional resource pool to use instead of root.
|
174
|
+
attr_accessor :resource_pool
|
175
|
+
|
176
|
+
# Creates a new Cluster Config model from parsed YAML.
|
177
|
+
#
|
178
|
+
# @param [Hash] config parsed YAML.
|
179
|
+
def initialize(config)
|
180
|
+
case config
|
181
|
+
when String
|
182
|
+
@name = config
|
183
|
+
else
|
184
|
+
@name = config.keys.first
|
185
|
+
@resource_pool = config[@name]["resource_pool"]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class << self
|
191
|
+
|
192
|
+
CONFIG_OPTIONS = [
|
193
|
+
:logger,
|
194
|
+
:client,
|
195
|
+
:rest_client,
|
196
|
+
:agent,
|
197
|
+
:copy_disks,
|
198
|
+
:vcenter,
|
199
|
+
:mem_overcommit,
|
200
|
+
]
|
201
|
+
|
202
|
+
CONFIG_OPTIONS.each do |option|
|
203
|
+
attr_accessor option
|
204
|
+
end
|
205
|
+
|
206
|
+
# Clear all of the properties.
|
207
|
+
#
|
208
|
+
# Used by unit tests.
|
209
|
+
# @return [void]
|
210
|
+
def clear
|
211
|
+
CONFIG_OPTIONS.each do |option|
|
212
|
+
self.instance_variable_set("@#{option}".to_sym, nil)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Setup the Config context based on the parsed YAML.
|
217
|
+
#
|
218
|
+
# @return [void]
|
219
|
+
def configure(config)
|
220
|
+
@logger = Bosh::Clouds::Config.logger
|
221
|
+
@schema.validate(config)
|
222
|
+
@agent = config["agent"]
|
223
|
+
|
224
|
+
unless config["vcenters"].size == 1
|
225
|
+
raise "vSphere CPI only supports a single vCenter."
|
226
|
+
end
|
227
|
+
@vcenter = VCenterConfig.new(config["vcenters"].first)
|
228
|
+
|
229
|
+
@client = Client.new("https://#{@vcenter.host}/sdk/vimService", {
|
230
|
+
"soap_log" => config["soap_log"] || config["cpi_log"]
|
231
|
+
})
|
232
|
+
@client.login(@vcenter.user, @vcenter.password, "en")
|
233
|
+
|
234
|
+
@rest_client = HTTPClient.new
|
235
|
+
@rest_client.send_timeout = 14400 # 4 hours, for stemcell uploads
|
236
|
+
@rest_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
237
|
+
|
238
|
+
# HACK: read the session from the SOAP client so we don't leak sessions
|
239
|
+
# when using the REST client
|
240
|
+
cookie_str = @client.stub.cookie
|
241
|
+
@rest_client.cookie_manager.parse(
|
242
|
+
cookie_str, URI.parse("https://#{@vcenter.host}"))
|
243
|
+
|
244
|
+
if config["mem_overcommit_ratio"]
|
245
|
+
@mem_overcommit = config["mem_overcommit_ratio"].to_f
|
246
|
+
else
|
247
|
+
@mem_overcommit = 1.0
|
248
|
+
end
|
249
|
+
|
250
|
+
@copy_disks = !!config["copy_disks"]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|