1 require 'puppetlabs_spec_helper/module_spec_helper'
2 require 'rspec-puppet-facts'
3 include RspecPuppetFacts
5 # Customize filters to ignore 3rd-party code
6 # If the rspec coverage report shows not-our-code results, add it here
9 RSpec::Puppet::Coverage.filters.push(*custom_filters)
12 # NB: This is a library of helper fns used by the rspec-puppet tests
15 # Tests that are common to all possible configurations
16 def generic_tests(options = {})
17 java_opts = options.fetch(:java_opts, '-Djava.net.preferIPv4Stack=true')
19 # Confirm that module compiles
21 it { should compile.with_all_deps }
23 # Confirm presence of classes
24 it { should contain_class('opendaylight') }
25 it { should contain_class('opendaylight::params') }
26 it { should contain_class('opendaylight::install') }
27 it { should contain_class('opendaylight::config') }
28 it { should contain_class('opendaylight::post_config') }
29 it { should contain_class('opendaylight::service') }
31 # Confirm relationships between classes
32 it { should contain_class('opendaylight::install').that_comes_before('Class[opendaylight::config]') }
33 it { should contain_class('opendaylight::config').that_requires('Class[opendaylight::install]') }
34 it { should contain_class('opendaylight::config').that_notifies('Class[opendaylight::service]') }
35 it { should contain_class('opendaylight::service').that_subscribes_to('Class[opendaylight::config]') }
36 it { should contain_class('opendaylight::service').that_comes_before('Class[opendaylight]') }
37 it { should contain_class('opendaylight::post_config').that_requires('Class[opendaylight::service]') }
38 it { should contain_class('opendaylight::post_config').that_comes_before('Class[opendaylight]') }
39 it { should contain_class('opendaylight').that_requires('Class[opendaylight::service]') }
41 # Confirm presence of generic resources
42 it { should contain_service('opendaylight') }
43 it { should contain_file('org.apache.karaf.features.cfg') }
45 # Confirm properties of generic resources
46 # NB: These hashes don't work with Ruby 1.8.7, but we
47 # don't support 1.8.7 so that's okay. See issue #36.
49 should contain_service('opendaylight').with(
50 'ensure' => 'running',
52 'hasstatus' => 'true',
53 'hasrestart' => 'true',
57 should contain_file('org.apache.karaf.features.cfg').with(
59 'path' => '/opt/opendaylight/etc/org.apache.karaf.features.cfg',
66 should contain_file_line('Karaf Java Options').with(
67 'ensure' => 'present',
68 'path' => '/opt/opendaylight/bin/karaf',
69 'line' => "JAVA_OPTS=#{java_opts}",
70 'match' => '^JAVA_OPTS=.*$',
71 'after' => '^PROGNAME=.*$'
77 # Shared tests that specialize in testing log file size and rollover
78 def log_settings(options = {})
79 # Extraxt params. The dafault value should be same as in opendaylight::params
80 log_max_size = options.fetch(:log_max_size, '10GB')
81 log_max_rollover = options.fetch(:log_max_rollover, 2)
82 log_mechanism = options.fetch(:log_mechanism, 'file')
84 if log_mechanism == 'console'
86 should contain_file_line('consoleappender').with(
87 'path' => '/opt/opendaylight/etc/org.ops4j.pax.logging.cfg',
88 'line' => 'karaf.log.console=INFO',
89 'after' => 'log4j2.rootLogger.appenderRef.Console.filter.threshold.type = ThresholdFilter',
90 'match' => '^karaf.log.console.*$'
95 should contain_file_line('logmaxsize').with(
96 'path' => '/opt/opendaylight/etc/org.ops4j.pax.logging.cfg',
97 'line' => "log4j2.appender.rolling.policies.size.size = #{log_max_size}",
98 'match' => '^log4j2.appender.rolling.policies.size.size.*$',
102 should contain_file_line('rolloverstrategy').with(
103 'path' => '/opt/opendaylight/etc/org.ops4j.pax.logging.cfg',
104 'line' => 'log4j2.appender.rolling.strategy.type = DefaultRolloverStrategy'
108 should contain_file_line('logmaxrollover').with(
109 'path' => '/opt/opendaylight/etc/org.ops4j.pax.logging.cfg',
110 'line' => "log4j2.appender.rolling.strategy.max = #{log_max_rollover}",
111 'match' => '^log4j2.appender.rolling.strategy.max.*$',
117 # Shared tests that specialize in testing Karaf feature installs
118 def karaf_feature_tests(options = {})
120 # NB: This default list should be the same as the one in opendaylight::params
121 # TODO: Remove this possible source of bugs^^
122 default_features = options.fetch(:default_features, ['standard', 'wrap', 'ssh'])
123 extra_features = options.fetch(:extra_features, [])
125 # The order of this list concat matters
126 features = default_features + extra_features
127 features_csv = features.join(',')
129 # Confirm properties of Karaf features config file
130 # NB: These hashes don't work with Ruby 1.8.7, but we
131 # don't support 1.8.7 so that's okay. See issue #36.
133 should contain_file('org.apache.karaf.features.cfg').with(
135 'path' => '/opt/opendaylight/etc/org.apache.karaf.features.cfg',
141 should contain_file_line('featuresBoot').with(
142 'path' => '/opt/opendaylight/etc/org.apache.karaf.features.cfg',
143 'line' => "featuresBoot=#{features_csv}",
144 'match' => '^featuresBoot=.*$',
149 # Shared tests that specialize in testing ODL's REST port config
150 def odl_rest_port_tests(options = {})
152 # NB: This default value should be the same as one in opendaylight::params
153 # TODO: Remove this possible source of bugs^^
154 odl_rest_port = options.fetch(:odl_rest_port, 8181)
155 odl_bind_ip = options.fetch(:odl_bind_ip, '0.0.0.0')
156 # Confirm properties of ODL REST port config file
157 # NB: These hashes don't work with Ruby 1.8.7, but we
158 # don't support 1.8.7 so that's okay. See issue #36.
160 should contain_augeas('ODL REST Port')
163 if not odl_bind_ip.eql? '0.0.0.0'
165 should contain_augeas('ODL REST IP')
166 should contain_file_line('set pax bind IP').with(
167 'ensure' => 'present',
168 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
169 'line' => "org.ops4j.pax.web.listening.addresses = #{odl_bind_ip}",
170 'require' => 'File[org.ops4j.pax.web.cfg]'
175 should_not contain_augeas('ODL REST IP')
180 should contain_file_line('set pax bind port').with(
181 'ensure' => 'present',
182 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
183 'line' => "org.osgi.service.http.port = #{odl_rest_port}",
184 'match' => '^#?org.osgi.service.http.port\s.*$',
185 'require' => 'File[org.ops4j.pax.web.cfg]'
190 def log_level_tests(options = {})
192 # NB: This default value should be the same as one in opendaylight::params
193 # TODO: Remove this possible source of bugs^^
194 log_levels = options.fetch(:log_levels, {})
197 # Should contain log level config file
199 should_not contain_file_line('logger-org.opendaylight.ovsdb-level')
202 should_not contain_file_line('logger-org.opendaylight.ovsdb-name')
205 # Verify each custom log level config entry
206 log_levels.each_pair do |logger, level|
207 underscored_version = "#{logger}".gsub('.', '_')
209 should contain_file_line("logger-#{logger}-level").with(
210 'ensure' => 'present',
211 'path' => '/opt/opendaylight/etc/org.ops4j.pax.logging.cfg',
212 'line' => "log4j2.logger.#{underscored_version}.level = #{level}",
213 'match' => "log4j2.logger.#{underscored_version}.level = .*$"
215 should contain_file_line("logger-#{logger}-name").with(
216 'ensure' => 'present',
217 'path' => '/opt/opendaylight/etc/org.ops4j.pax.logging.cfg',
218 'line' => "log4j2.logger.#{underscored_version}.name = #{logger}",
219 'match' => "log4j2.logger.#{underscored_version}.name = .*$"
226 def enable_ha_tests(options = {})
228 enable_ha = options.fetch(:enable_ha, false)
229 odl_bind_ip = options.fetch(:odl_bind_ip, '0.0.0.0')
230 ha_node_ips = options.fetch(:ha_node_ips, [])
231 ha_db_modules = options.fetch(:ha_db_modules, { 'default' => false })
233 ha_node_count = ha_node_ips.size
235 if (enable_ha) && (ha_node_count < 2)
236 # Check for HA_NODE_COUNT < 2
237 fail("Number of HA nodes less than 2: #{ha_node_count} and HA Enabled")
241 ha_node_index = ha_node_ips.index(odl_bind_ip)
243 should contain_file('akka.conf').with(
244 'path' => '/opt/opendaylight/configuration/initial/akka.conf',
248 'content' => /roles\s*=\s*\["member-#{ha_node_index}"\]/
252 ha_db_modules.each do |mod, urn|
253 it { should contain_file('module-shards.conf').with(
254 'path' => '/opt/opendaylight/configuration/initial/module-shards.conf',
258 'content' => /name = "#{mod}"/
261 it { should contain_file('modules.conf').with(
262 'path' => '/opt/opendaylight/configuration/initial/modules.conf',
268 it { should contain_file('modules.conf').with(
269 'path' => '/opt/opendaylight/configuration/initial/modules.conf',
273 'content' => /name = "#{mod}"/,
279 should_not contain_file('akka.conf')
280 should_not contain_file('module-shards.conf')
281 should_not contain_file('modules.conf')
286 def rpm_install_tests(options = {})
288 rpm_repo = options.fetch(:rpm_repo, 'https://nexus.opendaylight.org/content/repositories/opendaylight-fluorine-epel-7-$basearch-devel')
291 # Default to CentOS 7 Yum repo URL
293 # Confirm presence of RPM-related resources
294 it { should contain_yumrepo('opendaylight') }
295 it { should contain_package('opendaylight') }
297 # Confirm relationships between RPM-related resources
298 it { should contain_package('opendaylight').that_requires('Yumrepo[opendaylight]') }
299 it { should contain_yumrepo('opendaylight').that_comes_before('Package[opendaylight]') }
301 # Confirm properties of RPM-related resources
302 # NB: These hashes don't work with Ruby 1.8.7, but we
303 # don't support 1.8.7 so that's okay. See issue #36.
305 should contain_yumrepo('opendaylight').with(
308 'descr' => 'OpenDaylight SDN Controller',
309 'baseurl' => "#{rpm_repo}",
313 should contain_package('opendaylight').with(
314 'ensure' => 'present',
319 def deb_install_tests(options = {})
321 deb_repo = options.fetch(:deb_repo, 'ppa:odl-team/nitrogen')
323 # Confirm the presence of Deb-related resources
324 it { should contain_apt__ppa(deb_repo) }
325 it { should contain_package('opendaylight') }
327 # Confirm relationships between Deb-related resources
328 it { should contain_package('opendaylight').that_requires("Apt::Ppa[#{deb_repo}]") }
329 it { should contain_apt__ppa(deb_repo).that_comes_before('Package[opendaylight]') }
331 # Confirm presence of Deb-related resources
333 should contain_package('opendaylight').with(
334 'ensure' => 'present',
339 # Shared tests for unsupported OSs
340 def unsupported_os_tests(options = {})
342 expected_msg = options.fetch(:expected_msg)
343 rpm_repo = options.fetch(:rpm_repo, 'https://nexus.opendaylight.org/content/repositories/opendaylight-fluorine-epel-7-$basearch-devel')
345 # Confirm that classes fail on unsupported OSs
346 it { expect { should contain_class('opendaylight') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
347 it { expect { should contain_class('opendaylight::install') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
348 it { expect { should contain_class('opendaylight::config') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
349 it { expect { should contain_class('opendaylight::service') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
351 # Confirm that other resources fail on unsupported OSs
352 it { expect { should contain_yumrepo('opendaylight') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
353 it { expect { should contain_package('opendaylight') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
354 it { expect { should contain_service('opendaylight') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
355 it { expect { should contain_file('org.apache.karaf.features.cfg') }.to raise_error(Puppet::Error, /#{expected_msg}/) }
358 # Shared tests that specialize in testing SNAT mechanism
359 def snat_mechanism_tests(snat_mechanism='controller')
360 it { should contain_file('/opt/opendaylight/etc/opendaylight') }
361 it { should contain_file('/opt/opendaylight/etc/opendaylight/datastore')}
362 it { should contain_file('/opt/opendaylight/etc/opendaylight/datastore/initial')}
363 it { should contain_file('/opt/opendaylight/etc/opendaylight/datastore/initial/config')}
365 # Confirm snat_mechanism
367 should contain_file('netvirt-natservice-config.xml').with(
369 'path' => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/netvirt-natservice-config.xml',
372 'content' => /<nat-mode>#{snat_mechanism}<\/nat-mode>/
377 # Shared tests that specialize in testing SFC Config
379 it { should contain_file('/opt/opendaylight/etc/opendaylight') }
380 it { should contain_file('/opt/opendaylight/etc/opendaylight/datastore')}
381 it { should contain_file('/opt/opendaylight/etc/opendaylight/datastore/initial')}
382 it { should contain_file('/opt/opendaylight/etc/opendaylight/datastore/initial/config')}
385 should contain_file('netvirt-elanmanager-config.xml').with(
387 'path' => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/netvirt-elanmanager-config.xml',
390 'source' => 'puppet:///modules/opendaylight/netvirt-elanmanager-config.xml'
392 should contain_file('genius-itm-config.xml').with(
394 'path' => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/genius-itm-config.xml',
397 'source' => 'puppet:///modules/opendaylight/genius-itm-config.xml'
402 # Shared tests that specialize in testing VPP routing node config
403 def vpp_routing_node_tests(options = {})
405 # NB: This default list should be the same as the one in opendaylight::params
406 # TODO: Remove this possible source of bugs^^
407 routing_node = options.fetch(:routing_node, '')
409 if routing_node.empty?
410 it { should_not contain_file('org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.startup.cfg') }
411 it { should_not contain_file_line('routing-node') }
413 # Confirm properties of Karaf config file
414 # NB: These hashes don't work with Ruby 1.8.7, but we
415 # don't support 1.8.7 so that's okay. See issue #36.
417 should contain_file('org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.startup.cfg').with(
419 'path' => '/opt/opendaylight/etc/org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.startup.cfg',
425 should contain_file_line('routing-node').with(
426 'path' => '/opt/opendaylight/etc/org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.startup.cfg',
427 'line' => "routing-node=#{routing_node}",
428 'match' => '^routing-node=.*$',
434 # ODL username/password tests
435 def username_password_tests(username, password)
438 should contain_odl_user(username).with(
439 :password => password
444 # ODL websocket address tests
445 def odl_websocket_address_tests(options = {})
447 # NB: This default value should be the same as one in opendaylight::params
448 # TODO: Remove this possible source of bugs^^
449 odl_bind_ip = options.fetch(:odl_bind_ip, '0.0.0.0')
450 # Confirm properties of ODL REST port config file
451 # NB: These hashes don't work with Ruby 1.8.7, but we
452 # don't support 1.8.7 so that's okay. See issue #36.
454 if not odl_bind_ip.eql? '0.0.0.0'
456 should contain_file('/opt/opendaylight/etc/org.opendaylight.restconf.cfg').with(
458 'path' => '/opt/opendaylight/etc/org.opendaylight.restconf.cfg',
464 should contain_file_line('websocket-address').with(
465 'path' => '/opt/opendaylight/etc/org.opendaylight.restconf.cfg',
466 'line' => "websocket-address=#{odl_bind_ip}",
467 'match' => '^websocket-address=.*$',
472 should_not contain_file_line('websocket-address')
477 def odl_tls_tests(options = {})
478 enable_tls = options.fetch(:enable_tls, false)
479 tls_keystore_password = options.fetch(:tls_keystore_password, nil)
480 tls_trusted_certs = options.fetch(:tls_trusted_certs, [])
481 tls_keystore_password = options.fetch(:tls_keystore_password, nil)
482 tls_key_file = options.fetch(:tls_key_file, nil)
483 tls_cert_file = options.fetch(:tls_cert_file, nil)
484 tls_ca_cert_file = options.fetch(:tls_ca_cert_file, nil)
485 odl_rest_port = options.fetch(:odl_rest_port, 8181)
488 if tls_keystore_password.nil?
489 it { expect { should contain_class('opendaylight::config') }.to raise_error(Puppet::PreformattedError) }
493 if tls_key_file or tls_cert_file
494 if tls_key_file and tls_cert_file
496 should contain_odl_keystore('controller')
499 it { expect { should contain_class('opendaylight::config') }.to raise_error(Puppet::PreformattedError) }
503 should contain_augeas('Remove HTTP ODL REST Port')
504 should contain_augeas('ODL SSL REST Port')
505 should contain_file_line('set pax TLS port').with(
506 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
507 'line' => "org.osgi.service.http.port.secure = #{odl_rest_port}",
508 'match' => '^#?org.osgi.service.http.port.secure.*$',
510 should contain_file_line('set pax TLS keystore location').with(
511 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
512 'line' => 'org.ops4j.pax.web.ssl.keystore = configuration/ssl/ctl.jks',
513 'match' => '^#?org.ops4j.pax.web.ssl.keystore.*$',
515 should contain_file_line('set pax TLS keystore integrity password').with(
516 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
517 'line' => "org.ops4j.pax.web.ssl.password = #{tls_keystore_password}",
518 'match' => '^#?org.ops4j.pax.web.ssl.password.*$',
520 should contain_file_line('set pax TLS keystore password').with(
521 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
522 'line' => "org.ops4j.pax.web.ssl.keypassword = #{tls_keystore_password}",
523 'match' => '^#?org.ops4j.pax.web.ssl.keypassword.*$',
525 should contain_file('aaa-cert-config.xml').with(
527 'path' => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/aaa-cert-config.xml',
531 should contain_file('org.opendaylight.ovsdb.library.cfg').with(
533 'path' => '/opt/opendaylight/etc/org.opendaylight.ovsdb.library.cfg',
536 'source' => 'puppet:///modules/opendaylight/org.opendaylight.ovsdb.library.cfg'
538 should contain_file('/opt/opendaylight/configuration/ssl').with(
539 'ensure' => 'directory',
540 'path' => '/opt/opendaylight/configuration/ssl',
545 should contain_file_line('enable pax TLS').with(
546 'ensure' => 'present',
547 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
548 'line' => 'org.osgi.service.http.secure.enabled = true',
549 'match' => '^#?org.osgi.service.http.secure.enabled.*$',
551 should contain_file_line('disable pax HTTP').with(
552 'ensure' => 'present',
553 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
554 'line' => 'org.osgi.service.http.enabled = false',
555 'match' => '^#?org.osgi.service.http.enabled.*$',
557 should contain_file('org.ops4j.pax.web.cfg').with(
559 'path' => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
563 should contain_file('default-openflow-connection-config.xml').with(
565 'path' => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/default-openflow-connection-config.xml',
568 'content' => /<transport-protocol>TLS<\/transport-protocol>/