c34def9bdac9b50e59141bc07eca467a7f61c141
[integration/packaging/puppet-opendaylight.git] / spec / spec_helper.rb
1 require 'puppetlabs_spec_helper/module_spec_helper'
2 require 'rspec-puppet-facts'
3 include RspecPuppetFacts
4
5 # Customize filters to ignore 3rd-party code
6 # If the rspec coverage report shows not-our-code results, add it here
7 custom_filters = [
8 ]
9 RSpec::Puppet::Coverage.filters.push(*custom_filters)
10
11 #
12 # NB: This is a library of helper fns used by the rspec-puppet tests
13 #
14
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')
18
19   # Confirm that module compiles
20   it { should compile }
21   it { should compile.with_all_deps }
22
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') }
30
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]') }
40
41   # Confirm presence of generic resources
42   it { should contain_service('opendaylight') }
43   it { should contain_file('org.apache.karaf.features.cfg') }
44
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.
48   it {
49     should contain_service('opendaylight').with(
50       'ensure'      => 'running',
51       'enable'      => 'true',
52       'hasstatus'   => 'true',
53       'hasrestart'  => 'true',
54     )
55   }
56   it {
57     should contain_file('org.apache.karaf.features.cfg').with(
58       'ensure'      => 'file',
59       'path'        => '/opt/opendaylight/etc/org.apache.karaf.features.cfg',
60       'owner'   => 'odl',
61       'group'   => 'odl',
62     )
63   }
64
65   it {
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=.*$'
72     )
73   }
74
75 end
76
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')
83
84   if log_mechanism == 'console'
85     it {
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.*$'
91       )
92     }
93   else
94   it {
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.*$',
99     )
100   }
101   it {
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'
105     )
106   }
107   it {
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.*$',
112     )
113   }
114   end
115 end
116
117 # Shared tests that specialize in testing Karaf feature installs
118 def karaf_feature_tests(options = {})
119   # Extract params
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, [])
124
125   # The order of this list concat matters
126   features = default_features + extra_features
127   features_csv = features.join(',')
128
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.
132   it {
133     should contain_file('org.apache.karaf.features.cfg').with(
134       'ensure'      => 'file',
135       'path'        => '/opt/opendaylight/etc/org.apache.karaf.features.cfg',
136       'owner'   => 'odl',
137       'group'   => 'odl',
138     )
139   }
140   it {
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=.*$',
145     )
146   }
147 end
148
149 # Shared tests that specialize in testing ODL's REST port config
150 def odl_rest_port_tests(options = {})
151   # Extract params
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.
159   it {
160     should contain_augeas('ODL REST Port')
161   }
162
163   if not odl_bind_ip.eql? '0.0.0.0'
164     it {
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]'
171       )
172     }
173   else
174     it {
175       should_not contain_augeas('ODL REST IP')
176     }
177   end
178
179   it {
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]'
186     )
187   }
188 end
189
190 def log_level_tests(options = {})
191   # Extract params
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, {})
195
196   if log_levels.empty?
197     # Should contain log level config file
198     it {
199       should_not contain_file_line('logger-org.opendaylight.ovsdb-level')
200     }
201     it {
202       should_not contain_file_line('logger-org.opendaylight.ovsdb-name')
203     }
204   else
205     # Verify each custom log level config entry
206     log_levels.each_pair do |logger, level|
207       underscored_version = "#{logger}".gsub('.', '_')
208       it {
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 = .*$"
214         )
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 = .*$"
220         )
221       }
222     end
223   end
224 end
225
226 def enable_ha_tests(options = {})
227   # Extract params
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 })
232   # HA_NODE_IPS size
233   ha_node_count = ha_node_ips.size
234
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")
238   end
239
240   if enable_ha
241     ha_node_index = ha_node_ips.index(odl_bind_ip)
242     it {
243       should contain_file('akka.conf').with(
244         'path'    => '/opt/opendaylight/configuration/initial/akka.conf',
245         'ensure'  => 'file',
246         'owner'   => 'odl',
247         'group'   => 'odl',
248         'content' => /roles\s*=\s*\["member-#{ha_node_index}"\]/
249       )
250     }
251
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',
255         'ensure'  => 'file',
256         'owner'   => 'odl',
257         'group'   => 'odl',
258         'content' => /name = "#{mod}"/
259       )}
260       if mod == 'default'
261         it { should contain_file('modules.conf').with(
262           'path'    => '/opt/opendaylight/configuration/initial/modules.conf',
263           'ensure'  => 'file',
264           'owner'   => 'odl',
265           'group'   => 'odl'
266         )}
267       else
268         it { should contain_file('modules.conf').with(
269           'path'    => '/opt/opendaylight/configuration/initial/modules.conf',
270           'ensure'  => 'file',
271           'owner'   => 'odl',
272           'group'   => 'odl',
273           'content' => /name = "#{mod}"/,
274         )}
275       end
276     end
277   else
278     it {
279       should_not contain_file('akka.conf')
280       should_not contain_file('module-shards.conf')
281       should_not contain_file('modules.conf')
282       }
283   end
284 end
285
286 def rpm_install_tests(options = {})
287   # Extract params
288   rpm_repo = options.fetch(:rpm_repo, 'https://nexus.opendaylight.org/content/repositories/opendaylight-fluorine-epel-7-$basearch-devel')
289
290
291   # Default to CentOS 7 Yum repo URL
292
293   # Confirm presence of RPM-related resources
294   it { should contain_yumrepo('opendaylight') }
295   it { should contain_package('opendaylight') }
296
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]') }
300
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.
304   it {
305     should contain_yumrepo('opendaylight').with(
306       'enabled'     => '1',
307       'gpgcheck'    => '0',
308       'descr'       => 'OpenDaylight SDN Controller',
309       'baseurl'     => "#{rpm_repo}",
310     )
311   }
312   it {
313     should contain_package('opendaylight').with(
314       'ensure'   => 'present',
315     )
316   }
317 end
318
319 def deb_install_tests(options = {})
320   # Extract params
321   deb_repo = options.fetch(:deb_repo, 'ppa:odl-team/nitrogen')
322
323   # Confirm the presence of Deb-related resources
324   it { should contain_apt__ppa(deb_repo) }
325   it { should contain_package('opendaylight') }
326
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]') }
330
331   # Confirm presence of Deb-related resources
332   it {
333     should contain_package('opendaylight').with(
334       'ensure'   => 'present',
335     )
336   }
337 end
338
339 # Shared tests for unsupported OSs
340 def unsupported_os_tests(options = {})
341   # Extract params
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')
344
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}/) }
350
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}/) }
356 end
357
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')}
364
365   # Confirm snat_mechanism
366   it {
367     should contain_file('netvirt-natservice-config.xml').with(
368       'ensure'      => 'file',
369       'path'        => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/netvirt-natservice-config.xml',
370       'owner'   => 'odl',
371       'group'   => 'odl',
372       'content'     =>  /<nat-mode>#{snat_mechanism}<\/nat-mode>/
373       )
374     }
375 end
376
377 # Shared tests that specialize in testing SFC Config
378 def sfc_tests()
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')}
383
384   it {
385     should contain_file('netvirt-elanmanager-config.xml').with(
386       'ensure'  => 'file',
387       'path'    => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/netvirt-elanmanager-config.xml',
388       'owner'   => 'odl',
389       'group'   => 'odl',
390       'source'  => 'puppet:///modules/opendaylight/netvirt-elanmanager-config.xml'
391       )
392     should contain_file('genius-itm-config.xml').with(
393       'ensure'  => 'file',
394       'path'    => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/genius-itm-config.xml',
395       'owner'   => 'odl',
396       'group'   => 'odl',
397       'source'  => 'puppet:///modules/opendaylight/genius-itm-config.xml'
398       )
399     }
400 end
401
402 # Shared tests that specialize in testing VPP routing node config
403 def vpp_routing_node_tests(options = {})
404   # Extract params
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, '')
408
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') }
412   else
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.
416     it {
417       should contain_file('org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.startup.cfg').with(
418         'ensure'      => 'file',
419         'path'        => '/opt/opendaylight/etc/org.opendaylight.groupbasedpolicy.neutron.vpp.mapper.startup.cfg',
420         'owner'   => 'odl',
421         'group'   => 'odl',
422       )
423     }
424     it {
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=.*$',
429       )
430     }
431   end
432 end
433
434 # ODL username/password tests
435 def username_password_tests(username, password)
436
437   it {
438     should contain_odl_user(username).with(
439       :password => password
440     )
441   }
442 end
443
444 # ODL websocket address tests
445 def odl_websocket_address_tests(options = {})
446   # Extract params
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.
453
454   if not odl_bind_ip.eql? '0.0.0.0'
455     it {
456       should contain_file('/opt/opendaylight/etc/org.opendaylight.restconf.cfg').with(
457         'ensure'      => 'file',
458         'path'        => '/opt/opendaylight/etc/org.opendaylight.restconf.cfg',
459         'owner'   => 'odl',
460         'group'   => 'odl',
461       )
462     }
463     it {
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=.*$',
468       )
469     }
470   else
471     it {
472       should_not contain_file_line('websocket-address')
473     }
474   end
475 end
476
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)
486
487   if enable_tls
488     if tls_keystore_password.nil?
489       it { expect { should contain_class('opendaylight::config') }.to raise_error(Puppet::PreformattedError) }
490       return
491     end
492
493     if tls_key_file or tls_cert_file
494       if tls_key_file and tls_cert_file
495         it {
496           should contain_odl_keystore('controller')
497         }
498       else
499         it { expect { should contain_class('opendaylight::config') }.to raise_error(Puppet::PreformattedError) }
500       end
501     end
502     it {
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.*$',
509       )
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.*$',
514       )
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.*$',
519       )
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.*$',
524       )
525       should contain_file('aaa-cert-config.xml').with(
526         'ensure'  => 'file',
527         'path'    => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/aaa-cert-config.xml',
528         'owner'   => 'odl',
529         'group'   => 'odl',
530       )
531       should contain_file('org.opendaylight.ovsdb.library.cfg').with(
532         'ensure' => 'file',
533         'path'   => '/opt/opendaylight/etc/org.opendaylight.ovsdb.library.cfg',
534         'owner'  => 'odl',
535         'group'  => 'odl',
536         'source' => 'puppet:///modules/opendaylight/org.opendaylight.ovsdb.library.cfg'
537       )
538       should contain_file('/opt/opendaylight/configuration/ssl').with(
539         'ensure' => 'directory',
540         'path'   => '/opt/opendaylight/configuration/ssl',
541         'owner'  => 'odl',
542         'group'  => 'odl',
543         'mode'   => '0755'
544       )
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.*$',
550       )
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.*$',
556       )
557       should contain_file('org.ops4j.pax.web.cfg').with(
558         'ensure' => 'file',
559         'path'   => '/opt/opendaylight/etc/org.ops4j.pax.web.cfg',
560         'owner'  => 'odl',
561         'group'  => 'odl',
562       )
563       should contain_file('default-openflow-connection-config.xml').with(
564         'ensure'  => 'file',
565         'path'    => '/opt/opendaylight/etc/opendaylight/datastore/initial/config/default-openflow-connection-config.xml',
566         'owner'   => 'odl',
567         'group'   => 'odl',
568         'content' =>  /<transport-protocol>TLS<\/transport-protocol>/
569       )
570     }
571   end
572 end