vcloud-core 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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|