vcloud-core 0.4.0 → 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/.travis.yml CHANGED
@@ -2,8 +2,5 @@
2
2
  language: ruby
3
3
  rvm:
4
4
  - 1.9.3
5
- branches:
6
- except:
7
- - master
8
5
  notifications:
9
6
  email: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.5.0 (2014-05-30)
2
+
3
+ Features:
4
+
5
+ - `vcloud-query --version` now only returns the version string and no
6
+ usage information.
7
+ - Support 'pool' mode for VM IP address allocation.
8
+
1
9
  ## 0.4.0 (2014-05-23)
2
10
 
3
11
  Features:
data/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'rspec/core/rake_task'
2
- require 'cucumber/rake/task'
3
2
 
4
- task :default => [:rubocop, :spec, :features]
3
+ task :default => [:rubocop, :spec]
5
4
 
6
5
  RSpec::Core::RakeTask.new(:spec) do |task|
7
6
  # Set a bogus Fog credential, otherwise it's possible for the unit
@@ -12,11 +11,6 @@ RSpec::Core::RakeTask.new(:spec) do |task|
12
11
  task.pattern = FileList['spec/vcloud/**/*_spec.rb']
13
12
  end
14
13
 
15
- Cucumber::Rake::Task.new(:features) do |t|
16
- t.cucumber_opts = "--format pretty --no-source"
17
- t.fork = false
18
- end
19
-
20
14
  RSpec::Core::RakeTask.new('integration') do |t|
21
15
  t.pattern = FileList['spec/integration/**/*_spec.rb']
22
16
  end
data/bin/vcloud-query CHANGED
@@ -1,76 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'rubygems'
4
- require 'bundler/setup'
5
- require 'optparse'
6
- require 'methadone'
7
-
8
3
  require 'vcloud/core'
9
4
 
10
- class App
11
-
12
- include Methadone::Main
13
- include Methadone::CLILogging
14
- include Vcloud
15
-
16
- main do |type|
17
- Vcloud::Core::Query.new(type, options).run
18
- end
19
-
20
- on('-A', '--sort-asc', '=ATTRIBUTE', 'Sort ascending') do |v|
21
- options[:sortAsc] = v
22
- end
23
-
24
- on('-D', '--sort-desc', '=ATTRIBUTE', 'Sort descending') do |v|
25
- options[:sortDesc] = v
26
- end
27
-
28
- on('--fields', '=NAMES', 'Attribute or metadata key names') do |v|
29
- options[:fields] = v
30
- end
31
-
32
- on('--filter', '=FILTER', 'Filter expression') do |v|
33
- options[:filter] = v
34
- end
35
-
36
- on('-o', '--output-format', '=FORMAT', 'Output format: csv, tsv, yaml') do |v|
37
- options[:output_format] = v.downcase
38
- end
39
-
40
- arg :type, :optional
41
-
42
- description '
43
- vcloud-query takes a query type and returns all vCloud entities of
44
- that type, obeying supplied filter rules.
45
-
46
- Query types map to vCloud entities, for example: vApp, vm, orgVdc, orgVdcNetwork.
47
-
48
- Without a type argument, returns a list of available Entity Types to query.
49
-
50
- See https://github.com/alphagov/vcloud-tools/blob/master/README.md for more info.
51
-
52
- Example use:
53
-
54
- # get a list of all vApps, returning all available parameters, in YAML
55
-
56
- vcloud-query -o yaml vApp
57
-
58
- # get a list of all powered off VMs return the name and containerName (vapp
59
- # name)
60
-
61
- vcloud-query --filter "status==POWERED_OFF" --fields name,containerName vm
62
-
63
- # list all query types (types are left-most column, possible formats listed
64
- # on the left (records is default, and most useful)
65
-
66
- vcloud-query
67
-
68
- '
69
-
70
-
71
- version Vcloud::Core::VERSION
72
-
73
- #use_log_level_option
74
-
75
- go!
76
- end
5
+ Vcloud::Core::QueryCli.new(ARGV).run
data/lib/vcloud/core.rb CHANGED
@@ -16,6 +16,7 @@ require 'vcloud/core/vapp'
16
16
  require 'vcloud/core/vapp_template'
17
17
  require 'vcloud/core/org_vdc_network'
18
18
  require 'vcloud/core/query'
19
+ require 'vcloud/core/query_cli'
19
20
  require 'vcloud/core/query_runner'
20
21
 
21
22
  module Vcloud
@@ -0,0 +1,108 @@
1
+ require 'optparse'
2
+
3
+ module Vcloud
4
+ module Core
5
+ class QueryCli
6
+ def initialize(argv_array)
7
+ @usage_text = nil
8
+ @type = nil
9
+ @options = {}
10
+
11
+ parse(argv_array)
12
+ end
13
+
14
+ def run
15
+ begin
16
+ Vcloud::Core::Query.new(@type, @options).run
17
+ rescue => e
18
+ $stderr.puts(e)
19
+ exit 1
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def parse(args)
26
+ opt_parser = OptionParser.new do |opts|
27
+ opts.banner = <<-EOS
28
+ Usage: #{$0} [options] [type]
29
+
30
+ vcloud-query takes a query type and returns all vCloud entities of
31
+ that type, obeying supplied filter rules.
32
+
33
+ Query types map to vCloud entities, for example: vApp, vm, orgVdc, orgVdcNetwork.
34
+
35
+ Without a type argument, returns a list of available Entity Types to query.
36
+
37
+ See https://github.com/alphagov/vcloud-tools/blob/master/README.md for more info.
38
+
39
+ Example use:
40
+
41
+ # get a list of all vApps, returning all available parameters, in YAML
42
+
43
+ vcloud-query -o yaml vApp
44
+
45
+ # get a list of all powered off VMs return the name and containerName (vapp
46
+ # name)
47
+
48
+ vcloud-query --filter "status==POWERED_OFF" --fields name,containerName vm
49
+
50
+ # list all query types (types are left-most column, possible formats listed
51
+ # on the left (records is default, and most useful)
52
+
53
+ vcloud-query
54
+ EOS
55
+
56
+ opts.on('-A', '--sort-asc ATTRIBUTE', 'Sort ascending') do |v|
57
+ @options[:sortAsc] = v
58
+ end
59
+
60
+ opts.on('-D', '--sort-desc ATTRIBUTE', 'Sort descending') do |v|
61
+ @options[:sortDesc] = v
62
+ end
63
+
64
+ opts.on('--fields NAMES', 'Attribute or metadata key names') do |v|
65
+ @options[:fields] = v
66
+ end
67
+
68
+ opts.on('--filter FILTER', 'Filter expression') do |v|
69
+ @options[:filter] = v
70
+ end
71
+
72
+ opts.on('-o', '--output-format FORMAT', 'Output format: csv, tsv, yaml') do |v|
73
+ @options[:output_format] = v.downcase
74
+ end
75
+
76
+ opts.on("-h", "--help", "Print usage and exit") do
77
+ $stderr.puts opts
78
+ exit
79
+ end
80
+
81
+ opts.on("--version", "Display version and exit") do
82
+ puts Vcloud::Core::VERSION
83
+ exit
84
+ end
85
+ end
86
+
87
+ @usage_text = opt_parser.to_s
88
+ begin
89
+ opt_parser.parse!(args)
90
+ rescue OptionParser::InvalidOption => e
91
+ exit_error_usage(e)
92
+ end
93
+
94
+ if args.size > 1
95
+ exit_error_usage("too many arguments")
96
+ elsif args.size == 1
97
+ @type = args.first
98
+ end
99
+ end
100
+
101
+ def exit_error_usage(error)
102
+ $stderr.puts "#{$0}: #{error}"
103
+ $stderr.puts @usage_text
104
+ exit 2
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,5 +1,5 @@
1
1
  module Vcloud
2
2
  module Core
3
- VERSION = '0.4.0'
3
+ VERSION = '0.5.0'
4
4
  end
5
5
  end
@@ -89,9 +89,14 @@ module Vcloud
89
89
  NetworkConnectionIndex: i,
90
90
  IsConnected: true
91
91
  }
92
- ip_address = network[:ip_address]
93
- connection[:IpAddress] = ip_address unless ip_address.nil?
94
- connection[:IpAddressAllocationMode] = ip_address ? 'MANUAL' : 'DHCP'
92
+ ip_address = network[:ip_address]
93
+ allocation_mode = network[:allocation_mode]
94
+
95
+ allocation_mode = 'manual' if ip_address
96
+ allocation_mode = 'dhcp' unless %w{dhcp manual pool}.include?(allocation_mode)
97
+
98
+ connection[:IpAddressAllocationMode] = allocation_mode.upcase
99
+ connection[:IpAddress] = ip_address if ip_address
95
100
  connection
96
101
  end
97
102
  Vcloud::Fog::ServiceInterface.new.put_network_connection_system_section_vapp(id, section)
@@ -9,7 +9,7 @@ module Vcloud
9
9
  :get_execute_query, :get_vapp_metadata, :power_off_vapp, :shutdown_vapp, :session,
10
10
  :post_instantiate_vapp_template, :put_memory, :put_cpu, :power_on_vapp, :put_vapp_metadata_value,
11
11
  :put_vm, :get_edge_gateway, :get_network_complete, :delete_network, :post_create_org_vdc_network,
12
- :post_configure_edge_gateway_services
12
+ :post_configure_edge_gateway_services, :get_vdc
13
13
 
14
14
  #########################
15
15
  # FogFacade Inner class to represent a logic free facade over our interactions with Fog
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vcloud::Core::Vdc do
4
+
5
+ let(:uuid_matcher) { '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' }
6
+
7
+ before(:all) do
8
+ config_file = File.join(File.dirname(__FILE__), "../vcloud_tools_testing_config.yaml")
9
+ @test_data = Vcloud::Tools::Tester::TestParameters.new(config_file)
10
+ end
11
+
12
+ describe ".get_by_name" do
13
+
14
+ subject { Vcloud::Core::Vdc.get_by_name(name) }
15
+
16
+ context "when looking up a valid vDC name" do
17
+
18
+ let(:name) { @test_data.vdc_1_name }
19
+
20
+ it "returns a Vcloud::Core::Vdc object" do
21
+ expect(subject).to be_instance_of(Vcloud::Core::Vdc)
22
+ end
23
+
24
+ it "returns a Vdc object with a valid id" do
25
+ expect(subject.id).to match(/\A#{uuid_matcher}\Z/)
26
+ end
27
+
28
+ it "returns a Vdc object with our expected name" do
29
+ expect(subject.name).to eq(name)
30
+ end
31
+
32
+ end
33
+
34
+ context "when looking up an invalid vDC name" do
35
+
36
+ let(:name) { "bogus Vdc name 12p412irjof" }
37
+
38
+ it "throws an error" do
39
+ expect { subject }.to raise_error("vDc #{name} not found")
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ describe ".new" do
47
+
48
+ subject { Vcloud::Core::Vdc.new(vdc_id) }
49
+
50
+ context "when instantiating with a valid ID" do
51
+
52
+ let(:vdc_id) { Vcloud::Core::Vdc.get_by_name(@test_data.vdc_1_name).id }
53
+
54
+ it "returns a valid Vdc object" do
55
+ expect(subject).to be_instance_of(Vcloud::Core::Vdc)
56
+ end
57
+
58
+ it "has our expected #id" do
59
+ expect(subject.id).to eq(vdc_id)
60
+ end
61
+
62
+ it "has our expected #name" do
63
+ expect(subject.name).to eq(@test_data.vdc_1_name)
64
+ end
65
+
66
+ end
67
+
68
+ context "when instantiating with a valid UUID, that does not refer to a Vdc" do
69
+
70
+ let(:vdc_id) { '12345678-1234-1234-1234-123456789012' }
71
+
72
+ it "returns a valid Vdc object" do
73
+ expect(subject).to be_instance_of(Vcloud::Core::Vdc)
74
+ end
75
+
76
+ it "has our expected #id" do
77
+ expect(subject.id).to eq(vdc_id)
78
+ end
79
+
80
+ it "throws a Forbidden error when trying to access the #name of the Vdc" do
81
+ expect { subject.name }.to raise_error(
82
+ Fog::Compute::VcloudDirector::Forbidden,
83
+ /\ANo access to entity /
84
+ )
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,260 @@
1
+ require 'spec_helper'
2
+
3
+ describe Vcloud::Core::Vm do
4
+
5
+ before(:all) do
6
+ config_file = File.join(File.dirname(__FILE__), "../vcloud_tools_testing_config.yaml")
7
+ @test_data = Vcloud::Tools::Tester::TestParameters.new(config_file)
8
+ @network_names = [ @test_data.network_1, @test_data.network_2 ]
9
+ @network_ips = {
10
+ @test_data.network_1 => @test_data.network_1_ip,
11
+ @test_data.network_2 => @test_data.network_2_ip,
12
+ }
13
+ @test_case_vapps = IntegrationHelper.create_test_case_vapps(
14
+ 1,
15
+ @test_data.vdc_1_name,
16
+ @test_data.catalog,
17
+ @test_data.vapp_template,
18
+ @network_names,
19
+ "vcloud-core-vm-tests"
20
+ )
21
+ @vapp = @test_case_vapps.first
22
+ vapp_vms = @vapp.fog_vms.map do |fog_vm|
23
+ vm_id = fog_vm[:href].split('/').last
24
+ Vcloud::Core::Vm.new(vm_id, @vapp)
25
+ end
26
+ @vm = vapp_vms.first
27
+ end
28
+
29
+ context "#vcloud_attributes" do
30
+
31
+ it "has a :href element containing the expected VM id" do
32
+ expect(@vm.vcloud_attributes[:href].split('/').last).to eq(@vm.id)
33
+ end
34
+
35
+ end
36
+
37
+ context "#update_memory_size_in_mb" do
38
+
39
+ it "can increase the memory size by 512MB" do
40
+ initial_memory_size = Integer(@vm.memory) # Vm#memory returns a string
41
+ memory_to_add_in_mb = 512
42
+ new_memory_size = initial_memory_size + memory_to_add_in_mb
43
+ @vm.update_memory_size_in_mb(new_memory_size)
44
+ expect(Integer(@vm.memory)).to eq(new_memory_size)
45
+ end
46
+
47
+ it "can reduce the memory size by 512MB" do
48
+ initial_memory_size = Integer(@vm.memory) # Vm#memory returns a string
49
+ memory_to_remove_in_mb = 512
50
+ new_memory_size = initial_memory_size - memory_to_remove_in_mb
51
+ @vm.update_memory_size_in_mb(new_memory_size)
52
+ expect(Integer(@vm.memory)).to eq(new_memory_size)
53
+ end
54
+
55
+ end
56
+
57
+ context "#update_name" do
58
+
59
+ it "can update the name of the vm" do
60
+ current_name = @vm.name
61
+ new_name = "#{current_name}-updated"
62
+ @vm.update_name(new_name)
63
+ expect(@vm.name).to eq(new_name)
64
+ end
65
+
66
+ end
67
+
68
+ context "#vapp_name" do
69
+
70
+ it "can retrieve the name of its parent vApp" do
71
+ expect(@vm.vapp_name).to eq(@vapp.name)
72
+ end
73
+
74
+ end
75
+
76
+ context "#update_cpu_count" do
77
+
78
+ it "can increase the number of CPUs in a VM" do
79
+ initial_cpu_count = Integer(@vm.cpu) # Vm#cpu returns a string :(
80
+ new_cpu_count = initial_cpu_count * 2
81
+ @vm.update_cpu_count(new_cpu_count)
82
+ expect(Integer(@vm.cpu)).to eq(new_cpu_count)
83
+ end
84
+
85
+ it "can decrease the number of CPUs in a VM to 1" do
86
+ initial_cpu_count = Integer(@vm.cpu) # Vm#cpu returns a string :(
87
+ new_cpu_count = 1
88
+ expect(initial_cpu_count).to be > new_cpu_count
89
+ @vm.update_cpu_count(new_cpu_count)
90
+ expect(Integer(@vm.cpu)).to eq(new_cpu_count)
91
+ end
92
+
93
+ end
94
+
95
+ context "#update_metadata" do
96
+
97
+ before(:all) do
98
+ @initial_vm_metadata = Vcloud::Core::Vm.get_metadata(@vm.id)
99
+ @initial_vapp_metadata = Vcloud::Core::Vapp.get_metadata(@vapp.id)
100
+ end
101
+
102
+ it "updates the Vm metadata, if a single key/value is specified" do
103
+ @vm.update_metadata({"Test Key" => "test value"})
104
+ updated_metadata = Vcloud::Core::Vm.get_metadata(@vm.id)
105
+ # get_metadata is symbolizing the key names
106
+ expected_metadata = @initial_vm_metadata.merge({
107
+ :"Test Key" => "test value"
108
+ })
109
+ expect(updated_metadata).to eq(expected_metadata)
110
+ end
111
+
112
+ it "adds to the existing Vm metadata, rather than replacing it" do
113
+ @vm.update_metadata({"Another Test" => "test value 2"})
114
+ updated_metadata = Vcloud::Core::Vm.get_metadata(@vm.id)
115
+ # get_metadata is symbolizing the key names
116
+ expected_metadata = @initial_vm_metadata.merge({
117
+ :"Test Key" => "test value",
118
+ :"Another Test" => "test value 2",
119
+ })
120
+ expect(updated_metadata).to eq(expected_metadata)
121
+ end
122
+
123
+ it "has also updated parent vApp with the same metadata" do
124
+ updated_vapp_metadata = Vcloud::Core::Vapp.get_metadata(@vapp.id)
125
+ # get_metadata is symbolizing the key names
126
+ expected_vapp_metadata = @initial_vapp_metadata.merge({
127
+ :"Test Key" => "test value",
128
+ :"Another Test" => "test value 2",
129
+ })
130
+ expect(updated_vapp_metadata).to eq(expected_vapp_metadata)
131
+ end
132
+
133
+ end
134
+
135
+ context "#add_extra_disks" do
136
+
137
+ before(:all) do
138
+ @fog_model_vm = Vcloud::Fog::ModelInterface.new.get_vm_by_href(@vm.href)
139
+ @initial_vm_disks = get_vm_hard_disks(@fog_model_vm)
140
+ end
141
+
142
+ it "the VM should already have a single disk assigned" do
143
+ expect(@initial_vm_disks.size).to eq(1)
144
+ end
145
+
146
+ it "can successfully add a second disk" do
147
+ extra_disks = [ { size: '20480' } ]
148
+ @vm.add_extra_disks(extra_disks)
149
+ updated_vm_disks = get_vm_hard_disks(@fog_model_vm)
150
+ expect(updated_vm_disks.size).to eq(2)
151
+ end
152
+
153
+ it "can successfully add several disks in one call" do
154
+ extra_disks = [ { size: '20480' }, { size: '10240' } ]
155
+ disks_before_update = get_vm_hard_disks(@fog_model_vm)
156
+ @vm.add_extra_disks(extra_disks)
157
+ disks_after_update = get_vm_hard_disks(@fog_model_vm)
158
+ expect(disks_after_update.size).to eq(disks_before_update.size + extra_disks.size)
159
+ end
160
+
161
+ end
162
+
163
+ context "#configure_network_interfaces" do
164
+
165
+ it "can configure a single NIC, default DHCP" do
166
+ network_config = [
167
+ { :name => @network_names[0] }
168
+ ]
169
+ @vm.configure_network_interfaces(network_config)
170
+ # if number if nics is 1, API returns a Hash.
171
+ # This is a bug in Fog -- ensure_list! is needed. See
172
+ # https://github.com/fog/fog/issues/2927
173
+ vm_nics = @vm.vcloud_attributes[:NetworkConnectionSection][:NetworkConnection]
174
+ expect(vm_nics).to be_instance_of(Hash)
175
+ expect(vm_nics[:network]).to eq(network_config[0][:name])
176
+ expect(vm_nics[:IpAddressAllocationMode]).to eq('DHCP')
177
+ end
178
+
179
+ it "can configure dual NICs, both defaulting to DHCP" do
180
+ network_config = [
181
+ { :name => @network_names[0] },
182
+ { :name => @network_names[1] }
183
+ ]
184
+ @vm.configure_network_interfaces(network_config)
185
+ # if number if nics is 2+, API returns a Array.
186
+ # See https://github.com/fog/fog/issues/2927
187
+ vm_nics = @vm.vcloud_attributes[:NetworkConnectionSection][:NetworkConnection]
188
+ vm_nics = sort_nics_based_on_network_connection_index(vm_nics)
189
+ expect(vm_nics).to be_instance_of(Array)
190
+ expect(vm_nics.size).to eq(network_config.size)
191
+ expect(vm_nics[0][:network]).to eq(network_config[0][:name])
192
+ expect(vm_nics[0][:IpAddressAllocationMode]).to eq('DHCP')
193
+ expect(vm_nics[0][:NetworkConnectionIndex]).to eq('0')
194
+ expect(vm_nics[1][:network]).to eq(network_config[1][:name])
195
+ expect(vm_nics[1][:IpAddressAllocationMode]).to eq('DHCP')
196
+ expect(vm_nics[1][:NetworkConnectionIndex]).to eq('1')
197
+ end
198
+
199
+ it "can configure dual NICs with manually assigned IP addresses" do
200
+ network_config = [
201
+ { :name => @network_names[0], :ip_address => @network_ips[@network_names[0]] },
202
+ { :name => @network_names[1], :ip_address => @network_ips[@network_names[1]] },
203
+ ]
204
+ @vm.configure_network_interfaces(network_config)
205
+ # if number if nics is 2+, API returns a Array.
206
+ vm_nics = @vm.vcloud_attributes[:NetworkConnectionSection][:NetworkConnection]
207
+ vm_nics = sort_nics_based_on_network_connection_index(vm_nics)
208
+ expect(vm_nics).to be_instance_of(Array)
209
+ expect(vm_nics.size).to eq(network_config.size)
210
+ expect(vm_nics[0][:network]).to eq(network_config[0][:name])
211
+ expect(vm_nics[0][:IpAddress]).to eq(network_config[0][:ip_address])
212
+ expect(vm_nics[0][:IpAddressAllocationMode]).to eq('MANUAL')
213
+ expect(vm_nics[0][:NetworkConnectionIndex]).to eq('0')
214
+ expect(vm_nics[1][:network]).to eq(network_config[1][:name])
215
+ expect(vm_nics[1][:IpAddress]).to eq(network_config[1][:ip_address])
216
+ expect(vm_nics[1][:IpAddressAllocationMode]).to eq('MANUAL')
217
+ expect(vm_nics[1][:NetworkConnectionIndex]).to eq('1')
218
+ end
219
+
220
+ end
221
+
222
+ context "#update_storage_profile" do
223
+
224
+ it "can update the storage profile of a VM" do
225
+ available_storage_profiles = Vcloud::Core::QueryRunner.new.run(
226
+ 'orgVdcStorageProfile',
227
+ filter: "vdcName==#{@test_data.vdc_1_name}"
228
+ )
229
+ if available_storage_profiles.size == 1
230
+ pending("There is only one StorageProfile in vDC #{@test_data.vdc_1_name}: cannot test.")
231
+ end
232
+ original_storage_profile_name = @vm.vcloud_attributes[:StorageProfile][:name]
233
+ expect(original_storage_profile_name).to eq(@test_data.default_storage_profile_name)
234
+ @vm.update_storage_profile(@test_data.storage_profile)
235
+ expect(@vm.vcloud_attributes[:StorageProfile][:name]).to eq(@test_data.storage_profile)
236
+ end
237
+
238
+ end
239
+
240
+ after(:all) do
241
+ IntegrationHelper.delete_vapps(@test_case_vapps)
242
+ end
243
+
244
+ def get_vm_hard_disks(fog_model_vm)
245
+ # 'disks' Model VM method returns disks + controllers. Disks always have
246
+ # the name 'Hard Disk {n}' where (n >= 0).
247
+ fog_model_vm.disks.select { |disk| disk.name =~ /^Hard disk/ }
248
+ end
249
+
250
+ def sort_nics_based_on_network_connection_index(network_connection_list)
251
+ # The :NetworkConnection Array is not (necessarily) ordered when it is
252
+ # retrieved via the API.
253
+ # Instead, they are indexed by the :NetworkConnectionIndex value, which
254
+ # is returned by the API as a number-as-a-string (eg "0", "1")
255
+ network_connection_list.sort_by do |n|
256
+ Integer(n[:NetworkConnectionIndex])
257
+ end
258
+ end
259
+
260
+ end
data/spec/spec_helper.rb CHANGED
@@ -3,7 +3,6 @@ if ENV['COVERAGE']
3
3
 
4
4
  SimpleCov.profiles.define 'gem' do
5
5
  add_filter '/spec/'
6
- add_filter '/features/'
7
6
  add_filter '/vendor/'
8
7
 
9
8
  add_group 'Libraries', '/lib/'
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ class CommandRun
4
+ attr_accessor :stdout, :stderr, :exitstatus
5
+
6
+ def initialize(args)
7
+ out = StringIO.new
8
+ err = StringIO.new
9
+
10
+ $stdout = out
11
+ $stderr = err
12
+
13
+ begin
14
+ Vcloud::Core::QueryCli.new(args).run
15
+ @exitstatus = 0
16
+ rescue SystemExit => e
17
+ # Capture exit(n) value.
18
+ @exitstatus = e.status
19
+ end
20
+
21
+ @stdout = out.string.strip
22
+ @stderr = err.string.strip
23
+
24
+ $stdout = STDOUT
25
+ $stderr = STDERR
26
+ end
27
+ end
28
+
29
+ describe Vcloud::Core::QueryCli do
30
+ subject { CommandRun.new(args) }
31
+ let(:mock_query) {
32
+ double(:query, :run => true)
33
+ }
34
+
35
+ describe "normal usage" do
36
+ context "when given no arguments" do
37
+ let(:args) { %w{} }
38
+
39
+ it "should pass nil type and empty options hash, then exit normally" do
40
+ expect(Vcloud::Core::Query).to receive(:new).
41
+ with(nil, {}).and_return(mock_query)
42
+ expect(mock_query).to receive(:run)
43
+ expect(subject.exitstatus).to eq(0)
44
+ end
45
+ end
46
+
47
+ context "when given type" do
48
+ let(:args) { %w{vApp} }
49
+
50
+ it "should pass type and empty options hash, then exit normally" do
51
+ expect(Vcloud::Core::Query).to receive(:new).
52
+ with('vApp', {}).and_return(mock_query)
53
+ expect(mock_query).to receive(:run)
54
+ expect(subject.exitstatus).to eq(0)
55
+ end
56
+ end
57
+
58
+ context "when asked to display version" do
59
+ let(:args) { %w{--version} }
60
+
61
+ it "should not call Query" do
62
+ expect(Vcloud::Core::Query).not_to receive(:new)
63
+ end
64
+
65
+ it "should print version and exit normally" do
66
+ expect(subject.stdout).to eq(Vcloud::Core::VERSION)
67
+ expect(subject.exitstatus).to eq(0)
68
+ end
69
+ end
70
+
71
+ context "when asked to display help" do
72
+ let(:args) { %w{--help} }
73
+
74
+ it "should not call Query" do
75
+ expect(Vcloud::Core::Query).not_to receive(:new)
76
+ end
77
+
78
+ it "should print usage and exit normally" do
79
+ expect(subject.stderr).to match(/\AUsage: \S+ \[options\] \[type\]\n/)
80
+ expect(subject.exitstatus).to eq(0)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "simple arguments with values" do
86
+ {
87
+ :sortAsc => '--sort-asc',
88
+ :sortDesc => '--sort-desc',
89
+ :fields => '--fields',
90
+ :filter => '--filter',
91
+ }.each do |options_key, cli_arg|
92
+ context "when given #{cli_arg}" do
93
+ let(:args) { [cli_arg, 'giraffe'] }
94
+
95
+ it "should pass :#{options_key} in options hash and exit normally" do
96
+ expect(Vcloud::Core::Query).to receive(:new).
97
+ with(nil, { options_key => 'giraffe' }).
98
+ and_return(mock_query)
99
+ expect(mock_query).to receive(:run)
100
+ expect(subject.exitstatus).to eq(0)
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ describe "complex arguments with values" do
107
+ context "when given --output-format with mixed case value" do
108
+ let(:args) { %w{--output-format MixedCaseValue} }
109
+
110
+ it "should pass downcased value in options hash and exit normally" do
111
+ expect(Vcloud::Core::Query).to receive(:new).
112
+ with(nil, { :output_format => 'mixedcasevalue' }).
113
+ and_return(mock_query)
114
+ expect(mock_query).to receive(:run)
115
+ expect(subject.exitstatus).to eq(0)
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "incorrect usage" do
121
+ shared_examples "print usage and exit abnormally" do |error|
122
+ it "should not call Query" do
123
+ expect(Vcloud::Core::Query).not_to receive(:new)
124
+ end
125
+
126
+ it "should print error message and usage" do
127
+ expect(subject.stderr).to match(/\A\S+: #{error}\nUsage: \S+/)
128
+ end
129
+
130
+ it "should exit abnormally for incorrect usage" do
131
+ expect(subject.exitstatus).to eq(2)
132
+ end
133
+ end
134
+
135
+ context "when given more than type argument" do
136
+ let(:args) { %w{type_one type_two} }
137
+
138
+ it_behaves_like "print usage and exit abnormally", "too many arguments"
139
+ end
140
+
141
+ context "when given an unrecognised argument" do
142
+ let(:args) { %w{--this-is-garbage} }
143
+
144
+ it_behaves_like "print usage and exit abnormally", "invalid option: --this-is-garbage"
145
+ end
146
+ end
147
+
148
+ describe "error handling" do
149
+ context "when underlying code raises an exception" do
150
+ let(:args) { %w{} }
151
+
152
+ it "should print error without backtrace and exit abnormally" do
153
+ expect(Vcloud::Core::Query).to receive(:new).
154
+ with(nil, {}).and_raise("something went horribly wrong")
155
+ expect(subject.stderr).to eq("something went horribly wrong")
156
+ expect(subject.exitstatus).to eq(1)
157
+ end
158
+ end
159
+ end
160
+ end
@@ -241,6 +241,39 @@ module Vcloud
241
241
  @vm.configure_network_interfaces(network_config)
242
242
  end
243
243
 
244
+ it "should configure nic from pool" do
245
+ network_config = [{:name => 'Default', :allocation_mode => 'pool'}]
246
+ @fog_interface.should_receive(:put_network_connection_system_section_vapp).with(@vm_id, {
247
+ :PrimaryNetworkConnectionIndex => 0,
248
+ :NetworkConnection => [
249
+ {
250
+ :network => 'Default',
251
+ :needsCustomization => true,
252
+ :NetworkConnectionIndex => 0,
253
+ :IsConnected => true,
254
+ :IpAddressAllocationMode => "POOL"
255
+ }
256
+ ]})
257
+ @vm.configure_network_interfaces(network_config)
258
+ end
259
+
260
+ it "should prefer configuring nic with static address" do
261
+ network_config = [{:name => 'Default', :allocation_mode => 'dhcp', :ip_address => '192.168.1.1'}]
262
+ @fog_interface.should_receive(:put_network_connection_system_section_vapp).with(@vm_id, {
263
+ :PrimaryNetworkConnectionIndex => 0,
264
+ :NetworkConnection => [
265
+ {
266
+ :network => 'Default',
267
+ :needsCustomization => true,
268
+ :NetworkConnectionIndex => 0,
269
+ :IsConnected => true,
270
+ :IpAddress => "192.168.1.1",
271
+ :IpAddressAllocationMode => "MANUAL"
272
+ }
273
+ ]})
274
+ @vm.configure_network_interfaces(network_config)
275
+ end
276
+
244
277
  it "should configure single nic" do
245
278
  network_config = [{:name => 'Default', :ip_address => '192.168.1.1'}]
246
279
  @fog_interface.should_receive(:put_network_connection_system_section_vapp).with(@vm_id, {
data/vcloud-core.gemspec CHANGED
@@ -23,10 +23,8 @@ Gem::Specification.new do |s|
23
23
  s.required_ruby_version = '>= 1.9.2'
24
24
 
25
25
  s.add_runtime_dependency 'fog', '>= 1.22.0'
26
- s.add_runtime_dependency 'methadone'
27
26
  s.add_runtime_dependency 'mustache'
28
- s.add_development_dependency 'aruba', '~> 0.5.3'
29
- s.add_development_dependency 'cucumber', '~> 1.3.10'
27
+ s.add_development_dependency 'pry'
30
28
  s.add_development_dependency 'rake'
31
29
  s.add_development_dependency 'rspec', '~> 2.14.1'
32
30
  s.add_development_dependency 'rubocop'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vcloud-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-23 00:00:00.000000000 Z
12
+ date: 2014-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -28,7 +28,7 @@ dependencies:
28
28
  - !ruby/object:Gem::Version
29
29
  version: 1.22.0
30
30
  - !ruby/object:Gem::Dependency
31
- name: methadone
31
+ name: mustache
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
@@ -44,14 +44,14 @@ dependencies:
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
46
  - !ruby/object:Gem::Dependency
47
- name: mustache
47
+ name: pry
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  none: false
50
50
  requirements:
51
51
  - - ! '>='
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
- type: :runtime
54
+ type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  none: false
@@ -59,38 +59,6 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
- - !ruby/object:Gem::Dependency
63
- name: aruba
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: 0.5.3
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: 0.5.3
78
- - !ruby/object:Gem::Dependency
79
- name: cucumber
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 1.3.10
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ~>
92
- - !ruby/object:Gem::Version
93
- version: 1.3.10
94
62
  - !ruby/object:Gem::Dependency
95
63
  name: rake
96
64
  requirement: !ruby/object:Gem::Requirement
@@ -204,8 +172,6 @@ files:
204
172
  - README.md
205
173
  - Rakefile
206
174
  - bin/vcloud-query
207
- - features/support/env.rb
208
- - features/vcloud-query.feature
209
175
  - jenkins.sh
210
176
  - jenkins_integration_tests.sh
211
177
  - lib/vcloud/core.rb
@@ -218,6 +184,7 @@ files:
218
184
  - lib/vcloud/core/metadata_helper.rb
219
185
  - lib/vcloud/core/org_vdc_network.rb
220
186
  - lib/vcloud/core/query.rb
187
+ - lib/vcloud/core/query_cli.rb
221
188
  - lib/vcloud/core/query_runner.rb
222
189
  - lib/vcloud/core/vapp.rb
223
190
  - lib/vcloud/core/vapp_template.rb
@@ -231,6 +198,8 @@ files:
231
198
  - lib/vcloud/fog/service_interface.rb
232
199
  - spec/integration/README.md
233
200
  - spec/integration/core/query_runner_spec.rb
201
+ - spec/integration/core/vdc_spec.rb
202
+ - spec/integration/core/vm_spec.rb
234
203
  - spec/integration/edge_gateway/configure_edge_gateway_services_spec.rb
235
204
  - spec/integration/edge_gateway/edge_gateway_spec.rb
236
205
  - spec/integration/vcloud_tools_testing_config.yaml.template
@@ -251,6 +220,7 @@ files:
251
220
  - spec/vcloud/core/edge_gateway_spec.rb
252
221
  - spec/vcloud/core/metadata_helper_spec.rb
253
222
  - spec/vcloud/core/org_vdc_network_spec.rb
223
+ - spec/vcloud/core/query_cli_spec.rb
254
224
  - spec/vcloud/core/query_runner_spec.rb
255
225
  - spec/vcloud/core/query_spec.rb
256
226
  - spec/vcloud/core/vapp_spec.rb
@@ -282,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
282
252
  version: '0'
283
253
  segments:
284
254
  - 0
285
- hash: -2177023225627992680
255
+ hash: -1029676288451429784
286
256
  requirements: []
287
257
  rubyforge_project:
288
258
  rubygems_version: 1.8.23
@@ -290,10 +260,10 @@ signing_key:
290
260
  specification_version: 3
291
261
  summary: Core tools for interacting with VMware vCloud Director
292
262
  test_files:
293
- - features/support/env.rb
294
- - features/vcloud-query.feature
295
263
  - spec/integration/README.md
296
264
  - spec/integration/core/query_runner_spec.rb
265
+ - spec/integration/core/vdc_spec.rb
266
+ - spec/integration/core/vm_spec.rb
297
267
  - spec/integration/edge_gateway/configure_edge_gateway_services_spec.rb
298
268
  - spec/integration/edge_gateway/edge_gateway_spec.rb
299
269
  - spec/integration/vcloud_tools_testing_config.yaml.template
@@ -314,6 +284,7 @@ test_files:
314
284
  - spec/vcloud/core/edge_gateway_spec.rb
315
285
  - spec/vcloud/core/metadata_helper_spec.rb
316
286
  - spec/vcloud/core/org_vdc_network_spec.rb
287
+ - spec/vcloud/core/query_cli_spec.rb
317
288
  - spec/vcloud/core/query_runner_spec.rb
318
289
  - spec/vcloud/core/query_spec.rb
319
290
  - spec/vcloud/core/vapp_spec.rb
@@ -1,16 +0,0 @@
1
- require 'aruba/cucumber'
2
- require 'methadone/cucumber'
3
-
4
- ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
5
- LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
6
-
7
- Before do
8
- # Using "announce" causes massive warnings on 1.9.2
9
- @puts = true
10
- @original_rubylib = ENV['RUBYLIB']
11
- ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
12
- end
13
-
14
- After do
15
- ENV['RUBYLIB'] = @original_rubylib
16
- end
@@ -1,14 +0,0 @@
1
- Feature: "vcloud-query" works as a useful command-line tool
2
- In order to use "vcloud-query" from the CLI
3
- I want to have it behave like a typical Unix tool
4
- So I don't get surpised
5
-
6
- Scenario: Common arguments work
7
- When I get help for "vcloud-query"
8
- Then the exit status should be 0
9
- And the banner should be present
10
- And the banner should document that this app takes options
11
- And the following options should be documented:
12
- |--version|
13
- And the banner should document that this app's arguments are:
14
- |type|