When `enable_ha` is set to true the `ha_node_ips` should be populated with the
IP addresses that ODL will listen on for each node in the HA cluster and
-`ha_node_index` should be set with the index of the IP address from
-`ha_node_ips` for the particular node that puppet is configuring as part of the
+`odl_bind_ip` should be set with the IP address from `ha_node_ips` configured
+for the particular node that puppet is configuring as part of the
HA cluster.
+By default a single ODL instance will become the leader for the entire
+datastore. In order to distribute the datastore over multiple ODL instances,
+`ha_db_modules` parameter may be specified which will include the modules
+desired to separate out from the default shard, along with the Yang namespace
+for that module.
+
```puppet
class { 'opendaylight':
enable_ha => true,
ha_node_ips => ['10.10.10.1', '10.10.10.1', '10.10.10.3'],
- ha_node_index => 0,
+ odl_bind_ip => 0,
+ ha_db_modules => {'default' => false, 'topology' => 'urn:opendaylight:topology'}
}
```
Valid options: The boolean values `true` and `false`.
-Requires: `ha_node_ips`, `ha_node_index`
+Requires: `ha_node_ips`, `odl_bind_ip`
The ODL Clustering XML for HA are configured and enabled.
Required by: `enable_ha`
+##### `ha_db_modules`
+
+Specifies the modules to use for distributing and sharding the ODL datastore.
+
+Default: `{'default'=> false}`
+
+Valid options: A hash of module and Yang namespace for the module (default has no namespace).
+
+Requires: `enable_ha`
+
##### `ha_node_index`
Specifies the index of the IP for the node being configured from the array `ha_node_ips`.
Valid options: Index of a member of the array `ha_node_ips`: `0`.
-Required by: `enable_ha`, `ha_node_ips`
+This parameter is now deprecated and is no longer used.
##### `security_group_mode`
if $::opendaylight::enable_ha {
if $ha_node_count >= 2 {
# Configure ODL OSVDB Clustering
- $ha_node_ip_str = join($::opendaylight::ha_node_ips, ' ')
- exec { 'Configure ODL OVSDB Clustering':
- command => "configure_cluster.sh ${::opendaylight::ha_node_index} ${ha_node_ip_str}",
- path => '/opt/opendaylight/bin/:/usr/sbin:/usr/bin:/sbin:/bin',
- creates => '/opt/opendaylight/configuration/initial/akka.conf'
+ $cluster_config_dir = '/opt/opendaylight/configuration/initial'
+
+ file { $cluster_config_dir:
+ ensure => directory,
+ mode => '0755',
+ owner => 'odl',
+ group => 'odl',
+ }
+
+ file {'akka.conf':
+ ensure => file,
+ path => "${cluster_config_dir}/akka.conf",
+ owner => 'odl',
+ group => 'odl',
+ content => template('opendaylight/akka.conf.erb'),
+ require => File[$cluster_config_dir]
+ }
+
+ file {'modules.conf':
+ ensure => file,
+ path => "${cluster_config_dir}/modules.conf",
+ owner => 'odl',
+ group => 'odl',
+ content => template('opendaylight/modules.conf.erb'),
+ require => File[$cluster_config_dir]
+ }
+
+ file {'module-shards.conf':
+ ensure => file,
+ path => "${cluster_config_dir}/module-shards.conf",
+ owner => 'odl',
+ group => 'odl',
+ content => template('opendaylight/module-shards.conf.erb'),
+ require => File[$cluster_config_dir]
}
+
} else {
fail("Number of HA nodes less than 2: ${ha_node_count} and HA Enabled")
}
# Default: false.
# [*ha_node_ips*]
# Array of IPs for each node in the HA cluster.
-# [*ha_node_index*]
-# Index of ha_node_ips for this node.
+# [*ha_db_modules*]
+# Hash of modules and Yang namespaces to create database shards. Defaults to
+# { 'default' => false }. "default" module does not need a namespace.
# [*security_group_mode*]
# Sets the mode to use for security groups (stateful, learn, stateless, transparent)
# [*vpp_routing_node*]
# package installation.
# Defaults to true
#
+# === Deprecated Parameters
+#
+# [*ha_node_index*]
+# Index of ha_node_ips for this node.
+#
class opendaylight (
$default_features = $::opendaylight::params::default_features,
$extra_features = $::opendaylight::params::extra_features,
$enable_ha = $::opendaylight::params::enable_ha,
$ha_node_ips = $::opendaylight::params::ha_node_ips,
$ha_node_index = $::opendaylight::params::ha_node_index,
+ $ha_db_modules = $::opendaylight::params::ha_db_modules,
$security_group_mode = $::opendaylight::params::security_group_mode,
$vpp_routing_node = $::opendaylight::params::vpp_routing_node,
$java_opts = $::opendaylight::params::java_opts,
$enable_ha = false
$ha_node_ips = []
$ha_node_index = 0
+ $ha_db_modules = { 'default' => false }
$security_group_mode = 'stateful'
$vpp_routing_node = ''
$java_opts = '-Djava.net.preferIPv4Stack=true'
end
end
end
+
+ describe 'testing odl HA configuration' do
+ bind_ip = '127.0.0.1'
+ odl_ips = ['127.0.0.1', '127.0.0.2', '127.0.0.3']
+ context 'using default modules' do
+ install_odl(odl_bind_ip: bind_ip, enable_ha: true, ha_node_ips: odl_ips)
+
+ enable_ha_validations(odl_bind_ip: bind_ip, enable_ha: true,
+ ha_node_ips: odl_ips)
+ end
+
+ context 'specifying datastore modules' do
+ db_modules = {
+ 'default' => false,
+ 'topology' => 'urn:opendaylight:topology'
+ }
+ install_odl(odl_bind_ip: bind_ip, enable_ha: true, ha_node_ips: odl_ips,
+ ha_db_modules: db_modules)
+ enable_ha_validations(odl_bind_ip: bind_ip, enable_ha: true,
+ ha_node_ips: odl_ips, ha_db_modules: db_modules)
+ end
+ end
end
# Note that this function is defined in spec_helper
enable_ha_tests(enable_ha: true, ha_node_ips: ['0.0.0.0', '127.0.0.1'])
end
+
+ context 'using custom modules for sharding' do
+ let(:facts) {{
+ :osfamily => osfamily,
+ :operatingsystem => operatingsystem,
+ :operatingsystemmajrelease => operatingsystemmajrelease,
+ }}
+
+ let(:params) {{
+ :enable_ha => true,
+ :ha_node_ips => ['0.0.0.0', '127.0.0.1'],
+ :ha_db_modules => {'default' => false, 'topology' => 'urn:opendaylight:topology'}
+ }}
+
+ # Run shared tests applicable to all supported OSs
+ # Note that this function is defined in spec_helper
+ generic_tests
+
+ # Run test that specialize in checking ODL OVSDB HA config
+ # Note that this function is defined in spec_helper
+ enable_ha_tests(enable_ha: true, ha_node_ips: ['0.0.0.0', '127.0.0.1'])
+ end
end
end
'Class[Java::Params]',
'Class[Stdlib::Stages]',
'Class[Stdlib]',
- 'Exec[Configure ODL OVSDB Clustering]',
'Exec[download archive opendaylight.tar.gz and check sum]',
'Exec[download archive opendaylight-systemd.tar.gz and check sum]',
'Exec[opendaylight unpack]',
def enable_ha_tests(options = {})
# Extract params
enable_ha = options.fetch(:enable_ha, false)
+ odl_bind_ip = options.fetch(:odl_bind_ip, '0.0.0.0')
ha_node_ips = options.fetch(:ha_node_ips, [])
- ha_node_index = options.fetch(:ha_node_index, 0)
+ ha_db_modules = options.fetch(:ha_db_modules, { 'default' => false })
# HA_NODE_IPS size
ha_node_count = ha_node_ips.size
# Check for HA_NODE_COUNT < 2
fail("Number of HA nodes less than 2: #{ha_node_count} and HA Enabled")
end
+
+ if enable_ha
+ ha_node_index = ha_node_ips.index(odl_bind_ip)
+ it {
+ should contain_file('akka.conf').with(
+ 'path' => '/opt/opendaylight/configuration/initial/akka.conf',
+ 'ensure' => 'file',
+ 'owner' => 'odl',
+ 'group' => 'odl',
+ 'content' => /roles\s*=\s*\["member-#{ha_node_index}"\]/
+ )
+ }
+
+ ha_db_modules.each do |mod, urn|
+ it { should contain_file('module-shards.conf').with(
+ 'path' => '/opt/opendaylight/configuration/initial/module-shards.conf',
+ 'ensure' => 'file',
+ 'owner' => 'odl',
+ 'group' => 'odl',
+ 'content' => /name = "#{mod}"/
+ )}
+ if mod == 'default'
+ it { should contain_file('modules.conf').with(
+ 'path' => '/opt/opendaylight/configuration/initial/modules.conf',
+ 'ensure' => 'file',
+ 'owner' => 'odl',
+ 'group' => 'odl'
+ )}
+ else
+ it { should contain_file('modules.conf').with(
+ 'path' => '/opt/opendaylight/configuration/initial/modules.conf',
+ 'ensure' => 'file',
+ 'owner' => 'odl',
+ 'group' => 'odl',
+ 'content' => /name = "#{mod}"/,
+ )}
+ end
+ end
+ else
+ it {
+ should_not contain_file('akka.conf')
+ should_not contain_file('module-shards.conf')
+ should_not contain_file('modules.conf')
+ }
+ end
end
def rpm_install_tests(options = {})
default_features = options.fetch(:default_features,
['config', 'standard', 'region', 'package', 'kar', 'ssh', 'management'])
odl_rest_port = options.fetch(:odl_rest_port, 8080)
+ odl_bind_ip = options.fetch(:odl_bind_ip, '0.0.0.0')
log_levels = options.fetch(:log_levels, {})
enable_ha = options.fetch(:enable_ha, false)
ha_node_ips = options.fetch(:ha_node_ips, [])
ha_node_index = options.fetch(:ha_node_index, 0)
+ ha_db_modules = options.fetch(:ha_db_modules, { 'default' => false })
username = options.fetch(:username, 'admin')
password = options.fetch(:password, 'admin')
default_features => #{default_features},
extra_features => #{extra_features},
odl_rest_port=> #{odl_rest_port},
+ odl_bind_ip=> '#{odl_bind_ip}',
enable_ha=> #{enable_ha},
ha_node_ips=> #{ha_node_ips},
ha_node_index=> #{ha_node_index},
+ ha_db_modules=> #{ha_db_modules},
log_levels=> #{log_levels},
username=> #{username},
password=> #{password},
# TODO: Remove this possible source of bugs^^
enable_ha = options.fetch(:enable_ha, false)
ha_node_ips = options.fetch(:ha_node_ips, [])
- ha_node_index = options.fetch(:ha_node_index, 0)
+ odl_bind_ip = options.fetch(:odl_bind_ip, '0.0.0.0')
+ ha_db_modules = options.fetch(:ha_db_modules, { 'default' => false })
# HA_NODE_IPS size
ha_node_count = ha_node_ips.size
# Check for HA_NODE_COUNT < 2
fail("Number of HA nodes less than 2: #{ha_node_count} and HA Enabled")
end
+
+ if enable_ha
+ ha_node_index = ha_node_ips.index(odl_bind_ip)
+ describe file('/opt/opendaylight/configuration/initial/akka.conf') do
+ it { should be_file }
+ it { should be_owned_by 'odl' }
+ it { should be_grouped_into 'odl' }
+ its(:content) { should match /roles\s*=\s*\["member-#{ha_node_index}"\]/ }
+ end
+
+ ha_db_modules.each do |mod, urn|
+ describe file('/opt/opendaylight/configuration/initial/module-shards.conf') do
+ it { should be_file }
+ it { should be_owned_by 'odl' }
+ it { should be_grouped_into 'odl' }
+ its(:content) { should match /name = "#{mod}"/ }
+ end
+
+ if mod == 'default'
+ describe file('/opt/opendaylight/configuration/initial/modules.conf') do
+ it { should be_file }
+ it { should be_owned_by 'odl' }
+ it { should be_grouped_into 'odl' }
+ end
+ else
+ describe file('/opt/opendaylight/configuration/initial/modules.conf') do
+ it { should be_file }
+ it { should be_owned_by 'odl' }
+ it { should be_grouped_into 'odl' }
+ its(:content) { should match /name = "#{mod}"/ }
+ its(:content) { should match /namespace = "#{urn}"/ }
+ end
+ end
+ end
+ end
end
# Shared function that handles validations specific to RPM-type installs
--- /dev/null
+
+odl-cluster-data {
+ akka {
+ remote {
+ artery {
+ enabled = off
+ canonical.hostname = "<%= scope.lookupvar('opendaylight::odl_bind_ip') %>"
+ canonical.port = 2550
+ }
+ netty.tcp {
+ hostname = "<%= scope.lookupvar('opendaylight::odl_bind_ip') %>"
+ port = 2550
+ }
+ }
+
+ cluster {
+ # Remove ".tcp" when using artery.
+ seed-nodes = [
+ <% scope.lookupvar('opendaylight::ha_node_ips').each do |ha_ip| -%>
+ "akka.tcp://opendaylight-cluster-data@<%= ha_ip %>:2550",
+ <% end -%>
+ ]
+
+ roles = ["member-<%=scope.lookupvar('opendaylight::ha_node_ips').index(scope.lookupvar('opendaylight::odl_bind_ip'))%>"]
+
+ }
+
+ persistence {
+ # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by
+ # modifying the following two properties. The directory location specified may be a relative or absolute path.
+ # The relative path is always relative to KARAF_HOME.
+
+ # snapshot-store.local.dir = "target/snapshots"
+ # journal.leveldb.dir = "target/journal"
+
+ journal {
+ leveldb {
+ # Set native = off to use a Java-only implementation of leveldb.
+ # Note that the Java-only version is not currently considered by Akka to be production quality.
+
+ # native = off
+ }
+ }
+ }
+ }
+}
--- /dev/null
+module-shards = [
+ <% scope.lookupvar('opendaylight::ha_db_modules').each do |mod, urn| -%>
+ {
+ name = "<%= mod %>"
+ shards = [
+ {
+ name = "<%= mod %>"
+ replicas = [
+ <% scope.lookupvar('opendaylight::ha_node_ips').each_with_index do |ip, idx| -%>
+ "<%= "member-#{idx}" %>",
+ <% end -%>
+ ]
+ }
+ ]
+ },
+ <% end -%>
+]
--- /dev/null
+modules = [
+ <% scope.lookupvar('opendaylight::ha_db_modules').each do |mod, urn| -%>
+ <% if mod != "default" -%>
+ {
+ name = "<%= mod %>"
+ namespace = "<%= urn %>"
+ shard-strategy = "module"
+ },
+ <% end -%>
+ <% end -%>
+]