vmonkey 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c80f0a833b6907731b9809f68d81af87f3886cd0
4
+ data.tar.gz: 40ca2aa1adb647c8fc244834e46a6411b26775ec
5
+ SHA512:
6
+ metadata.gz: 0c785b37bee04305d7151a99d707364895ef5021ebfd405830d8c9d7eab47e54aa96997502274887e91842399b35a48d8ba2b2ceb3e988df790c4f77554d630f
7
+ data.tar.gz: f48409e63777562785d8c9c14c8f4b756bf5fb0d8fef9e478d5b181a1255c233910851b5e7dec29b620dc94b42d28944fe68a50c0e16f9edb9a46799a35b98cd
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .vmonkey
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vmonkey.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # VMonkey
2
+
3
+ VMonkey is a cheeky little feller who wants so very badly to make interacintg with vSphere more enjoyable. Let VMonkey fetch your VMs, clone your templates, set your properties, and more. VMonkey tinkers around in the uglier parts of the vSphere API so you don't have to. Enjoy!
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'vmonkey'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install vmonkey
18
+
19
+ ## Usage
20
+
21
+ ### $HOME/.vmonkey (optional)
22
+
23
+ ```yml
24
+ host: vsphere.host.name
25
+ user: vsphere_user
26
+ password: monkey!
27
+ insecure: false #or true
28
+ ssl: true #or false
29
+ datacenter: your_dc_name
30
+ cluster: your_cluster_or_compute_resource_name
31
+ ```
32
+
33
+ ### initial connection
34
+
35
+ ```ruby
36
+ # use connect opts from $HOME/.vmonkey
37
+ monkey = VMonkey.connect
38
+ ```
39
+
40
+ or
41
+
42
+ ```ruby
43
+ # use your own connect opts
44
+ monkey = VMonkey.connect opts_hash
45
+ ```
46
+
47
+ ### what is VMonkey
48
+
49
+ VMonkey.connect simply returns an instance of RbVmomi::VIM with added utility methods. The utility methods operate within the datacenter and cluster specified by the connection options.
50
+
51
+
52
+ ### VMonkey finds stuff
53
+
54
+ ```ruby
55
+ monkey.folder '/path/to/my/folder' # returns a Folder or nil
56
+ monkey.folder! '/path/to/my/folder' # returns a Folder or raises an error
57
+
58
+ monkey.vm '/path/to/my/vm' # returns a VirtualMachine or nil
59
+ monkey.vm! '/path/to/my/vm' # returns a VirtualMachine or raises an error
60
+
61
+ monkey.vapp '/path/to/my/vapp' # returns a VirtualApp or nil
62
+ monkey.vapp! '/path/to/my/vapp' # returns a VirtualApp or raises an error
63
+ ```
64
+
65
+ ### VMonkey puts his glitter on VirtualMachine instances
66
+
67
+ ```ruby
68
+ vm.annotation
69
+ vm.annotation = 'VMonkey is hot'
70
+
71
+ vm.move_to '/path/to/some_folder/clone_name' # moves the VM or raises if the destination exists
72
+ vm.move_to! '/path/to/some_folder/clone_name' # moves the VM, overwrting the detinsation VM if necessary
73
+
74
+ vm.clone_to '/path/to/some_folder/clone_name' # clones the VM to a Folder
75
+ vm.clone_to '/path/to/some_vapp/clone_name' # clones the VM to a VirtualApp
76
+
77
+ vm.property :foo # returns the value of a vApp property, or nil
78
+ vm.property! :foo # returns the value of a vApp property, or raises an error
79
+ vm.property :foo, 'bar' # set the value of a vApp property
80
+
81
+ vm.port_ready? 22 # true if the VM is listening a TCP port
82
+ vm.wait_for_port 22 # blocks until the port_ready? is true
83
+
84
+ vm.start # power on if needed
85
+ vm.stop # guest shutdown and power off
86
+ vm.destroy # deletes the VM from the inventory, no need to power off first
87
+ ```
88
+
89
+ ### VMonkey gives some love to other types, too
90
+
91
+ ```ruby
92
+ datacenter.find_pool # returns the default datacenter ResourcePool
93
+ datacenter.find_pool '/path/to/cluster' # returns the cluster's default ResourcePool
94
+ datacenter.find_pool '/path/to/vapp' # returns the vApp, since it's already a ResourcePool
95
+
96
+ vapp.find_vm 'vm_name' # returns a VM of the given name, or nil
97
+ ```
98
+
99
+ ### VMonkey earns his keep
100
+
101
+ Before:
102
+
103
+ ```ruby
104
+ config = [annotation: 'This makes VMonkey sad.']
105
+ spec = RbVmomi::VIM.VirtualMachineConfigSpec(config)
106
+ vm.ReconfigVM_Task(spec: spec).wait_for_completion
107
+ ```
108
+
109
+ After:
110
+
111
+ ```ruby
112
+ vm.annotation = 'VMonkey is so easy!'
113
+ ```
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork it ( https://github.com/[my-github-username]/vmonkey )
118
+ 2. Setup your test environment (`bundle exec rake spec` and follow the test setup instructions)
119
+ 3. Hack
120
+ 4. Pull request ( be sure to include updated specs )
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ ENV['VMONKEY_YML'] ||= File.expand_path(File.join(File.dirname(__FILE__), 'spec', '.vmonkey'))
5
+ RSpec::Core::RakeTask.new(:spec)
data/lib/vmonkey.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rbvmomi'
2
+ require_relative 'vmonkey/version'
3
+ require_relative 'vmonkey/vim/vim'
4
+
5
+ monkey_vim = File.join(File.dirname(__FILE__), 'vmonkey', 'vim')
6
+ RbVmomi::VIM.add_extension_dir monkey_vim
7
+
8
+ module VMonkey
9
+ def self.connect(opts = nil)
10
+ RbVmomi::VIM.monkey_connect(opts)
11
+ end
12
+ end
13
+
14
+ class String
15
+ def parent
16
+ p = self.split('/')[0...-1].join('/')
17
+ p == '' ? '/' : p
18
+ end
19
+
20
+ def basename
21
+ p = self.split('/').last
22
+ end
23
+ end
24
+
25
+ class RbVmomi::BasicTypes::ManagedObject
26
+ def monkey
27
+ _connection
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Vmonkey
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,34 @@
1
+ class RbVmomi::VIM::Datacenter
2
+
3
+ ## modified from knife-vsphere / base_vsphere_command.rb
4
+ def find_pool(pool_path = '/')
5
+ parent = self.hostFolder
6
+
7
+ pool_path.split('/').each do |path_element|
8
+ next if path_element == ''
9
+
10
+ case parent
11
+ when RbVmomi::VIM::Folder
12
+ chilln = parent.childEntity
13
+ when RbVmomi::VIM::ClusterComputeResource, RbVmomi::VIM::ComputeResource
14
+ chilln = parent.resourcePool.resourcePool
15
+ when RbVmomi::VIM::ResourcePool
16
+ chilln = parent.resourcePool
17
+ else
18
+ parent = nil
19
+ break
20
+ end
21
+
22
+ parent = chilln.find { |f| f.name == path_element }
23
+ end
24
+
25
+ unless parent.is_a?(RbVmomi::VIM::ResourcePool)
26
+ if parent.respond_to?(:resourcePool)
27
+ parent = parent.resourcePool
28
+ end
29
+ end
30
+
31
+ parent
32
+ end
33
+
34
+ end
@@ -0,0 +1,9 @@
1
+ class RbVmomi::VIM::Folder
2
+ def vm_pool
3
+ monkey.cluster.resourcePool
4
+ end
5
+
6
+ def vm_folder
7
+ self
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class RbVmomi::VIM::VirtualApp
2
+ def vm_pool
3
+ self
4
+ end
5
+
6
+ def vm_folder
7
+ parentVApp.nil? ? parentFolder : self.parentVApp.vm_folder
8
+ end
9
+
10
+ def find_vm(vm_name)
11
+ vm.find { |vm| vm.name == vm_name }
12
+ end
13
+ end
14
+
15
+
16
+
@@ -0,0 +1,178 @@
1
+ class RbVmomi::VIM::VirtualMachine
2
+
3
+ def destroy
4
+ self.PowerOffVM_Task.wait_for_completion unless runtime.powerState == 'poweredOff'
5
+ self.Destroy_Task.wait_for_completion
6
+ end
7
+
8
+ def clone_to(path, opts = {})
9
+ dest = monkey.get(path.parent)
10
+ unless dest.is_a? RbVmomi::VIM::Folder or dest.is_a? RbVmomi::VIM::VirtualApp
11
+ raise "Cannot clone_to [#{dest.pretty_path}] - destination must specify a Folder or VirtualApp"
12
+ end
13
+
14
+ params = _clone_params(path.basename, dest, opts)
15
+
16
+ self.CloneVM_Task(params).wait_for_completion
17
+ end
18
+
19
+ def annotation
20
+ config.annotation
21
+ end
22
+
23
+ def annotation=(value)
24
+ ReconfigVM_Task(spec: RbVmomi::VIM.VirtualMachineConfigSpec(annotation: value)).wait_for_completion
25
+ end
26
+
27
+ def property(*args)
28
+ case args.size
29
+ when 1
30
+ read_property(*args)
31
+ when 2
32
+ set_property(*args)
33
+ else
34
+ raise ArgumentError.new("wrong number of arguments (#{args.size} for 1 or 2)")
35
+ end
36
+ end
37
+
38
+ def property!(name)
39
+ read_property(name) || raise("vApp Property not found. [#{name}]")
40
+ end
41
+
42
+ def move_to(path)
43
+ monkey.vm(path) && raise("VirtualMachine already exists. [#{path}]")
44
+ rename = name != path.basename
45
+
46
+ to_folder = monkey.folder! path.parent
47
+ reparent = parent != to_folder
48
+
49
+ if reparent
50
+ Rename_Task(newName: "#{path.basename}-tmp").wait_for_completion if rename
51
+ to_folder.MoveIntoFolder_Task(list: [self]).wait_for_completion
52
+ Rename_Task(newName: path.basename).wait_for_completion if rename
53
+ else
54
+ Rename_Task(newName: path.basename).wait_for_completion
55
+ end
56
+ end
57
+
58
+ unless self.method_defined? :guest_ip
59
+ ## backported from rbvmomi 1.8 for rbvmomi 1.5 support
60
+ def guest_ip
61
+ g = self.guest
62
+ if g.ipAddress && (g.toolsStatus == "toolsOk" || g.toolsStatus == "toolsOld")
63
+ g.ipAddress
64
+ else
65
+ nil
66
+ end
67
+ end
68
+ end
69
+
70
+ def move_to!(path)
71
+ dest_vm = monkey.vm(path)
72
+ dest_vm.destroy if dest_vm
73
+
74
+ move_to(path)
75
+ end
76
+
77
+ def port_ready?(port)
78
+ ip = guest_ip or return false
79
+ tcp_socket = TCPSocket.new(ip, port)
80
+ readable = IO.select([tcp_socket], nil, nil, 5)
81
+ if readable
82
+ true
83
+ else
84
+ false
85
+ end
86
+ rescue Errno::ETIMEDOUT, Errno::EPERM, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH
87
+ false
88
+ ensure
89
+ tcp_socket && tcp_socket.close
90
+ end
91
+
92
+ def wait_for_port(port)
93
+ sleep 2 until port_ready?(port)
94
+ end
95
+
96
+ def stop
97
+ return if runtime.powerState == 'poweredOff'
98
+ ShutdownGuest
99
+ sleep 2 until runtime.powerState == 'poweredOff'
100
+ rescue
101
+ PowerOffVM_Task().wait_for_completion unless runtime.powerState == 'poweredOff'
102
+ end
103
+
104
+ def start
105
+ PowerOnVM_Task().wait_for_completion unless runtime.powerState == 'poweredOn'
106
+ end
107
+
108
+ def find_property(name)
109
+ config.vAppConfig.property.find { |p| p.props[:id] == name.to_s }
110
+ end
111
+
112
+ def read_property(name)
113
+ p = find_property(name)
114
+ p.nil? ? nil : p[:value]
115
+ end
116
+
117
+ def set_property(name, value)
118
+ if config.vAppConfig && config.vAppConfig.property
119
+ existing_property = find_property(name)
120
+ end
121
+
122
+ if existing_property
123
+ operation = 'edit'
124
+ property_key = existing_property.props[:key]
125
+ else
126
+ operation = 'add'
127
+ property_key = name.object_id
128
+ end
129
+
130
+ vm_config_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
131
+ vAppConfig: RbVmomi::VIM.VmConfigSpec(
132
+ property: [
133
+ RbVmomi::VIM.VAppPropertySpec(
134
+ operation: operation,
135
+ info: {
136
+ key: property_key,
137
+ id: name.to_s,
138
+ type: 'string',
139
+ userConfigurable: true,
140
+ value: value
141
+ })]))
142
+
143
+ if config.vAppConfig.nil? || config.vAppConfig.ovfEnvironmentTransport.empty?
144
+ vm_config_spec[:vAppConfig][:ovfEnvironmentTransport] = ['com.vmware.guestInfo']
145
+ end
146
+
147
+ ReconfigVM_Task( spec: vm_config_spec ).wait_for_completion
148
+ end
149
+
150
+ def _clone_params(vm_name, dest, opts)
151
+ {
152
+ name: vm_name,
153
+ folder: dest.vm_folder,
154
+ spec: _clone_spec(dest, opts)
155
+ }
156
+ end
157
+
158
+ def _clone_spec(dest, opts)
159
+ opts[:config] ||= {}
160
+
161
+ clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
162
+ location: RbVmomi::VIM.VirtualMachineRelocateSpec(pool: dest.vm_pool),
163
+ powerOn: false,
164
+ template: false
165
+ )
166
+
167
+ clone_spec.config = RbVmomi::VIM.VirtualMachineConfigSpec(deviceChange: Array.new)
168
+
169
+ clone_spec.customization = monkey.customization_spec(opts[:customization_spec])
170
+ clone_spec.config.annotation = opts[:config][:annotation]
171
+ clone_spec.config.numCPUs = opts[:config][:num_cpus]
172
+ clone_spec.config.memoryMB = opts[:config][:memory_mb]
173
+
174
+ clone_spec
175
+ end
176
+
177
+ end
178
+
@@ -0,0 +1,63 @@
1
+ require 'yaml'
2
+
3
+ module RbVmomi
4
+ class VIM
5
+
6
+ attr_accessor :dc
7
+ attr_accessor :cluster
8
+ attr_accessor :opts
9
+
10
+ def self.monkey_connect(opts = nil)
11
+ opts ||= self.read_yml_opts
12
+ vim = self.connect(opts)
13
+ vim.opts = opts
14
+ vim.dc = vim.serviceInstance.find_datacenter(vim.opts[:datacenter]) or raise "Datacenter not found [#{vim.opts[:datacenter]}]"
15
+ vim.cluster = vim.dc.find_compute_resource(vim.opts[:cluster]) or raise "Cluster not found [#{vim.opts[:cluster]}]"
16
+
17
+ vim
18
+ end
19
+
20
+ def folder(path)
21
+ dc.vmFolder.traverse path, RbVmomi::VIM::Folder
22
+ end
23
+
24
+ def folder!(path)
25
+ folder(path) || raise("Folder not found. [#{path}]")
26
+ end
27
+
28
+ def vm(path)
29
+ dc.vmFolder.traverse path, RbVmomi::VIM::VirtualMachine
30
+ end
31
+
32
+ def vm!(path)
33
+ vm(path) || raise("VirtualMachine not found. [#{path}]")
34
+ end
35
+
36
+ def vapp(path)
37
+ dc.vmFolder.traverse path, RbVmomi::VIM::VirtualApp
38
+ end
39
+
40
+ def vapp!(path)
41
+ vapp(path) || raise("VirtualApp not found. [#{path}]")
42
+ end
43
+
44
+ def get(path)
45
+ dc.vmFolder.traverse path
46
+ end
47
+
48
+ def customization_spec(spec_name)
49
+ return nil if spec_name.nil?
50
+ serviceContent.customizationSpecManager.GetCustomizationSpec(name: spec_name).spec
51
+ rescue
52
+ nil
53
+ end
54
+
55
+ private
56
+
57
+ def self.read_yml_opts
58
+ yml_path = File.expand_path( ENV['VMONKEY_YML'] || File.join(ENV['HOME'], '.vmonkey') )
59
+ YAML::load_file(yml_path).inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,31 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe RbVmomi::VIM::Datacenter do
4
+ before :all do
5
+ @monkey = VMonkey.connect
6
+ @cluster_path = "/#{@monkey.opts[:cluster]}"
7
+ end
8
+
9
+ describe '#find_pool' do
10
+ context 'with no params' do
11
+ subject { @pool ||= VMonkey.connect.dc.find_pool }
12
+
13
+ it { should_not be_nil }
14
+ its(:name) { should == 'host' }
15
+ end
16
+
17
+ context 'with cluster path' do
18
+ subject { @pool ||= VMonkey.connect.dc.find_pool @cluster_path }
19
+
20
+ it { should_not be_nil }
21
+ its(:name) { should == 'Resources' }
22
+ end
23
+
24
+ context 'with vApp path' do
25
+ subject { @pool ||= VMonkey.connect.dc.find_pool VM_SPEC_OPTS[:vapp_pool_path] }
26
+
27
+ it { should_not be_nil }
28
+ its(:name) { should == VM_SPEC_OPTS[:vapp_pool_path].split('/').last }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe RbVmomi::VIM::Folder do
4
+ before :all do
5
+ @monkey ||= VMonkey.connect
6
+ @folder ||= @monkey.folder VM_SPEC_OPTS[:working_folder]
7
+ end
8
+
9
+ describe '#vm_pool' do
10
+ subject { @folder.vm_pool }
11
+ it { should_not be_nil }
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../lib/vmonkey'
2
+
3
+ instructions = "
4
+ For the integration tests to run, you need:
5
+ - On the machine running the specs:
6
+ + #{ENV['VMONKEY_YML']} (see below)
7
+
8
+ - On your vSphere system
9
+ + A VM or Template from which VMs will be cloned (:template_path)
10
+ (Clones of this VM must listen on TCP port 22 on bootup)
11
+ + A working Folder into which VMs will be cloned (:working_folder)
12
+ + A working Folder into which VM will be moved (:working_folder2)
13
+ + An empty vApp into which cloned VMs will be placed (:vapp_path)
14
+ + A Customization Spec which will be applied to cloned VMs (:customization_spec)
15
+
16
+ Place the following in #{ENV['VMONKEY_YML']}
17
+ host: host_name_or_ip_address
18
+ user: user_name
19
+ password: password
20
+ insecure: true
21
+ ssl: true
22
+ datacenter: datacenter_name
23
+ cluster: cluster_name
24
+ spec:
25
+ :template_path: /path/to/a/vm_or_template
26
+ :working_folder: /path/to/a/folder
27
+ :working_folder2: /path/to/another/folder
28
+ :vapp_pool_path: /cluster_name/monkey_vapp
29
+ :vapp_path: /path/to/a/vapp
30
+ :customization_spec: name-of-a-cust-spec
31
+ "
32
+
33
+ raise instructions unless File.exists? ENV['VMONKEY_YML']
34
+
35
+ monkey = VMonkey.connect
36
+ VM_SPEC_OPTS = monkey.opts[:spec]
37
+ raise instructions unless monkey.folder VM_SPEC_OPTS[:working_folder]
38
+ raise instructions unless monkey.folder VM_SPEC_OPTS[:working_folder2]
39
+ raise instructions unless monkey.vm VM_SPEC_OPTS[:template_path]
40
+ raise instructions unless monkey.vapp VM_SPEC_OPTS[:vapp_path]
41
+ raise instructions unless monkey.dc.find_pool VM_SPEC_OPTS[:vapp_pool_path]
42
+ raise instructions unless monkey.customization_spec VM_SPEC_OPTS[:customization_spec]
43
+
44
+ RSpec.configure do |config|
45
+ config.color_enabled = true
46
+ config.tty = true
47
+ config.formatter = :documentation
48
+ end
data/spec/vapp_spec.rb ADDED
@@ -0,0 +1,14 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe RbVmomi::VIM::Folder do
4
+
5
+ before :all do
6
+ @monkey ||= VMonkey.connect
7
+ @vapp ||= @monkey.vapp VM_SPEC_OPTS[:vapp_path]
8
+ end
9
+
10
+ describe '#vm_pool' do
11
+ subject { @vapp.vm_pool }
12
+ it { should_not be_nil }
13
+ end
14
+ end
data/spec/vim_spec.rb ADDED
@@ -0,0 +1,77 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe RbVmomi::VIM do
4
+ before :all do
5
+ @monkey ||= VMonkey.connect
6
+ end
7
+
8
+ describe '#folder' do
9
+ subject { @folder ||= @monkey.folder VM_SPEC_OPTS[:working_folder] }
10
+
11
+ it { should_not be_nil }
12
+ its(:name) { should == VM_SPEC_OPTS[:working_folder].split('/').last }
13
+ end
14
+
15
+ describe '#folder!' do
16
+ it 'should raise a RuntimeError given a path to a non-existent folder' do
17
+ expect { @monkey.folder! '/xyzzy' }.to raise_error RuntimeError
18
+ end
19
+ end
20
+
21
+ describe '#vm' do
22
+ subject { @vm ||= @monkey.vm VM_SPEC_OPTS[:template_path] }
23
+
24
+ it { should_not be_nil }
25
+ its(:name) { should == VM_SPEC_OPTS[:template_path].split('/').last }
26
+ end
27
+
28
+ describe '#vm!' do
29
+ it 'should raise a RuntimeError given a path to a non-existent vm' do
30
+ expect { @monkey.vm! '/xyzzy' }.to raise_error RuntimeError
31
+ end
32
+ end
33
+
34
+ describe '#vapp' do
35
+ subject { @vapp ||= @monkey.vapp VM_SPEC_OPTS[:vapp_path] }
36
+
37
+ it { should_not be_nil }
38
+ its(:name) { should == VM_SPEC_OPTS[:vapp_path].split('/').last }
39
+ end
40
+
41
+ describe '#vapp!' do
42
+ it 'should raise a RuntimeError given a path to a non-existent vapp' do
43
+ expect { @monkey.vapp! '/xyzzy' }.to raise_error RuntimeError
44
+ end
45
+ end
46
+
47
+ describe '#get' do
48
+ context 'given a path to a real object' do
49
+ subject { @get ||= @monkey.get VM_SPEC_OPTS[:working_folder] }
50
+
51
+ it { should_not be_nil }
52
+ its(:name) { should == VM_SPEC_OPTS[:working_folder].split('/').last }
53
+ end
54
+
55
+ context 'given a path to a non-existent object' do
56
+ subject { @get_nil ||= @monkey.get '/xyzzy' }
57
+
58
+ it { should be_nil }
59
+ end
60
+ end
61
+
62
+ describe '#customization_spec' do
63
+ context 'given an existing customization spec' do
64
+ subject { @cspec ||= @monkey.customization_spec VM_SPEC_OPTS[:customization_spec] }
65
+
66
+ it { should_not be_nil }
67
+ end
68
+
69
+ context 'given a non-existing customization spec' do
70
+ subject { @cspec ||= @monkey.customization_spec 'xyzzy' }
71
+
72
+ it { should be_nil }
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,211 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe RbVmomi::VIM::VirtualMachine do
4
+ before :all do
5
+ @monkey ||= VMonkey.connect
6
+ @template = @monkey.vm VM_SPEC_OPTS[:template_path]
7
+ @vm_path = "#{VM_SPEC_OPTS[:working_folder]}/vmonkey_spec"
8
+ end
9
+
10
+ describe '#_clone_params' do
11
+ context 'with Folder destination' do
12
+ subject { @params ||= @template._clone_params(@vm_path.basename, @monkey.get(@vm_path.parent), {}) }
13
+
14
+ it { expect(subject[:name]).to eq @vm_path.basename }
15
+ it { expect(subject[:folder].name).to eq @vm_path.parent.basename }
16
+ it { expect(subject[:spec].powerOn).to eq false }
17
+ it { expect(subject[:spec].template).to eq false }
18
+ it { expect(subject[:spec].location.pool.name).to eq 'Resources' }
19
+ end
20
+
21
+ context 'with vApp destination' do
22
+ subject { @params ||= @template._clone_params(@vm_path.basename, @monkey.vapp(VM_SPEC_OPTS[:vapp_path]), {}) }
23
+
24
+ it { expect(subject[:name]).to eq @vm_path.basename }
25
+ it { expect(subject[:folder].name).to eq VM_SPEC_OPTS[:vapp_path].parent.basename }
26
+ it { expect(subject[:spec].powerOn).to eq false }
27
+ it { expect(subject[:spec].template).to eq false }
28
+ it { expect(subject[:spec].location.pool.name).to eq VM_SPEC_OPTS[:vapp_path].basename }
29
+
30
+ it { expect(subject[:spec].customization).to be_nil }
31
+ it { expect(subject[:spec].config.annotation).to be_nil }
32
+ it { expect(subject[:spec].config.numCPUs).to be_nil }
33
+ it { expect(subject[:spec].config.memoryMB).to be_nil }
34
+ end
35
+
36
+ context 'with config' do
37
+ subject do
38
+ @params ||=
39
+ @template._clone_params(
40
+ @vm_path.basename,
41
+ @monkey.get(@vm_path.parent),
42
+ customization_spec: VM_SPEC_OPTS[:customization_spec],
43
+ config: {
44
+ annotation: 'an annotation',
45
+ num_cpus: 3,
46
+ memory_mb: 1024
47
+ })
48
+ end
49
+
50
+ it { expect(subject[:spec].customization).to_not be_nil }
51
+ it { expect(subject[:spec].config.annotation).to eq 'an annotation' }
52
+ it { expect(subject[:spec].config.numCPUs).to eq 3 }
53
+ it { expect(subject[:spec].config.memoryMB).to eq 1024 }
54
+ end
55
+ end
56
+
57
+ context 'with a cloned VM' do
58
+ before(:all) { @spec_vm = @template.clone_to @vm_path }
59
+ after(:all) { @spec_vm.destroy }
60
+
61
+ describe '#clone' do
62
+ context 'to a Folder' do
63
+ subject { @monkey.vm @vm_path }
64
+ it { should_not be_nil }
65
+ end
66
+ end
67
+
68
+ describe '#annotation=' do
69
+ it 'sets the annotation' do
70
+ @spec_vm.annotation = 'xyzzy'
71
+ expect(@spec_vm.annotation).to eq 'xyzzy'
72
+ end
73
+ end
74
+
75
+ describe '#property' do
76
+ before(:all) do
77
+ @spec_vm.property :prop, 'xyzzy'
78
+ @spec_vm.property :prop2, 'abc123'
79
+ @spec_vm.property :prop2, 'abc456'
80
+ end
81
+
82
+ it { expect(@spec_vm.property :prop).to eq 'xyzzy' }
83
+ it { expect(@spec_vm.property :prop2).to eq 'abc456' }
84
+ it { expect(@spec_vm.property :xyzzy).to be_nil }
85
+ end
86
+
87
+ describe '#property!' do
88
+ it 'should raise a RuntimeError given a path to a non-existent property' do
89
+ expect { @spec_vm.property! :xyzzy }.to raise_error RuntimeError
90
+ end
91
+ end
92
+
93
+ describe '#move_to' do
94
+ it 'should raise a RuntimeError when given a path of an existing VM' do
95
+ expect { @spec_vm.move_to @vm_path }.to raise_error RuntimeError
96
+ end
97
+
98
+ it 'should move a vm to a new name in the same folder' do
99
+ parent = @spec_vm.parent
100
+
101
+ @spec_vm.move_to "#{@vm_path}-moved"
102
+ expect(@spec_vm.name).to eq "#{@vm_path.basename}-moved"
103
+ expect(@spec_vm.parent).to eq parent
104
+
105
+ @spec_vm.move_to @vm_path
106
+ expect(@spec_vm.name).to eq @vm_path.basename
107
+ expect(@spec_vm.parent).to eq parent
108
+ end
109
+
110
+ it 'should move a vm to the same name in a new folder' do
111
+ from_folder = @spec_vm.parent
112
+ from_name = @spec_vm.name
113
+ to_path = "#{VM_SPEC_OPTS[:working_folder2]}/#{@vm_path.basename}"
114
+ to_folder = @monkey.folder VM_SPEC_OPTS[:working_folder2]
115
+
116
+ @spec_vm.move_to to_path
117
+ expect(@spec_vm.name).to eq from_name
118
+ expect(@spec_vm.parent).to eq to_folder
119
+
120
+ @spec_vm.move_to @vm_path
121
+ expect(@spec_vm.name).to eq from_name
122
+ expect(@spec_vm.parent).to eq from_folder
123
+ end
124
+
125
+ it 'should move a vm to a new name in a new folder' do
126
+ from_folder = @spec_vm.parent
127
+ from_name = @spec_vm.name
128
+ to_name = "#{@vm_path.basename}-different"
129
+ to_path = "#{VM_SPEC_OPTS[:working_folder2]}/#{to_name}"
130
+ to_folder = @monkey.folder VM_SPEC_OPTS[:working_folder2]
131
+
132
+ @spec_vm.move_to to_path
133
+ expect(@spec_vm.name).to eq to_name
134
+ expect(@spec_vm.parent).to eq to_folder
135
+
136
+ @spec_vm.move_to @vm_path
137
+ expect(@spec_vm.name).to eq from_name
138
+ expect(@spec_vm.parent).to eq from_folder
139
+ end
140
+ end
141
+
142
+ describe '#move_to!' do
143
+ before(:all) do
144
+ @other_path = "#{@vm_path}-other"
145
+ @other_vm = @spec_vm.clone_to @other_path
146
+ end
147
+
148
+ after(:all) do
149
+ other_vm = @monkey.vm @other_path
150
+ other_vm.destroy if other_vm
151
+ end
152
+
153
+ it 'should overwrite a VM when given a path of an existing VM' do
154
+ @spec_vm.move_to! @other_path
155
+ expect(@monkey.vm @other_path).to_not be_nil
156
+
157
+ @spec_vm.move_to @vm_path
158
+ expect(@monkey.vm @other_path).to be_nil
159
+ end
160
+ end
161
+
162
+ describe '#stop' do
163
+ it 'should return successfully when the VM is already powered off' do
164
+ expect { @spec_vm.stop }.to_not raise_error
165
+ end
166
+ end
167
+
168
+ describe '#port_ready?' do
169
+ it 'should be false when the VM is powered off' do
170
+ expect( @spec_vm.port_ready? 22 ).to be_false
171
+ end
172
+ end
173
+
174
+ context 'that has had #start called' do
175
+ before(:all) { @spec_vm.start }
176
+
177
+ describe '#port_ready?' do
178
+ it 'should be false immediately following start' do
179
+ expect(@spec_vm.port_ready? 22).to be_false
180
+ end
181
+
182
+ it 'should be true after wait_for_port' do
183
+ @spec_vm.wait_for_port 22
184
+ expect(@spec_vm.port_ready? 22).to be_true
185
+ end
186
+ end
187
+
188
+ after(:all) { @spec_vm.stop }
189
+ end
190
+
191
+ end
192
+
193
+ describe '#clone' do
194
+ context 'to a vApp' do
195
+ before :all do
196
+ @vm_name_in_vapp = "#{@vm_path.basename}-vapp"
197
+ @template.clone_to "#{VM_SPEC_OPTS[:vapp_path]}/#{@vm_name_in_vapp}"
198
+ @spec_vapp = @monkey.vapp VM_SPEC_OPTS[:vapp_path]
199
+ end
200
+
201
+ subject { @spec_vapp.find_vm @vm_name_in_vapp }
202
+
203
+ it { should_not be_nil }
204
+
205
+ after :all do
206
+ (@spec_vapp.find_vm @vm_name_in_vapp).destroy
207
+ end
208
+ end
209
+ end
210
+
211
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe VMonkey do
4
+ describe '#connect' do
5
+ subject { @vim ||= VMonkey.connect }
6
+
7
+ it { should_not be_nil }
8
+ its(:dc) { should_not be_nil }
9
+ its(:cluster) { should_not be_nil }
10
+ end
11
+ end
12
+
data/vmonkey.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vmonkey/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'vmonkey'
8
+ spec.version = Vmonkey::VERSION
9
+ spec.authors = ['Brian Dupras', 'Dave Smith']
10
+ spec.email = ['brian@duprasville.com', 'dsmith@rallydev.com']
11
+ spec.summary = %q{ simple to use vsphere methods }
12
+ spec.description = %q{ simple to use vsphere methods }
13
+ spec.homepage = 'https://github.com/something/vmonkey'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'nokogiri', '= 1.5.5' #until vmware/rbvmomi issue #32 is fixed
22
+ spec.add_dependency 'rbvmomi', '~> 1.5'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec'
27
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vmonkey
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Dupras
8
+ - Dave Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-06-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '='
19
+ - !ruby/object:Gem::Version
20
+ version: 1.5.5
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '='
26
+ - !ruby/object:Gem::Version
27
+ version: 1.5.5
28
+ - !ruby/object:Gem::Dependency
29
+ name: rbvmomi
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '1.5'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '1.5'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '1.6'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: '1.6'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ description: ' simple to use vsphere methods '
85
+ email:
86
+ - brian@duprasville.com
87
+ - dsmith@rallydev.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .gitignore
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/vmonkey.rb
98
+ - lib/vmonkey/version.rb
99
+ - lib/vmonkey/vim/Datacenter.rb
100
+ - lib/vmonkey/vim/Folder.rb
101
+ - lib/vmonkey/vim/VirtualApp.rb
102
+ - lib/vmonkey/vim/VirtualMachine.rb
103
+ - lib/vmonkey/vim/vim.rb
104
+ - spec/datacenter_spec.rb
105
+ - spec/folder_spec.rb
106
+ - spec/spec_helper.rb
107
+ - spec/vapp_spec.rb
108
+ - spec/vim_spec.rb
109
+ - spec/virtualmachine_spec.rb
110
+ - spec/vmonkey_spec.rb
111
+ - vmonkey.gemspec
112
+ homepage: https://github.com/something/vmonkey
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.1.11
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: simple to use vsphere methods
136
+ test_files:
137
+ - spec/datacenter_spec.rb
138
+ - spec/folder_spec.rb
139
+ - spec/spec_helper.rb
140
+ - spec/vapp_spec.rb
141
+ - spec/vim_spec.rb
142
+ - spec/virtualmachine_spec.rb
143
+ - spec/vmonkey_spec.rb