vcloud-edge_gateway 0.0.1

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.
Files changed (58) hide show
  1. data/.gitignore +16 -0
  2. data/Gemfile +9 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +160 -0
  5. data/Rakefile +23 -0
  6. data/bin/vcloud-edge +12 -0
  7. data/jenkins.sh +11 -0
  8. data/lib/vcloud/config_loader.rb +27 -0
  9. data/lib/vcloud/config_validator.rb +207 -0
  10. data/lib/vcloud/edge_gateway/configuration_differ.rb +17 -0
  11. data/lib/vcloud/edge_gateway/configuration_generator/firewall_service.rb +63 -0
  12. data/lib/vcloud/edge_gateway/configuration_generator/id_ranges.rb +10 -0
  13. data/lib/vcloud/edge_gateway/configuration_generator/load_balancer_service.rb +243 -0
  14. data/lib/vcloud/edge_gateway/configuration_generator/nat_service.rb +54 -0
  15. data/lib/vcloud/edge_gateway/edge_gateway_configuration.rb +41 -0
  16. data/lib/vcloud/edge_gateway/version.rb +6 -0
  17. data/lib/vcloud/edge_gateway.rb +32 -0
  18. data/lib/vcloud/edge_gateway_services.rb +26 -0
  19. data/lib/vcloud/schema/edge_gateway.rb +15 -0
  20. data/lib/vcloud/schema/firewall_service.rb +39 -0
  21. data/lib/vcloud/schema/load_balancer_service.rb +129 -0
  22. data/lib/vcloud/schema/nat_service.rb +35 -0
  23. data/scripts/generate_fog_conf_file.sh +6 -0
  24. data/spec/erb_helper.rb +11 -0
  25. data/spec/integration/edge_gateway/data/firewall_config.yaml.erb +17 -0
  26. data/spec/integration/edge_gateway/data/firewall_config_updated_rule.yaml.erb +17 -0
  27. data/spec/integration/edge_gateway/data/firewall_rule_order_test.yaml.erb +24 -0
  28. data/spec/integration/edge_gateway/data/hairpin_nat_config.yaml.erb +13 -0
  29. data/spec/integration/edge_gateway/data/incorrect_firewall_config.yaml +14 -0
  30. data/spec/integration/edge_gateway/data/nat_and_firewall_config.yaml.erb +32 -0
  31. data/spec/integration/edge_gateway/data/nat_config.yaml.erb +17 -0
  32. data/spec/integration/edge_gateway/edge_gateway_services_spec.rb +132 -0
  33. data/spec/integration/edge_gateway/firewall_service_spec.rb +201 -0
  34. data/spec/integration/edge_gateway/nat_service_spec.rb +208 -0
  35. data/spec/spec_helper.rb +26 -0
  36. data/spec/vcloud/config_loader_spec.rb +112 -0
  37. data/spec/vcloud/config_validator_spec.rb +570 -0
  38. data/spec/vcloud/data/basic_preamble_test.erb +8 -0
  39. data/spec/vcloud/data/basic_preamble_test.erb.OUT +8 -0
  40. data/spec/vcloud/data/working.json +21 -0
  41. data/spec/vcloud/data/working.yaml +22 -0
  42. data/spec/vcloud/data/working_with_defaults.yaml +25 -0
  43. data/spec/vcloud/edge_gateway/configuration_differ_spec.rb +131 -0
  44. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-input.yaml +41 -0
  45. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-output.yaml +93 -0
  46. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-input.yaml +39 -0
  47. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-output.yaml +92 -0
  48. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-input.yaml +65 -0
  49. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-output.yaml +94 -0
  50. data/spec/vcloud/edge_gateway/configuration_generator/firewall_service_spec.rb +378 -0
  51. data/spec/vcloud/edge_gateway/configuration_generator/load_balancer_service_spec.rb +233 -0
  52. data/spec/vcloud/edge_gateway/configuration_generator/nat_service_spec.rb +360 -0
  53. data/spec/vcloud/edge_gateway/edge_gateway_configuration_spec.rb +182 -0
  54. data/spec/vcloud/edge_gateway/firewall_schema_validation_spec.rb +45 -0
  55. data/spec/vcloud/edge_gateway/load_balancer_schema_validation_spec.rb +153 -0
  56. data/spec/vcloud/edge_gateway/nat_schema_validation_spec.rb +93 -0
  57. data/vcloud-edge_gateway.gemspec +32 -0
  58. 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
@@ -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