knife-ovh-cloud 1.0.0.pre.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 560ff92c7398e4253a1da4f57c702411e2f34c9c
4
+ data.tar.gz: 40d70a9004086478882349b37a48a8060c2cf18e
5
+ SHA512:
6
+ metadata.gz: 22aa6e2eac62d522fb690447a2c50e95b673a400f2c0b22ca5ac85ce6e26b8c6ce32afa2adf1f449ab7130e11a7f0f73a357678d77c8d0efb31e42fb81ed586e
7
+ data.tar.gz: efc304d1a7af10de6f2b403e1a1ff02a251ba9bef54baab2c8f6e9ee63dd409750d90da52408134108b58b6b1f021529ea95870e623c2a501c27baf28db34e4c
@@ -0,0 +1,358 @@
1
+ #
2
+ # Author:: Ezra Pagel (<ezra@cpan.org>)
3
+ # Contributor:: Jesse Campbell (<hikeit@gmail.com>)
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+
7
+ require 'chef/knife'
8
+ require 'rbvmomi'
9
+
10
+ # Base class for vsphere knife commands
11
+ class Chef
12
+ class Knife
13
+ class BaseOvhCloudCommand < Knife
14
+
15
+ deps do
16
+ require 'chef/knife/bootstrap'
17
+ Chef::Knife::Bootstrap.load_deps
18
+ require 'fog'
19
+ require 'socket'
20
+ require 'net/ssh/multi'
21
+ require 'readline'
22
+ require 'chef/json_compat'
23
+ end
24
+
25
+ def self.get_common_options
26
+ unless defined? $default
27
+ $default = Hash.new
28
+ end
29
+
30
+ option :ovh_cloud_user,
31
+ :short => "-u USERNAME",
32
+ :long => "--vsuser USERNAME",
33
+ :description => "The username for vsphere"
34
+
35
+ option :ovh_cloud_pass,
36
+ :short => "-p PASSWORD",
37
+ :long => "--vspass PASSWORD",
38
+ :description => "The password for vsphere"
39
+
40
+ option :ovh_cloud_host,
41
+ :long => "--vshost HOST",
42
+ :description => "The vsphere host"
43
+
44
+ option :ovh_cloud_dc,
45
+ :short => "-D DATACENTER",
46
+ :long => "--vsdc DATACENTER",
47
+ :description => "The Datacenter for vsphere"
48
+
49
+ option :ovh_cloud_path,
50
+ :long => "--vspath SOAP_PATH",
51
+ :description => "The vsphere SOAP endpoint path"
52
+ $default[:ovh_cloud_path] = "/sdk"
53
+
54
+ option :ovh_cloud_port,
55
+ :long => "--vsport PORT",
56
+ :description => "The VI SDK port number to use"
57
+ $default[:ovh_cloud_port] = 443
58
+
59
+ option :vshere_nossl,
60
+ :long => "--vsnossl",
61
+ :description => "Disable SSL connectivity"
62
+
63
+ option :ovh_cloud_insecure,
64
+ :long => "--vsinsecure",
65
+ :description => "Disable SSL certificate verification"
66
+
67
+ option :folder,
68
+ :short => "-f FOLDER",
69
+ :long => "--folder FOLDER",
70
+ :description => "The folder to get VMs from"
71
+
72
+ option :proxy_host,
73
+ :long => '--proxyhost PROXY_HOSTNAME',
74
+ :description => 'Proxy hostname'
75
+
76
+ option :proxy_port,
77
+ :long => '--proxyport PROXY_PORT',
78
+ :description => 'Proxy port'
79
+
80
+ $default[:folder] = ''
81
+ end
82
+
83
+ def get_config(key)
84
+ key = key.to_sym
85
+ rval = config[key] || Chef::Config[:knife][key] || $default[key]
86
+ Chef::Log.debug("value for config item #{key}: #{rval}")
87
+ rval
88
+ end
89
+
90
+ def get_vim_connection
91
+
92
+ conn_opts = {
93
+ :host => get_config(:ovh_cloud_host),
94
+ :path => get_config(:ovh_cloud_path),
95
+ :port => get_config(:ovh_cloud_port),
96
+ :use_ssl => !get_config(:ovh_cloud_nossl),
97
+ :user => get_config(:ovh_cloud_user),
98
+ :password => get_config(:ovh_cloud_pass),
99
+ :insecure => get_config(:ovh_cloud_insecure),
100
+ :proxyHost => get_config(:proxy_host),
101
+ :proxyPort => get_config(:proxy_port)
102
+ }
103
+
104
+ # Grab the password from the command line
105
+ # if tt is not in the config file
106
+ if not conn_opts[:password]
107
+ conn_opts[:password] = get_password
108
+ end
109
+
110
+ # opt :debug, "Log SOAP messages", :short => 'd', :default => (ENV['RBVMOMI_DEBUG'] || false)
111
+
112
+ vim = RbVmomi::VIM.connect conn_opts
113
+ config[:vim] = vim
114
+ return vim
115
+ end
116
+
117
+ def get_password
118
+ @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
119
+ end
120
+
121
+ def get_vm(vmname)
122
+ vim = get_vim_connection
123
+ baseFolder = find_folder(get_config(:folder));
124
+ retval = traverse_folders_for_vm(baseFolder, vmname)
125
+ return retval
126
+ end
127
+
128
+ def traverse_folders_for_vm(folder, vmname)
129
+ children = folder.children.find_all
130
+ children.each do |child|
131
+ if child.class == RbVmomi::VIM::VirtualMachine && child.name == vmname
132
+ return child
133
+ elsif child.class == RbVmomi::VIM::Folder
134
+ vm = traverse_folders_for_vm(child, vmname)
135
+ if vm then return vm end
136
+ end
137
+ end
138
+ return false
139
+ end
140
+
141
+ def get_vms(vmname)
142
+ vim = get_vim_connection
143
+ baseFolder = find_folder(get_config(:folder));
144
+ retval = traverse_folders_for_vms(baseFolder, vmname)
145
+ return retval
146
+ end
147
+
148
+ def traverse_folders_for_vms(folder, vmname)
149
+ retval = []
150
+ children = folder.children.find_all
151
+ children.each do |child|
152
+ if child.class == RbVmomi::VIM::VirtualMachine && child.name == vmname
153
+ retval << child
154
+ elsif child.class == RbVmomi::VIM::Folder
155
+ retval.concat(traverse_folders_for_vms(child, vmname))
156
+ end
157
+ end
158
+ return retval
159
+ end
160
+
161
+ def traverse_folders_for_dc(folder, dcname)
162
+ children = folder.children.find_all
163
+ children.each do |child|
164
+ if child.class == RbVmomi::VIM::Datacenter && child.name == dcname
165
+ return child
166
+ elsif child.class == RbVmomi::VIM::Folder
167
+ dc = traverse_folders_for_dc(child, dcname)
168
+ if dc then return dc end
169
+ end
170
+ end
171
+ return false
172
+ end
173
+
174
+ def get_datacenter
175
+ dcname = get_config(:ovh_cloud_dc)
176
+ traverse_folders_for_dc(config[:vim].rootFolder, dcname) or abort "datacenter not found"
177
+ end
178
+
179
+ def find_folder(folderName)
180
+ dc = get_datacenter
181
+ baseEntity = dc.vmFolder
182
+ entityArray = folderName.split('/')
183
+ entityArray.each do |entityArrItem|
184
+ if entityArrItem != ''
185
+ baseEntity = baseEntity.childEntity.grep(RbVmomi::VIM::Folder).find { |f| f.name == entityArrItem } or
186
+ abort "no such folder #{folderName} while looking for #{entityArrItem}"
187
+ end
188
+ end
189
+ baseEntity
190
+ end
191
+
192
+ def find_network(networkName)
193
+ dc = get_datacenter
194
+ baseEntity = dc.network
195
+ baseEntity.find { |f| f.name == networkName } or abort "no such network #{networkName}"
196
+ end
197
+
198
+ def find_pool(poolName)
199
+ dc = get_datacenter
200
+ baseEntity = dc.hostFolder
201
+ entityArray = poolName.split('/')
202
+ entityArray.each do |entityArrItem|
203
+ if entityArrItem != ''
204
+ if baseEntity.is_a? RbVmomi::VIM::Folder
205
+ baseEntity = baseEntity.childEntity.find { |f| f.name == entityArrItem } or
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
208
+ baseEntity = baseEntity.resourcePool.resourcePool.find { |f| f.name == entityArrItem } or
209
+ abort "no such pool #{poolName} while looking for #{entityArrItem}"
210
+ elsif baseEntity.is_a? RbVmomi::VIM::ResourcePool
211
+ baseEntity = baseEntity.resourcePool.find { |f| f.name == entityArrItem } or
212
+ abort "no such pool #{poolName} while looking for #{entityArrItem}"
213
+ else
214
+ abort "Unexpected Object type encountered #{baseEntity.type} while finding resourcePool"
215
+ end
216
+ end
217
+ end
218
+
219
+ baseEntity = baseEntity.resourcePool if not baseEntity.is_a?(RbVmomi::VIM::ResourcePool) and baseEntity.respond_to?(:resourcePool)
220
+ baseEntity
221
+ end
222
+
223
+ def choose_datastore(dstores, size)
224
+ vmdk_size_kb = size.to_i * 1024 * 1024
225
+ vmdk_size_B = size.to_i * 1024 * 1024 * 1024
226
+
227
+ candidates = []
228
+ dstores.each do |store|
229
+ avail = number_to_human_size(store.summary[:freeSpace])
230
+ cap = number_to_human_size(store.summary[:capacity])
231
+ puts "#{ui.color("Datastore", :cyan)}: #{store.name} (#{avail}(#{store.summary[:freeSpace]}) / #{cap})"
232
+
233
+ # vm's can span multiple datastores, so instead of grabbing the first one
234
+ # let's find the first datastore with the available space on a LUN the vm
235
+ # is already using, or use a specified LUN (if given)
236
+
237
+
238
+ if (store.summary[:freeSpace] - vmdk_size_B) > 0
239
+ # also let's not use more than 90% of total space to save room for snapshots.
240
+ cap_remains = 100 * ((store.summary[:freeSpace].to_f - vmdk_size_B.to_f) / store.summary[:capacity].to_f)
241
+ if (cap_remains.to_i > 10)
242
+ candidates.push(store)
243
+ end
244
+ end
245
+ end
246
+ if candidates.length > 0
247
+ vmdk_datastore = candidates[0]
248
+ else
249
+ puts "Insufficient space on all LUNs designated or assigned to the virtual machine. Please specify a new target."
250
+ vmdk_datastore = nil
251
+ end
252
+ return vmdk_datastore
253
+ end
254
+
255
+
256
+ def find_datastores_regex(regex)
257
+ stores = Array.new()
258
+ puts "Looking for all datastores that match /#{regex}/"
259
+ dc = get_datacenter
260
+ baseEntity = dc.datastore
261
+ baseEntity.each do |ds|
262
+ if ds.name.match /#{regex}/
263
+ stores.push ds
264
+ end
265
+ end
266
+ return stores
267
+ end
268
+
269
+ def find_datastore(dsName)
270
+ dc = get_datacenter
271
+ baseEntity = dc.datastore
272
+ baseEntity.find { |f| f.info.name == dsName } or abort "no such datastore #{dsName}"
273
+ end
274
+
275
+ def find_datastorecluster(dsName)
276
+ dc = get_datacenter
277
+ baseEntity = dc.datastoreFolder.childEntity
278
+ baseEntity.find { |f| f.name == dsName and f.instance_of?(RbVmomi::VIM::StoragePod) } or abort "no such datastorecluster #{dsName}"
279
+ end
280
+
281
+ def find_device(vm, deviceName)
282
+ vm.config.hardware.device.each do |device|
283
+ return device if device.deviceInfo.label == deviceName
284
+ end
285
+ nil
286
+ end
287
+
288
+ def find_all_in_folder(folder, type)
289
+ if folder.instance_of?(RbVmomi::VIM::ClusterComputeResource) or folder.instance_of?(RbVmomi::VIM::ComputeResource)
290
+ folder = folder.resourcePool
291
+ end
292
+ if folder.instance_of?(RbVmomi::VIM::ResourcePool)
293
+ folder.resourcePool.grep(type)
294
+ elsif folder.instance_of?(RbVmomi::VIM::Folder)
295
+ folder.childEntity.grep(type)
296
+ else
297
+ puts "Unknown type #{folder.class}, not enumerating"
298
+ nil
299
+ end
300
+ end
301
+
302
+ def get_path_to_object(object)
303
+ if object.is_a?(RbVmomi::VIM:: ManagedEntity)
304
+ if object.parent.is_a?(RbVmomi::VIM:: ManagedEntity)
305
+ return get_path_to_object(object.parent) + "/" + object.parent.name
306
+ else
307
+ return ""
308
+ end
309
+ else
310
+ puts "Unknown type #{object.class}, not enumerating"
311
+ nil
312
+ end
313
+ end
314
+
315
+ def find_in_folder(folder, type, name)
316
+ folder.childEntity.grep(type).find { |o| o.name == name }
317
+ end
318
+
319
+ def fatal_exit(msg)
320
+ ui.fatal(msg)
321
+ exit 1
322
+ end
323
+
324
+ def tcp_test_port_vm(vm, port)
325
+ ip = vm.guest.ipAddress
326
+ if ip.nil?
327
+ sleep 2
328
+ return false
329
+ end
330
+ tcp_test_port(ip, port)
331
+ end
332
+
333
+ def tcp_test_port(hostname, port)
334
+ tcp_socket = TCPSocket.new(hostname, port)
335
+ readable = IO.select([tcp_socket], nil, nil, 5)
336
+ if readable
337
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}") if port == 22
338
+ true
339
+ else
340
+ false
341
+ end
342
+ rescue Errno::ETIMEDOUT
343
+ false
344
+ rescue Errno::EPERM
345
+ false
346
+ rescue Errno::ECONNREFUSED
347
+ sleep 2
348
+ false
349
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
350
+ sleep 2
351
+ false
352
+ ensure
353
+ tcp_socket && tcp_socket.close
354
+ end
355
+
356
+ end
357
+ end
358
+ end
@@ -0,0 +1,29 @@
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_ovh_cloud_command'
7
+
8
+ # Lists all customization specifications in the configured datacenter
9
+ class Chef::Knife::OvhCloudCustomizationList < Chef::Knife::BaseOvhCloudCommand
10
+
11
+ banner "knife ovh cloud customization list"
12
+
13
+ get_common_options
14
+
15
+ def run
16
+
17
+ $stdout.sync = true
18
+
19
+ vim = get_vim_connection
20
+
21
+ csm = vim.serviceContent.customizationSpecManager
22
+ csm.info.each do |c|
23
+ puts "#{ui.color("Customization Name", :cyan)}: #{c.name}"
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+
@@ -0,0 +1,58 @@
1
+ # Copyright (C) 2012, SCM Ventures AB
2
+ # Author: Ian Delahorne <ian@scmventures.se>
3
+ #
4
+ # Permission to use, copy, modify, and/or distribute this software for
5
+ # any purpose with or without fee is hereby granted, provided that the
6
+ # above copyright notice and this permission notice appear in all
7
+ # copies.
8
+ #
9
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10
+ # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12
+ # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13
+ # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
14
+ # OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15
+ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16
+ # PERFORMANCE OF THIS SOFTWARE
17
+
18
+ require 'chef/knife'
19
+ require 'chef/knife/base_ovh_cloud_command'
20
+
21
+ def number_to_human_size(number)
22
+ number = number.to_f
23
+ storage_units_fmt = ["byte", "kB", "MB", "GB", "TB"]
24
+ base = 1024
25
+ if number.to_i < base
26
+ unit = storage_units_fmt[0]
27
+ else
28
+ max_exp = storage_units_fmt.size - 1
29
+ exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
30
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
31
+ number /= base ** exponent
32
+ unit = storage_units_fmt[exponent]
33
+ end
34
+
35
+ return sprintf("%0.2f %s", number, unit)
36
+ end
37
+
38
+
39
+ # Lists all known data stores in datacenter with sizes
40
+ class Chef::Knife::OvhCloudDatastoreList < Chef::Knife::BaseOvhCloudCommand
41
+
42
+ banner "knife ovh cloud datastore list"
43
+
44
+ get_common_options
45
+
46
+ def run
47
+ $stdout.sync = true
48
+
49
+ vim = get_vim_connection
50
+ dc = get_datacenter
51
+ dc.datastore.each do |store|
52
+ avail = number_to_human_size(store.summary[:freeSpace])
53
+ cap = number_to_human_size(store.summary[:capacity])
54
+ puts "#{ui.color("Datastore", :cyan)}: #{store.name} (#{avail} / #{cap})"
55
+ end
56
+ end
57
+ end
58
+