vcloud-edge_gateway 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +16 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +160 -0
- data/Rakefile +23 -0
- data/bin/vcloud-edge +12 -0
- data/jenkins.sh +11 -0
- data/lib/vcloud/config_loader.rb +27 -0
- data/lib/vcloud/config_validator.rb +207 -0
- data/lib/vcloud/edge_gateway/configuration_differ.rb +17 -0
- data/lib/vcloud/edge_gateway/configuration_generator/firewall_service.rb +63 -0
- data/lib/vcloud/edge_gateway/configuration_generator/id_ranges.rb +10 -0
- data/lib/vcloud/edge_gateway/configuration_generator/load_balancer_service.rb +243 -0
- data/lib/vcloud/edge_gateway/configuration_generator/nat_service.rb +54 -0
- data/lib/vcloud/edge_gateway/edge_gateway_configuration.rb +41 -0
- data/lib/vcloud/edge_gateway/version.rb +6 -0
- data/lib/vcloud/edge_gateway.rb +32 -0
- data/lib/vcloud/edge_gateway_services.rb +26 -0
- data/lib/vcloud/schema/edge_gateway.rb +15 -0
- data/lib/vcloud/schema/firewall_service.rb +39 -0
- data/lib/vcloud/schema/load_balancer_service.rb +129 -0
- data/lib/vcloud/schema/nat_service.rb +35 -0
- data/scripts/generate_fog_conf_file.sh +6 -0
- data/spec/erb_helper.rb +11 -0
- data/spec/integration/edge_gateway/data/firewall_config.yaml.erb +17 -0
- data/spec/integration/edge_gateway/data/firewall_config_updated_rule.yaml.erb +17 -0
- data/spec/integration/edge_gateway/data/firewall_rule_order_test.yaml.erb +24 -0
- data/spec/integration/edge_gateway/data/hairpin_nat_config.yaml.erb +13 -0
- data/spec/integration/edge_gateway/data/incorrect_firewall_config.yaml +14 -0
- data/spec/integration/edge_gateway/data/nat_and_firewall_config.yaml.erb +32 -0
- data/spec/integration/edge_gateway/data/nat_config.yaml.erb +17 -0
- data/spec/integration/edge_gateway/edge_gateway_services_spec.rb +132 -0
- data/spec/integration/edge_gateway/firewall_service_spec.rb +201 -0
- data/spec/integration/edge_gateway/nat_service_spec.rb +208 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/vcloud/config_loader_spec.rb +112 -0
- data/spec/vcloud/config_validator_spec.rb +570 -0
- data/spec/vcloud/data/basic_preamble_test.erb +8 -0
- data/spec/vcloud/data/basic_preamble_test.erb.OUT +8 -0
- data/spec/vcloud/data/working.json +21 -0
- data/spec/vcloud/data/working.yaml +22 -0
- data/spec/vcloud/data/working_with_defaults.yaml +25 -0
- data/spec/vcloud/edge_gateway/configuration_differ_spec.rb +131 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-input.yaml +41 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-output.yaml +93 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-input.yaml +39 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-output.yaml +92 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-input.yaml +65 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-output.yaml +94 -0
- data/spec/vcloud/edge_gateway/configuration_generator/firewall_service_spec.rb +378 -0
- data/spec/vcloud/edge_gateway/configuration_generator/load_balancer_service_spec.rb +233 -0
- data/spec/vcloud/edge_gateway/configuration_generator/nat_service_spec.rb +360 -0
- data/spec/vcloud/edge_gateway/edge_gateway_configuration_spec.rb +182 -0
- data/spec/vcloud/edge_gateway/firewall_schema_validation_spec.rb +45 -0
- data/spec/vcloud/edge_gateway/load_balancer_schema_validation_spec.rb +153 -0
- data/spec/vcloud/edge_gateway/nat_schema_validation_spec.rb +93 -0
- data/vcloud-edge_gateway.gemspec +32 -0
- metadata +252 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
gateway: <%= edge_gateway_name %>
|
3
|
+
firewall_service:
|
4
|
+
firewall_rules:
|
5
|
+
- description: 'First Input Rule'
|
6
|
+
destination_port_range: '8081'
|
7
|
+
destination_ip: 10.10.1.2
|
8
|
+
source_ip: Any
|
9
|
+
- description: 'Second Input Rule'
|
10
|
+
destination_port_range: '8888'
|
11
|
+
destination_ip: 10.10.1.2
|
12
|
+
source_ip: Any
|
13
|
+
- description: 'Third Input Rule'
|
14
|
+
destination_port_range: '2222'
|
15
|
+
destination_ip: 10.10.1.2
|
16
|
+
source_ip: Any
|
17
|
+
- description: 'Fourth Input Rule'
|
18
|
+
destination_port_range: '9111'
|
19
|
+
destination_ip: 10.10.1.2
|
20
|
+
source_ip: Any
|
21
|
+
- description: 'Fifth Input Rule'
|
22
|
+
destination_port_range: '8113'
|
23
|
+
destination_ip: 10.10.1.2
|
24
|
+
source_ip: Any
|
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
gateway: <%= edge_gateway_name %>
|
3
|
+
nat_service:
|
4
|
+
nat_rules:
|
5
|
+
- enabled: true
|
6
|
+
description: 'A DNAT rule'
|
7
|
+
network_id: <%= org_vdc_network_id %>
|
8
|
+
rule_type: 'DNAT'
|
9
|
+
translated_ip: '10.10.1.2'
|
10
|
+
translated_port: '3412'
|
11
|
+
original_ip: <%= original_ip %>
|
12
|
+
original_port: '3412'
|
13
|
+
protocol: 'tcp'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
gateway: 'Test Edgegateway'
|
3
|
+
firewall_service:
|
4
|
+
policy: drop
|
5
|
+
log_default_action: true
|
6
|
+
firewall_rules_incorrect:
|
7
|
+
- enabled: true
|
8
|
+
description: 'A rule'
|
9
|
+
policy: allow
|
10
|
+
protocols: 'tcp'
|
11
|
+
destination_port_range: Any
|
12
|
+
destination_ip: Any
|
13
|
+
source_port_range: Any
|
14
|
+
source_ip: 192.0.2.2
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
gateway: <%= edge_gateway_name %>
|
3
|
+
nat_service:
|
4
|
+
nat_rules:
|
5
|
+
- enabled: true
|
6
|
+
network_id: <%= network_id %>
|
7
|
+
rule_type: 'DNAT'
|
8
|
+
translated_ip: '10.10.1.2-10.10.1.3'
|
9
|
+
translated_port: '3412'
|
10
|
+
original_ip: <%= original_ip %>
|
11
|
+
original_port: '3412'
|
12
|
+
protocol: 'tcp'
|
13
|
+
- enabled: true
|
14
|
+
network_id: <%= network_id %>
|
15
|
+
rule_type: 'SNAT'
|
16
|
+
translated_ip: <%= original_ip %>
|
17
|
+
original_ip: '10.10.1.2-10.10.1.3'
|
18
|
+
firewall_service:
|
19
|
+
policy: drop
|
20
|
+
log_default_action: true
|
21
|
+
firewall_rules:
|
22
|
+
- enabled: true
|
23
|
+
description: 'A rule'
|
24
|
+
policy: allow
|
25
|
+
protocols: 'tcp'
|
26
|
+
destination_port_range: Any
|
27
|
+
destination_ip: 10.10.1.2
|
28
|
+
source_port_range: Any
|
29
|
+
source_ip: 192.0.2.2
|
30
|
+
- enabled: true
|
31
|
+
destination_ip: '10.10.1.3-10.10.1.5'
|
32
|
+
source_ip: 192.0.2.2/24
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
gateway: <%= edge_gateway_name %>
|
3
|
+
nat_service:
|
4
|
+
nat_rules:
|
5
|
+
- enabled: true
|
6
|
+
network_id: <%= network_id %>
|
7
|
+
rule_type: 'DNAT'
|
8
|
+
translated_ip: '10.10.1.2-10.10.1.3'
|
9
|
+
translated_port: '3412'
|
10
|
+
original_ip: <%= original_ip %>
|
11
|
+
original_port: '3412'
|
12
|
+
protocol: 'tcp'
|
13
|
+
- enabled: true
|
14
|
+
network_id: <%= network_id %>
|
15
|
+
rule_type: 'SNAT'
|
16
|
+
translated_ip: <%= original_ip %>
|
17
|
+
original_ip: '10.10.1.2-10.10.1.3'
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Vcloud
|
4
|
+
describe EdgeGatewayServices do
|
5
|
+
|
6
|
+
required_env = {
|
7
|
+
'VCLOUD_EDGE_GATEWAY' => 'to name of VSE',
|
8
|
+
'VCLOUD_PROVIDER_NETWORK_ID' => 'to ID of VSE external network',
|
9
|
+
'VCLOUD_PROVIDER_NETWORK_IP' => 'to an available IP on VSE external network',
|
10
|
+
'VCLOUD_NETWORK1_ID' => 'to the ID of a VSE internal network',
|
11
|
+
'VCLOUD_NETWORK1_NAME' => 'to the name of the VSE internal network',
|
12
|
+
'VCLOUD_NETWORK1_IP' => 'to an ID on the VSE internal network',
|
13
|
+
}
|
14
|
+
|
15
|
+
error = false
|
16
|
+
required_env.each do |var,message|
|
17
|
+
unless ENV[var]
|
18
|
+
puts "Must set #{var} #{message}" unless ENV[var]
|
19
|
+
error = true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
Kernel.exit(2) if error
|
23
|
+
|
24
|
+
before(:all) do
|
25
|
+
@edge_name = ENV['VCLOUD_EDGE_GATEWAY']
|
26
|
+
@ext_net_id = ENV['VCLOUD_PROVIDER_NETWORK_ID']
|
27
|
+
@ext_net_ip = ENV['VCLOUD_PROVIDER_NETWORK_IP']
|
28
|
+
@ext_net_name = ENV['VCLOUD_PROVIDER_NETWORK_NAME']
|
29
|
+
@int_net_id = ENV['VCLOUD_NETWORK1_ID']
|
30
|
+
@int_net_ip = ENV['VCLOUD_NETWORK1_IP']
|
31
|
+
@int_net_name = ENV['VCLOUD_NETWORK1_NAME']
|
32
|
+
@files_to_delete = []
|
33
|
+
end
|
34
|
+
|
35
|
+
context "Test EdgeGatewayServices with multiple services" do
|
36
|
+
|
37
|
+
before(:all) do
|
38
|
+
reset_edge_gateway
|
39
|
+
@initial_config_file = generate_input_config_file('nat_and_firewall_config.yaml.erb', edge_gateway_erb_input)
|
40
|
+
@edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
context "Check update is functional" do
|
44
|
+
|
45
|
+
before(:all) do
|
46
|
+
local_config = ConfigLoader.new.load_config(@initial_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be starting our tests from an empty EdgeGateway" do
|
50
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration]
|
51
|
+
expect(remote_vcloud_config[:FirewallService][:FirewallRule].empty?).to be_true
|
52
|
+
expect(remote_vcloud_config[:NatService][:NatRule].empty?).to be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should only need to make one call to Core::EdgeGateway.update_configuration to update configuration" do
|
56
|
+
q = Query.new('edgeGateway', :filter => "name==#{@edge_name}")
|
57
|
+
result = q.get_all_results
|
58
|
+
latest_task = result.first[:task]
|
59
|
+
|
60
|
+
expect_any_instance_of(Core::EdgeGateway).to receive(:update_configuration).exactly(1).times.and_call_original
|
61
|
+
EdgeGatewayServices.new.update(@initial_config_file)
|
62
|
+
|
63
|
+
test_result = q.get_all_results
|
64
|
+
test_latest_task = test_result.first[:task]
|
65
|
+
|
66
|
+
# confirm that a task has been run on the EdgeGateway
|
67
|
+
expect(latest_task == test_latest_task).to be_false
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should now have nat and firewall rules configured" do
|
71
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration]
|
72
|
+
expect(remote_vcloud_config[:FirewallService][:FirewallRule].empty?).to be_false
|
73
|
+
expect(remote_vcloud_config[:NatService][:NatRule].empty?).to be_false
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should not update the EdgeGateway again if the config hasn't changed" do
|
77
|
+
q = Query.new('edgeGateway', :filter => "name==#{@edge_name}")
|
78
|
+
result = q.get_all_results
|
79
|
+
latest_task = result.first[:task]
|
80
|
+
|
81
|
+
EdgeGatewayServices.new.update(@initial_config_file)
|
82
|
+
|
83
|
+
test_result = q.get_all_results
|
84
|
+
test_latest_task = result.first[:task]
|
85
|
+
|
86
|
+
# No task has been run on the EdgeGateway since the one before update was called
|
87
|
+
expect(latest_task == test_latest_task).to be_true
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
after(:all) do
|
93
|
+
reset_edge_gateway unless ENV['VCLOUD_NO_RESET_VSE_AFTER']
|
94
|
+
remove_temp_config_files
|
95
|
+
end
|
96
|
+
|
97
|
+
def remove_temp_config_files
|
98
|
+
FileUtils.rm(@files_to_delete)
|
99
|
+
end
|
100
|
+
|
101
|
+
def reset_edge_gateway
|
102
|
+
edge_gateway = Core::EdgeGateway.get_by_name @edge_name
|
103
|
+
edge_gateway.update_configuration({
|
104
|
+
FirewallService: {IsEnabled: false, FirewallRule: []},
|
105
|
+
NatService: {:IsEnabled => "true", :NatRule => []},
|
106
|
+
LoadBalancerService: {
|
107
|
+
IsEnabled: "false",
|
108
|
+
Pool: [],
|
109
|
+
VirtualServer: []
|
110
|
+
}
|
111
|
+
})
|
112
|
+
end
|
113
|
+
|
114
|
+
def generate_input_config_file(data_file, erb_input)
|
115
|
+
config_erb = File.expand_path("data/#{data_file}", File.dirname(__FILE__))
|
116
|
+
output_file = ErbHelper.convert_erb_template_to_yaml(erb_input, config_erb)
|
117
|
+
@files_to_delete << output_file
|
118
|
+
output_file
|
119
|
+
end
|
120
|
+
|
121
|
+
def edge_gateway_erb_input
|
122
|
+
{
|
123
|
+
edge_gateway_name: @edge_name,
|
124
|
+
network_id: @ext_net_id,
|
125
|
+
original_ip: @ext_net_ip,
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Vcloud
|
4
|
+
describe EdgeGatewayServices do
|
5
|
+
|
6
|
+
required_env = {
|
7
|
+
'VCLOUD_EDGE_GATEWAY' => 'to name of VSE',
|
8
|
+
'VCLOUD_PROVIDER_NETWORK_ID' => 'to ID of VSE external network',
|
9
|
+
'VCLOUD_PROVIDER_NETWORK_IP' => 'to an available IP on VSE external network',
|
10
|
+
'VCLOUD_NETWORK1_ID' => 'to the ID of a VSE internal network',
|
11
|
+
'VCLOUD_NETWORK1_NAME' => 'to the name of the VSE internal network',
|
12
|
+
'VCLOUD_NETWORK1_IP' => 'to an ID on the VSE internal network',
|
13
|
+
}
|
14
|
+
|
15
|
+
error = false
|
16
|
+
required_env.each do |var,message|
|
17
|
+
unless ENV[var]
|
18
|
+
puts "Must set #{var} #{message}" unless ENV[var]
|
19
|
+
error = true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
Kernel.exit(2) if error
|
23
|
+
|
24
|
+
before(:all) do
|
25
|
+
@edge_name = ENV['VCLOUD_EDGE_GATEWAY']
|
26
|
+
@ext_net_id = ENV['VCLOUD_PROVIDER_NETWORK_ID']
|
27
|
+
@ext_net_ip = ENV['VCLOUD_PROVIDER_NETWORK_IP']
|
28
|
+
@ext_net_name = ENV['VCLOUD_PROVIDER_NETWORK_NAME']
|
29
|
+
@int_net_id = ENV['VCLOUD_NETWORK1_ID']
|
30
|
+
@int_net_ip = ENV['VCLOUD_NETWORK1_IP']
|
31
|
+
@int_net_name = ENV['VCLOUD_NETWORK1_NAME']
|
32
|
+
@files_to_delete = []
|
33
|
+
end
|
34
|
+
|
35
|
+
context "Test FirewallService specifics of EdgeGatewayServices" do
|
36
|
+
|
37
|
+
before(:all) do
|
38
|
+
reset_edge_gateway
|
39
|
+
@initial_firewall_config_file = generate_input_config_file('firewall_config.yaml.erb', edge_gateway_erb_input)
|
40
|
+
@edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
|
41
|
+
@firewall_service = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
context "Check input schema checking is working" do
|
45
|
+
|
46
|
+
it "should raise exception if input yaml does not match with schema" do
|
47
|
+
config_yaml = File.expand_path('data/incorrect_firewall_config.yaml', File.dirname(__FILE__))
|
48
|
+
expect(Vcloud::EdgeGateway.logger).to receive(:fatal)
|
49
|
+
expect { EdgeGatewayServices.new.update(config_yaml) }.to raise_error('Supplied configuration does not match supplied schema')
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
context "Check update is functional" do
|
55
|
+
|
56
|
+
before(:all) do
|
57
|
+
local_config = ConfigLoader.new.load_config(@initial_firewall_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
|
58
|
+
@local_vcloud_config = EdgeGateway::ConfigurationGenerator::FirewallService.new.generate_fog_config(local_config[:firewall_service])
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be starting our tests from an empty firewall" do
|
62
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
|
63
|
+
expect(remote_vcloud_config[:FirewallRule].empty?).to be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should only need to make one call to Core::EdgeGateway.update_configuration" do
|
67
|
+
expect_any_instance_of(Core::EdgeGateway).to receive(:update_configuration).exactly(1).times.and_call_original
|
68
|
+
EdgeGatewayServices.new.update(@initial_firewall_config_file)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should have configured at least one firewall rule" do
|
72
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
|
73
|
+
expect(remote_vcloud_config[:FirewallRule].empty?).to be_false
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should have configured the same number of firewall rules as in our configuration" do
|
77
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
|
78
|
+
expect(remote_vcloud_config[:FirewallRule].size).
|
79
|
+
to eq(@local_vcloud_config[:FirewallRule].size)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "and then should not configure the firewall service if updated again with the same configuration (idempotency)" do
|
83
|
+
expect(Vcloud::EdgeGateway.logger).to receive(:info).with('EdgeGatewayServices.update: Configuration is already up to date. Skipping.')
|
84
|
+
EdgeGatewayServices.new.update(@initial_firewall_config_file)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "ConfigurationDiffer should return empty if local and remote firewall configs match" do
|
88
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
|
89
|
+
differ = EdgeGateway::ConfigurationDiffer.new(@local_vcloud_config, remote_vcloud_config)
|
90
|
+
diff_output = differ.diff
|
91
|
+
expect(diff_output).to eq([])
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should highlight a difference if local firewall config has been updated" do
|
95
|
+
input_config_file = generate_input_config_file('firewall_config_updated_rule.yaml.erb', edge_gateway_erb_input)
|
96
|
+
|
97
|
+
local_config = ConfigLoader.new.load_config(input_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
|
98
|
+
local_firewall_config = EdgeGateway::ConfigurationGenerator::FirewallService.new.generate_fog_config(local_config[:firewall_service])
|
99
|
+
|
100
|
+
edge_gateway = Core::EdgeGateway.get_by_name local_config[:gateway]
|
101
|
+
remote_config = edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration]
|
102
|
+
remote_firewall_config = remote_config[:FirewallService]
|
103
|
+
|
104
|
+
differ = EdgeGateway::ConfigurationDiffer.new(local_firewall_config, remote_firewall_config)
|
105
|
+
diff_output = differ.diff
|
106
|
+
|
107
|
+
expect(diff_output.empty?).to be_false
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
context "ensure EdgeGateway FirewallService configuration is as expected" do
|
113
|
+
before(:all) do
|
114
|
+
@firewall_service = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should configure firewall rule with destination and source ip addresses" do
|
118
|
+
expect(@firewall_service[:FirewallRule].first).to eq({:Id => "1",
|
119
|
+
:IsEnabled => "true",
|
120
|
+
:MatchOnTranslate => "false",
|
121
|
+
:Description => "A rule",
|
122
|
+
:Policy => "allow",
|
123
|
+
:Protocols => {:Tcp => "true"},
|
124
|
+
:Port => "-1",
|
125
|
+
:DestinationPortRange => "Any",
|
126
|
+
:DestinationIp => "10.10.1.2",
|
127
|
+
:SourcePort => "-1",
|
128
|
+
:SourcePortRange => "Any",
|
129
|
+
:SourceIp => "192.0.2.2",
|
130
|
+
:EnableLogging => "false"})
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should configure firewall rule with destination and source ip ranges" do
|
134
|
+
expect(@firewall_service[:FirewallRule].last).to eq({:Id => "2",
|
135
|
+
:IsEnabled => "true",
|
136
|
+
:MatchOnTranslate => "false",
|
137
|
+
:Description => "",
|
138
|
+
:Policy => "allow",
|
139
|
+
:Protocols => {:Tcp => "true"},
|
140
|
+
:Port => "-1",
|
141
|
+
:DestinationPortRange => "Any",
|
142
|
+
:DestinationIp => "10.10.1.3-10.10.1.5",
|
143
|
+
:SourcePort => "-1",
|
144
|
+
:SourcePortRange => "Any",
|
145
|
+
:SourceIp => "192.0.2.2/24",
|
146
|
+
:EnableLogging => "false"})
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
context "Specific FirewallService update tests" do
|
152
|
+
|
153
|
+
it "should have the same rule order as the input rule order" do
|
154
|
+
input_config_file = generate_input_config_file('firewall_rule_order_test.yaml.erb', edge_gateway_erb_input)
|
155
|
+
EdgeGatewayServices.new.update(input_config_file)
|
156
|
+
remote_rules = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService][:FirewallRule]
|
157
|
+
remote_descriptions_list = remote_rules.map {|rule| rule[:Description]}
|
158
|
+
expect(remote_descriptions_list).
|
159
|
+
to eq([
|
160
|
+
"First Input Rule",
|
161
|
+
"Second Input Rule",
|
162
|
+
"Third Input Rule",
|
163
|
+
"Fourth Input Rule",
|
164
|
+
"Fifth Input Rule"
|
165
|
+
])
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
after(:all) do
|
172
|
+
reset_edge_gateway unless ENV['VCLOUD_NO_RESET_VSE_AFTER']
|
173
|
+
FileUtils.rm(@files_to_delete)
|
174
|
+
end
|
175
|
+
|
176
|
+
def reset_edge_gateway
|
177
|
+
edge_gateway = Core::EdgeGateway.get_by_name @edge_name
|
178
|
+
edge_gateway.update_configuration({
|
179
|
+
FirewallService: {IsEnabled: false, FirewallRule: []},
|
180
|
+
})
|
181
|
+
end
|
182
|
+
|
183
|
+
def generate_input_config_file(data_file, erb_input)
|
184
|
+
config_erb = File.expand_path("data/#{data_file}", File.dirname(__FILE__))
|
185
|
+
output_file = ErbHelper.convert_erb_template_to_yaml(erb_input, config_erb)
|
186
|
+
@files_to_delete << output_file
|
187
|
+
output_file
|
188
|
+
end
|
189
|
+
|
190
|
+
def edge_gateway_erb_input
|
191
|
+
{
|
192
|
+
:edge_gateway_name => @edge_name,
|
193
|
+
:edge_gateway_ext_network_id => @ext_net_id,
|
194
|
+
:edge_gateway_ext_network_ip => @ext_net_ip,
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Vcloud
|
4
|
+
describe EdgeGatewayServices do
|
5
|
+
|
6
|
+
required_env = {
|
7
|
+
'VCLOUD_EDGE_GATEWAY' => 'to name of VSE',
|
8
|
+
'VCLOUD_PROVIDER_NETWORK_ID' => 'to ID of VSE external network',
|
9
|
+
'VCLOUD_PROVIDER_NETWORK_IP' => 'to an available IP on VSE external network',
|
10
|
+
'VCLOUD_NETWORK1_ID' => 'to the ID of a VSE internal network',
|
11
|
+
'VCLOUD_NETWORK1_NAME' => 'to the name of the VSE internal network',
|
12
|
+
'VCLOUD_NETWORK1_IP' => 'to an ID on the VSE internal network',
|
13
|
+
}
|
14
|
+
|
15
|
+
error = false
|
16
|
+
required_env.each do |var,message|
|
17
|
+
unless ENV[var]
|
18
|
+
puts "Must set #{var} #{message}" unless ENV[var]
|
19
|
+
error = true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
Kernel.exit(2) if error
|
23
|
+
|
24
|
+
before(:all) do
|
25
|
+
@edge_name = ENV['VCLOUD_EDGE_GATEWAY']
|
26
|
+
@ext_net_id = ENV['VCLOUD_PROVIDER_NETWORK_ID']
|
27
|
+
@ext_net_ip = ENV['VCLOUD_PROVIDER_NETWORK_IP']
|
28
|
+
@ext_net_name = ENV['VCLOUD_PROVIDER_NETWORK_NAME']
|
29
|
+
@int_net_id = ENV['VCLOUD_NETWORK1_ID']
|
30
|
+
@int_net_ip = ENV['VCLOUD_NETWORK1_IP']
|
31
|
+
@int_net_name = ENV['VCLOUD_NETWORK1_NAME']
|
32
|
+
@files_to_delete = []
|
33
|
+
end
|
34
|
+
|
35
|
+
context "Test NatService specifics of EdgeGatewayServices" do
|
36
|
+
|
37
|
+
before(:all) do
|
38
|
+
reset_edge_gateway
|
39
|
+
@initial_nat_config_file = generate_input_config_file(
|
40
|
+
'nat_config.yaml.erb', {
|
41
|
+
edge_gateway_name: @edge_name,
|
42
|
+
network_id: @ext_net_id,
|
43
|
+
original_ip: @ext_net_ip,
|
44
|
+
}
|
45
|
+
)
|
46
|
+
@edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Check update is functional" do
|
50
|
+
|
51
|
+
before(:all) do
|
52
|
+
local_config = ConfigLoader.new.load_config(@initial_nat_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
|
53
|
+
@local_vcloud_config = EdgeGateway::ConfigurationGenerator::NatService.new(@edge_name, local_config[:nat_service]).generate_fog_config
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should be starting our tests from an empty NatService" do
|
57
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
|
58
|
+
expect(remote_vcloud_config[:NatRule].empty?).to be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should only make one EdgeGateway update task, to minimise EdgeGateway reload events" do
|
62
|
+
start_time = DateTime.now()
|
63
|
+
task_list_before_update = get_all_edge_gateway_update_tasks_ordered_by_start_date_since_time(start_time)
|
64
|
+
EdgeGatewayServices.new.update(@initial_nat_config_file)
|
65
|
+
task_list_after_update = get_all_edge_gateway_update_tasks_ordered_by_start_date_since_time(start_time)
|
66
|
+
expect(task_list_after_update.size - task_list_before_update.size).to be(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should have configured at least one NAT rule" do
|
70
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
|
71
|
+
expect(remote_vcloud_config[:NatRule].empty?).to be_false
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should have configured the same number of nat rules as in our configuration" do
|
75
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
|
76
|
+
expect(remote_vcloud_config[:NatRule].size).
|
77
|
+
to eq(@local_vcloud_config[:NatRule].size)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "ConfigurationDiffer should return empty if local and remote nat configs match" do
|
81
|
+
remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
|
82
|
+
differ = EdgeGateway::ConfigurationDiffer.new(@local_vcloud_config, remote_vcloud_config)
|
83
|
+
diff_output = differ.diff
|
84
|
+
expect(diff_output).to eq([])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "and then should not configure the firewall service if updated again with the same configuration (idempotency)" do
|
88
|
+
expect(Vcloud::EdgeGateway.logger).to receive(:info).with('EdgeGatewayServices.update: Configuration is already up to date. Skipping.')
|
89
|
+
EdgeGatewayServices.new.update(@initial_nat_config_file)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
context "ensure updated EdgeGateway NatService configuration is as expected" do
|
95
|
+
before(:all) do
|
96
|
+
@nat_service = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should configure DNAT rule" do
|
100
|
+
dnat_rule = @nat_service[:NatRule].first
|
101
|
+
expect(dnat_rule).not_to be_nil
|
102
|
+
expect(dnat_rule[:RuleType]).to eq('DNAT')
|
103
|
+
expect(dnat_rule[:Id]).to eq('65537')
|
104
|
+
expect(dnat_rule[:IsEnabled]).to eq('true')
|
105
|
+
expect(dnat_rule[:GatewayNatRule][:Interface][:href]).to include(@ext_net_id)
|
106
|
+
expect(dnat_rule[:GatewayNatRule][:OriginalIp]).to eq(@ext_net_ip)
|
107
|
+
expect(dnat_rule[:GatewayNatRule][:OriginalPort]).to eq('3412')
|
108
|
+
expect(dnat_rule[:GatewayNatRule][:TranslatedIp]).to eq('10.10.1.2-10.10.1.3')
|
109
|
+
expect(dnat_rule[:GatewayNatRule][:TranslatedPort]).to eq('3412')
|
110
|
+
expect(dnat_rule[:GatewayNatRule][:Protocol]).to eq('tcp')
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should configure SNAT rule" do
|
114
|
+
snat_rule = @nat_service[:NatRule].last
|
115
|
+
expect(snat_rule).not_to be_nil
|
116
|
+
expect(snat_rule[:RuleType]).to eq('SNAT')
|
117
|
+
expect(snat_rule[:Id]).to eq('65538')
|
118
|
+
expect(snat_rule[:IsEnabled]).to eq('true')
|
119
|
+
expect(snat_rule[:GatewayNatRule][:Interface][:href]).to include(@ext_net_id)
|
120
|
+
expect(snat_rule[:GatewayNatRule][:OriginalIp]).to eq('10.10.1.2-10.10.1.3')
|
121
|
+
expect(snat_rule[:GatewayNatRule][:TranslatedIp]).to eq(@ext_net_ip)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
context "ensure hairpin NAT rules are specifiable" do
|
127
|
+
|
128
|
+
it "and then should configure hairpin NATting with orgVdcNetwork" do
|
129
|
+
input_config_file = generate_input_config_file('hairpin_nat_config.yaml.erb', {
|
130
|
+
edge_gateway_name: @edge_name,
|
131
|
+
org_vdc_network_id: @int_net_id,
|
132
|
+
original_ip: @int_net_ip,
|
133
|
+
})
|
134
|
+
|
135
|
+
EdgeGatewayServices.new.update(input_config_file)
|
136
|
+
|
137
|
+
edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
|
138
|
+
nat_service = edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
|
139
|
+
expected_rule = nat_service[:NatRule].first
|
140
|
+
expect(expected_rule).not_to be_nil
|
141
|
+
expect(expected_rule[:RuleType]).to eq('DNAT')
|
142
|
+
expect(expected_rule[:Id]).to eq('65537')
|
143
|
+
expect(expected_rule[:RuleType]).to eq('DNAT')
|
144
|
+
expect(expected_rule[:IsEnabled]).to eq('true')
|
145
|
+
expect(expected_rule[:GatewayNatRule][:Interface][:name]).to eq(@int_net_name)
|
146
|
+
expect(expected_rule[:GatewayNatRule][:OriginalIp]).to eq(@int_net_ip)
|
147
|
+
expect(expected_rule[:GatewayNatRule][:OriginalPort]).to eq('3412')
|
148
|
+
expect(expected_rule[:GatewayNatRule][:TranslatedIp]).to eq('10.10.1.2')
|
149
|
+
expect(expected_rule[:GatewayNatRule][:TranslatedPort]).to eq('3412')
|
150
|
+
expect(expected_rule[:GatewayNatRule][:Protocol]).to eq('tcp')
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should raise error if network provided in rule does not exist" do
|
154
|
+
random_network_id = SecureRandom.uuid
|
155
|
+
input_config_file = generate_input_config_file('nat_config.yaml.erb', {
|
156
|
+
edge_gateway_name: @edge_name,
|
157
|
+
network_id: random_network_id,
|
158
|
+
original_ip: @int_net_ip,
|
159
|
+
})
|
160
|
+
expect{EdgeGatewayServices.new.update(input_config_file)}.
|
161
|
+
to raise_error("unable to find gateway network interface with id #{random_network_id}")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
after(:all) do
|
166
|
+
reset_edge_gateway unless ENV['VCLOUD_NO_RESET_VSE_AFTER']
|
167
|
+
remove_temp_config_files
|
168
|
+
end
|
169
|
+
|
170
|
+
def remove_temp_config_files
|
171
|
+
FileUtils.rm(@files_to_delete)
|
172
|
+
end
|
173
|
+
|
174
|
+
def reset_edge_gateway
|
175
|
+
edge_gateway = Core::EdgeGateway.get_by_name @edge_name
|
176
|
+
edge_gateway.update_configuration({
|
177
|
+
NatService: {:IsEnabled => "true", :NatRule => []},
|
178
|
+
})
|
179
|
+
end
|
180
|
+
|
181
|
+
def generate_input_config_file(data_file, erb_input)
|
182
|
+
config_erb = File.expand_path("data/#{data_file}", File.dirname(__FILE__))
|
183
|
+
output_file = ErbHelper.convert_erb_template_to_yaml(erb_input, config_erb)
|
184
|
+
@files_to_delete << output_file
|
185
|
+
output_file
|
186
|
+
end
|
187
|
+
|
188
|
+
def edge_gateway_erb_input
|
189
|
+
{
|
190
|
+
:edge_gateway_name => @edge_name,
|
191
|
+
:edge_gateway_ext_network_id => @ext_net_id,
|
192
|
+
:edge_gateway_ext_network_ip => @ext_net_ip,
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
def get_all_edge_gateway_update_tasks_ordered_by_start_date_since_time(timestamp)
|
197
|
+
vcloud_time = timestamp.strftime('%FT%T.000Z')
|
198
|
+
q = Query.new('task',
|
199
|
+
:filter => "name==networkConfigureEdgeGatewayServices;objectName==#{@edge_name};startDate=ge=#{vcloud_time}",
|
200
|
+
:sortDesc => 'startDate',
|
201
|
+
)
|
202
|
+
q.get_all_results
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'erb_helper'
|
3
|
+
|
4
|
+
SimpleCov.profiles.define 'gem' do
|
5
|
+
add_filter '/spec/'
|
6
|
+
add_filter '/features/'
|
7
|
+
add_filter '/vendor/'
|
8
|
+
|
9
|
+
add_group 'Libraries', '/lib/'
|
10
|
+
end
|
11
|
+
|
12
|
+
SimpleCov.start 'gem'
|
13
|
+
|
14
|
+
require 'bundler/setup'
|
15
|
+
require 'vcloud/edge_gateway'
|
16
|
+
|
17
|
+
|
18
|
+
SimpleCov.at_exit do
|
19
|
+
SimpleCov.result.format!
|
20
|
+
# do not change the coverage percentage, instead add more unit tests to fix coverage failures.
|
21
|
+
if SimpleCov.result.covered_percent < 60
|
22
|
+
print "ERROR::BAD_COVERAGE\n"
|
23
|
+
print "Coverage is less than acceptable limit(71%). Please add more tests to improve the coverage"
|
24
|
+
exit(1)
|
25
|
+
end
|
26
|
+
end
|