Fixes clustering to use template instead of exec 93/59493/7
authorTim Rozet <trozet@redhat.com>
Fri, 23 Jun 2017 21:35:09 +0000 (17:35 -0400)
committerTimothy Rozet <trozet@redhat.com>
Thu, 13 Jul 2017 14:04:04 +0000 (14:04 +0000)
Adds ability to specify modules to use when breaking up the datastore
into shards.  By default we just use the 'default' module which contains
all of the datastore, but a user is able to pass a hash of modules and
namespaces if they desire to break up the datastore into separate
shards.

Deprecates any need to use ha_node_index param.

Change-Id: I05c88741c16c433baa11fe4cbc43bc04fc8c2625
Signed-off-by: Tim Rozet <trozet@redhat.com>
README.markdown
manifests/config.pp
manifests/init.pp
manifests/params.pp
spec/acceptance/class_spec.rb
spec/classes/opendaylight_spec.rb
spec/spec_helper.rb
spec/spec_helper_acceptance.rb
templates/akka.conf.erb [new file with mode: 0644]
templates/module-shards.conf.erb [new file with mode: 0644]
templates/modules.conf.erb [new file with mode: 0644]

index ead8b6d8b5f167a3c51eeaab11c60546670516c5..91de61e244282fbf00efa2fe33a220b986c0a2d0 100644 (file)
@@ -177,15 +177,22 @@ To enable ODL HA, use the `enable_ha` flag. It's disabled by default.
 
 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'}
 }
 ```
 
@@ -294,7 +301,7 @@ Default: `false`
 
 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.
 
@@ -308,6 +315,16 @@ Valid options: An array of IP addresses `['10.10.10.1', '10.10.10.1', '10.10.10.
 
 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`.
@@ -316,7 +333,7 @@ Default: ''
 
 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`
 
index 97e182c984369b2c71b778ee1d8f2075be4b14dd..0f1bf962aa0f7649cca8411e068177638bd16ecf 100644 (file)
@@ -44,12 +44,42 @@ class opendaylight::config {
   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")
     }
index 34d42bd264f9db330febb364602242780c7d15ef..4834b6100a07bbf8cf64f361cbc8e4996b2a487c 100644 (file)
@@ -25,8 +25,9 @@
 #   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,
@@ -49,6 +55,7 @@ class opendaylight (
   $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,
index eaa6ef63d3180bae311d383bb04400c46a019efe..ee6aaf0d71883623100b42407dfe7bbb67ddcfc9 100644 (file)
@@ -18,6 +18,7 @@ class opendaylight::params {
   $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'
index 341bf2ca5a1d7c76782650a66af12ff9e898bfac..0ea10005232627df3e00d0c92d697bd366edbba2 100644 (file)
@@ -134,4 +134,26 @@ describe 'opendaylight class' do
     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
index 5bd3088321cf61c5c04dcfe8e5644ce346270aab..7c0a1d1e8fc245c66a8c14fa50e3a8fa62a6bd38 100644 (file)
@@ -496,6 +496,28 @@ describe 'opendaylight' do
         # 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
 
index af6e87478cac45ffd3397629b53bae1f1ae24246..0c7d38391c357e9cd92248a25aa220d4ed818900 100644 (file)
@@ -16,7 +16,6 @@ custom_filters = [
   '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]',
@@ -166,8 +165,9 @@ end
 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
 
@@ -175,6 +175,51 @@ def enable_ha_tests(options = {})
     # 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 = {})
index 08b8060218d1b7b88a740551ce3474d7641991db..2563c60dc83a8bd4e23c66d7c06cb91168d56346 100644 (file)
@@ -61,10 +61,12 @@ def install_odl(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')
 
@@ -77,9 +79,11 @@ def install_odl(options = {})
       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},
@@ -276,7 +280,8 @@ def enable_ha_validations(options = {})
   # 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
 
@@ -284,6 +289,41 @@ def enable_ha_validations(options = {})
     # 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
diff --git a/templates/akka.conf.erb b/templates/akka.conf.erb
new file mode 100644 (file)
index 0000000..99ea577
--- /dev/null
@@ -0,0 +1,46 @@
+
+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
+        }
+      }
+    }
+  }
+}
diff --git a/templates/module-shards.conf.erb b/templates/module-shards.conf.erb
new file mode 100644 (file)
index 0000000..0bdc8db
--- /dev/null
@@ -0,0 +1,17 @@
+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 -%>
+]
diff --git a/templates/modules.conf.erb b/templates/modules.conf.erb
new file mode 100644 (file)
index 0000000..c5831b5
--- /dev/null
@@ -0,0 +1,11 @@
+modules = [
+        <% scope.lookupvar('opendaylight::ha_db_modules').each do |mod, urn| -%>
+        <% if mod != "default" -%>
+        {
+                name = "<%= mod %>"
+                namespace = "<%= urn %>"
+                shard-strategy = "module"
+        },
+        <% end -%>
+        <% end -%>
+]