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 +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|
|