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 +0 -3
- data/CHANGELOG.md +8 -0
- data/Rakefile +1 -7
- data/bin/vcloud-query +1 -72
- data/lib/vcloud/core.rb +1 -0
- data/lib/vcloud/core/query_cli.rb +108 -0
- data/lib/vcloud/core/version.rb +1 -1
- data/lib/vcloud/core/vm.rb +8 -3
- data/lib/vcloud/fog/service_interface.rb +1 -1
- data/spec/integration/core/vdc_spec.rb +91 -0
- data/spec/integration/core/vm_spec.rb +260 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/vcloud/core/query_cli_spec.rb +160 -0
- data/spec/vcloud/core/vm_spec.rb +33 -0
- data/vcloud-core.gemspec +1 -3
- metadata +13 -42
- data/features/support/env.rb +0 -16
- data/features/vcloud-query.feature +0 -14
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
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
|
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
|
-
|
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
@@ -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
|
data/lib/vcloud/core/version.rb
CHANGED
data/lib/vcloud/core/vm.rb
CHANGED
@@ -89,9 +89,14 @@ module Vcloud
|
|
89
89
|
NetworkConnectionIndex: i,
|
90
90
|
IsConnected: true
|
91
91
|
}
|
92
|
-
ip_address
|
93
|
-
|
94
|
-
|
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
@@ -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
|
data/spec/vcloud/core/vm_spec.rb
CHANGED
@@ -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 '
|
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
|
+
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-
|
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:
|
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:
|
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: :
|
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: -
|
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
|
data/features/support/env.rb
DELETED
@@ -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|
|