knife-vsphere 1.0.0.pre.3 → 1.0.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/chef/knife/base_vsphere_command.rb +18 -8
- data/lib/chef/knife/vshpere_vm_move.rb +50 -5
- data/lib/chef/knife/vsphere_cpu_ratio.rb +45 -0
- data/lib/chef/knife/vsphere_datastore_list.rb +14 -0
- data/lib/chef/knife/vsphere_datastorecluster_list.rb +19 -8
- data/lib/chef/knife/vsphere_datastorecluster_maxfree.rb +44 -8
- data/lib/chef/knife/vsphere_folder_list.rb +30 -0
- data/lib/chef/knife/vsphere_hosts_list.rb +18 -25
- data/lib/chef/knife/vsphere_pool_list.rb +9 -7
- data/lib/chef/knife/vsphere_vm_clone.rb +29 -20
- data/lib/chef/knife/vsphere_vm_delete.rb +66 -66
- data/lib/chef/knife/vsphere_vm_list.rb +30 -35
- data/lib/chef/knife/vsphere_vm_snapshot.rb +1 -0
- data/lib/chef/knife/vsphere_vm_toolsconfig.rb +52 -0
- data/lib/knife-vsphere/version.rb +1 -1
- metadata +49 -38
- checksums.yaml +0 -7
@@ -146,7 +146,7 @@ class Chef
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def traverse_folders_for_vms(folder, vmname)
|
149
|
-
|
149
|
+
retval = []
|
150
150
|
children = folder.children.find_all
|
151
151
|
children.each do |child|
|
152
152
|
if child.class == RbVmomi::VIM::VirtualMachine && child.name == vmname
|
@@ -204,7 +204,7 @@ class Chef
|
|
204
204
|
if baseEntity.is_a? RbVmomi::VIM::Folder
|
205
205
|
baseEntity = baseEntity.childEntity.find { |f| f.name == entityArrItem } or
|
206
206
|
abort "no such pool #{poolName} while looking for #{entityArrItem}"
|
207
|
-
elsif baseEntity.is_a? RbVmomi::VIM::ClusterComputeResource or baseEntity.is_a? RbVmomi::VIM::ComputeResource
|
207
|
+
elsif baseEntity.is_a? RbVmomi::VIM::ClusterComputeResource or baseEntity.is_a? RbVmomi::VIM::ComputeResource
|
208
208
|
baseEntity = baseEntity.resourcePool.resourcePool.find { |f| f.name == entityArrItem } or
|
209
209
|
abort "no such pool #{poolName} while looking for #{entityArrItem}"
|
210
210
|
elsif baseEntity.is_a? RbVmomi::VIM::ResourcePool
|
@@ -272,10 +272,20 @@ class Chef
|
|
272
272
|
baseEntity.find { |f| f.info.name == dsName } or abort "no such datastore #{dsName}"
|
273
273
|
end
|
274
274
|
|
275
|
-
def find_datastorecluster(dsName)
|
276
|
-
|
277
|
-
|
278
|
-
|
275
|
+
def find_datastorecluster(dsName, folder = nil)
|
276
|
+
if ! folder
|
277
|
+
dc = get_datacenter
|
278
|
+
folder = dc.datastoreFolder
|
279
|
+
end
|
280
|
+
folder.childEntity.each do |child|
|
281
|
+
if child.class.to_s == 'Folder'
|
282
|
+
ds = find_datastorecluster(dsName, child)
|
283
|
+
if ds then return ds end
|
284
|
+
elsif child.class.to_s == 'StoragePod' && child.name == dsName
|
285
|
+
return child
|
286
|
+
end
|
287
|
+
end
|
288
|
+
return nil
|
279
289
|
end
|
280
290
|
|
281
291
|
def find_device(vm, deviceName)
|
@@ -304,8 +314,8 @@ class Chef
|
|
304
314
|
if object.parent.is_a?(RbVmomi::VIM:: ManagedEntity)
|
305
315
|
return get_path_to_object(object.parent) + "/" + object.parent.name
|
306
316
|
else
|
307
|
-
|
308
|
-
|
317
|
+
return ""
|
318
|
+
end
|
309
319
|
else
|
310
320
|
puts "Unknown type #{object.class}, not enumerating"
|
311
321
|
nil
|
@@ -21,6 +21,50 @@ class Chef::Knife::VsphereVmMove < Chef::Knife::BaseVsphereCommand
|
|
21
21
|
:long => "--dest-folder FOLDER",
|
22
22
|
:description => "The destination folder into which the VM or template should be moved"
|
23
23
|
|
24
|
+
option :datastore,
|
25
|
+
:long => "--datastore STORE",
|
26
|
+
:description => "The datastore into which to put the cloned VM"
|
27
|
+
|
28
|
+
option :thin_provision,
|
29
|
+
:long => "--thin-provision",
|
30
|
+
:description => "Indicates whether disk should be thin provisioned.",
|
31
|
+
:boolean => true
|
32
|
+
|
33
|
+
option :thick_provision,
|
34
|
+
:long => "--thick-provision",
|
35
|
+
:description => "Indicates whether disk should be thick provisioned.",
|
36
|
+
:boolean => true
|
37
|
+
|
38
|
+
# Convert VM
|
39
|
+
def convert_vm(vm)
|
40
|
+
rspec = nil
|
41
|
+
dc = get_datacenter
|
42
|
+
hosts = find_all_in_folder(dc.hostFolder, RbVmomi::VIM::ComputeResource)
|
43
|
+
rp = hosts.first.resourcePool
|
44
|
+
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => rp)
|
45
|
+
|
46
|
+
if get_config(:thin_provision)
|
47
|
+
puts "Thin provsisioning #{vm.name}"
|
48
|
+
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => find_datastore(get_config(:datastore)), :transform => :sparse)
|
49
|
+
end
|
50
|
+
|
51
|
+
if get_config(:thick_provision)
|
52
|
+
puts "Thick provsisioning #{vm.name}"
|
53
|
+
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => find_datastore(get_config(:datastore)), :transform => :flat)
|
54
|
+
end
|
55
|
+
|
56
|
+
task = vm.RelocateVM_Task(:spec => rspec)
|
57
|
+
task.wait_for_completion
|
58
|
+
end
|
59
|
+
|
60
|
+
# Move VM
|
61
|
+
def move_vm(vm)
|
62
|
+
dest_name = config[:dest_name] || vmname
|
63
|
+
dest_folder = config[:dest_folder].nil? ? (vm.parent) : (find_folder(get_config(:dest_folder)))
|
64
|
+
|
65
|
+
vm.Rename_Task(:newName => dest_name).wait_for_completion unless vmname == dest_name
|
66
|
+
dest_folder.MoveIntoFolder_Task(:list => [vm]).wait_for_completion unless folder == dest_folder
|
67
|
+
end
|
24
68
|
|
25
69
|
def run
|
26
70
|
$stdout.sync = true
|
@@ -38,10 +82,11 @@ class Chef::Knife::VsphereVmMove < Chef::Knife::BaseVsphereCommand
|
|
38
82
|
vm = find_in_folder(folder, RbVmomi::VIM::VirtualMachine, vmname) or
|
39
83
|
abort "VM #{vmname} not found"
|
40
84
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
85
|
+
if get_config(:thin_provision) || get_config(:thick_provision)
|
86
|
+
convert_vm(vm)
|
87
|
+
else
|
88
|
+
move_vm(vm)
|
89
|
+
end
|
46
90
|
end
|
47
91
|
end
|
92
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
require 'chef/knife/base_vsphere_command'
|
3
|
+
|
4
|
+
class Chef::Knife::VsphereCpuRatio < Chef::Knife::BaseVsphereCommand
|
5
|
+
banner 'knife vsphere cpu ratio [CLUSTER] [HOST]'
|
6
|
+
|
7
|
+
get_common_options
|
8
|
+
|
9
|
+
def run
|
10
|
+
$stdout.sync = true
|
11
|
+
|
12
|
+
cluster_name = @name_args[0]
|
13
|
+
host_name = @name_args[1]
|
14
|
+
|
15
|
+
get_vim_connection
|
16
|
+
|
17
|
+
dc = get_datacenter
|
18
|
+
hf = dc.hostFolder
|
19
|
+
|
20
|
+
cluster = cluster_name.nil? ? hf.childEntity : hf.childEntity.select { |c| c.name == cluster_name }
|
21
|
+
|
22
|
+
if cluster.empty?
|
23
|
+
fatal_exit("Cluster #{cluster_name} not found.")
|
24
|
+
end
|
25
|
+
|
26
|
+
cluster.each { |c|
|
27
|
+
host = host_name.nil? ? c.host : c.host.select { |h| h.name == host_name }
|
28
|
+
if host.empty?
|
29
|
+
fatal_exit("Host not found in cluster #{c.name}.")
|
30
|
+
end
|
31
|
+
|
32
|
+
puts "### Cluster #{c.name} ###"
|
33
|
+
|
34
|
+
host.each { |h|
|
35
|
+
v_cpu = h.vm.inject(0) { |sum, vm| sum + vm.config.hardware.numCPU }
|
36
|
+
p_cpu = h.summary.hardware.numCpuThreads
|
37
|
+
|
38
|
+
ratio = 1.0 * v_cpu/p_cpu
|
39
|
+
|
40
|
+
puts "#{h.name}: #{ratio}"
|
41
|
+
}
|
42
|
+
puts ''
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
@@ -43,6 +43,12 @@ class Chef::Knife::VsphereDatastoreList < Chef::Knife::BaseVsphereCommand
|
|
43
43
|
|
44
44
|
get_common_options
|
45
45
|
|
46
|
+
option :list,
|
47
|
+
:long => "--list",
|
48
|
+
:short => "-L",
|
49
|
+
:description => "Indicates whether to list VM's in datastore",
|
50
|
+
:boolean => true
|
51
|
+
|
46
52
|
def run
|
47
53
|
$stdout.sync = true
|
48
54
|
|
@@ -52,6 +58,14 @@ class Chef::Knife::VsphereDatastoreList < Chef::Knife::BaseVsphereCommand
|
|
52
58
|
avail = number_to_human_size(store.summary[:freeSpace])
|
53
59
|
cap = number_to_human_size(store.summary[:capacity])
|
54
60
|
puts "#{ui.color("Datastore", :cyan)}: #{store.name} (#{avail} / #{cap})"
|
61
|
+
if get_config(:list)
|
62
|
+
store.vm.each do |vms|
|
63
|
+
hostName = vms.guest[:hostName]
|
64
|
+
guestFullName = vms.guest[:guestFullName]
|
65
|
+
guestState = vms.guest[:guestState]
|
66
|
+
puts "#{ui.color("VM Name:", :green)} #{hostName} #{ui.color("OS:", :magenta)} #{guestFullName} #{ui.color("State:", :cyan)} #{guestState}"
|
67
|
+
end
|
68
|
+
end
|
55
69
|
end
|
56
70
|
end
|
57
71
|
end
|
@@ -35,6 +35,24 @@ def number_to_human_size(number)
|
|
35
35
|
return sprintf("%0.2f %s", number, unit)
|
36
36
|
end
|
37
37
|
|
38
|
+
def traverse_folders_for_dsclusters(folder)
|
39
|
+
print_dsclusters_in_folder(folder)
|
40
|
+
folder.childEntity.each do |child|
|
41
|
+
if child.class.to_s == 'Folder'
|
42
|
+
traverse_folders_for_dsclusters(child)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_dsclusters_in_folder (folder)
|
48
|
+
folder.childEntity.each do |child|
|
49
|
+
if child.class.to_s == "StoragePod"
|
50
|
+
avail = number_to_human_size(child.summary[:freeSpace])
|
51
|
+
cap = number_to_human_size(child.summary[:capacity])
|
52
|
+
puts "#{ui.color("DatastoreCluster", :cyan)}: #{child.name} (#{avail} / #{cap})"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
38
56
|
|
39
57
|
# Lists all known data store cluster in datacenter with sizes
|
40
58
|
class Chef::Knife::VsphereDatastoreclusterList < Chef::Knife::BaseVsphereCommand
|
@@ -45,16 +63,9 @@ class Chef::Knife::VsphereDatastoreclusterList < Chef::Knife::BaseVsphereCommand
|
|
45
63
|
|
46
64
|
def run
|
47
65
|
$stdout.sync = true
|
48
|
-
|
49
66
|
vim = get_vim_connection
|
50
67
|
dc = get_datacenter
|
51
|
-
dc.datastoreFolder
|
52
|
-
if store.class.to_s == "StoragePod"
|
53
|
-
avail = number_to_human_size(store.summary[:freeSpace])
|
54
|
-
cap = number_to_human_size(store.summary[:capacity])
|
55
|
-
puts "#{ui.color("DatastoreCluster", :cyan)}: #{store.name} (#{avail} / #{cap})"
|
56
|
-
end
|
57
|
-
end
|
68
|
+
traverse_folders_for_dsclusters(dc.datastoreFolder)
|
58
69
|
end
|
59
70
|
end
|
60
71
|
|
@@ -18,6 +18,36 @@
|
|
18
18
|
require 'chef/knife'
|
19
19
|
require 'chef/knife/base_vsphere_command'
|
20
20
|
|
21
|
+
def is_max_dscluster(dscluster, max_dscluster)
|
22
|
+
if ! max_dscluster
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
|
26
|
+
if dscluster.summary[:freeSpace] > max_dscluster.summary[:freeSpace]
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_max_dscluster(folder, max_dscluster, regex)
|
34
|
+
folder.childEntity.each do |child|
|
35
|
+
if child.class.to_s == 'Folder'
|
36
|
+
sub_max = find_max_dscluster(child, max_dscluster, regex)
|
37
|
+
if is_max_dscluster(sub_max, max_dscluster)
|
38
|
+
max_dscluster = sub_max
|
39
|
+
end
|
40
|
+
elsif child.class.to_s == 'StoragePod'
|
41
|
+
|
42
|
+
if is_max_dscluster(child, max_dscluster) && regex.match(child.name)
|
43
|
+
max_dscluster = child
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
return max_dscluster
|
49
|
+
end
|
50
|
+
|
21
51
|
# Gets the data store cluster with the most free space in datacenter
|
22
52
|
class Chef::Knife::VsphereDatastoreclusterMaxfree < Chef::Knife::BaseVsphereCommand
|
23
53
|
|
@@ -35,14 +65,20 @@ class Chef::Knife::VsphereDatastoreclusterMaxfree < Chef::Knife::BaseVsphereComm
|
|
35
65
|
|
36
66
|
vim = get_vim_connection
|
37
67
|
dcname = get_config(:vsphere_dc)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
68
|
+
regex = /#{Regexp.escape( get_config(:regex))}/
|
69
|
+
max_dscluster = nil
|
70
|
+
|
71
|
+
vim = get_vim_connection
|
72
|
+
dc = get_datacenter
|
73
|
+
|
74
|
+
max_dscluster = find_max_dscluster(dc.datastoreFolder, max_dscluster, regex)
|
75
|
+
|
76
|
+
if max_dscluster
|
77
|
+
puts max_dscluster.name
|
78
|
+
else
|
79
|
+
puts "No datastore clusters found"
|
80
|
+
exit 1
|
45
81
|
end
|
46
|
-
|
82
|
+
|
47
83
|
end
|
48
84
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
require 'chef/knife'
|
6
|
+
require 'chef/knife/base_vsphere_command'
|
7
|
+
|
8
|
+
# Lists all vm folders
|
9
|
+
class Chef::Knife::VsphereFolderList < Chef::Knife::BaseVsphereCommand
|
10
|
+
|
11
|
+
banner "knife vsphere folder list"
|
12
|
+
|
13
|
+
get_common_options
|
14
|
+
|
15
|
+
def traverse_folders(folder, indent_level)
|
16
|
+
|
17
|
+
puts "#{" " * indent_level} #{ui.color("Folder", :cyan)}: " + folder.name
|
18
|
+
|
19
|
+
folders = find_all_in_folder(folder, RbVmomi::VIM::Folder)
|
20
|
+
folders.each do |child|
|
21
|
+
traverse_folders(child, indent_level + 1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
vim = get_vim_connection
|
27
|
+
baseFolder = find_folder(get_config(:folder));
|
28
|
+
traverse_folders(baseFolder, 0)
|
29
|
+
end
|
30
|
+
end
|
@@ -2,6 +2,7 @@ require 'chef/knife'
|
|
2
2
|
require 'chef/knife/base_vsphere_command'
|
3
3
|
require 'rbvmomi'
|
4
4
|
require 'netaddr'
|
5
|
+
|
5
6
|
#list hosts belonging to pool
|
6
7
|
class Chef::Knife::VsphereHostsList < Chef::Knife::BaseVsphereCommand
|
7
8
|
banner "knife vsphere hosts list"
|
@@ -12,38 +13,30 @@ class Chef::Knife::VsphereHostsList < Chef::Knife::BaseVsphereCommand
|
|
12
13
|
:short => "-h",
|
13
14
|
:description => "Target pool"
|
14
15
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
if child.class == RbVmomi::VIM::ClusterComputeResource || child.class == RbVmomi::VIM::ComputeResource || child.class == RbVmomi::VIM::ResourcePool
|
19
|
-
if child.name == poolname then return child end
|
20
|
-
elsif child.class == RbVmomi::VIM::Folder
|
21
|
-
pool = traverse_folders_for_pool(child, poolname)
|
22
|
-
if pool then return pool end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
return false
|
16
|
+
def find_pools(folder, poolname = nil)
|
17
|
+
pools = folder.children.find_all.select {|p| p.is_a?(RbVmomi::VIM::ComputeResource) || p.is_a?(RbVmomi::VIM::ResourcePool) }
|
18
|
+
poolname.nil? ? pools : pools.select {|p| p.name == poolname }
|
26
19
|
end
|
27
20
|
|
28
21
|
def run
|
29
|
-
poolname = config[:pool]
|
30
|
-
if poolname.nil?
|
31
|
-
show_usage
|
32
|
-
fatal_exit("You must specify a resource pool or cluster name (see knife vsphere pool list)")
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
22
|
vim = get_vim_connection
|
37
23
|
dc = get_datacenter
|
38
24
|
folder = dc.hostFolder
|
39
25
|
|
40
|
-
|
26
|
+
target_pool = config[:pool]
|
41
27
|
|
42
|
-
|
43
|
-
|
28
|
+
pools = find_pools(folder, target_pool)
|
29
|
+
if target_pool && pools.empty?
|
30
|
+
puts "Pool #{target_pool} not found"
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
pools.each do |pool|
|
35
|
+
puts "#{ui.color("Pool", :cyan)}: #{pool.name}"
|
36
|
+
hosts = pool.host || []
|
44
37
|
hosts.each do |hostc|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
38
|
+
puts " #{ui.color("Host", :cyan)}: #{hostc.name}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
49
42
|
end
|
@@ -13,13 +13,16 @@ class Chef::Knife::VspherePoolList < Chef::Knife::BaseVsphereCommand
|
|
13
13
|
get_common_options
|
14
14
|
|
15
15
|
def traverse_folders(folder)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
return if folder.is_a? RbVmomi::VIM::VirtualApp
|
17
|
+
|
18
|
+
puts "#{ui.color("Pool", :cyan)}: "+(folder.path[3..-1].map { |x| x[1] }.* '/') if
|
19
|
+
folder.is_a? RbVmomi::VIM::ResourcePool
|
20
|
+
|
21
|
+
folders = find_all_in_folder(folder, RbVmomi::VIM::ManagedObject) || []
|
22
|
+
folders.each do |child|
|
23
|
+
traverse_folders(child)
|
22
24
|
end
|
25
|
+
|
23
26
|
end
|
24
27
|
|
25
28
|
def find_pool_folder(folderName)
|
@@ -36,7 +39,6 @@ class Chef::Knife::VspherePoolList < Chef::Knife::BaseVsphereCommand
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def run
|
39
|
-
$stdout.sync = true
|
40
42
|
vim = get_vim_connection
|
41
43
|
baseFolder = find_pool_folder(get_config(:folder));
|
42
44
|
traverse_folders(baseFolder)
|
@@ -47,6 +47,16 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
47
47
|
:description => "Indicates whether to use linked clones.",
|
48
48
|
:boolean => false
|
49
49
|
|
50
|
+
option :linked_clone,
|
51
|
+
:long => "--linked-clone",
|
52
|
+
:description => "Indicates whether to use linked clones.",
|
53
|
+
:boolean => false
|
54
|
+
|
55
|
+
option :thin_provision,
|
56
|
+
:long => "--thin-provision",
|
57
|
+
:description => "Indicates whether disk should be thin provisioned.",
|
58
|
+
:boolean => true
|
59
|
+
|
50
60
|
option :annotation,
|
51
61
|
:long => "--annotation TEXT",
|
52
62
|
:description => "Add TEXT in Notes field from annotation"
|
@@ -113,6 +123,10 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
113
123
|
:description => "Indicates whether to bootstrap the VM",
|
114
124
|
:boolean => false
|
115
125
|
|
126
|
+
option :environment,
|
127
|
+
:long => "--environment ENVIRONMENT",
|
128
|
+
:description => "Environment to add the node to for bootstrapping"
|
129
|
+
|
116
130
|
option :fqdn,
|
117
131
|
:long => "--fqdn SERVER_FQDN",
|
118
132
|
:description => "Fully qualified hostname for bootstrapping"
|
@@ -233,6 +247,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
233
247
|
end
|
234
248
|
|
235
249
|
vim = get_vim_connection
|
250
|
+
vdm = vim.serviceContent.virtualDiskManager
|
236
251
|
|
237
252
|
dc = get_datacenter
|
238
253
|
|
@@ -310,7 +325,6 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
310
325
|
|
311
326
|
# Builds a CloneSpec
|
312
327
|
def generate_clone_spec (src_config)
|
313
|
-
|
314
328
|
rspec = nil
|
315
329
|
if get_config(:resource_pool)
|
316
330
|
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => find_pool(get_config(:resource_pool)))
|
@@ -343,15 +357,13 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
343
357
|
end
|
344
358
|
end
|
345
359
|
|
346
|
-
if get_config(:
|
347
|
-
|
348
|
-
:powerOn => false,
|
349
|
-
:template => true)
|
350
|
-
else
|
351
|
-
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => rspec,
|
352
|
-
:powerOn => false,
|
353
|
-
:template => false)
|
360
|
+
if get_config(:thin_provision)
|
361
|
+
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:transform => :sparse, :pool => find_pool(get_config(:resource_pool)))
|
354
362
|
end
|
363
|
+
|
364
|
+
is_template = !get_config(:mark_as_template).nil?
|
365
|
+
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => rspec, :powerOn => false,:template => is_template)
|
366
|
+
|
355
367
|
clone_spec.config = RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => Array.new)
|
356
368
|
|
357
369
|
if get_config(:annotation)
|
@@ -375,7 +387,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
375
387
|
card.backing.port = switch_port
|
376
388
|
rescue
|
377
389
|
# not connected to a distibuted switch?
|
378
|
-
card.backing
|
390
|
+
card.backing = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:network => network, :deviceName => network.name)
|
379
391
|
end
|
380
392
|
dev_spec = RbVmomi::VIM.VirtualDeviceConfigSpec(:device => card, :operation => "edit")
|
381
393
|
clone_spec.config.deviceChange.push dev_spec
|
@@ -417,24 +429,21 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
417
429
|
config[:vmname]
|
418
430
|
end
|
419
431
|
|
420
|
-
if src_config.guestId.downcase.include?("
|
432
|
+
if src_config.guestId.downcase.include?("windows")
|
433
|
+
if cust_spec.identity.nil?
|
434
|
+
fatal_exit("Please provide Windows Guest Customization")
|
435
|
+
else
|
436
|
+
cust_spec.identity.userData.computerName = RbVmomi::VIM.CustomizationFixedName(:name => hostname)
|
437
|
+
end
|
438
|
+
else
|
421
439
|
ident = RbVmomi::VIM.CustomizationLinuxPrep
|
422
|
-
|
423
440
|
ident.hostName = RbVmomi::VIM.CustomizationFixedName(:name => hostname)
|
424
|
-
|
425
441
|
if get_config(:customization_domain)
|
426
442
|
ident.domain = get_config(:customization_domain)
|
427
443
|
else
|
428
444
|
ident.domain = ''
|
429
445
|
end
|
430
|
-
|
431
446
|
cust_spec.identity = ident
|
432
|
-
elsif src_config.guestId.downcase.include?("windows")
|
433
|
-
if cust_spec.identity.nil?
|
434
|
-
fatal_exit("Please provide Windows Guest Customization")
|
435
|
-
else
|
436
|
-
cust_spec.identity.userData.computerName = RbVmomi::VIM.CustomizationFixedName(:name => hostname)
|
437
|
-
end
|
438
447
|
end
|
439
448
|
end
|
440
449
|
|
@@ -1,66 +1,66 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
-
# License:: Apache License, Version 2.0
|
4
|
-
#
|
5
|
-
|
6
|
-
require 'chef/knife'
|
7
|
-
require 'chef/knife/base_vsphere_command'
|
8
|
-
require 'rbvmomi'
|
9
|
-
|
10
|
-
# These two are needed for the '--purge' deletion case
|
11
|
-
require 'chef/node'
|
12
|
-
require 'chef/api_client'
|
13
|
-
|
14
|
-
# Delete a virtual machine from vCenter
|
15
|
-
class Chef::Knife::VsphereVmDelete < Chef::Knife::BaseVsphereCommand
|
16
|
-
|
17
|
-
banner "knife vsphere vm delete VMNAME"
|
18
|
-
|
19
|
-
option :purge,
|
20
|
-
:short => "-P",
|
21
|
-
:long => "--purge",
|
22
|
-
:boolean => true,
|
23
|
-
:description => "Destroy corresponding node and client on the Chef Server, in addition to destroying the VM itself."
|
24
|
-
|
25
|
-
get_common_options
|
26
|
-
|
27
|
-
# Extracted from Chef::Knife.delete_object, because it has a
|
28
|
-
# confirmation step built in... By specifying the '--purge'
|
29
|
-
# flag (and also explicitly confirming the server destruction!)
|
30
|
-
# the user is already making their intent known. It is not
|
31
|
-
# necessary to make them confirm two more times.
|
32
|
-
def destroy_item(itemClass, name, type_name)
|
33
|
-
object = itemClass.load(name)
|
34
|
-
object.destroy
|
35
|
-
puts "Deleted #{type_name} #{name}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def run
|
39
|
-
$stdout.sync = true
|
40
|
-
|
41
|
-
vmname = @name_args[0]
|
42
|
-
|
43
|
-
if vmname.nil?
|
44
|
-
show_usage
|
45
|
-
fatal_exit("You must specify a virtual machine name")
|
46
|
-
end
|
47
|
-
|
48
|
-
vim = get_vim_connection
|
49
|
-
|
50
|
-
baseFolder = find_folder(get_config(:folder));
|
51
|
-
|
52
|
-
vm =
|
53
|
-
fatal_exit("VM #{vmname} not found")
|
54
|
-
|
55
|
-
vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == "poweredOff"
|
56
|
-
vm.Destroy_Task
|
57
|
-
puts "Deleted virtual machine #{vmname}"
|
58
|
-
|
59
|
-
if config[:purge]
|
60
|
-
destroy_item(Chef::Node, vmname, "node")
|
61
|
-
destroy_item(Chef::ApiClient, vmname, "client")
|
62
|
-
else
|
63
|
-
puts "Corresponding node and client for the #{vmname} server were not deleted and remain registered with the Chef Server"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'chef/knife'
|
7
|
+
require 'chef/knife/base_vsphere_command'
|
8
|
+
require 'rbvmomi'
|
9
|
+
|
10
|
+
# These two are needed for the '--purge' deletion case
|
11
|
+
require 'chef/node'
|
12
|
+
require 'chef/api_client'
|
13
|
+
|
14
|
+
# Delete a virtual machine from vCenter
|
15
|
+
class Chef::Knife::VsphereVmDelete < Chef::Knife::BaseVsphereCommand
|
16
|
+
|
17
|
+
banner "knife vsphere vm delete VMNAME"
|
18
|
+
|
19
|
+
option :purge,
|
20
|
+
:short => "-P",
|
21
|
+
:long => "--purge",
|
22
|
+
:boolean => true,
|
23
|
+
:description => "Destroy corresponding node and client on the Chef Server, in addition to destroying the VM itself."
|
24
|
+
|
25
|
+
get_common_options
|
26
|
+
|
27
|
+
# Extracted from Chef::Knife.delete_object, because it has a
|
28
|
+
# confirmation step built in... By specifying the '--purge'
|
29
|
+
# flag (and also explicitly confirming the server destruction!)
|
30
|
+
# the user is already making their intent known. It is not
|
31
|
+
# necessary to make them confirm two more times.
|
32
|
+
def destroy_item(itemClass, name, type_name)
|
33
|
+
object = itemClass.load(name)
|
34
|
+
object.destroy
|
35
|
+
puts "Deleted #{type_name} #{name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
$stdout.sync = true
|
40
|
+
|
41
|
+
vmname = @name_args[0]
|
42
|
+
|
43
|
+
if vmname.nil?
|
44
|
+
show_usage
|
45
|
+
fatal_exit("You must specify a virtual machine name")
|
46
|
+
end
|
47
|
+
|
48
|
+
vim = get_vim_connection
|
49
|
+
|
50
|
+
baseFolder = find_folder(get_config(:folder));
|
51
|
+
|
52
|
+
vm = traverse_folders_for_vm(baseFolder, vmname) or
|
53
|
+
fatal_exit("VM #{vmname} not found")
|
54
|
+
|
55
|
+
vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == "poweredOff"
|
56
|
+
vm.Destroy_Task
|
57
|
+
puts "Deleted virtual machine #{vmname}"
|
58
|
+
|
59
|
+
if config[:purge]
|
60
|
+
destroy_item(Chef::Node, vmname, "node")
|
61
|
+
destroy_item(Chef::ApiClient, vmname, "client")
|
62
|
+
else
|
63
|
+
puts "Corresponding node and client for the #{vmname} server were not deleted and remain registered with the Chef Server"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -15,52 +15,47 @@ class Chef::Knife::VsphereVmList < Chef::Knife::BaseVsphereCommand
|
|
15
15
|
option :recursive,
|
16
16
|
:long => "--recursive",
|
17
17
|
:short => "-r",
|
18
|
-
:description => "Recurse
|
18
|
+
:description => "Recurse into sub-folders"
|
19
19
|
|
20
|
-
|
21
|
-
:long => "--only-folders",
|
22
|
-
:description => "Print only sub-folders"
|
20
|
+
def traverse_folders(folder, is_top = false, recurse = false)
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
vms = find_all_in_folder(folder, RbVmomi::VIM::VirtualMachine).select {|v| v.config && !v.config.template }
|
23
|
+
if vms.any?
|
24
|
+
puts "#{ui.color("Folder", :cyan)}: "+(folder.path[3..-1].map { |x| x[1] }.* '/')
|
25
|
+
vms.each { |v| print_vm(v) }
|
26
|
+
elsif is_top
|
27
|
+
puts "#{ui.color("No VMs", :cyan)}"
|
30
28
|
end
|
31
|
-
end
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
ui.color("on", :green)
|
39
|
-
when PsOff
|
40
|
-
ui.color("off", :red)
|
41
|
-
when PsSuspended
|
42
|
-
ui.color("suspended", :yellow)
|
43
|
-
end
|
44
|
-
puts "#{ui.color("VM Name:", :cyan)} #{vm.name}\t#{ui.color("IP:", :magenta)} #{vm.guest.ipAddress}\t#{ui.color("RAM:", :magenta)} #{vm.summary.config.memorySizeMB}\t#{ui.color("State:", :cyan)} #{state}"
|
30
|
+
if (recurse)
|
31
|
+
folders = find_all_in_folder(folder, RbVmomi::VIM::Folder)
|
32
|
+
folders.each do |child|
|
33
|
+
traverse_folders(child, false, recurse)
|
34
|
+
end
|
45
35
|
end
|
36
|
+
|
46
37
|
end
|
47
38
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
39
|
+
def print_vm(vm)
|
40
|
+
state = case vm.runtime.powerState
|
41
|
+
when PsOn
|
42
|
+
ui.color("on", :green)
|
43
|
+
when PsOff
|
44
|
+
ui.color("off", :red)
|
45
|
+
when PsSuspended
|
46
|
+
ui.color("suspended", :yellow)
|
47
|
+
end
|
48
|
+
puts "\t#{ui.color("VM Name:", :cyan)} #{vm.name}"
|
49
|
+
"\t\t#{ui.color("IP:", :magenta)} #{vm.guest.ipAddress}"
|
50
|
+
"\t\t#{ui.color("RAM:", :magenta)} #{vm.summary.config.memorySizeMB}"
|
51
|
+
"\t\t#{ui.color("State:", :cyan)} #{state}"
|
53
52
|
end
|
54
53
|
|
55
54
|
def run
|
56
|
-
$stdout.sync = true
|
57
55
|
vim = get_vim_connection
|
58
56
|
baseFolder = find_folder(get_config(:folder));
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
print_subfolders(baseFolder)
|
63
|
-
print_vms_in_folder(baseFolder)
|
64
|
-
end
|
57
|
+
recurse = get_config(:recursive)
|
58
|
+
is_top = true
|
59
|
+
traverse_folders(baseFolder, is_top, recurse)
|
65
60
|
end
|
66
61
|
end
|
@@ -106,6 +106,7 @@ class Chef::Knife::VsphereVmSnapshot < Chef::Knife::BaseVsphereCommand
|
|
106
106
|
tree.each do |node|
|
107
107
|
if node.name == name
|
108
108
|
snapshot = node.snapshot
|
109
|
+
break
|
109
110
|
elsif !node.childSnapshotList.empty?
|
110
111
|
snapshot = find_node(node.childSnapshotList, name)
|
111
112
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Author:: Malte Heidenreich
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'chef/knife'
|
5
|
+
require 'chef/knife/base_vsphere_command'
|
6
|
+
require 'rbvmomi'
|
7
|
+
require 'netaddr'
|
8
|
+
|
9
|
+
class Chef::Knife::VsphereVmToolsconfig < Chef::Knife::BaseVsphereCommand
|
10
|
+
banner "knife vsphere vm toolsconfig PROPERTY VALUE. See \"https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.ToolsConfigInfo.html\" for available properties and types."
|
11
|
+
|
12
|
+
option :empty,
|
13
|
+
:short => "-e",
|
14
|
+
:long => "--empty",
|
15
|
+
:description => "Allow empty string"
|
16
|
+
get_common_options
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
vmname = @name_args[0]
|
21
|
+
if vmname.nil?
|
22
|
+
show_usage
|
23
|
+
fatal_exit("You must specify a virtual machine name")
|
24
|
+
end
|
25
|
+
|
26
|
+
property = @name_args[1]
|
27
|
+
if property.nil?
|
28
|
+
show_usage
|
29
|
+
fatal_exit("You must specify a property to modify")
|
30
|
+
end
|
31
|
+
|
32
|
+
value = @name_args[2]
|
33
|
+
if value.nil? && !get_config(:empty)
|
34
|
+
show_usage
|
35
|
+
fatal_exit("You must specify a value")
|
36
|
+
end
|
37
|
+
|
38
|
+
value = "" if get_config(:empty)
|
39
|
+
|
40
|
+
vim = get_vim_connection
|
41
|
+
|
42
|
+
dc = get_datacenter
|
43
|
+
folder = find_folder(get_config(:folder)) || dc.vmFolder
|
44
|
+
|
45
|
+
vm = traverse_folders_for_vm(folder, vmname) or abort "VM #{vmname} not found"
|
46
|
+
|
47
|
+
vmConfigSpec = RbVmomi::VIM.VirtualMachineConfigSpec(:tools => RbVmomi::VIM.ToolsConfigInfo(property => value))
|
48
|
+
vm.ReconfigVM_Task(:spec => vmConfigSpec)
|
49
|
+
|
50
|
+
puts "property #{property} updated successfully"
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,53 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-vsphere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Ezra Pagel
|
8
|
-
autorequire:
|
9
|
+
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2014-
|
12
|
+
date: 2014-12-30 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
15
|
+
name: netaddr
|
14
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
15
18
|
requirements:
|
16
19
|
- - ~>
|
17
20
|
- !ruby/object:Gem::Version
|
18
21
|
version: 1.5.0
|
19
|
-
name: netaddr
|
20
|
-
prerelease: false
|
21
22
|
type: :runtime
|
23
|
+
prerelease: false
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
23
26
|
requirements:
|
24
27
|
- - ~>
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: 1.5.0
|
27
30
|
- !ruby/object:Gem::Dependency
|
31
|
+
name: chef
|
28
32
|
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
29
34
|
requirements:
|
30
|
-
- - '>='
|
35
|
+
- - ! '>='
|
31
36
|
- !ruby/object:Gem::Version
|
32
37
|
version: 0.10.0
|
33
|
-
name: chef
|
34
|
-
prerelease: false
|
35
38
|
type: :runtime
|
39
|
+
prerelease: false
|
36
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
37
42
|
requirements:
|
38
|
-
- - '>='
|
43
|
+
- - ! '>='
|
39
44
|
- !ruby/object:Gem::Version
|
40
45
|
version: 0.10.0
|
41
46
|
- !ruby/object:Gem::Dependency
|
47
|
+
name: rbvmomi
|
42
48
|
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
43
50
|
requirements:
|
44
51
|
- - ~>
|
45
52
|
- !ruby/object:Gem::Version
|
46
53
|
version: 1.5.1
|
47
|
-
name: rbvmomi
|
48
|
-
prerelease: false
|
49
54
|
type: :runtime
|
55
|
+
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
51
58
|
requirements:
|
52
59
|
- - ~>
|
53
60
|
- !ruby/object:Gem::Version
|
@@ -58,55 +65,59 @@ executables: []
|
|
58
65
|
extensions: []
|
59
66
|
extra_rdoc_files: []
|
60
67
|
files:
|
61
|
-
- lib/chef/knife/
|
62
|
-
- lib/chef/knife/
|
63
|
-
- lib/chef/knife/vshpere_vm_move.rb
|
64
|
-
- lib/chef/knife/vshpere_vm_net.rb
|
65
|
-
- lib/chef/knife/vsphere_customization_list.rb
|
68
|
+
- lib/chef/knife/vsphere_vm_query.rb
|
69
|
+
- lib/chef/knife/vsphere_cpu_ratio.rb
|
66
70
|
- lib/chef/knife/vsphere_datastorecluster_list.rb
|
67
71
|
- lib/chef/knife/vsphere_datastorecluster_maxfree.rb
|
68
|
-
- lib/chef/knife/
|
69
|
-
- lib/chef/knife/
|
72
|
+
- lib/chef/knife/vshpere_vm_migrate.rb
|
73
|
+
- lib/chef/knife/vsphere_vm_property_get.rb
|
74
|
+
- lib/chef/knife/vsphere_vm_state.rb
|
75
|
+
- lib/chef/knife/vshpere_vm_net.rb
|
76
|
+
- lib/chef/knife/vsphere_vm_execute.rb
|
70
77
|
- lib/chef/knife/vsphere_hosts_list.rb
|
71
|
-
- lib/chef/knife/
|
72
|
-
- lib/chef/knife/
|
73
|
-
- lib/chef/knife/
|
74
|
-
- lib/chef/knife/vsphere_vlan_list.rb
|
78
|
+
- lib/chef/knife/vsphere_vm_snapshot.rb
|
79
|
+
- lib/chef/knife/vsphere_vm_toolsconfig.rb
|
80
|
+
- lib/chef/knife/vsphere_vm_markastemplate.rb
|
75
81
|
- lib/chef/knife/vsphere_vm_clone.rb
|
76
|
-
- lib/chef/knife/
|
82
|
+
- lib/chef/knife/base_vsphere_command.rb
|
83
|
+
- lib/chef/knife/vsphere_vm_vmdk_add.rb
|
84
|
+
- lib/chef/knife/vsphere_customization_list.rb
|
85
|
+
- lib/chef/knife/vsphere_template_list.rb
|
77
86
|
- lib/chef/knife/vsphere_vm_delete.rb
|
78
|
-
- lib/chef/knife/
|
87
|
+
- lib/chef/knife/vsphere_datastore_list.rb
|
88
|
+
- lib/chef/knife/vsphere_vm_config.rb
|
89
|
+
- lib/chef/knife/vsphere_vlan_list.rb
|
90
|
+
- lib/chef/knife/vsphere_datastore_maxfree.rb
|
79
91
|
- lib/chef/knife/vsphere_vm_list.rb
|
80
|
-
- lib/chef/knife/
|
81
|
-
- lib/chef/knife/
|
92
|
+
- lib/chef/knife/vsphere_pool_query.rb
|
93
|
+
- lib/chef/knife/vshpere_vm_move.rb
|
94
|
+
- lib/chef/knife/vsphere_pool_list.rb
|
95
|
+
- lib/chef/knife/vsphere_folder_list.rb
|
82
96
|
- lib/chef/knife/vsphere_vm_property_set.rb
|
83
|
-
- lib/chef/knife/vsphere_vm_query.rb
|
84
|
-
- lib/chef/knife/vsphere_vm_snapshot.rb
|
85
|
-
- lib/chef/knife/vsphere_vm_state.rb
|
86
|
-
- lib/chef/knife/vsphere_vm_vmdk_add.rb
|
87
97
|
- lib/knife-vsphere/version.rb
|
88
98
|
homepage: http://github.com/ezrapagel/knife-vsphere
|
89
99
|
licenses:
|
90
100
|
- Apache
|
91
|
-
|
92
|
-
post_install_message:
|
101
|
+
post_install_message:
|
93
102
|
rdoc_options: []
|
94
103
|
require_paths:
|
95
104
|
- lib
|
96
105
|
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
97
107
|
requirements:
|
98
|
-
- - '>='
|
108
|
+
- - ! '>='
|
99
109
|
- !ruby/object:Gem::Version
|
100
110
|
version: '0'
|
101
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
102
113
|
requirements:
|
103
|
-
- - '>='
|
114
|
+
- - ! '>='
|
104
115
|
- !ruby/object:Gem::Version
|
105
116
|
version: '0'
|
106
117
|
requirements: []
|
107
|
-
rubyforge_project:
|
108
|
-
rubygems_version:
|
109
|
-
signing_key:
|
110
|
-
specification_version:
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.23
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
111
122
|
summary: vSphere Support for Knife
|
112
123
|
test_files: []
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: ca44c31e612974c4085c95cebd7ce9290782a76d
|
4
|
-
data.tar.gz: 03d2eae019a9a4d898e5a9bc94391aa640d28de2
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: f6d164cc5012c7eee2e351f7c241bc1fbd24f47b468fedb6f84e84ce9a7135c30481179eb46c9d2877a8c39dc4a667bc246ec9f8d2bc781677d61c3fa091a287
|
7
|
-
data.tar.gz: 03eeac904d852e5394f1a6d573fae6f664a202e72805a8a87a883d71f112d2677fc6f568b898237f1c2ab07bad3ee46f7a497521b9efd7519078a825633a6de2
|