diff --git a/.gitignore b/.gitignore index 17868e3..7b7ed66 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ package/yast2-sap-ha-*.tar.bz2 .byebug_history .vscode/ .vs/ -sap_ha_unattended_install_log.txt \ No newline at end of file +sap_ha_unattended_install_log.txt + +.tmp/ diff --git a/.solargraph.yml b/.solargraph.yml deleted file mode 100644 index d362071..0000000 --- a/.solargraph.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -include: -- "**/*.rb" -- "/usr/share/YaST2/*.rb" -exclude: -- spec/**/* -- test/**/* -- vendor/**/* -- ".bundle/**/*" -require: [] -domains: [] -reporters: -- rubocop -- require_not_found -plugins: [] -max_files: 5000 diff --git a/.yardopts b/.yardopts index 3a00f15..22ce944 100644 --- a/.yardopts +++ b/.yardopts @@ -1,7 +1 @@ --no-private ---protected ---markup markdown ---output-dir doc/autodocs ---readme README.md ---file doc/yast-sap-ha.md -src/**/*.rb diff --git a/Rakefile b/Rakefile index b48b28c..db32aea 100644 --- a/Rakefile +++ b/Rakefile @@ -27,4 +27,3 @@ Yast::Tasks.configuration do |conf| conf.exclude_files << /test/ conf.exclude_files << /aux/ end - diff --git a/data/tmpl_cluster_config.erb b/data/tmpl_cluster_config.erb deleted file mode 100644 index e3a0e74..0000000 --- a/data/tmpl_cluster_config.erb +++ /dev/null @@ -1,96 +0,0 @@ -# -# defaults -# - -rsc_defaults \ - resource-stickiness="1000" \ - migration-threshold="5000" - -op_defaults \ - timeout="600" - -# -# production HANA -# - -primitive rsc_ip_<%= @system_id -%>_HDB<%= @instance -%> ocf:heartbeat:IPaddr2 \ - params \ - ip="<%= @virtual_ip %>" cidr_netmask="<%= @virtual_ip_mask %>" \ - op start timeout="20" op stop timeout="20" \ - op monitor interval="10" timeout="20" - -primitive rsc_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> ocf:suse:SAPHanaTopology \ - params \ - SID="<%= @system_id -%>" \ - InstanceNumber="<%= @instance -%>" \ - op monitor interval="10" timeout="600" \ - op start interval="0" timeout="600" \ - op stop interval="0" timeout="300" - -primitive rsc_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> ocf:suse:SAPHana \ - params \ - SID="<%= @system_id -%>" \ - InstanceNumber="<%= @instance -%>" \ - PREFER_SITE_TAKEOVER="<%= @prefer_takeover -%>" \ - AUTOMATED_REGISTER="<%= @auto_register -%>" \ - DUPLICATE_PRIMARY_TIMEOUT="7200" \ - op start interval="0" timeout="3600" \ - op stop interval="0" timeout="3600" \ - op promote interval="0" timeout="3600" \ - op monitor interval="60" role="Master" timeout="700" \ - op monitor interval="61" role="Slave" timeout="700" - -ms msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> rsc_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> \ - meta clone-max="2" clone-node-max="1" interleave="true" - -<% if @global_config.platform == "azure" %> - -primitive rsc_nc_<%= @system_id -%>_HDB<%= @instance -%> anything \ - params binfile="/usr/bin/nc" cmdline_options="-l -k 62503" \ - meta resource-stickiness=0 \ - op monitor timeout="20" interval="10" depth="0" - -group g_ip_<%= @system_id -%>_HDB<%= @instance -%> rsc_ip_<%= @system_id -%>_HDB<%= @instance -%> rsc_nc_<%= @system_id -%>_HDB<%= @instance -%> - -colocation col_saphana_ip_<%= @system_id -%>_HDB<%= @instance -%> 2000: g_ip_<%= @system_id -%>_HDB<%= @instance -%>:Started msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%>:Master - -<% else %> - -colocation col_saphana_ip_<%= @system_id -%>_HDB<%= @instance -%> 2000: rsc_ip_<%= @system_id -%>_HDB<%= @instance -%>:Started msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%>:Master - -<% end %> - -clone cln_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> rsc_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> \ - meta is-managed="true" clone-node-max="1" interleave="true" - -order ord_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> Optional: cln_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> - - -<% if @additional_instance %> - -# -# non-production HANA and constraints -# - -primitive rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%> ocf:heartbeat:SAPDatabase \ - params DBTYPE="HDB" SID="<%= @np_system_id -%>" \ - MONITOR_SERVICES="hdbindexserver|hdbnameserver" \ - op start interval="0" timeout="600" \ - op monitor interval="120" timeout="700" \ - op stop interval="0" timeout="300" \ - meta priority="100" - -location loc_<%= @np_system_id -%>_never_<%= primary_host_name -%> \ - rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%> -inf: <%= primary_host_name -%> - - -colocation col_<%= @np_system_id -%>_never_with_PRDip \ - -inf: rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%>:Started \ - rsc_ip_<%= @system_id -%>_HDB<%= @instance -%> - - -order ord_<%= @np_system_id -%>_stop_before_PRDpromote inf: \ - rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%>:stop \ - msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%>:promote - -<% end %> diff --git a/data/tmpl_srhook.py.erb b/data/tmpl_srhook.py.erb deleted file mode 100644 index 83cfcc9..0000000 --- a/data/tmpl_srhook.py.erb +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python2 -""" -Auto-generated HA/DR hook script - -""" - -dbuser="<%= @hook_script_parameters[:hook_db_user_name] %>" -dbpwd="<%= @hook_script_parameters[:hook_db_password] %>" -dbinst="<%= @hook_script_parameters[:hook_db_instance] %>" -dbport="<%= @hook_script_parameters[:hook_port_number] %>" - -stmnt1 = "ALTER SYSTEM ALTER CONFIGURATION ('global.ini','SYSTEM') UNSET ('memorymanager','global_allocation_limit') WITH RECONFIGURE" -stmnt2 = "ALTER SYSTEM ALTER CONFIGURATION ('global.ini','SYSTEM') UNSET ('system_replication','preload_column_tables') WITH RECONFIGURE" - -import os, time, dbapi - -from hdb_ha_dr.client import HADRBase, Helper - -class srTakeover(HADRBase): - def __init__(self, *args, **kwargs): - # delegate construction to base class - super(srTakeover, self).__init__(*args, **kwargs) - - def about(self): - return {"provider_company" : "SUSE", - "provider_name" : "srTakeover", # provider name = class name - "provider_description" : "Replication takeover script to set parameters to default.", - "provider_version" : "1.0"} - - def startup(self, hostname, storage_partition, system_replication_mode, **kwargs): - self.tracer.debug("enter startup hook; %s" % locals()) - self.tracer.debug(self.config.toString()) - self.tracer.info("leave startup hook") - return 0 - - def shutdown(self, hostname, storage_partition, system_replication_mode, **kwargs): - self.tracer.debug("enter shutdown hook; %s" % locals()) - self.tracer.debug(self.config.toString()) - self.tracer.info("leave shutdown hook") - return 0 - - def failover(self, hostname, storage_partition, system_replication_mode, **kwargs): - self.tracer.debug("enter failover hook; %s" % locals()) - self.tracer.debug(self.config.toString()) - self.tracer.info("leave failover hook") - return 0 - - def stonith(self, failingHost, **kwargs): - self.tracer.debug("enter stonith hook; %s" % locals()) - self.tracer.debug(self.config.toString()) - # e.g. stonith of params["failed_host"] - # e-g- set vIP active - self.tracer.info("leave stonith hook") - return 0 - - def preTakeover(self, isForce, **kwargs): - """Pre takeover hook.""" - self.tracer.info("%s.preTakeover method called with isForce=%s" % - (self.__class__.__name__, isForce)) - if not isForce: - # run pre takeover code - # run pre-check, return != 0 in case of error => will abort takeover - return 0 - else: - # possible force-takeover only code - # usually nothing to do here - return 0 - - def postTakeover(self, rc, **kwargs): - """Post takeover hook.""" - self.tracer.info("%s.postTakeover method called with rc=%s" % (self.__class__.__name__, rc)) - if rc == 0: - # normal takeover succeeded - conn = dbapi.connect('localhost',dbport,dbuser,dbpwd) - cursor = conn.cursor() - cursor.execute(stmnt1) - cursor.execute(stmnt2) - return 0 - elif rc == 1: - # waiting for force takeover - conn = dbapi.connect('localhost',dbport,dbuser,dbpwd) - cursor = conn.cursor() - cursor.execute(stmnt1) - cursor.execute(stmnt2) - return 0 - elif rc == 2: - # error, something went wrong - return 0 \ No newline at end of file diff --git a/package/yast2-sap-ha.changes b/package/yast2-sap-ha.changes index 4c26555..b8b919b 100644 --- a/package/yast2-sap-ha.changes +++ b/package/yast2-sap-ha.changes @@ -1,7 +1,25 @@ ------------------------------------------------------------------- -Wed Aug 30 20:16:10 UTC 2023 - Josef Reidinger +Mon Aug 7 05:13:47 UTC 2023 - Peter Varkoly -- 5.0.0 (bsc#1185510) +- Set default value for global_alloc_limit to "0" +- Fix evaluation CustOpt settings. (bsc#1209204) +- Remove superfluously BuildRequires: HANA-Firewall +- 5.0.0 (#bsc1185510) + +------------------------------------------------------------------- +Fri May 26 04:52:05 UTC 2023 - Peter Varkoly + +- yast2-sap-ha for Cost-Opt scenario is not up-to-date with SR takeover in best practice guide (bsc#1209204) +- yast2-sap-ha can not configure firewall (bsc#1211027) +- Rework package sturcture to use the yast2 defaults +- New function to get the primary hostname on the master. +- Fix setting secondary and primary hostname for the template +- Do not enbale and start csync2 by installing the package. This is unsecure. +- The hook creation is deprecated. This was removed from wizard and from backend. + This functionality now will be provided by the susCostOpt.py delivered by SAPHanaSR + Now a key sus__costopt must be created. +- yast2-sap-ha: csync2 configuration not enabled (bsc#1202112) +- 4.6.1 ------------------------------------------------------------------- Mon Mar 6 12:58:58 UTC 2023 - Ladislav Slezák @@ -24,35 +42,54 @@ Thu Feb 23 11:21:43 UTC 2023 - Peter Varkoly - L3: yast2-sap-ha error - Could not adjust global.ini for the production system (bsc#1207740) -- yast2-sap-ha: csync2 configuration not enabled (bsc#1202112) -- 4.4.1 +- Add csync2 to buildrequires +- 4.5.6 ------------------------------------------------------------------- -Thu Dec 29 10:28:37 UTC 2022 - Peter Varkoly +Thu Dec 29 11:09:12 UTC 2022 - Peter Varkoly - Clean up Rakefile -- 1.0.18 - -------------------------------------------------------------------- -Thu Dec 22 09:47:04 UTC 2022 - Peter Varkoly - - Use ruby base64 to replace uuencode/uudecode (bsc#1206601) -- 1.0.17 +- yast2-sap-ha: csync2 configuration not enabled (bsc#1202112) + Enable csync2 by installing the package. yast2-sap-ha will + not be executed on the second node. +- 4.5.5 ------------------------------------------------------------------- -Tue Sep 6 10:06:08 UTC 2022 - Peter Varkoly +Tue Sep 9 10:06:08 UTC 2022 - Peter Varkoly - YaST2 HA Setup for SAP Products - cannot input several instance numbers (bsc#1202979) -- 1.0.16 +- 4.5.4 + +------------------------------------------------------------------- +Thu Sep 8 07:01:42 UTC 2022 - Michal Filka + +- bsc#1203227 + - replaced fgrep by grep -F +- 4.5.3 + +------------------------------------------------------------------- +Tue Aug 9 08:16:47 UTC 2022 - Peter Varkoly + +- yast2-sap-ha: csync2 configuration not enabled (bsc#1202112) +- 4.5.2 ------------------------------------------------------------------- Mon Jun 13 14:32:45 UTC 2022 - Peter Varkoly - YaST2 sap_ha tool does not allow digits at the beginning of site names (bsc#1200427) -- 1.0.15 +- 4.5.1 + +------------------------------------------------------------------- +Tue Jun 7 07:37:53 UTC 2022 - Peter Varkoly + +- ruby-xmlrpc is not part of stdlib anymore + Update requirement +- Adapt version to normal yast2 version +- 4.5.0 ------------------------------------------------------------------- Sun May 22 18:09:27 UTC 2022 - Peter Varkoly @@ -73,17 +110,17 @@ Fri May 13 12:09:33 UTC 2022 - Peter Varkoly - 1.0.13 ------------------------------------------------------------------- -Tue May 3 13:58:15 UTC 2022 - Peter Varkoly +Tue May 3 13:56:48 UTC 2022 - Peter Varkoly -- softdog missing in Yast while configuring HA for SAP Products - (bsc#1199029) +- Adapt ntp tests +- (bsc#1199029) + softdog missing in Yast while configuring HA for SAP Products - 1.0.12 ------------------------------------------------------------------- Tue Dec 7 15:12:47 UTC 2021 - Peter Varkoly - kmod-compat has broken dependencies (bsc#1186618) - Update requirement - 1.0.11 ------------------------------------------------------------------- @@ -93,6 +130,7 @@ Tue Oct 12 16:08:26 UTC 2021 - Peter Varkoly (bsc#1190774) Add SAPHanaSR via global.ini as proposed in https://documentation.suse.com/sbp/all/html/SLES4SAP-hana-sr-guide-PerfOpt-15/index.html#id-1.10.6.6" +- Remove HANA takeover hook script. This is not necessary for HANA 2 any more. - 1.0.10 ------------------------------------------------------------------- diff --git a/package/yast2-sap-ha.spec b/package/yast2-sap-ha.spec index 1fe93f9..314e75b 100644 --- a/package/yast2-sap-ha.spec +++ b/package/yast2-sap-ha.spec @@ -1,7 +1,7 @@ # # spec file for package yast2-sap-ha # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,49 +12,48 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # Name: yast2-sap-ha Version: 5.0.0 Release: 0 - BuildArch: noarch - Source0: %{name}-%{version}.tar.bz2 Source1: yast2-sap-ha-rpmlintrc +Requires: conntrack-tools +Requires: corosync +Requires: corosync-qdevice +Requires: crmsh +Requires: csync2 +Requires: hawk2 +Requires: pacemaker +Requires: rubygem(%{rb_default_ruby_abi}:xmlrpc) Requires: yast2 +Requires: yast2-cluster >= 4.3.8 Requires: yast2-ruby-bindings -Requires: csync2 -Requires: corosync +Requires: yast2-ntp-client # for opening URLs Requires: xdg-utils # for handling the SSH client Requires: expect +Requires: firewalld Requires: openssh -Requires: yast2-cluster -Requires: yast2-ntp-client -# for lsblk +Requires: HANA-Firewall >= 2.0.3 Requires: util-linux -# lsmod, modprobe Requires: SAPHanaSR Requires: kmod -# configuration parser -Requires: augeas-lenses -Requires: rubygem(%{rb_default_ruby_abi}:cfa) # for pidof Requires: sysvinit-tools -# xmlrpc was removed from stdlib -%if 0%{?suse_version} >= 1540 -Requires: rubygem(%{rb_default_ruby_abi}:xmlrpc) -BuildRequires: rubygem(%{rb_default_ruby_abi}:xmlrpc) -%endif -BuildRequires: augeas-lenses BuildRequires: csync2 +BuildRequires: firewalld BuildRequires: kmod +BuildRequires: rubygem(%{rb_default_ruby_abi}:rspec) +BuildRequires: rubygem(%{rb_default_ruby_abi}:xmlrpc) +BuildRequires: rubygem(%{rb_default_ruby_abi}:yast-rake) BuildRequires: sysvinit-tools BuildRequires: update-desktop-files BuildRequires: util-linux @@ -64,19 +63,15 @@ BuildRequires: yast2-devtools BuildRequires: yast2-ntp-client BuildRequires: yast2-packager BuildRequires: yast2-ruby-bindings -BuildRequires: rubygem(%{rb_default_ruby_abi}:cfa) -BuildRequires: rubygem(%{rb_default_ruby_abi}:rspec) -BuildRequires: rubygem(%{rb_default_ruby_abi}:yast-rake) Summary: SUSE High Availability Setup for SAP Products -License: GPL-2.0 +License: GPL-2.0-only Group: System/YaST -Url: http://www.suse.com +URL: http://www.suse.com %description -A YaST2 module to enable high availability for SAP HANA and SAP NetWeaver installations. +A YaST2 module to enable high availability for SAP HANA installations. %prep -%define augeas_dir %{_datarootdir}/augeas/lenses/dist %setup -n %{name}-%{version} %check @@ -85,26 +80,11 @@ rake test:unit %build %install -mkdir -p %{buildroot}%{yast_dir}/data/sap_ha/ mkdir -p %{buildroot}%{yast_vardir}/sap_ha/ -mkdir -p %{yast_scrconfdir} -mkdir -p %{buildroot}%{augeas_dir} rake install DESTDIR="%{buildroot}" -# wizard help files -install -m 644 data/*.html %{buildroot}%{yast_dir}/data/sap_ha/ -# ruby templates -install -m 644 data/*.erb %{buildroot}%{yast_dir}/data/sap_ha/ -# HA scenarios definitions -install -m 644 data/scenarios.yaml %{buildroot}%{yast_dir}/data/sap_ha/ -# SSH invocation wrapper -install -m 755 data/check_ssh.expect %{buildroot}%{yast_dir}/data/sap_ha/ -# Augeas lens for SAP INI files -install -m 644 data/sapini.aug %{buildroot}%{augeas_dir} %post -/usr/bin/systemctl enable csync2.socket -/usr/bin/systemctl start csync2.socket %files %defattr(-,root,root) @@ -115,6 +95,6 @@ install -m 644 data/sapini.aug %{buildroot}%{augeas_dir} %{yast_dir}/data/sap_ha/ %{yast_vardir}/sap_ha/ %{yast_scrconfdir}/*.scr -%{augeas_dir}/sapini.aug +%{yast_ybindir} %changelog diff --git a/pry_debug.rb b/pry_debug.rb index 3abda2b..b002168 100644 --- a/pry_debug.rb +++ b/pry_debug.rb @@ -1,25 +1,25 @@ -require 'pry' -require 'yaml' +require "pry" +require "yaml" -ENV['Y2DIR'] = File.expand_path('../src', __FILE__) +ENV["Y2DIR"] = File.expand_path("../src", __FILE__) -require_relative 'test/test_helper' +require_relative "test/test_helper" -require_relative 'src/lib/sap_ha/configuration.rb' -require 'sap_ha/system/ssh' -require 'sap_ha/system/local' -require 'sap_ha/semantic_checks' -require 'sap_ha/helpers' +require_relative "src/lib/sap_ha/configuration.rb" +require "sap_ha/system/ssh" +require "sap_ha/system/local" +require "sap_ha/semantic_checks" +require "sap_ha/helpers" -require_relative 'src/clients/sap_ha.rb' +require_relative "src/clients/sap_ha.rb" def sequence seq = Yast::SAPHA.sequence - print 'product_check -> ' + print "product_check -> " current_key = "scenario_selection" - while true + loop do print current_key - print ' -> ' + print " -> " current_key = seq[current_key][:next] break if current_key.nil? end @@ -38,27 +38,27 @@ def rpc(node) XMLRPC::Client.new(ip, "/RPC2", 8080) end -def process +def process Yast::SSH.instance.run_rpc_server("192.168.103.22") sleep 3 c = prepare_hana_config y = c.dump(true) s = rpc(:hana2) - s.call('sapha.import_config', y) - s.call('sapha.config.start_setup') + s.call("sapha.import_config", y) + s.call("sapha.config.start_setup") for component_id in c.components puts "--- configuring component #{component_id} ---" func = "sapha.config_#{component_id.to_s[1..-1]}.bogus_apply" s.call(func) end - s.call('sapha.config.end_setup') - puts s.call('sapha.config.collect_log') - s.call('sapha.shutdown') + s.call("sapha.config.end_setup") + puts s.call("sapha.config.collect_log") + s.call("sapha.shutdown") # s.call('system.listMethods') end def read_config - YAML.load(File.read('config.yml')) + YAML.load(File.read("config.yml")) end def create_sequence(conf) @@ -75,7 +75,7 @@ def create_sequence(conf) ntp: "ntp", next: "installation", back: :back - } if current == 'config_overview' + } if current == "config_overview" { back: :back, abort: :abort, @@ -85,99 +85,95 @@ def create_sequence(conf) } end seq = {} - pages_seq = conf.scenario['screen_sequence'] + pages_seq = conf.scenario["screen_sequence"] (0...pages_seq.length).each do |ix| - seq['ws_start'] = pages_seq[0] if ix == 0 - seq[pages_seq[ix]] = make_entry.call(pages_seq[ix], pages_seq[ix+1]) + seq["ws_start"] = pages_seq[0] if ix == 0 + seq[pages_seq[ix]] = make_entry.call(pages_seq[ix], pages_seq[ix + 1]) end seq end c = SapHA::HAConfiguration.new c = prepare_hana_config(c, notest: true, transport_mode: :multicast) -c.cluster.nodes[:node1][:ip_ring1] = '192.168.101.1' -c.cluster.nodes[:node1][:ip_ring2] = '192.168.103.1' +c.cluster.nodes[:node1][:ip_ring1] = "192.168.101.1" +c.cluster.nodes[:node1][:ip_ring2] = "192.168.103.1" r = rpc :hana1 y = c.dump(true) -require_relative 'src/lib/sap_ha/system/connectivity' -h = SapHA::System::Host.new('hana01', ['192.168.103.21']) - +require_relative "src/lib/sap_ha/system/connectivity" +h = SapHA::System::Host.new("hana01", ["192.168.103.21"]) def cccccc @config = SapHA::HAConfiguration.new @config.set_product_id "HANA" - @config.set_scenario_name 'Scale Up: Performance-optimized' + @config.set_scenario_name "Scale Up: Performance-optimized" + @config.cluster.import( + number_of_rings: 2, + transport_mode: :unicast, + # transport_mode: :multicast, + cluster_name: "hana_sysrep", + expected_votes: 2, + rings: { + ring1: { + address: "192.168.101.0", + port: "5405", + id: 1, + mcast: "" + # mcast: '239.0.0.1' + }, + ring2: { + address: "192.168.103.0", + port: "5405", + id: 2, + mcast: "" + # mcast: '239.0.0.2' + } + } + ) @config.cluster.import( - number_of_rings: 2, - transport_mode: :unicast, - # transport_mode: :multicast, - cluster_name: 'hana_sysrep', - expected_votes: 2, - rings: { - ring1: { - address: '192.168.101.0', - port: '5405', - id: 1, - mcast: '' - # mcast: '239.0.0.1' - }, - ring2: { - address: '192.168.103.0', - port: '5405', - id: 2, - mcast: '' - # mcast: '239.0.0.2' - } - } - ) - @config.cluster.import( - number_of_rings: 2, - number_of_nodes: 2, - nodes: { - node1: { - host_name: "hana01", - ip_ring1: "192.168.101.21", - ip_ring2: "192.168.103.21", - node_id: '1' - }, - node2: { - host_name: "hana02", - ip_ring1: "192.168.101.22", - ip_ring2: "192.168.103.22", - node_id: '2' - } - } - ) - @config.fencing.import(devices: [{ name: '/dev/vdb', type: 'disk', uuid: '' }]) - @config.watchdog.import(to_install: ['softdog']) - @config.hana.import( - system_id: 'XXX', - instance: '00', - virtual_ip: '192.168.101.100', - backup_user: 'xxxadm' - ) - ntp_cfg = { - "synchronize_time" => false, - "sync_interval" => 5, - "start_at_boot" => true, - "start_in_chroot" => false, - "ntp_policy" => "auto", - "restricts" => [], - "peers" => [ - { "type" => "server", - "address" => "ntp.local", - "options" => " iburst", - "comment" => "# key (6) for accessing server variables\n" - } - ] + number_of_rings: 2, + number_of_nodes: 2, + nodes: { + node1: { + host_name: "hana01", + ip_ring1: "192.168.101.21", + ip_ring2: "192.168.103.21", + node_id: "1" + }, + node2: { + host_name: "hana02", + ip_ring1: "192.168.101.22", + ip_ring2: "192.168.103.22", + node_id: "2" } - @config.ntp.read_configuration - return @config + } + ) + @config.fencing.import(devices: [{ name: "/dev/vdb", type: "disk", uuid: "" }]) + @config.watchdog.import(to_install: ["softdog"]) + @config.hana.import( + system_id: "XXX", + instance: "00", + virtual_ip: "192.168.101.100", + backup_user: "xxxadm" + ) + ntp_cfg = { + "synchronize_time" => false, + "sync_interval" => 5, + "start_at_boot" => true, + "start_in_chroot" => false, + "ntp_policy" => "auto", + "restricts" => [], + "peers" => [ + { "type" => "server", + "address" => "ntp.local", + "options" => " iburst", + "comment" => "# key (6) for accessing server variables\n" } + ] + } + @config.ntp.read_configuration + return @config end c2 = cccccc -binding.pry - puts nil diff --git a/data/check_ssh.expect b/src/bin/check_ssh.expect similarity index 100% rename from data/check_ssh.expect rename to src/bin/check_ssh.expect diff --git a/src/clients/sap_ha.rb b/src/clients/sap_ha.rb index 92cfb3a..a0efbd1 100644 --- a/src/clients/sap_ha.rb +++ b/src/clients/sap_ha.rb @@ -21,6 +21,7 @@ require "yast" require "psych" +require "sap_ha/configuration" require "sap_ha/helpers" require "sap_ha/node_logger" require "sap_ha/wizard/cluster_nodes_page" @@ -36,7 +37,6 @@ require "sap_ha/wizard/list_selection" require "sap_ha/wizard/rich_text" require "sap_ha/wizard/scenario_selection_page" -require "sap_ha/configuration" # YaST module module Yast @@ -47,6 +47,7 @@ class SAPHAClass < Client Yast.import "UI" Yast.import "Wizard" Yast.import "Sequencer" + Yast.import "Service" Yast.import "Popup" include Yast::UIShortcuts include Yast::Logger @@ -54,7 +55,7 @@ class SAPHAClass < Client def initialize log.warn "--- called #{self.class}.#{__callee__}: CLI arguments are #{WFM.Args} ---" - begin + begin if WFM.Args.include?("readconfig") ix = WFM.Args.index("readconfig") + 1 begin @@ -65,7 +66,7 @@ def initialize @config.imported = true if WFM.Args.include?("unattended") @config.unattended = true - end + end else @config = SapHA::HAConfiguration.new end @@ -189,7 +190,7 @@ def initialize next: :ws_finish } } - + @unattended_sequence = { "ws_start" => "product_check", "product_check" => { @@ -214,7 +215,6 @@ def initialize } } - @aliases = { "product_check" => -> { product_check }, "file_import_check" => -> { file_import_check }, @@ -235,7 +235,7 @@ def initialize "config_overview" => -> { configuration_overview }, "summary" => -> { show_summary } } - end + end end def main @@ -245,32 +245,32 @@ def main Wizard.CreateDialog Wizard.SetDialogTitle("HA Setup for SAP Products") begin - if @config.unattended - Sequencer.Run(@aliases, @unattended_sequence) + if @config.unattended + Sequencer.Run(@aliases, @unattended_sequence) else - Sequencer.Run(@aliases, @sequence) + Sequencer.Run(@aliases, @sequence) end rescue StandardError => e # FIXME: y2start overrides the return code, therefore exit prematurely without # shutting down Yast properly, see bsc#1099871 # If the error was not catched until here, we know that is a unattended installation. # exit!(1) - @unattended_error = "Error occurred during the unattended installation: #{e.message}" - log.error @unattended_error + @unattended_error = "Error occurred during the unattended installation: #{e.message}" + log.error @unattended_error puts @unattended_error Popup.TimedError(@unattended_error, 10) ensure Wizard.CloseDialog if @config.unattended - if @unattended_error.nil? - success = SapHA::Helpers.write_file("/var/log/YaST2/sap_ha_unattended_install_log.txt", SapHA::NodeLogger.text) + if @unattended_error.nil? + SapHA::Helpers.write_file("/var/log/YaST2/sap_ha_unattended_install_log.txt", SapHA::NodeLogger.text) log.info "Execution Finished: Please, verify the log /var/log/YaST2/sap_ha_unattended_install_log.txt" - # FIXME: yast redirects stdout, therefore the usage of the CommanlineClass is needed to write on the stdout, but as the - # the dependent modules we have (cluster, firewall, ntp) demands UI existence, we cannot call the module without creating the UI object. + # FIXME: yast redirects stdout, therefore the usage of the CommanlineClass is needed to write on the stdout, but as the + # the dependent modules we have (cluster, firewall, ntp) demands UI existence, we cannot call the module without creating the UI object. # The best option is to presente a Timed Popup to the user. Popup.TimedMessage("Execution Finished: Please, verify the log /var/log/YaST2/sap_ha_unattended_install_log.txt", 10) end - end + end end end @@ -288,25 +288,23 @@ def product_check return :unknown end # TODO: here we should check if the symbol can be handled by th - #stat = Yast::Cluster.LoadClusterConfig - #Yast::Cluster.load_csync2_confe Sequencer + # stat = Yast::Cluster.LoadClusterConfig + # Yast::Cluster.load_csync2_confe Sequencer @config.product.fetch("id", "abort").downcase.to_sym end def file_import_check - begin - log.debug "--- called #{self.class}.#{__callee__} ---" - SapHA::SAPHAUnattendedInstall.new(@config).check_config - rescue StandardError => e - if @config.unattended - # Will be trated by the caller to collect the log. - raise e - else - # Adjust the WF to show the Summary with the problems. - return :unknown - end + log.debug "--- called #{self.class}.#{__callee__} ---" + SapHA::SAPHAUnattendedInstall.new(@config).check_config + rescue StandardError => e + if @config.unattended + # Will be trated by the caller to collect the log. + raise e + else + # Adjust the WF to show the Summary with the problems. + return :unknown end - end + end def scenario_selection log.debug "--- called #{self.class}.#{__callee__} ---" @@ -436,6 +434,9 @@ def run_unattended_install def show_summary log.debug "--- called #{self.class}.#{__callee__} ---" + if File.exist?(SapHA::Helpers.var_file_path("need_to_start_firewalld")) + Service.Start("firewalld") + end SapHA::Wizard::SetupSummaryPage.new(@config).run end diff --git a/src/clients/sap_ha_rpc.rb b/src/clients/sap_ha_rpc.rb index 006463f..18b2760 100644 --- a/src/clients/sap_ha_rpc.rb +++ b/src/clients/sap_ha_rpc.rb @@ -19,24 +19,22 @@ # Summary: SUSE High Availability Setup for SAP Products: XML RPC Server Client # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/rpc_server' +require "yast" +require "sap_ha/rpc_server" module Yast # An XML RPC Yast Client class SapHARPCClass < Client - def initialize @server = SapHA::RPCServer.new at_exit { @server.shutdown } end - + def main # the following call blocks @server.start # when .shutdown is called @server.close_port - Yast::SuSEFirewall.ActivateConfiguration end end diff --git a/src/data/sap_ha/GLOBAL_INI_SAPHANA_SR b/src/data/sap_ha/GLOBAL_INI_SAPHANA_SR new file mode 100644 index 0000000..6ddde79 --- /dev/null +++ b/src/data/sap_ha/GLOBAL_INI_SAPHANA_SR @@ -0,0 +1,7 @@ +[ha_dr_provider_saphanasr] +provider = SAPHanaSR +path = /usr/share/SAPHanaSR/ +execution_order = 1 + +[trace] +ha_dr_saphanasr = info diff --git a/src/data/sap_ha/GLOBAL_INI_SUS_CHKSRV b/src/data/sap_ha/GLOBAL_INI_SUS_CHKSRV new file mode 100644 index 0000000..f9bb4b6 --- /dev/null +++ b/src/data/sap_ha/GLOBAL_INI_SUS_CHKSRV @@ -0,0 +1,8 @@ +[ha_dr_provider_suschksrv] +provider = susChkSrv +path = /usr/share/SAPHanaSR/ +execution_order = 3 +action_on_lost=stop + +[trace] +ha_dr_suschksrv = info diff --git a/src/data/sap_ha/GLOBAL_INI_SUS_COSTOPT.erb b/src/data/sap_ha/GLOBAL_INI_SUS_COSTOPT.erb new file mode 100644 index 0000000..a9e6414 --- /dev/null +++ b/src/data/sap_ha/GLOBAL_INI_SUS_COSTOPT.erb @@ -0,0 +1,14 @@ +[memorymanager] +global_allocation_limit = <%= @production_constraints[:global_alloc_limit] -%> + +[system_replication] +preload_column_tables = <%= @production_constraints[:preload_column_tables] -%> + +[ha_dr_provider_suscostopt] +provider = susCostOpt +path = /usr/share/SAPHanaSR/ +userkey = sus_<%= @system_id -%>_costopt +execution_order = 2 + +[trace] +ha_dr_suscostopt = info diff --git a/src/data/sap_ha/GLOBAL_INI_SUS_TKOVER b/src/data/sap_ha/GLOBAL_INI_SUS_TKOVER new file mode 100644 index 0000000..c5f2055 --- /dev/null +++ b/src/data/sap_ha/GLOBAL_INI_SUS_TKOVER @@ -0,0 +1,7 @@ +[ha_dr_provider_sustkover] +provider = susTkOver +path = /usr/share/SAPHanaSR/ +execution_order = 2 + +[trace] +ha_dr_sustkover = info diff --git a/src/data/sap_ha/SUDOERS_HANASR.erb b/src/data/sap_ha/SUDOERS_HANASR.erb new file mode 100644 index 0000000..e2d4bfe --- /dev/null +++ b/src/data/sap_ha/SUDOERS_HANASR.erb @@ -0,0 +1,9 @@ +# SAPHanaSR-ScaleUp entries for writing srHook cluster attribute +<%= @system_id -%>adm ALL=(ALL) NOPASSWD: /usr/sbin/crm_attribute -n hana_<%= @system_id -%>_site_srHook_* + +# SAPHanaSR-ScaleUp entries for writing srHook cluster attribute +Cmnd_Alias SOK_SITEA = /usr/sbin/crm_attribute -n hana_<%= @system_id -%>_site_srHook_<%= @site_name_1 -%> -v SOK -t crm_config -s SAPHanaSR +Cmnd_Alias SFAIL_SITEA = /usr/sbin/crm_attribute -n hana_<%= @system_id -%>_site_srHook_<%= @site_name_1 -%> -v SFAIL -t crm_config -s SAPHanaSR +Cmnd_Alias SOK_SITEB = /usr/sbin/crm_attribute -n hana_<%= @system_id -%>_site_srHook_<%= @site_name_2 -%> -v SOK -t crm_config -s SAPHanaSR +Cmnd_Alias SFAIL_SITEB = /usr/sbin/crm_attribute -n hana_<%= @system_id -%>_site_srHook_<%= @site_name_2 -%> -v SFAIL -t crm_config -s SAPHanaSR +<%= @system_id -%>adm ALL=(ALL) NOPASSWD: SOK_SITEA, SFAIL_SITEA, SOK_SITEB, SFAIL_SITEB diff --git a/data/help_cluster_nodes.html b/src/data/sap_ha/help_cluster_nodes.html similarity index 100% rename from data/help_cluster_nodes.html rename to src/data/sap_ha/help_cluster_nodes.html diff --git a/data/help_comm_layer.html b/src/data/sap_ha/help_comm_layer.html similarity index 68% rename from data/help_comm_layer.html rename to src/data/sap_ha/help_comm_layer.html index 67c5087..addb7ea 100644 --- a/data/help_comm_layer.html +++ b/src/data/sap_ha/help_comm_layer.html @@ -20,10 +20,16 @@

Communication Layer

Please make sure that the provided IP addresses correspond to the network addresses of the rings.

Note: the number of nodes for HANA scale-up clusters is fixed and cannot be changed.

-

Enable Corosync Secure Authentication: +

There are 3 possibility for the Firewall configuration:

+
    +
  • Firewall is configured The firewall on all nodes is already configured. All necessary ports for cluster and SAP HANA are opened. In this case only port 8080 will be temporary opened on the slave node during the setup
  • +
  • Turn off Firewall The firewall will be turned of on all nodes during the setup.
  • +
  • Configure Firewall The firewall will be configured by using HANA-firewall and by enabling the cluster service.
  • +
+ Enable Corosync Secure Authentication: require the HMAC/SHA1 authentication for cluster messages authentication. This option reduces total throughput of the cluster messaging level and consumes extra CPU cycles for encryption.

Enable csync2: enable the csync2 service that synchronizes cluster-related configuration files between nodes.

- \ No newline at end of file + diff --git a/data/help_fencing.html b/src/data/sap_ha/help_fencing.html similarity index 100% rename from data/help_fencing.html rename to src/data/sap_ha/help_fencing.html diff --git a/data/help_hana.html b/src/data/sap_ha/help_hana.html similarity index 100% rename from data/help_hana.html rename to src/data/sap_ha/help_hana.html diff --git a/data/help_hana_cost_optimized.html b/src/data/sap_ha/help_hana_cost_optimized.html similarity index 100% rename from data/help_hana_cost_optimized.html rename to src/data/sap_ha/help_hana_cost_optimized.html diff --git a/data/help_join_cluster.html b/src/data/sap_ha/help_join_cluster.html similarity index 100% rename from data/help_join_cluster.html rename to src/data/sap_ha/help_join_cluster.html diff --git a/data/help_ntp.html b/src/data/sap_ha/help_ntp.html similarity index 100% rename from data/help_ntp.html rename to src/data/sap_ha/help_ntp.html diff --git a/data/help_prereq_hana_su_co.html b/src/data/sap_ha/help_prereq_hana_su_co.html similarity index 68% rename from data/help_prereq_hana_su_co.html rename to src/data/sap_ha/help_prereq_hana_su_co.html index 39b578b..4ba20d4 100644 --- a/data/help_prereq_hana_su_co.html +++ b/src/data/sap_ha/help_prereq_hana_su_co.html @@ -5,9 +5,6 @@

SAP HANA System Replication Scale Up: Cost-Optimized

There are two machines with SAP HANA installed.
Note that the machine you run the wizard on becomes the primary node in the cluster. -
  • - There is a firewall configured using the SAP HANA Firewall module in Administrator Settings, and HANA_HIGH_AVAILABILITY and HANA_SYSTEM_REPLICATION services are enabled on all relevant network interfaces. -
  • You have created one of the following:
      @@ -20,16 +17,10 @@

      SAP HANA System Replication Scale Up: Cost-Optimized

  • -
  • - You have prepared a takeover hook script for HANA (will be prompted for at one of the following stages of the Wizard). -
  • The second node has to have a secondary, non-production HANA instance installed, but not running.
  • You've created a HANA Secure User Store key QASSAPDBCTRL for monitoring purposes (please refer to the SUSE SAP HANA SR Cost Optimized Scenario best practice guide, section 4.2.7).
  • -
  • - For SUSE Linux Enterprise Server version 15 and later: please ensure that during the HA setup procedure the TCP port 8080 is open on the secondary node. After the HA setup is complete, please enable the cluster service in firewalld on both nodes. -
  • - \ No newline at end of file + diff --git a/data/help_prereq_hana_su_co_azure.html b/src/data/sap_ha/help_prereq_hana_su_co_azure.html similarity index 80% rename from data/help_prereq_hana_su_co_azure.html rename to src/data/sap_ha/help_prereq_hana_su_co_azure.html index 7c35434..949ec31 100644 --- a/data/help_prereq_hana_su_co_azure.html +++ b/src/data/sap_ha/help_prereq_hana_su_co_azure.html @@ -18,9 +18,6 @@

    SAP HANA System Replication Scale Up: Cost-Optimized in Microsoft® Azure There are two machines with SAP HANA installed.
    Note that the machine you run the wizard on becomes the primary node in the cluster. -
  • - There is a firewall configured using the SAP HANA Firewall module in Administrator Settings, and HANA_HIGH_AVAILABILITY and HANA_SYSTEM_REPLICATION services are enabled on all relevant network interfaces. -
  • The master node SSH public key is authorized for SSH access to the secondary node. This is needed when SSH password access is disabled, which is the default on Microsoft Azure.
  • @@ -39,16 +36,10 @@

    SAP HANA System Replication Scale Up: Cost-Optimized in Microsoft® Azure -
  • - You have prepared a takeover hook script for HANA (will be prompted for at one of the following stages of the Wizard). -
  • The second node has to have a secondary, non-production HANA instance installed, but not running.
  • You've created a HANA Secure User Store key QASSAPDBCTRL for monitoring purposes (please refer to the SUSE SAP HANA SR Cost Optimized Scenario best practice guide, section 4.2.7).
  • -
  • - For SUSE Linux Enterprise Server version 15 and later: please ensure that during the HA setup procedure the TCP port 8080 is open on the secondary node. After the HA setup is complete, please enable the cluster service in firewalld on both nodes. -
  • - \ No newline at end of file + diff --git a/data/help_prereq_hana_su_mt.html b/src/data/sap_ha/help_prereq_hana_su_mt.html similarity index 73% rename from data/help_prereq_hana_su_mt.html rename to src/data/sap_ha/help_prereq_hana_su_mt.html index 2fe88ac..32a0561 100644 --- a/data/help_prereq_hana_su_mt.html +++ b/src/data/sap_ha/help_prereq_hana_su_mt.html @@ -5,10 +5,6 @@

    SAP HANA System Replication Scale Up: Multi-Tier

    There are two machines with SAP HANA installed.
    Note that the machine you run the wizard on becomes the primary node in the cluster. -
  • - There is a firewall configured using the SAP HANA Firewall module in Administrator Settings, and HANA_HIGH_AVAILABILITY and - HANA_SYSTEM_REPLICATION services are enabled on all relevant network interfaces. -
  • You have created one of the following:
      @@ -30,7 +26,4 @@

      SAP HANA System Replication Scale Up: Multi-Tier

      --remoteInstance=$(instance_number) --replicationMode=async --name=$(site_name). -
    • - For SUSE Linux Enterprise Server version 15 and later: please ensure that during the HA setup procedure the TCP port 8080 is open on the secondary node. After the HA setup is complete, please enable the cluster service in firewalld on both nodes. -
    • -
    \ No newline at end of file + diff --git a/data/help_prereq_hana_su_mt_azure.html b/src/data/sap_ha/help_prereq_hana_su_mt_azure.html similarity index 83% rename from data/help_prereq_hana_su_mt_azure.html rename to src/data/sap_ha/help_prereq_hana_su_mt_azure.html index 22f3924..127eba5 100644 --- a/data/help_prereq_hana_su_mt_azure.html +++ b/src/data/sap_ha/help_prereq_hana_su_mt_azure.html @@ -18,10 +18,6 @@

    SAP HANA System Replication Scale Up: Multi-Tier in Microsoft® Azure® There are two machines with SAP HANA installed.
    Note that the machine you run the wizard on becomes the primary node in the cluster.

  • -
  • - There is a firewall configured using the SAP HANA Firewall module in Administrator Settings, and HANA_HIGH_AVAILABILITY and - HANA_SYSTEM_REPLICATION services are enabled on all relevant network interfaces. -
  • The master node SSH public key is authorized for SSH access to the secondary node. This is needed when SSH password access is disabled, which is the default on Microsoft Azure.
  • @@ -49,7 +45,4 @@

    SAP HANA System Replication Scale Up: Multi-Tier in Microsoft® Azure® --remoteInstance=$(instance_number) --replicationMode=async --name=$(site_name). -
  • - For SUSE Linux Enterprise Server version 15 and later: please ensure that during the HA setup procedure the TCP port 8080 is open on the secondary node. After the HA setup is complete, please enable the cluster service in firewalld on both nodes. -
  • - \ No newline at end of file + diff --git a/data/help_prereq_hana_su_po.html b/src/data/sap_ha/help_prereq_hana_su_po.html similarity index 69% rename from data/help_prereq_hana_su_po.html rename to src/data/sap_ha/help_prereq_hana_su_po.html index a7f5c20..3dfe52c 100644 --- a/data/help_prereq_hana_su_po.html +++ b/src/data/sap_ha/help_prereq_hana_su_po.html @@ -5,9 +5,6 @@

    SAP HANA System Replication Scale Up: Performance-Optimized

    There are two machines with SAP HANA installed.
    Note that the machine you run the wizard on becomes the primary node in the cluster. -
  • - There is a firewall configured using the SAP HANA Firewall module in Administrator Settings, and HANA_HIGH_AVAILABILITY and HANA_SYSTEM_REPLICATION services are enabled on all relevant network interfaces. -
  • You have created one of the following:
      @@ -21,8 +18,5 @@

      SAP HANA System Replication Scale Up: Performance-Optimized

  • -
  • - For SUSE Linux Enterprise Server version 15 and later: please ensure that during the HA setup procedure the TCP port 8080 is open on the secondary node. After the HA setup is complete, please enable the cluster service in firewalld on both nodes. -
  • - \ No newline at end of file + diff --git a/data/help_prereq_hana_su_po_azure.html b/src/data/sap_ha/help_prereq_hana_su_po_azure.html similarity index 82% rename from data/help_prereq_hana_su_po_azure.html rename to src/data/sap_ha/help_prereq_hana_su_po_azure.html index 3d1a8fa..0c4e499 100644 --- a/data/help_prereq_hana_su_po_azure.html +++ b/src/data/sap_ha/help_prereq_hana_su_po_azure.html @@ -18,9 +18,6 @@

    SAP HANA System Replication Scale Up: Performance-Optimized in Microsoft® There are two machines with SAP HANA installed.
    Note that the machine you run the wizard on becomes the primary node in the cluster. -
  • - There is a firewall configured using the SAP HANA Firewall module in Administrator Settings, and HANA_HIGH_AVAILABILITY and HANA_SYSTEM_REPLICATION services are enabled on all relevant network interfaces. -
  • The master node SSH public key is authorized for SSH access to the secondary node. This is needed when SSH password access is disabled, which is the default on Microsoft Azure.
  • @@ -40,8 +37,5 @@

    SAP HANA System Replication Scale Up: Performance-Optimized in Microsoft® -
  • - For SUSE Linux Enterprise Server version 15 and later: please ensure that during the HA setup procedure the TCP port 8080 is open on the secondary node. After the HA setup is complete, please enable the cluster service in firewalld on both nodes. -
  • - \ No newline at end of file + diff --git a/data/help_product_not_found.html b/src/data/sap_ha/help_product_not_found.html similarity index 100% rename from data/help_product_not_found.html rename to src/data/sap_ha/help_product_not_found.html diff --git a/data/help_setup_summary.html b/src/data/sap_ha/help_setup_summary.html similarity index 100% rename from data/help_setup_summary.html rename to src/data/sap_ha/help_setup_summary.html diff --git a/data/help_watchdog.html b/src/data/sap_ha/help_watchdog.html similarity index 100% rename from data/help_watchdog.html rename to src/data/sap_ha/help_watchdog.html diff --git a/data/sapini.aug b/src/data/sap_ha/sapini.aug similarity index 100% rename from data/sapini.aug rename to src/data/sap_ha/sapini.aug diff --git a/data/scenarios.yaml b/src/data/sap_ha/scenarios.yaml similarity index 100% rename from data/scenarios.yaml rename to src/data/sap_ha/scenarios.yaml diff --git a/src/data/sap_ha/tmpl_cluster_config.erb b/src/data/sap_ha/tmpl_cluster_config.erb new file mode 100644 index 0000000..b3ac6d3 --- /dev/null +++ b/src/data/sap_ha/tmpl_cluster_config.erb @@ -0,0 +1,93 @@ +# +# defaults +# + +property cib-bootstrap-options: \ + have-watchdog="true" \ + stonith-enabled="true" \ + stonith-action="reboot" \ + stonith-timeout="150s" + +rsc_defaults rsc-options: \ + resource-stickiness="1000" \ + migration-threshold="3" + +op_defaults op-options: \ + timeout="600" \ + record-pending=true + +# +# production HANA +# + +primitive rsc_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> ocf:suse:SAPHanaTopology \ + op monitor interval="10" timeout="600" \ + op start interval="0" timeout="600" \ + op stop interval="0" timeout="300" \ + params SID="<%= @system_id -%>" InstanceNumber="<%= @instance -%>" + +clone cln_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> rsc_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> \ + meta clone-node-max="1" interleave="true" + +primitive rsc_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> ocf:suse:SAPHana \ + op start interval="0" timeout="3600" \ + op stop interval="0" timeout="3600" \ + op promote interval="0" timeout="3600" \ + op monitor interval="60" role="Master" timeout="700" \ + op monitor interval="61" role="Slave" timeout="700" \ + params SID="<%= @system_id -%>" InstanceNumber="<%= @instance -%>" PREFER_SITE_TAKEOVER="<%= @prefer_takeover -%>" \ + DUPLICATE_PRIMARY_TIMEOUT="7200" AUTOMATED_REGISTER="<%= @auto_register -%>" \ + meta priority="100" + +ms msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> rsc_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> \ + meta clone-max="2" clone-node-max="1" interleave="true" + +primitive rsc_ip_<%= @system_id -%>_HDB<%= @instance -%> ocf:heartbeat:IPaddr2 \ + op monitor interval="10" timeout="20" \ + params ip="<%= @virtual_ip %>" cidr_netmask="<%= @virtual_ip_mask %>" + +<% if @global_config.platform == "azure" %> + +primitive rsc_nc_<%= @system_id -%>_HDB<%= @instance -%> azure-lb port=625<%= @instance -%> \ + op monitor timeout=20s interval=10 \ + meta resource-stickiness=0 + +group g_ip_<%= @system_id -%>_HDB<%= @instance -%> rsc_ip_<%= @system_id -%>_HDB<%= @instance -%> rsc_nc_<%= @system_id -%>_HDB<%= @instance -%> + +colocation col_saphana_ip_<%= @system_id -%>_HDB<%= @instance -%> <%= @additional_instance ? "3000" : "2000" -%>: g_ip_<%= @system_id -%>_HDB<%= @instance -%>:Started \ + msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%>:Master + +<% else %> + +colocation col_saphana_ip_<%= @system_id -%>_HDB<%= @instance -%> <%= @additional_instance ? "3000" : "2000" -%>: rsc_ip_<%= @system_id -%>_HDB<%= @instance -%>:Started \ + msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%>:Master + +<% end %> + +order ord_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> Optional: cln_SAPHanaTopology_<%= @system_id -%>_HDB<%= @instance -%> \ + msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%> + +<% if @additional_instance %> + +# +# non-production HANA and constraints +# + +primitive rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%> ocf:heartbeat:SAPInstance \ + params InstanceName="<%= @np_system_id -%>_HDB<%= @np_instance -%>_<%= secondary_host_name -%>" \ + MONITOR_SERVICES="hdbindexserver|hdbnameserver" \ + START_PROFILE="/usr/sap/QAS/SYS/profile/<%= @np_system_id -%>_HDB<%= @np_instance -%>_<%= secondary_host_name -%>" \ + op start interval="0" timeout="600" \ + op monitor interval="120" timeout="700" \ + op stop interval="0" timeout="300" + +location loc_<%= @np_system_id -%>_never_<%= primary_host_name -%> rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%> -inf: <%= primary_host_name -%> + +colocation col_<%= @np_system_id -%>_never_with_<%= @system_id -%>ip -inf: rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%>:Started \ + rsc_ip_<%= @system_id -%>_HDB<%= @instance -%> + +order ord_<%= @np_system_id -%>_stop_before_<%= @system_id -%>-promote mandatory: \ + rsc_SAP_<%= @np_system_id -%>_HDB<%= @np_instance -%>:stop \ + msl_SAPHana_<%= @system_id -%>_HDB<%= @instance -%>:promote + +<% end %> diff --git a/data/tmpl_config_overview_con.erb b/src/data/sap_ha/tmpl_config_overview_con.erb similarity index 100% rename from data/tmpl_config_overview_con.erb rename to src/data/sap_ha/tmpl_config_overview_con.erb diff --git a/data/tmpl_config_overview_gui.erb b/src/data/sap_ha/tmpl_config_overview_gui.erb similarity index 100% rename from data/tmpl_config_overview_gui.erb rename to src/data/sap_ha/tmpl_config_overview_gui.erb diff --git a/data/tmpl_csync2_config.erb b/src/data/sap_ha/tmpl_csync2_config.erb similarity index 100% rename from data/tmpl_csync2_config.erb rename to src/data/sap_ha/tmpl_csync2_config.erb diff --git a/src/lib/cfa/global_ini.rb b/src/lib/cfa/global_ini.rb deleted file mode 100644 index 86d660d..0000000 --- a/src/lib/cfa/global_ini.rb +++ /dev/null @@ -1,102 +0,0 @@ -# encoding: utf-8 - -# ------------------------------------------------------------------------------ -# Copyright (c) 2018 SUSE Linux GmbH, Nuernberg, Germany. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of version 2 of the GNU General Public License as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, contact SUSE Linux GmbH. -# -# ------------------------------------------------------------------------------ -# -# Summary: SUSE High Availability Setup for SAP Products: HANA configuration -# Authors: Ayoub Belarbi - -require "yast" -require "yast2/target_file" - -require "cfa/base_model" -require "cfa/matcher" -require "cfa/augeas_parser" - -module CFA - # class representing HANA global.ini config file model. - # It provides helper to manipulate with the global.ini file. - # It uses CFA framework and Augeas parser. - # @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/BaseModel - # @see http://www.rubydoc.info/github/config-files-api/config_files_api/CFA/AugeasParser - class GlobalIni < BaseModel - include Yast::Logger - - def initialize(path, file_handler: nil) - super(AugeasParser.new("sapini.lns"), path, file_handler: file_handler) - end - - # Replaces or adds a config Tree and subtree. - # @param [String] tree_key - # @param [String] subtree_key - # @param [String] subtree_value - # @return [void] - def set_config(tree_key, subtree_key, subtree_value) - entries = data.select(key_matcher(tree_key)) - if entries.empty? - entry = AugeasTree.new - entry[subtree_key] = subtree_value - data.add(tree_key, entry) - log.info "Successfully created tree '#{tree_key}' with subtree '#{subtree_key}' and value '#{subtree_value}'" - else - entries.each do |e| - if e[:value] && e[:value][subtree_key] - e[:value][subtree_key] = subtree_value - log.info "Successfully modified subtree '#{subtree_key}' in tree '#{tree_key}' with value '#{subtree_value}'" - else - e[subtree_key] = subtree_value - log.info "Successfully created new subtree '#{subtree_key}' in tree '#{tree_key}' with value '#{subtree_value}'" - end - end - end - end - - # Removes all occurrences of a given tree or subtree. - # If called with only the tree key (tree key), it will remove all - # the entire tree - # @param [String] tree_key - # @param [String] subtree_key - # @return [void] - def delete_config(tree_key, subtree_key = "") - entries = data.select(key_matcher(tree_key)) - if entries.empty? - log.info "No tree found with id '#{tree_key}'. Doing nothing." - elsif subtree_key == "" - entries.each do |e| - data.delete(e[:key]) - log.info "Deleting tree '#{tree_key}'" - end - else - entries.each do |e| - if e[:value] && e[:value][subtree_key] - e[:value].delete(subtree_key) - log.info "Successfully deleted subtree '#{subtree_key}' in tree '#{tree_key}'" - else - log.info "Didn't found tree '#{tree_key}' with subtree '#{subtree_key}'" - end - end - end - end - - private - - # Returns matcher for cfa to find entries with given key - def key_matcher(key) - Matcher.new { |k, _v| k == key } - end - - end -end diff --git a/src/lib/sap_ha/configuration.rb b/src/lib/sap_ha/configuration.rb index 7b89538..7eca746 100644 --- a/src/lib/sap_ha/configuration.rb +++ b/src/lib/sap_ha/configuration.rb @@ -19,18 +19,20 @@ # Summary: SUSE High Availability Setup for SAP Products: Top-level configuration # Authors: Ilya Manyugin -require 'yast' -require 'erb' -require 'psych' - -require 'sap_ha/helpers' -require 'sap_ha/node_logger' -require 'sap_ha/configuration/cluster' -require 'sap_ha/configuration/cluster_finalizer' -require 'sap_ha/configuration/fencing' -require 'sap_ha/configuration/watchdog' -require 'sap_ha/configuration/hana' -require 'sap_ha/configuration/ntp' +require "yast" +require "erb" +require "psych" + +require "sap_ha/helpers" +require "sap_ha/node_logger" +require "sap_ha/configuration/cluster" +require "sap_ha/configuration/cluster_finalizer" +require "sap_ha/configuration/fencing" +require "sap_ha/configuration/watchdog" +require "sap_ha/configuration/hana" +require "sap_ha/configuration/ntp" +require "sap_ha/system/local" +require "sap_ha/system/shell_commands" module SapHA # Module's configuration @@ -60,6 +62,7 @@ class HAConfiguration include Yast::Logger include Yast::I18n include SapHA::Exceptions + include SapHA::System::ShellCommands def initialize(role = :master) @timestamp = Time.now @@ -84,13 +87,14 @@ def initialize(role = :master) @ntp = Configuration::NTP.new(self) @config_sequence = [] @platform = SapHA::Helpers.platform_check + @fw_state = exec_status("/usr/bin/firewall-cmd","--state").exitstatus end # Function to refresh the proposals of some modules. This is neccessary when # loading an old configuration to detect new hardware. def refresh_all_proposals - @watchdog.refresh_proposals - @fencing.read_system + @watchdog.refresh_proposals + @fencing.read_system end # Product ID setter. Raises an ScenarioNotFoundException if the ID was not found @@ -99,11 +103,11 @@ def set_product_id(value) log.debug "--- called #{self.class}.#{__callee__}(#{value}) ---" @product_id = value product = @yaml_configuration.find do |p| - p.fetch('id', '') == @product_id + p.fetch("id", "") == @product_id end raise ProductNotFoundException, "Could not find product with ID '#{value}'" unless product @product = product.dup - @product_name = @product['string_name'] + @product_name = @product["string_name"] end # Scenario Name setter. Raises an ScenarioNotFoundException if the name was not found @@ -114,7 +118,7 @@ def set_scenario_name(value) raise ProductNotFoundException, "Setting scenario name before setting the Product ID" if @product.nil? @scenario_name = value - @scenario = @product['scenarios'].find { |s| s['name'] == @scenario_name } + @scenario = @product["scenarios"].find { |s| s["name"] == @scenario_name } unless @scenario log.error("Scenario name '#{@scenario_name}' not found in the scenario list") raise ScenarioNotFoundException @@ -124,8 +128,8 @@ def set_scenario_name(value) def apply_scenario log.debug "--- called #{self.class}.#{__callee__}() ---" - if @scenario['config_sequence'] - @config_sequence = @scenario['config_sequence'].map do |el| + if @scenario["config_sequence"] + @config_sequence = @scenario["config_sequence"].map do |el| instv = "@#{el}".to_sym unless instance_variable_defined?(instv) log.error "Scenario #{@scenario} requires a configuration object"\ @@ -137,25 +141,24 @@ def apply_scenario object: instance_variable_get(instv), # local configuration object screen_name: instance_variable_get(instv).screen_name, # screen name for GUI rpc_object: "sapha.config_#{el}", - rpc_method: "sapha.config_#{el}.apply" - } + rpc_method: "sapha.config_#{el}.apply" } end else log.error "Scenario #{@scenario} does not set a configuration sequence." raise GUIFatal, "Scenario configuration is incorrect. Please check the logs." end @cluster.set_fixed_nodes( - @scenario.fetch('fixed_number_of_nodes', false), - @scenario.fetch('number_of_nodes', 2) + @scenario.fetch("fixed_number_of_nodes", false), + @scenario.fetch("number_of_nodes", 2) ) - @hana.additional_instance = @scenario['additional_instance'] || false if @hana + @hana.additional_instance = @scenario["additional_instance"] || false if @hana end def all_scenarios log.debug "--- called #{self.class}.#{__callee__}() ---" raise ProductNotFoundException, "Getting scenarios list before setting the Product ID" if @product.nil? - @product['scenarios'].map { |s| s['name'] } + @product["scenarios"].map { |s| s["name"] } end # Generate help string for all scenarios @@ -163,7 +166,7 @@ def scenarios_help log.debug "--- called #{self.class}.#{__callee__}() ---" raise ProductNotFoundException, "Getting scenarios help before setting the Product ID" if @product.nil? - (@product['scenarios'].map { |s| s['description'] }).join('

    ') + (@product["scenarios"].map { |s| s["description"] }).join("

    ") end # Can the cluster be set up? @@ -205,15 +208,21 @@ def dump(slave = false, force = false) def start_setup log.debug "--- called #{self.class}.#{__callee__} ---" @timestamp = Time.now - NodeLogger.info( - "Starting setup process on node #{SapHA::NodeLogger.node_name}") + NodeLogger.info( "Starting setup process on node #{SapHA::NodeLogger.node_name}") true end def end_setup log.debug "--- called #{self.class}.#{__callee__} ---" - NodeLogger.info( - "Finished setup process on node #{SapHA::NodeLogger.node_name}") + NodeLogger.info( "Finished setup process on node #{SapHA::NodeLogger.node_name}") + # Start firewall if this was running by starting the module on slave nodes + if @fw_state == 0 + if @role == :master + SapHA::Helpers.write_var_file("need_to_start_firewalld","") + else + SapHA::System::Local.systemd_unit(:stop, :service, "firewalld") + end + end true end @@ -224,19 +233,18 @@ def collect_log def write_config log.debug "--- called #{self.class}.#{__callee__} ---" - SapHA::Helpers.write_var_file('configuration.yml', dump(false, true), - timestamp: @timestamp) + SapHA::Helpers.write_var_file("configuration.yml", dump(false, true), timestamp: @timestamp) end - private + private # Load scenarios from the YAML configuration file def load_scenarios log.debug "--- called #{self.class}.#{__callee__} ---" begin - Psych.unsafe_load_file(SapHA::Helpers.data_file_path('scenarios.yaml')) + Psych.unsafe_load_file(SapHA::Helpers.data_file_path("scenarios.yaml")) rescue NoMethodError - Psych.load_file(SapHA::Helpers.data_file_path('scenarios.yaml')) + Psych.load_file(SapHA::Helpers.data_file_path("scenarios.yaml")) end end end # class Configuration diff --git a/src/lib/sap_ha/configuration/base_config.rb b/src/lib/sap_ha/configuration/base_config.rb index 061e369..243bdd8 100644 --- a/src/lib/sap_ha/configuration/base_config.rb +++ b/src/lib/sap_ha/configuration/base_config.rb @@ -19,10 +19,10 @@ # Summary: SUSE High Availability Setup for SAP Products: Base configuration class # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/exceptions' -require 'sap_ha/semantic_checks' -require 'sap_ha/node_logger' +require "yast" +require "sap_ha/exceptions" +require "sap_ha/semantic_checks" +require "sap_ha/node_logger" module SapHA module Configuration @@ -51,14 +51,14 @@ def encode_with(coder) key = variable_name.to_s[1..-1] coder[key] = instance_variable_get(variable_name) end - coder['instance_variables'] = instance_variables - @yaml_exclude if @yaml_exclude + coder["instance_variables"] = instance_variables - @yaml_exclude if @yaml_exclude end def init_with(coder) log.debug "--- #{self.class}.#{__callee__}(#{coder}) ---" raise @exception_type, - "The object has no field named `instance_variables`" if coder['instance_variables'].nil? - coder['instance_variables'].each do |variable_name| + "The object has no field named `instance_variables`" if coder["instance_variables"].nil? + coder["instance_variables"].each do |variable_name| key = variable_name.to_s[1..-1] instance_variable_set(variable_name, coder[key]) end @@ -91,7 +91,7 @@ def import(hash) log.debug "--- #{self.class}.#{__callee__}: #{hash} ---" return if hash.nil? || hash.empty? hash.each do |k, v| - name = k.to_s.start_with?('@') ? k : "@#{k}".to_sym + name = k.to_s.start_with?("@") ? k : "@#{k}".to_sym instance_variable_set(name, v) end end @@ -122,7 +122,7 @@ def html_errors <% end %> " - ERB.new(tmpl, nil, '-').result(binding) + ERB.new(tmpl, nil, "-").result(binding) end def prepare_description diff --git a/src/lib/sap_ha/configuration/cluster.rb b/src/lib/sap_ha/configuration/cluster.rb index f7a795c..742e994 100644 --- a/src/lib/sap_ha/configuration/cluster.rb +++ b/src/lib/sap_ha/configuration/cluster.rb @@ -17,17 +17,18 @@ # ------------------------------------------------------------------------------ # # Summary: SUSE High Availability Setup for SAP Products: Cluster members configuration +# Authors: Peter Varkoly # Authors: Ilya Manyugin -require 'yast' -require 'erb' -require 'socket' -require_relative 'base_config' -require 'sap_ha/system/local' -require 'sap_ha/system/network' -require 'sap_ha/exceptions' +require "yast" +require "erb" +require "socket" +require_relative "base_config" +require "sap_ha/system/local" +require "sap_ha/system/network" +require "sap_ha/exceptions" -Yast.import 'UI' +Yast.import "UI" module SapHA module Configuration @@ -35,27 +36,27 @@ module Configuration class Cluster < BaseConfig attr_reader :nodes, :rings, :number_of_rings, :transport_mode, :fixed_number_of_nodes, :keys attr_accessor :cluster_name, :expected_votes, :enable_secauth, :enable_csync2, :append_hosts, - :host_passwords + :host_passwords, :fw_config include Yast::UIShortcuts include SapHA::Exceptions include Yast::Logger CSYNC2_INCLUDED_FILES = [ - '/etc/corosync/corosync.conf', - '/etc/corosync/authkey', - '/etc/sysconfig/pacemaker', - '/etc/drbd.d', - '/etc/drbd.conf', - '/etc/lvm/lvm.conf', - '/etc/multipath.conf', - '/etc/ha.d/ldirectord.cf', - '/etc/ctdb/nodes', - '/etc/samba/smb.conf', - '/etc/booth', - '/etc/sysconfig/sbd', - '/etc/csync2/csync2.cfg', - '/etc/csync2/key_hagroup' + "/etc/corosync/corosync.conf", + "/etc/corosync/authkey", + "/etc/sysconfig/pacemaker", + "/etc/drbd.d", + "/etc/drbd.conf", + "/etc/lvm/lvm.conf", + "/etc/multipath.conf", + "/etc/ha.d/ldirectord.cf", + "/etc/ctdb/nodes", + "/etc/samba/smb.conf", + "/etc/booth", + "/etc/sysconfig/sbd", + "/etc/csync2/csync2.cfg", + "/etc/csync2/key_hagroup" ].freeze def initialize(global_config) @@ -71,13 +72,14 @@ def initialize(global_config) @number_of_rings = 1 @expected_votes = 2 @exception_type = ClusterConfigurationException - @cluster_name = 'hacluster' - @enable_secauth = false + @cluster_name = "hacluster" + @enable_secauth = true @enable_csync2 = false @keys = {} - @append_hosts = false + @append_hosts = true # host name to root passwd mapping @host_passwords = {} + @fw_config = "off" init_rings init_nodes @yaml_exclude << :@host_passwords @@ -89,7 +91,7 @@ def init_with(coder) @yaml_exclude = [:@nlog] unless @yaml_exclude.nil? # always exclude passwords from the configuration @yaml_exclude << :@host_passwords - @host_passwords = {} unless coder['instance_variables'].include?(:@host_passwords) + @host_passwords = {} unless coder["instance_variables"].include?(:@host_passwords) end def set_fixed_nodes(fixed, number) @@ -114,14 +116,14 @@ def number_of_rings=(value) @rings = {} (1..@number_of_rings).each do |ix| key = "ring#{ix}".to_sym - if rings_old.key?(key) - @rings[key] = rings_old[key] + @rings[key] = if rings_old.key?(key) + rings_old[key] else - @rings[key] = { - address: '', - port: '', + { + address: "", + port: "", id: ix, - mcast: '' + mcast: "" } end end @@ -175,17 +177,17 @@ def update_node(k, values) end def render_csync2_config(group_name, includes, key_path, hosts) - SapHA::Helpers.render_template('tmpl_csync2_config.erb', binding) + SapHA::Helpers.render_template("tmpl_csync2_config.erb", binding) end def description(component = :both) def comm_description(dsc) - dsc.parameter('Transport mode', @transport_mode) - dsc.parameter('Cluster name', @cluster_name) - dsc.parameter('Expected votes', @expected_votes) - dsc.parameter('Corosync secure authentication', @enable_secauth) - dsc.parameter('Enable csync2', @enable_csync2) - dsc.list_begin('Rings') + dsc.parameter("Transport mode", @transport_mode) + dsc.parameter("Cluster name", @cluster_name) + dsc.parameter("Expected votes", @expected_votes) + dsc.parameter("Corosync secure authentication", @enable_secauth) + dsc.parameter("Enable csync2", @enable_csync2) + dsc.list_begin("Rings") @rings.each do |_, ring| str = "ring " << dsc.iparam(ring[:address]) << " : " << dsc.iparam(ring[:port]) if multicast? @@ -197,14 +199,14 @@ def comm_description(dsc) end def nodes_description(dsc) - dsc.list_begin('') + dsc.list_begin("") @nodes.each do |_, node| str = "#{node[:host_name]}: " \ << dsc.iparam([node[:ip_ring1], node[:ip_ring2]][0...@number_of_rings].join(", ")) dsc.list_item(str) end dsc.list_end - dsc.parameter('Append /etc/hosts', @append_hosts) + dsc.parameter("Append /etc/hosts", @append_hosts) end prepare_description do |dsc| @@ -276,6 +278,17 @@ def other_nodes_ext end.compact end + def get_primary_on_primary + SapHA::System::Network.ip_addresses.each do |my_ip| + @nodes.each do |_, node| + if node[:ip_ring1] == my_ip + return node[:host_name] + end + end + end + return nil + end + def validate(verbosity = :verbose) validate_comm_layer(verbosity).concat(validate_nodes(verbosity)) end @@ -283,21 +296,21 @@ def validate(verbosity = :verbose) def validate_comm_layer(verbosity = :verbose) SemanticChecks.instance.check(verbosity) do |check| check.equal(@rings.length, @number_of_rings, - 'Number of table entries is not equal to the number of allowed rings.') + "Number of table entries is not equal to the number of allowed rings.") check.element_in_set(@transport_mode, [:unicast, :multicast], - 'The value should be either Unicast or Multicast', 'Transport Mode') + "The value should be either Unicast or Multicast", "Transport Mode") check.element_in_set(@number_of_rings, [1, 2], - 'The value should be either 1 or 2', 'Number of Rings') - check.identifier(@cluster_name, nil, 'Cluster Name') + "The value should be either 1 or 2", "Number of Rings") + check.identifier(@cluster_name, nil, "Cluster Name") check.element_in_set(@enable_secauth, [true, false], nil, - 'Enable corosync secure authentication') + "Enable corosync secure authentication") check.element_in_set(@enable_csync2, [true, false], nil, - 'Enable csync2') + "Enable csync2") @rings.each do |_, ring| ring_validator(check, ring) end check.unique(@rings.map { |_, r| r[:address] }, - 'IP addresses of the rings are not unique') + "IP addresses of the rings are not unique") end end @@ -305,49 +318,49 @@ def validate_nodes(verbosity = :verbose) SemanticChecks.instance.check(verbosity) do |check| @nodes.map { |_, node| node_validator(check, node) } check.integer_in_range(@expected_votes, 1, @number_of_nodes, nil, - 'Expected votes') + "Expected votes") check.unique(@nodes.map { |_, v| v[:ip_ring1] }, - 'IP addresses in ring #1 are not unique') + "IP addresses in ring #1 are not unique") check.ipsv4_in_network_cidr(@nodes.map { |_, v| v[:ip_ring1] }, - @rings[:ring1][:address], nil, 'IP addresses in ring #1') + @rings[:ring1][:address], nil, "IP addresses in ring #1") if @number_of_rings == 2 check.unique(@nodes.map { |_, v| v[:ip_ring2] }, - 'IP addresses in ring #2 are not unique') + "IP addresses in ring #2 are not unique") check.ipsv4_in_network_cidr(@nodes.map { |_, v| v[:ip_ring2] }, - @rings[:ring2][:address], nil, 'IP addresses in ring #2') + @rings[:ring2][:address], nil, "IP addresses in ring #2") end local_ips = SapHA::System::Network.ip_addresses # Local IPs can be empty if we're not running as root unless local_ips.empty? check.intersection_not_empty(@nodes.map { |_, v| v[:ip_ring1] }, local_ips, 'Could not find local node\'s IP address among the configured nodes', - 'IP addresses for ring 1') + "IP addresses for ring 1") check.intersection_not_empty(@nodes.map { |_, v| v[:ip_ring2] }, local_ips, 'Could not find local node\'s IP address among the configured nodes', - 'IP addresses for ring 2') if @number_of_rings == 2 + "IP addresses for ring 2") if @number_of_rings == 2 end end end def node_validator(check, node) - check.ipv4(node[:ip_ring1], 'IP Ring 1') - check.ipv4_in_network_cidr(node[:ip_ring1], @rings[:ring1][:address], 'IP Ring 1') + check.ipv4(node[:ip_ring1], "IP Ring 1") + check.ipv4_in_network_cidr(node[:ip_ring1], @rings[:ring1][:address], "IP Ring 1") if @number_of_rings == 2 - check.ipv4(node[:ip_ring2], 'IP Ring 2') - check.ipv4_in_network_cidr(node[:ip_ring2], @rings[:ring2][:address], 'IP Ring 2') + check.ipv4(node[:ip_ring2], "IP Ring 2") + check.ipv4_in_network_cidr(node[:ip_ring2], @rings[:ring2][:address], "IP Ring 2") end - check.hostname(node[:host_name], 'Hostname') + check.hostname(node[:host_name], "Hostname") # check.nonneg_integer(node[:node_id], 'Node ID') end def ring_validator(check, ring) - check.ipv4(ring[:address], 'Ring IP Address') - check.port(ring[:port], 'Ring Port Number') - check.ipv4_multicast(ring[:mcast], 'Multicast Address') if multicast? + check.ipv4(ring[:address], "Ring IP Address") + check.port(ring[:port], "Ring Port Number") + check.ipv4_multicast(ring[:mcast], "Multicast Address") if multicast? end def apply(role) - @nlog.info('Applying Cluster Configuration') + @nlog.info("Applying Cluster Configuration") flag = true SapHA::System::Local.append_hosts_file(@nodes) if @append_hosts if role == :master @@ -358,30 +371,25 @@ def apply(role) SapHA::System::Local.write_csync2_key(@keys[:csync2]) if @enable_csync2 end status = cluster_apply - @nlog.log_status(status, 'Exported configuration for yast2-cluster', - 'Could not export configuration for yast2-cluster') + @nlog.log_status(status, "Exported configuration for yast2-cluster", + "Could not export configuration for yast2-cluster") flag &= status + #Handle firewall + SapHA::System::Local.config_firewall(@fw_config, role) flag &= SapHA::System::Local.start_cluster_services flag &= SapHA::System::Local.cluster_maintenance(:on) if role == :master flag &= SapHA::System::Local.add_stonith_resource if role == :master - # Do not touch firewalld settings on SLE-15, yast2-cluster will do it - @nlog.info('Please enable the cluster firewalld service manually') - # TODO clarify if this is neccessary - # status = SapHA::System::Local.open_ports(role, @rings, @number_of_rings) - # flag &= status - # @nlog.log_status(status, 'Opened necessary communication ports', - # 'Could not open necessary communication ports') flag end def html_errors(component = :both) - case component + errors = case component when :comm_layer - errors = validate_comm_layer(:verbose) + validate_comm_layer(:verbose) when :nodes - errors = validate_nodes(:verbose) + validate_nodes(:verbose) else - errors = validate(:verbose) + validate(:verbose) end tmpl = "
      @@ -390,33 +398,33 @@ def html_errors(component = :both) <% end %>
    " - ERB.new(tmpl, nil, '-').result(binding) + ERB.new(tmpl, nil, "-").result(binding) end - private + private def init_nodes (1..@number_of_nodes).each do |i| @nodes["node#{i}".to_sym] = { host_name: "node#{i}", - ip_ring1: '', - ip_ring2: '', + ip_ring1: "", + ip_ring2: "", node_id: i.to_s } end end def ip_split_mask(addr) - addr.split('/').first + addr.split("/").first end def init_rings (1..@number_of_rings).each do |ix| @rings["ring#{ix}".to_sym] = { - address: '', - port: '5405', + address: "", + port: "5405", id: ix, - mcast: '' + mcast: "" } end end @@ -434,7 +442,7 @@ def generate_csync2_key def cluster_apply cluster_export = generate_cluster_export SapHA::System::Local.yast_cluster_export(cluster_export) - SapHA::System::Local.change_password('hacluster', 'linux') + SapHA::System::Local.change_password("hacluster", "linux") end def generate_cluster_export @@ -443,14 +451,14 @@ def generate_cluster_export host_names = @nodes.map { |_, e| e[:host_name] } cluster_configuration = { "secauth" => @enable_secauth, - "transport" => (multicast? ? 'udp' : "udpu"), + "transport" => (multicast? ? "udp" : "udpu"), "bindnetaddr1" => @rings[:ring1][:address_no_mask], "memberaddr" => memberaddr, "mcastaddr1" => @rings[:ring1][:mcast], "mcastport1" => @rings[:ring1][:port], "cluster_name" => @cluster_name, "expected_votes" => @expected_votes.to_s, - "two_node" => (@nodes.length == 2 ? '1' : '0'), + "two_node" => (@nodes.length == 2 ? "1" : "0"), "enable2" => @number_of_rings == 2, "autoid" => true, "rrpmode" => "none", @@ -464,11 +472,12 @@ def generate_cluster_export end cluster_configuration["csync2key"] = @keys[:csync2] if @keys[:csync2] cluster_configuration["corokey"] = @keys[:corosync] if @keys[:corosync] - if @number_of_rings == 2 + log.info "--- generate_cluster_export.#{cluster_configuration} ---" + cluster_configuration["rrpmode"] = if @number_of_rings == 2 # TODO: rrp mode - cluster_configuration['rrpmode'] = 'passive' + "passive" else - cluster_configuration['rrpmode'] = 'none' + "none" end cluster_configuration end diff --git a/src/lib/sap_ha/configuration/cluster_finalizer.rb b/src/lib/sap_ha/configuration/cluster_finalizer.rb index 8404e4c..d75b348 100644 --- a/src/lib/sap_ha/configuration/cluster_finalizer.rb +++ b/src/lib/sap_ha/configuration/cluster_finalizer.rb @@ -19,14 +19,14 @@ # Summary: SUSE High Availability Setup for SAP Products: Cluster members configuration # Authors: Ilya Manyugin -require 'yast' -require 'erb' -require 'socket' -require_relative 'base_config' -require 'sap_ha/system/local' -require 'sap_ha/exceptions' +require "yast" +require "erb" +require "socket" +require_relative "base_config" +require "sap_ha/system/local" +require "sap_ha/exceptions" -Yast.import 'UI' +Yast.import "UI" module SapHA module Configuration diff --git a/src/lib/sap_ha/configuration/fencing.rb b/src/lib/sap_ha/configuration/fencing.rb index f1a7127..9b6cd2c 100644 --- a/src/lib/sap_ha/configuration/fencing.rb +++ b/src/lib/sap_ha/configuration/fencing.rb @@ -19,9 +19,9 @@ # Summary: SUSE High Availability Setup for SAP Products: Fencing configuration # Authors: Ilya Manyugin -require 'yast' -require_relative 'base_config' -Yast.import 'UI' +require "yast" +require_relative "base_config" +Yast.import "UI" module SapHA module Configuration @@ -64,12 +64,12 @@ def validate(verbosity = :verbose) end def description - ds = @devices.join(', ') - options = @sbd_options.empty? ? 'none' : @sbd_options + ds = @devices.join(", ") + options = @sbd_options.empty? ? "none" : @sbd_options prepare_description do |dsc| - dsc.parameter('Configured devices', ds) - dsc.parameter('Delayed start', @sbd_delayed_start) - dsc.parameter('SBD options', options) + dsc.parameter("Configured devices", ds) + dsc.parameter("Delayed start", @sbd_delayed_start) + dsc.parameter("SBD options", options) end end @@ -87,7 +87,7 @@ def table_items end def popup_validator(check, dev_path) - check.block_device(dev_path, 'Device path') + check.block_device(dev_path, "Device path") end def add_device(dev_path) @@ -109,42 +109,42 @@ def remove_device_by_id(dev_id) def read_sysconfig @sysconfig = { - device: Yast::SCR.Read(Yast::Path.new('.sysconfig.sbd.SBD_DEVICE')), - pacemaker: Yast::SCR.Read(Yast::Path.new('.sysconfig.sbd.SBD_PACEMAKER')), - startmode: Yast::SCR.Read(Yast::Path.new('.sysconfig.sbd.SBD_STARTMODE')), - delay_start: Yast::SCR.Read(Yast::Path.new('.sysconfig.sbd.SBD_DELAY_START')), - watchdog: Yast::SCR.Read(Yast::Path.new('.sysconfig.sbd.SBD_WATCHDOG')), - options: Yast::SCR.Read(Yast::Path.new('.sysconfig.sbd.SBD_OPTS')) + device: Yast::SCR.Read(Yast::Path.new(".sysconfig.sbd.SBD_DEVICE")), + pacemaker: Yast::SCR.Read(Yast::Path.new(".sysconfig.sbd.SBD_PACEMAKER")), + startmode: Yast::SCR.Read(Yast::Path.new(".sysconfig.sbd.SBD_STARTMODE")), + delay_start: Yast::SCR.Read(Yast::Path.new(".sysconfig.sbd.SBD_DELAY_START")), + watchdog: Yast::SCR.Read(Yast::Path.new(".sysconfig.sbd.SBD_WATCHDOG")), + options: Yast::SCR.Read(Yast::Path.new(".sysconfig.sbd.SBD_OPTS")) } true end def write_sysconfig - devices = @devices.join(';') - Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd.SBD_DEVICE'), devices) - Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd.SBD_PACEMAKER'), "yes") - Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd.SBD_STARTMODE'), "always") - Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd.SBD_DELAY_START'), @sbd_delayed_start) - Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd.SBD_WATCHDOG'), "yes") - Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd.SBD_OPTS'), @sbd_options) - commit = Yast::SCR.Write(Yast::Path.new('.sysconfig.sbd'), nil) + devices = @devices.join(";") + Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd.SBD_DEVICE"), devices) + Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd.SBD_PACEMAKER"), "yes") + Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd.SBD_STARTMODE"), "always") + Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd.SBD_DELAY_START"), @sbd_delayed_start) + Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd.SBD_WATCHDOG"), "yes") + Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd.SBD_OPTS"), @sbd_options) + commit = Yast::SCR.Write(Yast::Path.new(".sysconfig.sbd"), nil) if commit - @nlog.info('Wrote SBD system configuration') + @nlog.info("Wrote SBD system configuration") else - @nlog.warn('Could not write the SBD system configuration') + @nlog.warn("Could not write the SBD system configuration") end commit end def apply(role) - @nlog.info('Appying Fencing Configuration') + @nlog.info("Appying Fencing Configuration") return false unless configured? flag = write_sysconfig flag &= SapHA::System::Local.initialize_sbd(@devices) if role == :master flag end - private + private def handle_sysconfig handle = ->(sett, default) { (sett.nil? || sett.empty?) ? default : sett } diff --git a/src/lib/sap_ha/configuration/hana.rb b/src/lib/sap_ha/configuration/hana.rb index 22305eb..cb6266a 100644 --- a/src/lib/sap_ha/configuration/hana.rb +++ b/src/lib/sap_ha/configuration/hana.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # ------------------------------------------------------------------------------ -# Copyright (c) 2016 SUSE Linux GmbH, Nuernberg, Germany. +# Copyright (c) 2023 SUSE Linux GmbH, Nuernberg, Germany. # # This program is free software; you can redistribute it and/or modify it under # the terms of version 2 of the GNU General Public License as published by the @@ -17,14 +17,15 @@ # ------------------------------------------------------------------------------ # # Summary: SUSE High Availability Setup for SAP Products: HANA configuration +# Authors: Peter Varkoly # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/system/shell_commands' -require 'sap_ha/system/local' -require 'sap_ha/system/hana' -require 'sap_ha/system/network' -require_relative 'base_config' +require "yast" +require "sap_ha/system/shell_commands" +require "sap_ha/system/local" +require "sap_ha/system/hana" +require "sap_ha/system/network" +require_relative "base_config" module SapHA module Configuration @@ -46,12 +47,21 @@ class HANA < BaseConfig :replication_mode, :operation_mode, :additional_instance, - :hook_script, - :hook_script_parameters, :production_constraints - HANA_REPLICATION_MODES = ['sync', 'syncmem', 'async'].freeze - HANA_OPERATION_MODES = ['delta_datashipping', 'logreplay'].freeze + HANA_REPLICATION_MODES = ["sync", "syncmem", "async"].freeze + HANA_OPERATION_MODES = ["delta_datashipping", "logreplay"].freeze + HANA_FW_SERVICES = [ + "hana-cockpit", + "hana-database-client", + "hana-data-provisioning", + "hana-http-web-access", + "hana-internal-distributed-communication", + "hana-internal-system-replication", + "hana-lifecycle-manager", + "sap-software-provisioning-manager", + "sap-special-support" + ].freeze include Yast::UIShortcuts include SapHA::System::ShellCommands @@ -61,51 +71,37 @@ def initialize(global_config) super log.debug "--- #{self.class}.#{__callee__} ---" @screen_name = "HANA Configuration" - @system_id = 'NDB' - @instance = '00' - @virtual_ip = '' - @virtual_ip_mask = '24' + @system_id = "" + @instance = "" + @virtual_ip = "" + @virtual_ip_mask = "24" @replication_mode = HANA_REPLICATION_MODES.first @operation_mode = HANA_OPERATION_MODES.first @prefer_takeover = true @auto_register = false - @site_name_1 = 'WALLDORF' - @site_name_2 = 'ROT' - @backup_user = 'system' - @backup_file = 'backup' + @site_name_1 = "" + @site_name_2 = "" + @backup_user = "system" + @backup_file = "backup" @perform_backup = true # Extended configuration for Cost-Optimized scenario @additional_instance = false - @np_system_id = 'QAS' - @np_instance = '10' - @hook_script_parameters = {} + @np_system_id = "QAS" + @np_instance = "10" @production_constraints = {} - @hook_script = "" end def additional_instance=(value) @additional_instance = value return unless value - @hook_script_parameters = { - generated: false, - hook_execution_order: '1', - hook_db_user_name: 'SYSTEM', - hook_db_password: '', - hook_port_number: '3' + @np_instance + '15', - hook_db_instance: @np_instance - } @production_constraints = { - global_alloc_limit: 65_536.to_s, - preload_column_tables: 'false' + global_alloc_limit: "0", + preload_column_tables: "false" } end def np_instance=(value) @np_instance = value - unless @hook_script_parameters[:generated] - @hook_script_parameters[:hook_db_instance] = @np_instance - @hook_script_parameters[:hook_port_number] = '3' + @np_instance + '15' - end end def configured? @@ -114,145 +110,113 @@ def configured? def validate(verbosity = :verbose) SemanticChecks.instance.check(verbosity) do |check| - check.ipv4(@virtual_ip, 'Virtual IP') - check.nonneg_integer(@virtual_ip_mask, 'Virtual IP mask') - check.integer_in_range(@virtual_ip_mask, 1, 32, 'CIDR mask has to be between 1 and 32.', - 'Virtual IP mask') - check.sap_instance_number(@instance, nil, 'Instance Number') - check.sap_sid(@system_id, nil, 'System ID') + check.ipv4(@virtual_ip, "Virtual IP") + check.nonneg_integer(@virtual_ip_mask, "Virtual IP mask") + check.integer_in_range(@virtual_ip_mask, 1, 32, "CIDR mask has to be between 1 and 32.", + "Virtual IP mask") + check.sap_instance_number(@instance, nil, "Instance Number") + check.sap_sid(@system_id, nil, "System ID") check.element_in_set(@replication_mode, HANA_REPLICATION_MODES, - "Value should be one of the following: #{HANA_REPLICATION_MODES.join(',')}.", - 'Replication mode' - ) - if @operation_mode == 'logreplay' - # Logreplay is only available for SPS11+ - version = SapHA::System::Hana.version(@system_id) - # TODO: remove debug - flag = SapHA::Helpers.version_comparison('1.00.110', version, '>=') - check.report_error(flag, - "Operation mode 'logreplay' is only available for HANA SPS11+"\ - " (detected version #{version || 'Unknown' }).", 'Operation mode', @operation_mode) - end - check.identifier(@site_name_1, nil, 'Site name 1') - check.identifier(@site_name_2, nil, 'Site name 2') + "Value should be one of the following: #{HANA_REPLICATION_MODES.join(",")}.", + "Replication mode") + check.identifier(@site_name_1, nil, "Site name 1") + check.identifier(@site_name_2, nil, "Site name 2") check.element_in_set(@prefer_takeover, [true, false], - nil, 'Prefer site takeover') + nil, "Prefer site takeover") check.element_in_set(@auto_register, [true, false], - nil, 'Automatic registration') + nil, "Automatic registration") check.element_in_set(@perform_backup, [true, false], - nil, 'Perform backup') + nil, "Perform backup") # Backup settings should be only validated on the master node if @perform_backup && @global_config.role == :master - check.identifier(@backup_file, nil, 'Backup settings/Backup file name') - check.identifier(@backup_user, nil, 'Backup settings/Secure store key') + check.identifier(@backup_file, nil, "Backup settings/Backup file name") + check.identifier(@backup_user, nil, "Backup settings/Secure store key") keys = SapHA::System::Hana.check_secure_store(@system_id).map(&:downcase) check.element_in_set(@backup_user.downcase, keys, - "There is no such HANA user store key detected.", 'Secure store key') + "There is no such HANA user store key detected.", "Secure store key") end if @additional_instance - check.sap_instance_number(@np_instance, nil, 'Non-Production Instance Number') - check.sap_sid(@np_system_id, nil, 'Non-Production System ID') - check.not_equal(@instance, @np_instance, 'SAP HANA instance numbers should not collide', - 'Instance number') - check.not_equal(@system_id, @np_system_id, 'SAP HANA System IDs should not collide', - 'System ID') - hook_script_validation(check, @hook_script_parameters) + check.sap_instance_number(@np_instance, nil, "Non-Production Instance Number") + check.sap_sid(@np_system_id, nil, "Non-Production System ID") + check.not_equal(@instance, @np_instance, "SAP HANA instance numbers should not collide", + "Instance number") + check.not_equal(@system_id, @np_system_id, "SAP HANA System IDs should not collide", + "System ID") production_constraints_validation(check, @production_constraints) - check.non_empty_string(@hook_script, "The failover hook script was not generated.", - '', true) end end end def description prepare_description do |dsc| - dsc.header('Production instance') if @additional_instance - dsc.parameter('System ID', @system_id) - dsc.parameter('Instance', @instance) - dsc.parameter('Replication mode', @replication_mode) - dsc.parameter('Operation mode', @operation_mode) - dsc.parameter('Virtual IP', @virtual_ip + '/' + @virtual_ip_mask) - dsc.parameter('Prefer takeover', @prefer_takeover) - dsc.parameter('Automatic registration', @auto_register) - dsc.parameter('Site 1 name', @site_name_1) - dsc.parameter('Site 2 name', @site_name_2) - dsc.parameter('Perform backup', @perform_backup) + dsc.header("Production instance") if @additional_instance + dsc.parameter("System ID", @system_id) + dsc.parameter("Instance", @instance) + dsc.parameter("Replication mode", @replication_mode) + dsc.parameter("Operation mode", @operation_mode) + dsc.parameter("Virtual IP", @virtual_ip + "/" + @virtual_ip_mask) + dsc.parameter("Prefer takeover", @prefer_takeover) + dsc.parameter("Automatic registration", @auto_register) + dsc.parameter("Site 1 name", @site_name_1) + dsc.parameter("Site 2 name", @site_name_2) + dsc.parameter("Perform backup", @perform_backup) if @perform_backup - dsc.parameter('Secure store key', @backup_user) - dsc.parameter('Backup file', @backup_file) + dsc.parameter("Secure store key", @backup_user) + dsc.parameter("Backup file", @backup_file) end if @additional_instance - dsc.header('Non-production instance') - dsc.parameter('System ID', @np_system_id) - dsc.parameter('Instance', @np_instance) - dsc.header('Production system constraints') - dsc.parameter('Global allocation limit (MB)', + dsc.header("Non-production instance") + dsc.parameter("System ID", @np_system_id) + dsc.parameter("Instance", @np_instance) + dsc.header("Production system constraints") + dsc.parameter("Global allocation limit (MB)", @production_constraints[:global_alloc_limit]) - dsc.parameter('Column tables preload', + dsc.parameter("Column tables preload", @production_constraints[:preload_column_tables]) end end end - def hook_generated? - @hook_script_parameters[:generated] - end - - def hook_script_parameters=(value) - @hook_script_parameters.merge!(value) - @hook_script = SapHA::Helpers.render_template('tmpl_srhook.py.erb', binding) - @hook_script_parameters[:generated] = true - end - # Validator for the backup settings popup # @param check [SapHA::SemanticCheck] # @param hash [Hash] input fields' contents def hana_backup_validator(check, hash) - check.identifier(hash[:backup_file], nil, 'Backup file name') - check.identifier(hash[:backup_user], nil, 'Secure store key') + check.identifier(hash[:backup_file], nil, "Backup file name") + check.identifier(hash[:backup_user], nil, "Secure store key") keys = SapHA::System::Hana.check_secure_store(@system_id).map(&:downcase) check.element_in_set(hash[:backup_user].downcase, keys, - "There is no such HANA user store key detected.", 'Secure store key') - end - - # Validator for the hook script settings popup - # @param check [SapHA::SemanticCheck] - # @param hash [Hash] input fields' contents - def hook_script_validation(check, hash) - check.nonneg_integer(hash[:hook_execution_order], 'Hook execution order') - check.identifier(hash[:hook_db_user_name], nil, 'DB user name') - check.port(hash[:hook_port_number], 'Port number') + "There is no such HANA user store key detected.", "Secure store key") end # Validator for the production instance constraints popup # @param check [SapHA::SemanticCheck] # @param hash [Hash] input fields' contents def production_constraints_validation(check, hash) - check.element_in_set(hash[:preload_column_tables], ['true', 'false'], - 'The field must contain a boolean value: "true" or "false"', 'Preload column tables') - check.nonneg_integer(hash[:global_alloc_limit], 'Global allocation limit') + check.element_in_set(hash[:preload_column_tables], ["true", "false"], + "The field must contain a boolean value: 'true' or 'false'", "Preload column tables") + check.nonneg_integer(hash[:global_alloc_limit], "Global allocation limit") end # Validator for the non-production instance constraints popup # @param check [SapHA::SemanticCheck] # @param hash [Hash] input fields' contents def non_production_constraints_validation(check, hash) - check.element_in_set(hash[:preload_column_tables], ['true', 'false'], - 'The field must contain a boolean value: "true" or "false"', 'Preload column tables') - check.nonneg_integer(hash[:global_alloc_limit], 'Global allocation limit') + check.element_in_set(hash[:preload_column_tables], ["true", "false"], + "The field must contain a boolean value: 'true' or 'false'", "Preload column tables") + check.nonneg_integer(hash[:global_alloc_limit], "Global allocation limit") end def apply(role) return false unless configured? - @nlog.info('Appying HANA Configuration') + @nlog.info("Appying HANA Configuration") + config_firewall(role) if role == :master - SapHA::System::Hana.hdb_start(@system_id) if @perform_backup - secondary_host_name = @global_config.cluster.other_nodes_ext.first[:hostname] SapHA::System::Hana.make_backup(@system_id, @backup_user, @backup_file, @instance) - secondary_password = @global_config.cluster.host_passwords[secondary_host_name] - SapHA::System::Hana.copy_ssfs_keys(@system_id, secondary_host_name, secondary_password) end + secondary_host_name = @global_config.cluster.other_nodes_ext.first[:hostname] + secondary_password = @global_config.cluster.host_passwords[secondary_host_name] + SapHA::System::Hana.copy_ssfs_keys(@system_id, secondary_host_name, secondary_password) SapHA::System::Hana.enable_primary(@system_id, @site_name_1) configure_crm else # secondary node @@ -260,15 +224,11 @@ def apply(role) primary_host_name = @global_config.cluster.other_nodes_ext.first[:hostname] SapHA::System::Hana.enable_secondary(@system_id, @site_name_2, primary_host_name, @instance, @replication_mode, @operation_mode) - if @additional_instance # cost-optimized scenario - SapHA::System::Hana.hdb_stop(@np_system_id) - SapHA::System::Hana.adjust_production_system(@system_id, - @hook_script_parameters.merge(@production_constraints)) - # SapHA::System::Hana.adjust_non_production_system(@np_system_id) - end - SapHA::System::Hana.hdb_start(@system_id) cleanup_hana_resources + SapHA::System::Hana.hdb_start(@system_id) end + adapt_sudoers + adjust_global_ini(role) true end @@ -277,22 +237,83 @@ def cleanup_hana_resources # https://docs.microsoft.com/en-us/azure/virtual-machines/workloads/sap/sap-hana-high-availability if @global_config.platform == "azure" rsc = "rsc_SAPHana_#{@system_id}_HDB#{@instance}" - cleanup_status = exec_status('crm', 'resource', 'cleanup', rsc) + cleanup_status = exec_status("crm", "resource", "cleanup", rsc) @nlog.log_status(cleanup_status.exitstatus == 0, - "Performed resource cleanup for #{rsc}", - "Could not clean up #{rsc}") + "Performed resource cleanup for #{rsc}", + "Could not clean up #{rsc}") end - end + end def configure_crm # TODO: move this to SapHA::System::Local.configure_crm - primary_host_name = @global_config.cluster.other_nodes_ext.first[:hostname] - crm_conf = Helpers.render_template('tmpl_cluster_config.erb', binding) - file_path = Helpers.write_var_file('cluster.config', crm_conf) - out, status = exec_outerr_status('crm', 'configure', 'load', 'update', file_path) + primary_host_name = @global_config.cluster.get_primary_on_primary + secondary_host_name = @global_config.cluster.other_nodes_ext.first[:hostname] + crm_conf = Helpers.render_template("tmpl_cluster_config.erb", binding) + file_path = Helpers.write_var_file("cluster.config", crm_conf) + out, status = exec_outerr_status("crm", "configure", "load", "update", file_path) @nlog.log_status(status.exitstatus == 0, - 'Configured necessary cluster resources for HANA System Replication', - 'Could not configure HANA cluster resources', out) + "Configured necessary cluster resources for HANA System Replication", + "Could not configure HANA cluster resources", out) + end + + def config_firewall(role) + case @global_config.cluster.fw_config + when "done" + @nlog.info("Firewall is already configured") + when "off" + @nlog.info("Firewall will be turned off") + SapHA::System::Local.systemd_unit(:stop, :service, "firewalld") + when "setup" + @nlog.info("Firewall will be configured for HANA services.") + instances = Yast::SCR.Read(Yast::Path.new(".sysconfig.hana-firewall.HANA_INSTANCE_NUMBERS")).split + instances << @instance + Yast::SCR.Write(Yast::Path.new(".sysconfig.hana-firewall.HANA_INSTANCE_NUMBERS"), instances) + Yast::SCR.Write(Yast::Path.new(".sysconfig.hana-firewall"), nil) + _s = exec_status("/usr/sbin/hana-firewall", "generate-firewalld-services") + _s = exec_status("/usr/bin/firewall-cmd", "--reload") + if role != :master + _s = exec_status("/usr/bin/firewall-cmd", "--add-port", "8080/tcp") + end + HANA_FW_SERVICES.each do |service| + _s = exec_status("/usr/bin/firewall-cmd", "--add-service", service) + _s = exec_status("/usr/bin/firewall-cmd", "--permanent", "--add-service", service) + end + else + @nlog.info("Invalide firewall configuration status") + end + end + + # Creates the sudoers file + def adapt_sudoers + if File.exist?(SapHA::Helpers.data_file_path("SUDOERS_HANASR.erb")) + Helpers.write_file("/etc/sudoers.d/saphanasr.conf",Helpers.render_template("SUDOERS_HANASR.erb", binding)) + end + end + + # Activates all necessary plugins based on role an scenario + def adjust_global_ini(role) + # SAPHanaSR is needed on all nodes + add_plugin_to_global_ini("SAPHANA_SR") + if @additional_instance + # cost optimized + add_plugin_to_global_ini("SUS_COSTOPT") if role != :master + else + # performance optimized + add_plugin_to_global_ini("SUS_CHKSRV") + add_plugin_to_global_ini("SUS_TKOVER") + end + command = ["hdbnsutil", "-reloadHADRProviders"] + out, status = su_exec_outerr_status("#{@system_id.downcase}adm", *command) + end + + # Activates the plugin in global ini + def add_plugin_to_global_ini(plugin) + sr_path = Helpers.data_file_path("GLOBAL_INI_#{plugin}") + if File.exist?("#{sr_path}.erb") + sr_path = Helpers.write_var_file(plugin, Helpers.render_template("GLOBAL_INI_#{plugin}.erb", binding)) + end + command = ["/usr/sbin/SAPHanaSR-manageProvider", "--add", "--sid", @system_id, sr_path] + out, status = su_exec_outerr_status("#{@system_id.downcase}adm", *command) end end end diff --git a/src/lib/sap_ha/configuration/ntp.rb b/src/lib/sap_ha/configuration/ntp.rb index dd50645..8b25fed 100644 --- a/src/lib/sap_ha/configuration/ntp.rb +++ b/src/lib/sap_ha/configuration/ntp.rb @@ -19,13 +19,13 @@ # Summary: SUSE High Availability Setup for SAP Products: Cluster members configuration # Authors: Ilya Manyugin -require 'yast' -require 'erb' -require 'socket' -require_relative 'base_config' +require "yast" +require "erb" +require "socket" +require_relative "base_config" -Yast.import 'NtpClient' -Yast.import 'Progress' +Yast.import "NtpClient" +Yast.import "Progress" module SapHA module Configuration @@ -67,19 +67,19 @@ def validate(verbosity = :verbose) def description prepare_description do |dsc| - dsc.parameter('Synchronize with servers', @used_servers.join(', ')) - dsc.parameter('Start at boot', start_at_boot?) + dsc.parameter("Synchronize with servers", @used_servers.join(", ")) + dsc.parameter("Start at boot", start_at_boot?) end end def start_at_boot? - @config["ntp_sync"] == 'systemd' + @config["ntp_sync"] == "systemd" end def apply(role) return false unless configured? # Master has the configuration in place already - @nlog.info('Appying NTP Configuration') + @nlog.info("Appying NTP Configuration") return true if role == :master Yast::NtpClient.Import @config stat = Yast::NtpClient.Write diff --git a/src/lib/sap_ha/configuration/watchdog.rb b/src/lib/sap_ha/configuration/watchdog.rb index 73cb240..a790272 100644 --- a/src/lib/sap_ha/configuration/watchdog.rb +++ b/src/lib/sap_ha/configuration/watchdog.rb @@ -19,15 +19,14 @@ # Summary: SUSE High Availability Setup for SAP Products: Watchdog configuration # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/system/watchdog' -require_relative 'base_config' +require "yast" +require "sap_ha/system/watchdog" +require_relative "base_config" module SapHA module Configuration # Watchdog configuration class Watchdog < BaseConfig - attr_reader :to_install, :configured, :proposals, :loaded include Yast::UIShortcuts @@ -62,9 +61,9 @@ def validate(verbosity = :verbose) def description prepare_description do |dsc| - dsc.parameter('Configured modules', @configured.join(', ')) unless @configured.empty? - dsc.parameter('Already loaded modules', @loaded.join(', ')) unless @loaded.empty? - dsc.parameter('Modules to install', @to_install.join(', ')) unless @to_install.empty? + dsc.parameter("Configured modules", @configured.join(", ")) unless @configured.empty? + dsc.parameter("Already loaded modules", @loaded.join(", ")) unless @loaded.empty? + dsc.parameter("Modules to install", @to_install.join(", ")) unless @to_install.empty? end end @@ -81,7 +80,7 @@ def remove_from_config(wdt_module) def apply(role) return false unless configured? - @nlog.info('Appying Watchdog Configuration') + @nlog.info("Appying Watchdog Configuration for #{role}") stat = true @to_install.each do |module_name| stat &= System::Watchdog.install(module_name) diff --git a/src/lib/sap_ha/helpers.rb b/src/lib/sap_ha/helpers.rb index 44741c3..27b17ff 100644 --- a/src/lib/sap_ha/helpers.rb +++ b/src/lib/sap_ha/helpers.rb @@ -21,11 +21,11 @@ # Authors: Peter Varkoly require "yast/i18n" -require 'erb' -require 'tmpdir' -require 'sap_ha/exceptions' -require 'net/http' -require 'psych' +require "erb" +require "tmpdir" +require "sap_ha/exceptions" +require "net/http" +require "psych" module SapHA # Common routines @@ -38,14 +38,14 @@ class HelpersClass attr_reader :rpc_server_cmd - FILE_DATE_TIME_FORMAT = '%Y%m%d_%H%M%S'.freeze + FILE_DATE_TIME_FORMAT = "%Y%m%d_%H%M%S".freeze def initialize textdomain "hana-ha" @storage = {} - if ENV['Y2DIR'] # tests/local run - @data_path = 'data/' - @var_path = File.join(Dir.tmpdir, 'yast-sap-ha-tmp') + if ENV["Y2DIR"] # tests/local run + @data_path = "src/data/sap_ha/" + @var_path = File.join(Dir.tmpdir, "yast-sap-ha-tmp") begin Dir.mkdir(@var_path) rescue StandardError => e @@ -53,16 +53,16 @@ def initialize end # We get the Y2DIR relative RPC Server location as it is running on dev mode. y2dir_path = File.expand_path("../", __FILE__) - @rpc_server_cmd = 'systemd-cat /usr/bin/ruby '\ + @rpc_server_cmd = "systemd-cat /usr/bin/ruby "\ "#{y2dir_path}/rpc_server.rb" else # production - @data_path = '/usr/share/YaST2/data/sap_ha' - @var_path = '/var/lib/YaST2/sap_ha' + @data_path = "/usr/share/YaST2/data/sap_ha" + @var_path = "/var/lib/YaST2/sap_ha" # /sbin/yast in SLES, /usr/sbin/yast in OpenSuse # @rpc_server_cmd = 'yast sap_ha_rpc' # TODO: fix it - @rpc_server_cmd = 'systemd-cat /usr/bin/ruby '\ - '/usr/share/YaST2/lib/sap_ha/rpc_server.rb' + @rpc_server_cmd = "systemd-cat /usr/bin/ruby "\ + "/usr/share/YaST2/lib/sap_ha/rpc_server.rb" end end @@ -71,7 +71,7 @@ def render_template(basename, binding) log.debug "--- called #{self.class}.#{__callee__}(#{basename}) ---" full_path = data_file_path(basename) if !@storage.key? basename - template = ERB.new(read_file(full_path), nil, '-') + template = ERB.new(read_file(full_path), nil, "-") @storage[basename] = template end begin @@ -85,12 +85,12 @@ def render_template(basename, binding) end # Load the help file by its name - def load_help(basename, platform="") + def load_help(basename, platform = "") log.debug "--- called #{self.class}.#{__callee__}(#{basename}) ---" - if platform == "bare-metal" || platform.to_s.strip.empty? - file_name = "help_#{basename}.html" + file_name = if platform == "bare-metal" || platform.to_s.strip.empty? + "help_#{basename}.html" else - file_name = "help_#{basename}_#{platform}.html" + "help_#{basename}_#{platform}.html" end if !@storage.key? file_name full_path = File.join(@data_path, file_name) @@ -129,7 +129,7 @@ def write_var_file(basename, data, options = {}) # @param scenario_name [String] def get_configuration_files(product_id = nil, scenario_name = nil) log.debug "--- called #{self.class}.#{__callee__}(#{product_id}, #{scenario_name}) ---" - files = Dir.chdir(@var_path) { Dir.glob('configuration_*.yml') } + files = Dir.chdir(@var_path) { Dir.glob("configuration_*.yml") } begin configs = files.map { |fn| Psych.unsafe_load(read_file(var_file_path(fn))) } rescue NoMethodError @@ -146,7 +146,7 @@ def get_configuration_files(product_id = nil, scenario_name = nil) def write_file(path, data) log.debug "--- called #{self.class}.#{__callee__}(#{path}, #{data}) ---" begin - File.open(path, 'wb') do |fh| + File.open(path, "wb") do |fh| fh.write(data) end rescue RuntimeError => e @@ -158,8 +158,8 @@ def write_file(path, data) def open_url(url) log.debug "--- called #{self.class}.#{__callee__}(#{url}) ---" - require 'yast' - Yast.import 'UI' + require "yast" + Yast.import "UI" Yast::UI.BusyCursor system("xdg-open #{url}") sleep 5 @@ -171,12 +171,12 @@ def timestamp_file(basename, timestamp = nil) return basename if timestamp.nil? ext = File.extname(basename) name = File.basename(basename, ext) - basename = "#{name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}#{ext}" + basename = "#{name}_#{Time.now.strftime("%Y%m%d_%H%M%S")}#{ext}" end - def version_comparison(version_target, version_current, cmp = '>=') + def version_comparison(version_target, version_current, cmp = ">=") log.debug "--- called #{self.class}.#{__callee__}(#{version_target}, #{version_current}, #{cmp}) ---" - Gem::Dependency.new('', cmp + version_target).match?('', version_current) + Gem::Dependency.new("", cmp + version_target).match?("", version_current) rescue StandardError => e log.error "HANA version comparison failed: target=#{version_target},"\ " current=#{version_current}, cmp=#{cmp}." @@ -207,10 +207,10 @@ def is_azure? begin response = meta_service.request(request) case response - when Net::HTTPSuccess then - return true - else - return false + when Net::HTTPSuccess then + return true + else + return false end rescue Net::OpenTimeout => e log.error("Network timeout checking Azure metadata service: #{e.message}.") @@ -221,7 +221,7 @@ def is_azure? end end - private + private # Read file's contents def read_file(path) diff --git a/src/lib/sap_ha/node_logger.rb b/src/lib/sap_ha/node_logger.rb index 3c270ef..31bc642 100644 --- a/src/lib/sap_ha/node_logger.rb +++ b/src/lib/sap_ha/node_logger.rb @@ -18,11 +18,11 @@ # Summary: SUSE High Availability Setup for SAP Products: In-memory logger class # Authors: Ilya Manyugin -require 'yast' -require 'singleton' -require 'logger' -require 'stringio' -require 'socket' +require "yast" +require "singleton" +require "logger" +require "stringio" +require "socket" module SapHA # Log info messages, warnings and errors into memory @@ -100,12 +100,12 @@ def summary def to_html(txt) time_rex = '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}' rules = [ - { rex: /^\[(.*)\] (#{time_rex})\s+(OUTPUT): (.*)$/, color: '#808080' }, # gray - { rex: /^\[(.*)\] (#{time_rex})\s+(DEBUG): (.*)$/, color: '#808080' }, # gray - { rex: /^\[(.*)\] (#{time_rex})\s+(INFO): (.*)$/, color: '#009900' }, # green - { rex: /^\[(.*)\] (#{time_rex})\s+(WARN): (.*)$/, color: '#e6b800' }, # yellow - { rex: /^\[(.*)\] (#{time_rex})\s+(ERROR): (.*)$/, color: '#800000' }, # error - { rex: /^\[(.*)\] (#{time_rex})\s+(FATAL): (.*)$/, color: '#800000' }, # fatal error + { rex: /^\[(.*)\] (#{time_rex})\s+(OUTPUT): (.*)$/, color: "#808080" }, # gray + { rex: /^\[(.*)\] (#{time_rex})\s+(DEBUG): (.*)$/, color: "#808080" }, # gray + { rex: /^\[(.*)\] (#{time_rex})\s+(INFO): (.*)$/, color: "#009900" }, # green + { rex: /^\[(.*)\] (#{time_rex})\s+(WARN): (.*)$/, color: "#e6b800" }, # yellow + { rex: /^\[(.*)\] (#{time_rex})\s+(ERROR): (.*)$/, color: "#800000" }, # error + { rex: /^\[(.*)\] (#{time_rex})\s+(FATAL): (.*)$/, color: "#800000" }, # fatal error ] lines = txt.split("\n").map do |line| rule = rules.find { |r| r[:rex].match(line) } @@ -148,7 +148,7 @@ def log_status(status, msg_if_true, msg_if_false, stdout = nil, log_output_on_su status end - private + private # # Proxy calls to the logger class if they are not found in NodeLogger # # @param [Symbol] method diff --git a/src/lib/sap_ha/rpc_server.rb b/src/lib/sap_ha/rpc_server.rb index 191a78a..2e41cec 100644 --- a/src/lib/sap_ha/rpc_server.rb +++ b/src/lib/sap_ha/rpc_server.rb @@ -1,7 +1,7 @@ # encoding: utf-8 # ------------------------------------------------------------------------------ -# Copyright (c) 2016 SUSE Linux GmbH, Nuernberg, Germany. +# Copyright (c) 2023 SUSE Linux GmbH, Nuernberg, Germany. # # This program is free software; you can redistribute it and/or modify it under # the terms of version 2 of the GNU General Public License as published by the @@ -17,21 +17,19 @@ # ------------------------------------------------------------------------------ # # Summary: SUSE High Availability Setup for SAP Products: XML RPC Server +# Authors: Peter Varkoly # Authors: Ilya Manyugin -ENV['Y2DIR'] = File.expand_path('../../../../src', __FILE__) +#ENV["Y2DIR"] = File.expand_path("../../../../src", __FILE__) -require 'yast' +require "yast" require "xmlrpc/server" require "sap_ha/configuration" require "sap_ha/helpers" require "sap_ha/system/shell_commands" -require 'psych' -require 'logger' -require 'socket' - -Yast.import 'SuSEFirewall' -Yast.import 'Service' +require "psych" +require "logger" +require "socket" module SapHA # RPC Server for inter-node communication @@ -51,19 +49,17 @@ module SapHA class RPCServer include System::ShellCommands - LOG_FILE_PATH = '/tmp/rpc_serv' - def initialize(options = {}) + @log_file_path = SapHA::Helpers.var_file_path("rpc_serv") init_logger @logger.info "--- #{self.class}.#{__callee__} ---" - if options[:local] - @server = XMLRPC::Server.new(8080, '127.0.0.1', 50, @fh) + @server = if options[:local] + XMLRPC::Server.new(8080, "127.0.0.1", 50, @fh) else - @server = XMLRPC::Server.new(8080, '0.0.0.0', 50, @fh) + XMLRPC::Server.new(8080, "0.0.0.0", 50, @fh) end @port_opened = false - # Do not alter the firewall settings on SLE-15 - #open_port unless options[:local] or Yast::OSRelease.ReleaseVersion.start_with?('15') + open_port install_handlers(options[:test]) # Mutex for 'busy' @mutex = Mutex.new @@ -75,14 +71,14 @@ def initialize(options = {}) def init_logger begin - @fh = File.open(LOG_FILE_PATH, File::WRONLY | File::APPEND | File::CREAT | File::EXCL) + @fh = File.open(@log_file_path, File::WRONLY | File::APPEND | File::CREAT | File::EXCL) rescue Errno::EEXIST - @fh = File.open(LOG_FILE_PATH, File::WRONLY | File::APPEND) + @fh = File.open(@log_file_path, File::WRONLY | File::APPEND) end @fh.flock(File::LOCK_EX) @fh.sync = true @fh.flock(File::LOCK_UN) - @logger = Logger.new('/tmp/rpc_serv') + @logger = Logger.new(@log_file_path) @logger.level = Logger::INFO @logger.formatter = proc do |severity, datetime, _progname, msg| date = datetime.strftime("%Y-%m-%d %H:%M:%S") @@ -90,7 +86,6 @@ def init_logger end end - # Install method handlers def install_handlers(test_handlers = nil) @logger.info "--- #{self.class}.#{__callee__}: installing handlers ---" @@ -98,16 +93,16 @@ def install_handlers(test_handlers = nil) @server.add_introspection if test_handlers # Configuration import routine - @server.add_handler('sapha.import_config') do |yaml_string| + @server.add_handler("sapha.import_config") do |yaml_string| @logger.info "RPC sapha.import_config ---" begin - SapHA::Helpers.write_var_file('sapha_config.yaml', yaml_string) - begin + SapHA::Helpers.write_var_file("sapha_config.yaml", yaml_string) + begin @config = Psych.unsafe_load(yaml_string) - rescue NoMethodError + rescue NoMethodError @config = Psych.load(yaml_string) - end - @server.add_handler('sapha.config', @config) + end + @server.add_handler("sapha.config", @config) # for every component, expose sapha.config_{component}.apply method @config.config_sequence.each do |component| @@ -115,7 +110,7 @@ def install_handlers(test_handlers = nil) @server.add_handler(component[:rpc_method]) do |role| @logger.info "RPC #{component[:rpc_method]} ---" @mutex.synchronize { @busy = true } - @thread = Thread.new { + @thread = Thread.new do @logger.info "Thread #{component[:rpc_method]}(#{role}): started" begin obj.apply(role) @@ -125,8 +120,8 @@ def install_handlers(test_handlers = nil) end @logger.info "Thread #{component[:rpc_method]}(#{role}): finished" @mutex.synchronize { @busy = false } - } - 'wait' + end + "wait" end end rescue StandardError => e @@ -136,28 +131,28 @@ def install_handlers(test_handlers = nil) end if test_handlers - @server.add_handler('sapha.test_apply') do |role| - @logger.info "RPC sapha.test_apply ---" - @thread = Thread.new { + @server.add_handler("sapha.test_apply") do |role| + @logger.info "RPC sapha.test_apply #{role}---" + @thread = Thread.new do @mutex.synchronize { @busy = true } sleep 30 @mutex.synchronize { @busy = false } - } - 'wait' + end + "wait" end end - @server.add_handler('sapha.ping') do + @server.add_handler("sapha.ping") do @logger.info "RPC sapha.ping ---" true end - @server.add_handler('sapha.shutdown') do + @server.add_handler("sapha.shutdown") do @logger.info "RPC sapha.shutdown ---" shutdown end - @server.add_handler('sapha.busy') do + @server.add_handler("sapha.busy") do @mutex.synchronize do @logger.info "RPC sapha.busy? = #{@busy} ---" if @busy @@ -166,7 +161,6 @@ def install_handlers(test_handlers = nil) @busy end end - end def start @@ -176,12 +170,12 @@ def start def shutdown @logger.info "--- #{self.class}.#{__callee__} ---" - Thread.new { + Thread.new do sleep 3 # if we have any tasks still running, wait until they are finished @thread.join if @thread @server.shutdown - } + end true end @@ -191,37 +185,26 @@ def immediate_shutdown @server.shutdown end - # open the RPC Server port by manipulating the iptables directly + # open the RPC Server port by manipulating the fireall directly def open_port @logger.info "--- #{self.class}.#{__callee__} ---" - rule_no = get_rule_number - return if rule_no - _out, rc = exec_output_status('/usr/sbin/iptables', '-I', - 'INPUT', '1', '-p', 'tcp', '--dport', '8080', '-j', 'ACCEPT') + _out, status = exec_outerr_status("/usr/bin/firewall-cmd", "--state") + return if status.exitstatus != 0 + out, status = exec_output_status("/usr/bin/firewall-cmd", "--add-port", "8080/tcp") + @logger.info "open_port: status=#{status}, out=#{out}" @port_opened = true - rc.exitstatus == 0 + status.exitstatus == 0 end # close the RPC Server port by manipulating the iptables directly def close_port @logger.info "--- #{self.class}.#{__callee__} ---" - return unless @port_opened - rule_no = get_rule_number - puts "close_port: rule_no=#{rule_no} #{!!rule_no}" - return unless rule_no - out, rc = exec_output_status('/usr/sbin/iptables', '-D', 'INPUT', rule_no.to_s) - puts "close_port: rc=#{rc}, out=#{out}" + _out, status = exec_outerr_status("/usr/bin/firewall-cmd", "--state") + return if status.exitstatus != 0 + out, status = exec_output_status("/usr/bin/firewall-cmd", "--remove-port", "8080/tcp") + @logger.info "close_port: status=#{status}, out=#{out}" @port_opened = false - rc.exitstatus == 0 - end - - # get iptables rule number for the RPC Server port - def get_rule_number - @logger.info "--- #{self.class}.#{__callee__} ---" - out = pipeline(['/usr/sbin/iptables', '-L', 'INPUT', '-n', '-v', '--line-number'], - ['/usr/bin/awk', '$11 == "tcp" && $12 == "dpt:8080" && $4 == "ACCEPT" { print $1 }']) - return nil if out.empty? - Integer(out.strip) + status.exitstatus == 0 end end end @@ -231,6 +214,5 @@ def get_rule_number at_exit { server.shutdown } server.start server.close_port - Yast::SuSEFirewall.ActivateConfiguration # TODO: what if we demonize the process, by returning 0 at a successful server start? end diff --git a/src/lib/sap_ha/sap_ha_installation.rb b/src/lib/sap_ha/sap_ha_installation.rb index 3236baf..76f5587 100644 --- a/src/lib/sap_ha/sap_ha_installation.rb +++ b/src/lib/sap_ha/sap_ha_installation.rb @@ -18,12 +18,12 @@ # Summary: SUSE High Availability Setup for SAP Products: Cluster setup class # Authors: Ilya Manyugin -require 'yast' -require 'xmlrpc/client' -require 'sap_ha/system/ssh' -require 'sap_ha/exceptions' -require 'sap_ha/system/connectivity' -require 'sap_ha/node_logger' +require "yast" +require "xmlrpc/client" +require "sap_ha/system/ssh" +require "sap_ha/exceptions" +require "sap_ha/system/connectivity" +require "sap_ha/node_logger" module SapHA # class that controls the cluster configuration @@ -63,7 +63,7 @@ def run :next end - private + private def next_task log.debug "--- #{self.class}.#{__callee__} ---" @@ -82,8 +82,8 @@ def remote_configuration(node) SapHA::System::Connectivity.configure(node[:hostname]) do |rpc| begin rpc.connect - rpc.polling_call('sapha.import_config', @yaml_config) - rpc.polling_call('sapha.config.start_setup') + rpc.polling_call("sapha.import_config", @yaml_config) + rpc.polling_call("sapha.config.start_setup") rescue SapHA::Exceptions::RPCRecoverableException => e log.debug "--- #{self.class}.#{__callee__}(#{node}) :: caught RPCRecoverableException ---" NodeLogger.fatal "Could not connect to node #{node[:hostname]}. Cluster setup is interrupted." @@ -96,9 +96,9 @@ def remote_configuration(node) rpc.polling_call(component[:rpc_method], :slave) next_task end - rpc.polling_call('sapha.config.end_setup') - NodeLogger.import rpc.polling_call('sapha.config.collect_log') - rpc.polling_call('sapha.shutdown') + rpc.polling_call("sapha.config.end_setup") + NodeLogger.import rpc.polling_call("sapha.config.collect_log") + rpc.polling_call("sapha.shutdown") end true end @@ -128,10 +128,10 @@ def prepare def calculate_gui log.debug "--- #{self.class}.#{__callee__} ---" - tasks = ['Connecting'] + tasks = ["Connecting"] tasks.concat(@config.config_sequence.map { |e| e[:screen_name] }) - stages = ['Configure local node'] - titles = ['Configuring local node'] + stages = ["Configure local node"] + titles = ["Configuring local node"] @other_nodes.each do |n| stages << "Configure remote node [#{n[:hostname]}]" titles << "Configuring remote node [#{n[:hostname]}]" diff --git a/src/lib/sap_ha/sap_ha_unattended_install.rb b/src/lib/sap_ha/sap_ha_unattended_install.rb index 78efd39..e2351a9 100644 --- a/src/lib/sap_ha/sap_ha_unattended_install.rb +++ b/src/lib/sap_ha/sap_ha_unattended_install.rb @@ -19,14 +19,13 @@ # Summary: SUSE High Availability Setup for SAP Products: unattended installation # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' -require 'sap_ha/exceptions' -require 'sap_ha/system/ssh' -require 'sap_ha/node_logger' -require 'sap_ha/wizard/gui_installation_page' -require 'sap_ha/configuration' - +require "yast" +require "sap_ha/helpers" +require "sap_ha/exceptions" +require "sap_ha/system/ssh" +require "sap_ha/node_logger" +require "sap_ha/wizard/gui_installation_page" +require "sap_ha/configuration" # YaST module module SapHA @@ -42,28 +41,23 @@ def initialize(config) end def check_config - begin - validate_config - check_ssh - :next - rescue UnattendedModeException, ConfigValidationException => e - puts e.message - log.error e.message - NodeLogger.fatal "The imported configuration file did not pass on all checks. Please, review the errors and try again." - # Raise the error and let the caller resolve how to present it. - raise e - end + validate_config + check_ssh + :next + rescue UnattendedModeException, ConfigValidationException => e + log.error e.message + NodeLogger.fatal "The imported configuration file did not pass on all checks. Please, review the errors and try again." + # Raise the error and let the caller resolve how to present it. + raise e end def run - begin - # Encapsulate the call and pass nil as UI requirement - SapHA::SAPHAInstallation.new(@config, nil).run - :next - end + # Encapsulate the call and pass nil as UI requirement + SapHA::SAPHAInstallation.new(@config, nil).run + :next end - private + private def validate_config errors = @config.verbose_validate @@ -79,7 +73,7 @@ def validate_config NodeLogger.fatal "Please fix the errors in the configuration file and try again" raise ConfigValidationException, "Errors detected in the configuration" end - if ! @config.can_install? + if !@config.can_install? NodeLogger.fatal "The Configuration file is not complete." raise ConfigValidationException, "Configuration file is not complete" end @@ -110,8 +104,8 @@ def check_ssh log.error e.message log.error "Could not SSH to host #{h[:hostname]}: password provided in the "\ "configuration file is incorrect" - NodeLogger.fatal "Could not SSH to host #{h[:hostname]}: password provided in the "\ - "configuration file is incorrect" + NodeLogger.fatal "Could not SSH to host #{h[:hostname]}: password provided in the "\ + "configuration file is incorrect" next end rescue SSHException => e @@ -121,9 +115,9 @@ def check_ssh end end - if ! failed_nodes.empty? + if !failed_nodes.empty? NodeLogger.fatal "Error while connecting to the following node(s): #{failed_nodes.join(", ")}" - raise ConfigValidationException,"Error while connecting to the following node(s): #{failed_nodes.join(", ")}" + raise ConfigValidationException, "Error while connecting to the following node(s): #{failed_nodes.join(", ")}" end end end diff --git a/src/lib/sap_ha/semantic_checks.rb b/src/lib/sap_ha/semantic_checks.rb index 1b2a1f9..a03dbce 100644 --- a/src/lib/sap_ha/semantic_checks.rb +++ b/src/lib/sap_ha/semantic_checks.rb @@ -35,10 +35,10 @@ class SemanticChecks attr_accessor :silent attr_reader :checks_passed - #Site identifier regexp. That is what SAP allows. All ASCII charactest from 33 until 125 - #expect of '*' and '/'. The identifier can be 256 character long - #However, for security and technical reasons, we only allow alphanumeric characters as well as '-' and '_'. - #The identifier must not be longer than 30 characters and it must be minimum 2 long. + # Site identifier regexp. That is what SAP allows. All ASCII charactest from 33 until 125 + # expect of '*' and '/'. The identifier can be 256 character long + # However, for security and technical reasons, we only allow alphanumeric characters as well as '-' and '_'. + # The identifier must not be longer than 30 characters and it must be minimum 2 long. IDENTIFIER_REGEXP = Regexp.new("^[a-zA-Z0-9][a-zA-Z0-9_\-]{1,29}$") SAP_SID_REGEXP = Regexp.new("^[A-Z][A-Z0-9]{2}$") SAP_INST_NUM_REGEX = Regexp.new("^[0-9]{2}$") @@ -153,7 +153,7 @@ def nonneg_integer(value, field_name = "") # @param field_name [String] name of the field in the form def element_in_set(element, set, message = "", field_name = "") flag = set.include? element - message = "The value must be in the set [#{set.join(', ')}]" if message.nil? || message.empty? + message = "The value must be in the set [#{set.join(", ")}]" if message.nil? || message.empty? report_error(flag, message, field_name, element) end @@ -240,7 +240,7 @@ def integer_in_range(value, low, high, message = "", field_name = "") def sap_sid(value, message = "", field_name = "") message = "A valid SAP System ID consists of three characters, starts with a letter, and "\ " must not collide with one of the reserved IDs" if message.nil? || message.empty? - flag = !SAP_SID_REGEXP.match(value).nil? && !RESERVED_SAP_SIDS.include?(value) + flag = SAP_SID_REGEXP.match?(value) && !RESERVED_SAP_SIDS.include?(value) report_error(flag, message, field_name, value) end @@ -281,9 +281,9 @@ def block_device(value, field_name) def check(verbosity) old_silent = @silent @silent = if verbosity == :verbose - false - else - true + false + else + true end transaction_begin yield self @@ -335,7 +335,7 @@ def report_error(flag, message, field_name, value) nil end - private + private def error_string(field_name, explanation, value = nil) field_name = field_name.strip diff --git a/src/lib/sap_ha/system/connectivity.rb b/src/lib/sap_ha/system/connectivity.rb index 37e43fc..6ea0030 100644 --- a/src/lib/sap_ha/system/connectivity.rb +++ b/src/lib/sap_ha/system/connectivity.rb @@ -19,12 +19,12 @@ # Summary: SUSE High Availability Setup for SAP Products: Node connectivity # Authors: Ilya Manyugin -require 'yast' -require 'socket' -require 'xmlrpc/client' -require 'sap_ha/exceptions' -require 'sap_ha/node_logger' -require_relative 'shell_commands' +require "yast" +require "socket" +require "xmlrpc/client" +require "sap_ha/exceptions" +require "sap_ha/node_logger" +require_relative "shell_commands" module SapHA module System @@ -49,7 +49,7 @@ def connect log.info "Creating a new RPC client for host #{self[:host_name]}" unless self[:rpc_client] self[:rpc_client] = XMLRPC::Client.new(ip, "/RPC2", 8080) begin - call('sapha.ping') + call("sapha.ping") rescue RPCRecoverableException => e log.debug "--- #{self.class}.#{__callee__}: Caught RPCRecoverableException ---" log.error "Error connecting to the XML RPC server on node #{self[:host_name]}: "\ @@ -86,7 +86,7 @@ def rpc_server_running? def stop_rpc_server log.info "--- #{self.class}.#{__callee__} ---" - call('sapha.shutdown') + call("sapha.shutdown") end def kill_rpc_server @@ -111,7 +111,7 @@ def recover_rpc_server def ping? log.info "--- #{self.class}.#{__callee__} ---" - call('sapha.ping') + call("sapha.ping") end # Perform a polling call: call the method that returns immediately, @@ -122,7 +122,7 @@ def polling_call(method_name, *args) raise RPCCallException, "Cannot call method #{method_name}: "\ "client #{self[:host_name]} is not connected." unless self[:rpc_client] - raise RPCCallException, "Node #{self[:host_name]} is busy configuring something!" if call('sapha.busy') + raise RPCCallException, "Node #{self[:host_name]} is busy configuring something!" if call("sapha.busy") retval = call(method_name, *args) return retval unless retval == "wait" @@ -139,7 +139,7 @@ def polling_call(method_name, *args) true end - private + private # Wrap the .call method with exception handlers def call(method_name, *args) @@ -152,7 +152,7 @@ def call(method_name, *args) error_count += 1 time_out *= 2 log.info "Retry \##{error_count} in #{time_out} seconds: "\ - "calling #{method_name}(#{args.join(', ')}) on node #{self[:host_name]}." + "calling #{method_name}(#{args.join(", ")}) on node #{self[:host_name]}." sleep(time_out) return true else @@ -162,7 +162,7 @@ def call(method_name, *args) end begin - log.info "calling #{method_name}(#{args.join(', ')}) on node #{self[:host_name]}." + log.info "calling #{method_name}(#{args.join(", ")}) on node #{self[:host_name]}." self[:rpc_client].call(method_name, *args) rescue Errno::ECONNREFUSED => e log.debug "--- #{self.class}.#{__callee__} :: caught Errno::ECONNREFUSED --- " @@ -171,19 +171,19 @@ def call(method_name, *args) raise RPCRecoverableException, "Could not connect to the RPC server on node #{self[:host_name]}: #{e.message}" rescue Net::HTTPBadResponse => e log.debug "--- #{self.class}.#{__callee__} :: caught Net::HTTPBadResponse --- " - log.error "Got bad response from node #{self[:host_name]}: #{e.message}. Call #{method_name}(#{args.join(', ')})." + log.error "Got bad response from node #{self[:host_name]}: #{e.message}. Call #{method_name}(#{args.join(", ")})." retry if try_again.call raise RPCFatalException, "Got bad response from node #{self[:host_name]}: #{e.message}" rescue Net::ReadTimeout log.debug "--- #{self.class}.#{__callee__} :: caught Net::ReadTimeout --- " - log.error "Call #{method_name}(#{args.join(', ')}) --- time out" + log.error "Call #{method_name}(#{args.join(", ")}) --- time out" retry if try_again.call - raise RPCFatalException, "Call #{method_name}(#{args.join(', ')}) --- time out" + raise RPCFatalException, "Call #{method_name}(#{args.join(", ")}) --- time out" rescue StandardError => e log.debug "--- #{self.class}.#{__callee__} :: caught StandardError --- " - log.error "Call #{method_name}(#{args.join(', ')}): #{e.message}" + log.error "Call #{method_name}(#{args.join(", ")}): #{e.message}" retry if try_again.call - raise RPCFatalException, "Call #{method_name}(#{args.join(', ')}): #{e.message}" + raise RPCFatalException, "Call #{method_name}(#{args.join(", ")}): #{e.message}" end end end @@ -220,24 +220,18 @@ def init_from_config(cfg) def run_rpc_servers # TODO: retry on failure or raise log.info "--- #{self.class}.#{__callee__} ---" - @nodes.values.each do |host| - host.run_rpc_server - end + @nodes.values.each(&:run_rpc_server) end # Connect to all nodes def connect_to_all log.debug "--- #{self.class}.#{__callee__} ---" - @nodes.values.each do |host| - host.connect - end + @nodes.values.each(&:connect) end def check_connectivity log.debug "--- #{self.class}.#{__callee__} ---" - @nodes.values.each do |host| - host.ping? - end.all? + @nodes.values.each(&:ping?).all? end def configure(host_name) @@ -251,7 +245,6 @@ def configure(host_name) end end - def call(host_name, rpc_method, *args) log.debug "--- #{self.class}.#{__callee__}(#{host_name}, #{rpc_method}, #{args}) ---" raise RPCFatalException, "Could not find the host #{host_name}."\ diff --git a/src/lib/sap_ha/system/hana.rb b/src/lib/sap_ha/system/hana.rb index a858d83..964ce20 100644 --- a/src/lib/sap_ha/system/hana.rb +++ b/src/lib/sap_ha/system/hana.rb @@ -19,13 +19,12 @@ # Summary: SUSE High Availability Setup for SAP Products: HANA configuration # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/exceptions' -require 'sap_ha/helpers' -require 'sap_ha/node_logger' -require 'sap_ha/system/ssh' -require_relative 'shell_commands' -require "cfa/global_ini" +require "yast" +require "sap_ha/exceptions" +require "sap_ha/helpers" +require "sap_ha/node_logger" +require "sap_ha/system/ssh" +require_relative "shell_commands" module SapHA module System @@ -36,15 +35,13 @@ class HanaClass include SapHA::Exceptions include Yast::Logger - HANA_GLOBAL_INI = "/hana/shared/%s/global/hdb/custom/config/global.ini".freeze - # Check if HBD daemon is running # @param system_id [String] SAP SID of the HANA instance # @param instance_number [String] HANA instance number def check_hdb_daemon_running(system_id, instance_number) log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{instance_number}) ---" procname = "hdb.sap#{system_id.upcase}_HDB#{instance_number}" - _out, status = exec_outerr_status('pidof', procname) + _out, status = exec_outerr_status("pidof", procname) status.exitstatus == 0 end @@ -57,14 +54,8 @@ def make_backup(system_id, secstore_user, file_name, instance_number) log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{secstore_user},"\ " #{file_name}, #{instance_number}) ---" user_name = "#{system_id.downcase}adm" - # do backup differently for HANA 2.0 - version = version(system_id) - if SapHA::Helpers.version_comparison('2.00.010', version, '>=') - command = 'hdbsql', '-U', secstore_user, '-d', 'SYSTEMDB', - "\"BACKUP DATA FOR FULL SYSTEM USING FILE ('#{file_name}')\"" - else - command = 'hdbsql', '-U', secstore_user, "\"BACKUP DATA USING FILE ('#{file_name}')\"" - end + command = "hdbsql", "-i", instance_number, "-U", secstore_user, "-d", "SYSTEMDB", + "\"BACKUP DATA FOR FULL SYSTEM USING FILE ('#{file_name}')\"" out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status( status.exitstatus == 0, @@ -80,20 +71,18 @@ def make_backup(system_id, secstore_user, file_name, instance_number) def hdb_start(system_id) log.info "--- called #{self.class}.#{__callee__}(#{system_id}) ---" user_name = "#{system_id.downcase}adm" - command = ['HDB', 'start'] + command = ["HDB", "start"] out, status = su_exec_outerr_status(user_name, *command) s = NodeLogger.log_status(status.exitstatus == 0, "Started HANA #{system_id}", "Could not start HANA #{system_id}, will retry.", - out - ) + out) return true if s out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Started HANA #{system_id}", "Could not start HANA #{system_id}, bailing out.", - out - ) + out) end # Get the HANA version as a string @@ -102,7 +91,7 @@ def hdb_start(system_id) def version(system_id) log.info "--- called #{self.class}.#{__callee__}(#{system_id}) ---" user_name = "#{system_id.downcase}adm" - command = ['HDB', 'version'] + command = ["HDB", "version"] out, status = su_exec_outerr_status(user_name, *command) unless status.exitstatus == 0 NodeLogger.error("Could not retrieve HANA version, assuming legacy version") @@ -119,20 +108,18 @@ def version(system_id) def hdb_stop(system_id) log.info "--- called #{self.class}.#{__callee__}(#{system_id}) ---" user_name = "#{system_id.downcase}adm" - command = ['HDB', 'stop'] + command = ["HDB", "stop"] out, status = su_exec_outerr_status(user_name, *command) s = NodeLogger.log_status(status.exitstatus == 0, "Stopped HANA #{system_id}", "Could not stop HANA #{system_id}, will retry.", - out - ) + out) return true if s out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Stopped HANA #{system_id}", "Could not stop HANA #{system_id}, bailing out.", - out - ) + out) end # Enable System Replication on the primary HANA system @@ -141,13 +128,12 @@ def hdb_stop(system_id) def enable_primary(system_id, site_name) log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{site_name}) ---" user_name = "#{system_id.downcase}adm" - command = ['hdbnsutil', '-sr_enable', "--name=#{site_name}"] + command = ["hdbnsutil", "-sr_enable", "--name=#{site_name}"] out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Enabled HANA (#{system_id}) System Replication on the primary site #{site_name}", "Could not enable HANA (#{system_id}) System Replication on the primary site #{site_name}", - out - ) + out) end # Enable System Replication on the secondary HANA system @@ -161,28 +147,14 @@ def enable_secondary(system_id, site_name, host_name_primary, instance, rmode, o log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{site_name},"\ " #{host_name_primary}, #{instance}, #{rmode}, #{omode}) ---" user_name = "#{system_id.downcase}adm" - version = version(system_id) - # Select an appropriate command-line switch for replication mode - # Assume legacy `mode` by default (pre-SPS12) - rmode_string = if SapHA::Helpers.version_comparison('1.00.120', version, '>=') - "--replicationMode=#{rmode}" - else - "--mode=#{rmode}" - end - omode_string = if SapHA::Helpers.version_comparison('1.00.110', version, '>=') - "--operationMode=#{omode}" - else - nil - end - command = ['hdbnsutil', '-sr_register', "--remoteHost=#{host_name_primary}", - "--remoteInstance=#{instance}", rmode_string, omode_string, - "--name=#{site_name}"].reject(&:nil?) + command = ["hdbnsutil", "-sr_register", "--remoteHost=#{host_name_primary}", + "--remoteInstance=#{instance}", "--replicationMode=#{rmode}", + "--operationMode=#{omode}", "--name=#{site_name}"].reject(&:nil?) out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Enabled HANA (#{system_id}) System Replication on the secondary host #{site_name}", "Could not enable HANA (#{system_id}) System Replication on the secondary host", - out - ) + out) end # List the keys out of the HANA secure user store @@ -191,7 +163,7 @@ def check_secure_store(system_id) log.info "--- called #{self.class}.#{__callee__}(#{system_id}) ---" regex = /^KEY (\w+)$/ user_name = "#{system_id.downcase}adm" - command = ['hdbuserstore', 'list'] + command = ["hdbuserstore", "list"] out, status = su_exec_outerr_status(user_name, *command) unless status.exitstatus == 0 log.error "Could not get the list of keys in the HANA secure user store"\ @@ -204,67 +176,44 @@ def check_secure_store(system_id) def set_secute_store(system_id, key_name, env, user_name, password) log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{key_name}, ...) ---" su_name = "#{system_id.downcase}adm" - command = ['hdbuserstore', 'set', key_name, env, user_name, password] + command = ["hdbuserstore", "set", key_name, env, user_name, password] out, status = su_exec_outerr_status(su_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Successfully set key #{key_name} in the secure user store on system #{system_id}", "Could not set key #{key_name} in the secure user store on system #{system_id}", - out - ) - end - - # Implement adjustments to the non-production system - # @param system_id [String] HANA System ID (production) - # @param options [Hash] production system options - def adjust_non_production_system(system_id, options = {}) - log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{options.inspect}) ---" - adjust_system(system_id, options, 'non-production') - end - - # Implement adjustments to the production system, so that a non-production - # HANA could be run along it - # @param system_id [String] HANA System ID (production) - # @param options [Hash] production system options - def adjust_production_system(system_id, options = {}) - log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{options.inspect}) ---" - adjust_system(system_id, options, 'production') do |ini| - ini.set_config('system_replication', 'preload_column_tables', options[:preload_column_tables]) - end + out) end # Create a user for monitoring the non-production HANA on the secondary node # @param system_id [String] HANA System ID (production) - # @param instance_number [#to_s] + # @param instance_number [#to_s] def create_monitoring_user(system_id, instance_number) log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{instance_number}) ---" user_name = "#{system_id.downcase}adm" - command_prefix = ['hdbsql', '-u', 'system', '-i', instance_number.to_s, - '-n', 'localhost:31013'] + command_prefix = ["hdbsql", "-u", "system", "-i", instance_number.to_s, + "-n", "localhost:31013"] command = command_prefix.clone << '"CREATE USER SC PASSWORD L1nuxLab"' out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Created user SC for HANA instance #{system_id}/#{instance_number}", "Could not create user SC for HANA instance #{system_id}/#{instance_number}", - out - ) + out) command = command_prefix.clone << '"GRANT MONITORING TO SC"' out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Granted MONITORING to user SC on HANA instance #{system_id}/#{instance_number}", "Could not grant MONITORING to user SC on HANA instance #{system_id}/#{instance_number}", - out - ) + out) command = command_prefix.clone << '"ALTER USER SC DISABLE PASSWORD LIFETIME"' out, status = su_exec_outerr_status(user_name, *command) NodeLogger.log_status(status.exitstatus == 0, "Disabled password lifetime for user SC on HANA instance #{system_id}/#{instance_number}", "Could not disable password lifetime for user"\ " SC on HANA instance #{system_id}/#{instance_number}", - out - ) - command_prefix = ['hdbsql', '-u', 'sc', '-i', instance_number.to_s, '-n', 'localhost:31013'] + out) + command_prefix = ["hdbsql", "-u", "sc", "-i", instance_number.to_s, "-n", "localhost:31013"] command = command_prefix << '"SELECT * FROM DUMMY"' - out, status = su_exec_outerr_status(user_name, *command) + _out, _status = su_exec_outerr_status(user_name, *command) end # Execute an HDBSQL command @@ -278,13 +227,13 @@ def hdbsql_command(system_id, user_name, instance_number, password, environment, log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{user_name},"\ " #{instance_number}, password, #{environment}, #{statement}) ---" su_name = "#{system_id.downcase}adm" - cmd = 'hdbsql', '-x', '-u', user_name, '-i', instance_number.to_s, '-p', password - cmd << '-n' << environment unless environment.empty? + cmd = "hdbsql", "-x", "-u", user_name, "-i", instance_number.to_s, "-p", password + cmd << "-n" << environment unless environment.empty? cmd << '"' << statement.gsub('"', "\\\"") << '"' out, status = su_exec_outerr_status_mask_password([7], su_name, *cmd) if status.exitstatus != 0 # remove the password from the command line - pass_index = (cmd.index('-p') || 0) + 1 + pass_index = (cmd.index("-p") || 0) + 1 cmd[pass_index] = "*" * cmd[pass_index].length NodeLogger.error "Error executing command #{cmd.join(" ")}" NodeLogger.output out @@ -297,16 +246,6 @@ def hdbsql_command(system_id, user_name, instance_number, password, environment, # @param system_id [String] HANA System ID def copy_ssfs_keys(system_id, secondary_host_name, password) log.info "--- called #{self.class}.#{__callee__}(#{system_id}, #{secondary_host_name} ---" - # TODO: check the paths, ideally taking them from the ENV - # according to the docs, they should be: - # $DIR_INSTANCE/../global/security/rsecssfs/data/SSFS_.DAT - # $DIR_INSTANCE/../global/security/rsecssfs/key/SSFS_.KEY - # but those paths are invalid - hana_version = version(system_id) - unless SapHA::Helpers.version_comparison('2.00', hana_version, '>=') - log.info "No need to copy SSFS keys for HANA version #{hana_version}" - return - end # Check if is it possible to create a SSH connection without password in case it is nil if password.nil? begin @@ -335,39 +274,6 @@ def copy_ssfs_keys(system_id, secondary_host_name, password) end end - private - - # Implement adjustment of the system - def adjust_system(system_id, options = {}, system_type = "", &block) - # Change the global.ini - global_ini_path = HANA_GLOBAL_INI % system_id.upcase - unless File.exist?(global_ini_path) - NodeLogger.error "Could not adjust global.ini for the #{system_type} system:" - NodeLogger.output "File #{global_ini_path} does not exist" - return false - end - begin - global_ini = CFA::GlobalIni.new(global_ini_path) - global_ini.load - global_ini.set_config('memorymanager', 'global_allocation_limit', options[:global_alloc_limit]) - global_ini.set_config('ha_dr_provider_SAPHanaSR', 'provider', 'SAPHanaSR') - global_ini.set_config('ha_dr_provider_SAPHanaSR', 'path', '/usr/share/SAPHanaSR') - global_ini.set_config('ha_dr_provider_SAPHanaSR', 'execution_order', '1') - global_ini.set_config('trace', 'ha_dr_saphanasr', 'info') - - block.call(global_ini) if block_given? - - global_ini.save - rescue StandardError => e - NodeLogger.error "Could not adjust global.ini for the #{system_type} system:" - NodeLogger.output e.message - return false - else - NodeLogger.info "Successfully adjusted global.ini for the #{system_type} system #{system_id}" - true - end - end - end # HanaClass Hana = HanaClass.instance end # namespace System diff --git a/src/lib/sap_ha/system/local.rb b/src/lib/sap_ha/system/local.rb index c3b42e6..518ea43 100644 --- a/src/lib/sap_ha/system/local.rb +++ b/src/lib/sap_ha/system/local.rb @@ -20,27 +20,25 @@ # Authors: Ilya Manyugin # Peter Varkoly -require 'yast' -require 'socket' -require 'sap_ha/exceptions' -require 'sap_ha/helpers' -require 'sap_ha/node_logger' +require "yast" +require "socket" +require "sap_ha/exceptions" +require "sap_ha/helpers" +require "sap_ha/node_logger" require "base64" -require_relative 'shell_commands' +require_relative "shell_commands" # In yast2 4.1.3 a reorganization of the YaST systemd library was introduced. When running on an # older version, just fall back to the old SystemdService module (bsc#1146220). begin - require 'yast2/systemd/service' - require 'yast2/systemd/socket' + require "yast2/systemd/service" + require "yast2/systemd/socket" rescue LoadError - Yast.import 'SystemdService' - Yast.import 'SystemdSocket' + Yast.import "SystemdService" + Yast.import "SystemdSocket" end -Yast.import 'SuSEFirewallServices' -Yast.import 'SuSEFirewall' -Yast.import 'Cluster' +Yast.import "Cluster" module SapHA module System @@ -51,23 +49,23 @@ class LocalClass include SapHA::Exceptions include Yast::Logger - COROSYNC_KEY_PATH = '/etc/corosync/authkey'.freeze - CSYNC2_KEY_PATH = '/etc/csync2/key_hagroup'.freeze + COROSYNC_KEY_PATH = "/etc/corosync/authkey".freeze + CSYNC2_KEY_PATH = "/etc/csync2/key_hagroup".freeze # List all block devices on the system def block_devices devices = {} - Dir.glob('/dev/disk/by-*').map do |p| - dev_map = Dir.glob(File.join(p, '*')).map { |e| [File.basename(e), e] }.to_h + Dir.glob("/dev/disk/by-*").map do |p| + dev_map = Dir.glob(File.join(p, "*")).map { |e| [File.basename(e), e] }.to_h devices[File.basename(p)] = dev_map end # if there are no udev-generated IDs, fall-back to /dev/* - out, status = exec_outerr_status('lsblk', '-nlp', '-oName', '-e11') + out, status = exec_outerr_status("lsblk", "-nlp", "-oName", "-e11") if status.exitstatus != 0 log.error "Failed calling lsblk: #{out}" else dev_map = out.split("\n").map { |e| [File.basename(e), e] }.to_h - devices['by-device'] = dev_map + devices["by-device"] = dev_map end devices end @@ -82,10 +80,10 @@ def systemd_unit(action, unit_type, unit_name) raise LocalSystemException, "Unknown action #{action} on systemd #{unit_type}" end unit = if unit_type == :service - find_service(unit_name) - else - find_socket(unit_name) - end + find_service(unit_name) + else + find_socket(unit_name) + end if unit.nil? NodeLogger.error "Could not #{action} #{unit_type} "\ "#{unit_name}: #{unit_type} does not exist" @@ -111,7 +109,7 @@ def append_hosts_file(hosts) "#{h[:ip_ring1]}\t#{h[:host_name]} \# added by yast2-sap-ha" end.join("\n") begin - File.open('/etc/hosts', 'a') { |fh| fh.puts(str) } + File.open("/etc/hosts", "a") { |fh| fh.puts(str) } success = true rescue StandardError => e out = e.message @@ -125,7 +123,7 @@ def append_hosts_file(hosts) end def generate_csync2_key - out, status = exec_outerr_status('/usr/sbin/csync2', '-k', CSYNC2_KEY_PATH) + out, status = exec_outerr_status("/usr/sbin/csync2", "-k", CSYNC2_KEY_PATH) NodeLogger.log_status(status.exitstatus == 0, "Generated the csync2 authentication key", "Could not generate the csync2 authentication key", @@ -156,7 +154,7 @@ def write_csync2_key(data) end def generate_corosync_key - out, status = exec_outerr_status('/usr/sbin/corosync-keygen', '-l') + out, status = exec_outerr_status("/usr/sbin/corosync-keygen", "-l") NodeLogger.log_status(status.exitstatus == 0, "Generated the corosync authentication key", "Could not generate the corosync authentication key", @@ -188,32 +186,43 @@ def write_corosync_key(data) # join an existing cluster def join_cluster(_ip_address) - raise 'Not implemented' + raise "Not implemented" end - def open_ports(role, rings, number_of_rings) - # 30865 for csync2 - # 5560 for mgmtd - # 7630 for hawk2 - # 21064 for dlm - tcp_ports = ["30865", "5560", "7630", "21064"] - udp_ports = rings.map { |_, r| r[:port].to_s }[0...number_of_rings].uniq - Yast::SuSEFirewallServices.SetNeededPortsAndProtocols( - "service:cluster", "tcp_ports" => tcp_ports, "udp_ports" => udp_ports) - Yast::SuSEFirewall.ResetReadFlag - Yast::SuSEFirewall.Read - Yast::SuSEFirewall.SetServicesForZones(["service:cluster", "service:sshd"], ["EXT"], true) - written = Yast::SuSEFirewall.Write - if role == :master - Yast::SuSEFirewall.ActivateConfiguration - else - written - end + def config_firewall(fw_config, role) + case fw_config + when "done" + NodeLogger.info("Firewall is already configured") + when "off" + NodeLogger.info("Firewall will be turned off") + systemd_unit(:stop, :service, "firewalld") + when "setup" + NodeLogger.info("Firewall will be configured for cluster services.") + out, status = exec_outerr_status("/usr/bin/firewall-cmd", "--state") + return if status.exitstatus != 0 + out, status = exec_outerr_status("/usr/bin/firewall-cmd", "--add-service", "cluster") + NodeLogger.log_status( + status.exitstatus == 0, + "Open cluster service in firewall", + "Could not open cluster service in firewall", + out + ) + out, status = exec_outerr_status("/usr/bin/firewall-cmd", "--permanent", "--add-service", "cluster") + NodeLogger.log_status( + status.exitstatus == 0, + "Open cluster service permanent in firewall", + "Could not open cluster service permanent in firewall", + out + ) + if role != :master + exec_status("/usr/bin/firewall-cmd", "--add-port", "8080/tcp") + end + end end def change_password(user_name, password) cmd_string = "#{user_name}:#{password}" - out, status = exec_outerr_status_stdin('chpasswd', cmd_string) + out, status = exec_outerr_status_stdin("chpasswd", cmd_string) NodeLogger.log_status(status.exitstatus == 0, "Changed password for user #{user_name}", "Could not change password for user #{user_name}", @@ -222,23 +231,23 @@ def change_password(user_name, password) def start_cluster_services success = true - success &= systemd_unit(:enable, :service, 'sbd') - success &= systemd_unit(:enable, :socket, 'csync2') - success &= systemd_unit(:start, :socket, 'csync2') - success &= systemd_unit(:enable, :service, 'pacemaker') - success &= systemd_unit(:start, :service, 'pacemaker') - success &= systemd_unit(:enable, :service, 'hawk') - success &= systemd_unit(:start, :service, 'hawk') + success &= systemd_unit(:enable, :service, "sbd") + success &= systemd_unit(:enable, :socket, "csync2") + success &= systemd_unit(:start, :socket, "csync2") + success &= systemd_unit(:enable, :service, "pacemaker") + success &= systemd_unit(:start, :service, "pacemaker") + success &= systemd_unit(:enable, :service, "hawk") + success &= systemd_unit(:start, :service, "hawk") NodeLogger.log_status( success, - 'Enabled and started cluster-required systemd units', - 'Could not enable and start cluster-required systemd units' + "Enabled and started cluster-required systemd units", + "Could not enable and start cluster-required systemd units" ) end def cluster_maintenance(action = :on) - mm = action == :on ? 'true' : 'false' - cmd = ['crm', 'configure', 'property', "maintenance-mode=#{mm}"] + mm = action == :on ? "true" : "false" + cmd = ["crm", "configure", "property", "maintenance-mode=#{mm}"] out, status = exec_outerr_status(*cmd) NodeLogger.log_status( status.exitstatus == 0, @@ -254,20 +263,19 @@ def yast_cluster_export(settings) Yast::Cluster.Read log.debug "--- Exporting Cluster settings to yast2-cluster: #{settings}" Yast::Cluster.Import(settings) - stat = Yast::Cluster.Write - NodeLogger.log_status(stat, 'Wrote cluster settings', 'Could not write cluster settings') + stat = Yast::Cluster.Write + NodeLogger.log_status(stat, "Wrote cluster settings", "Could not write cluster settings") end # Add the SBD stonith resource to the cluster def add_stonith_resource log.debug "--- called #{self.class}.#{__callee__} ---" - out, status = exec_outerr_status('crm', 'configure', - 'primitive', 'stonith-sbd', 'stonith:external/sbd') + out, status = exec_outerr_status("crm", "configure", + "primitive", "stonith-sbd", "stonith:external/sbd") NodeLogger.log_status(status.exitstatus == 0, - 'Added a primitive to the cluster: stonith-sbd', - 'Could not add the stonith-sbd primitive to the cluster', - out - ) + "Added a primitive to the cluster: stonith-sbd", + "Could not add the stonith-sbd primitive to the cluster", + out) end # Initialize the SBD devices @@ -277,12 +285,11 @@ def initialize_sbd(devices) flag = true devices.each do |device| log.warn "Initializing the SBD device on #{device}" - status = exec_status('sbd', '-d', device, 'create') + status = exec_status("sbd", "-d", device, "create") log.warn "SBD initialization on #{device} returned #{status.exitstatus}" flag &= NodeLogger.log_status(status.exitstatus == 0, "Successfully initialized the SBD device #{device}", - "Could not initialize the SBD device #{device}" - ) + "Could not initialize the SBD device #{device}") end flag end @@ -298,7 +305,6 @@ def find_socket(name) socket_api = defined?(Yast2::Systemd::Socket) ? Yast2::Systemd::Socket : Yast::SystemdSocket socket_api.find!(name) end - end Local = LocalClass.instance end diff --git a/src/lib/sap_ha/system/network.rb b/src/lib/sap_ha/system/network.rb index 9944868..e8b6297 100644 --- a/src/lib/sap_ha/system/network.rb +++ b/src/lib/sap_ha/system/network.rb @@ -19,12 +19,12 @@ # Summary: SUSE High Availability Setup for SAP Products: Network configuration # Authors: Ilya Manyugin -require 'yast' -require 'socket' -require 'ipaddr' +require "yast" +require "socket" +require "ipaddr" -Yast.import 'NetworkInterfaces' -Yast.import 'Netmask' +Yast.import "NetworkInterfaces" +Yast.import "Netmask" module SapHA module System @@ -49,7 +49,7 @@ def ip_addresses # Get a list of network addresses on the local node's interface def network_addresses interfaces = Socket.getifaddrs.select do |iface| - iface.addr && iface.addr.ipv4? && !iface.addr.ipv4_loopback? + iface.addr && iface.addr.ipv4? && !iface.addr.ipv4_loopback? end interfaces.map do |iface| IPAddr.new(iface.addr.ip_address).mask(iface.netmask.ip_address).to_s @@ -62,8 +62,8 @@ def network_addresses_cidr iface.addr && iface.addr.ipv4? && !iface.addr.ipv4_loopback? end interfaces.map do |iface| - IPAddr.new(iface.addr.ip_address).mask(iface.netmask.ip_address).to_s + '/' + - IPAddr.new(iface.netmask.ip_address).to_i.to_s(2).count('1').to_s + IPAddr.new(iface.addr.ip_address).mask(iface.netmask.ip_address).to_s + "/" + + IPAddr.new(iface.netmask.ip_address).to_i.to_s(2).count("1").to_s end end diff --git a/src/lib/sap_ha/system/shell_commands.rb b/src/lib/sap_ha/system/shell_commands.rb index 1af3cb4..5538347 100644 --- a/src/lib/sap_ha/system/shell_commands.rb +++ b/src/lib/sap_ha/system/shell_commands.rb @@ -19,15 +19,14 @@ # Summary: SUSE High Availability Setup for SAP Products: Shell commands proxy mix-in # Authors: Ilya Manyugin -require 'open3' -require 'timeout' -require 'yast' +require "open3" +require "timeout" +require "yast" module SapHA module System # Shell commands proxy mix-in module ShellCommands - include Yast::Logger class FakeProcessStatus @@ -49,12 +48,11 @@ def exec_status(*command) # @return [Process::Status] def exec_status_mask_password(mask_fields, *command) cmd_echo = command.clone - mask_fields.each {|fn| cmd_echo[fn] = '*no echo*'} + mask_fields.each { |fn| cmd_echo[fn] = "*no echo*" } log.info "Executing command #{cmd_echo}" Open3.popen3(*command) { |_, _, _, wait_thr| wait_thr.value } end - # Execute command and return its status and output (stdout) # @return [[Process::Status, String]] def exec_output_status(*command) @@ -90,7 +88,7 @@ def pipe(*commands) # @return [[String, String]] [stdout_and_stderr, status] def su_exec_outerr_status(user_name, *params) log.info "Executing #{params} as user #{user_name}" - Open3.capture2e('su', '-lc', params.join(' '), user_name) + Open3.capture2e("su", "-lc", params.join(" "), user_name) rescue SystemCallError => e return ["System call failed with ERRNO=#{e.errno}: #{e.message}", FakeProcessStatus.new(1)] end @@ -100,15 +98,15 @@ def su_exec_outerr_status(user_name, *params) # @return [[String, String]] [stdout_and_stderr, status] def su_exec_outerr_status_mask_password(mask_fields, user_name, *params) cmd_echo = params.clone - mask_fields.each {|fn| cmd_echo[fn] = '*no echo*'} + mask_fields.each { |fn| cmd_echo[fn] = "*no echo*" } log.info "Executing #{cmd_echo} as user #{user_name}" - Open3.capture2e('su', '-lc', params.join(' '), user_name) + Open3.capture2e("su", "-lc", params.join(" "), user_name) rescue SystemCallError => e return ["System call failed with ERRNO=#{e.errno}: #{e.message}", FakeProcessStatus.new(1)] end def pipeline(cmd1, cmd2) - Open3.pipeline_r(cmd1, cmd2, {err: "/dev/null"}) { |out, wait_thr| out.read } + Open3.pipeline_r(cmd1, cmd2, err: "/dev/null") { |out, _thr| out.read } end end end diff --git a/src/lib/sap_ha/system/ssh.rb b/src/lib/sap_ha/system/ssh.rb index a562536..5695929 100644 --- a/src/lib/sap_ha/system/ssh.rb +++ b/src/lib/sap_ha/system/ssh.rb @@ -19,13 +19,13 @@ # Summary: SUSE High Availability Setup for SAP Products: Remote SSH invocation # Authors: Ilya Manyugin -require 'yast' -require 'fileutils' -require 'tmpdir' -require 'sap_ha/helpers' -require 'sap_ha/exceptions' -require 'sap_ha/node_logger' -require_relative 'shell_commands.rb' +require "yast" +require "fileutils" +require "tmpdir" +require "sap_ha/helpers" +require "sap_ha/exceptions" +require "sap_ha/node_logger" +require_relative "shell_commands.rb" module SapHA module System @@ -38,8 +38,8 @@ class SSH def initialize log.debug "--- #{self.class}.#{__callee__} --- " - @script_path = Helpers.data_file_path('check_ssh.expect') - @ssh_user_dir = File.join(Dir.home, '.ssh') + @script_path = "/usr/lib/YaST2/bin/check_ssh.expect" + @ssh_user_dir = File.join(Dir.home, ".ssh") reinit_identities create_user_identities unless check_user_identities authorize_own_keys @@ -102,9 +102,9 @@ def copy_keys_to(host, password) stat = exec_status_mask_password([5], "/usr/bin/expect", "-f", @script_path, "copy-id", host, password) check_status(stat, host) - ssh_dir = File.join(Dir.home, '.ssh') + ssh_dir = File.join(Dir.home, ".ssh") @user_identities.each do |key| - out, stat = exec_outerr_status('scp', key, "#{host}:#{key}") + out, stat = exec_outerr_status("scp", key, "#{host}:#{key}") if stat.exitstatus == 0 log.info "Copied SSH key #{key} to host #{host}." else @@ -113,7 +113,7 @@ def copy_keys_to(host, password) end end @user_pubkeys.each do |key| - out, stat = exec_outerr_status('scp', key, "#{host}:#{key}") + out, stat = exec_outerr_status("scp", key, "#{host}:#{key}") if stat.exitstatus == 0 log.info "Copied SSH public key #{key} to host #{host}." else @@ -121,8 +121,8 @@ def copy_keys_to(host, password) result = false end end - ak = File.join(ssh_dir, 'authorized_keys') - out, stat = exec_outerr_status('scp', ak, "#{host}:#{ak}") + ak = File.join(ssh_dir, "authorized_keys") + out, stat = exec_outerr_status("scp", ak, "#{host}:#{ak}") if stat.exitstatus == 0 log.info "Copied 'authorized_keys' to host #{host}." else @@ -130,7 +130,7 @@ def copy_keys_to(host, password) result = false end stat = exec_status_mask_password([5], "/usr/bin/expect", "-f", @script_path, - "authorize", host, password) + "authorize", host, password) NodeLogger.log_status(result, "Copied SSH keys to node #{host}", "Could not copy SSH keys to node #{host}") end @@ -141,13 +141,13 @@ def copy_keys_from(host, overwrite = false, password = "") # Create the .shh directory log.info "SSH::copy_keys(#{host}, overwrite=#{overwrite})" begin - ssh_dir = File.join(Dir.home, '.ssh') - Dir.mkdir(ssh_dir, 0700) + ssh_dir = File.join(Dir.home, ".ssh") + Dir.mkdir(ssh_dir, 0o700) rescue Errno::EEXIST log.debug "#{ssh_dir} already exists" end # Create a temporary directory for the keys - tmpdir = Dir.mktmpdir('sap-ha-keys-') + tmpdir = Dir.mktmpdir("sap-ha-keys-") log.debug "Created tmp directory #{tmpdir}" log.info "Retrieving SSH keys from node #{host}" begin @@ -168,8 +168,8 @@ def copy_keys_from(host, overwrite = false, password = "") end ::FileUtils.mv source_path, target_path keys_copied += 1 - source_pub_key = source_path + '.pub' - target_pub_key = target_path + '.pub' + source_pub_key = source_path + ".pub" + target_pub_key = target_path + ".pub" if File.exist? source_pub_key ::FileUtils.mv source_pub_key, target_pub_key, force: true authorize_key target_pub_key @@ -196,7 +196,7 @@ def run_rpc_server(host) def rpc_server_running?(host) log.debug "--- #{self.class}.#{__callee__} --- " stat = exec_status("ssh", "-o", "StrictHostKeyChecking=no", "root@#{host}", - 'ps aux | grep [r]pc_server.rb') + "ps aux | grep [r]pc_server.rb") stat.exitstatus == 0 end @@ -212,18 +212,18 @@ def run_command(host, *cmd) exec_status("ssh", "-o", "StrictHostKeyChecking=no", "-f", "root@#{host}", *cmd) end - private + private def authorize_own_keys @user_pubkeys.each { |p| authorize_key p } end def authorize_key(path) - auth_keys_path = File.join(@ssh_user_dir, 'authorized_keys') + auth_keys_path = File.join(@ssh_user_dir, "authorized_keys") if exec_status("grep", "-q", "-s", path, auth_keys_path.to_s).exitstatus != 0 log.info "Adding key #{path} to #{auth_keys_path}" key = File.read(path) - File.open(auth_keys_path, mode: 'a') do |fh| + File.open(auth_keys_path, mode: "a") do |fh| fh << "\n" fh << key end diff --git a/src/lib/sap_ha/system/watchdog.rb b/src/lib/sap_ha/system/watchdog.rb index e7e4bf3..5894575 100644 --- a/src/lib/sap_ha/system/watchdog.rb +++ b/src/lib/sap_ha/system/watchdog.rb @@ -19,16 +19,16 @@ # Summary: SUSE High Availability Setup for SAP Products: System watchdog configuration # Authors: Ilya Manyugin -require 'yast' -require 'open3' -require_relative 'shell_commands' -require 'sap_ha/node_logger' +require "yast" +require "open3" +require_relative "shell_commands" +require "sap_ha/node_logger" -Yast.import 'Kernel' +Yast.import "Kernel" module SapHA module System - class WatchdogException < Exception + class WatchdogException < RuntimeError end # System watchdog configuration @@ -112,7 +112,7 @@ def list_watchdogs log.error "Could not find the kernel modules source directory #{MODULES_PATH}" return [] end - Dir.glob(MODULES_PATH + '/*.ko*').map { |path| File.basename(path).gsub(/\.ko[\.\S+]*$/,'') } + Dir.glob(MODULES_PATH + "/*.ko*").map { |path| File.basename(path).gsub(/\.ko[\.]*[\S]*$/, "") } end # Look into the /etc/modules-load.d and list all of the modules @@ -123,14 +123,14 @@ def modules_to_load end def load(module_name) - out, rc = exec_outerr_status('/usr/sbin/modprobe', module_name) + out, rc = exec_outerr_status("/usr/sbin/modprobe", module_name) NodeLogger.log_status(rc.exitstatus == 0, "Loaded watchdog module #{module_name}", "Could not load module #{module_name}. modprobe returned rc=#{rc.exitstatus}", out) end - private + private def lsmod Open3.popen3("/usr/sbin/lsmod") do |_, stdout, _, _| diff --git a/src/lib/sap_ha/wizard/base_wizard_page.rb b/src/lib/sap_ha/wizard/base_wizard_page.rb index 91fabf7..f1fec45 100644 --- a/src/lib/sap_ha/wizard/base_wizard_page.rb +++ b/src/lib/sap_ha/wizard/base_wizard_page.rb @@ -19,18 +19,18 @@ # Summary: SUSE High Availability Setup for SAP Products: Base YaST Wizard page # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' -require 'sap_ha/exceptions' -require 'sap_ha/semantic_checks' +require "yast" +require "sap_ha/helpers" +require "sap_ha/exceptions" +require "sap_ha/semantic_checks" -Yast.import 'Wizard' +Yast.import "Wizard" module SapHA module Wizard # Base Wizard page class class BaseWizardPage - Yast.import 'UI' + Yast.import "UI" include Yast::Logger include Yast::I18n include Yast::UIShortcuts @@ -91,7 +91,7 @@ def run main_loop end - protected + protected # Run the main input processing loop # Ideally, this method should not be redefined (if we lived in a perfect world) @@ -118,7 +118,7 @@ def main_loop end end - private + private # Create a Wizard page with just a RichText widget on it # @param title [String] @@ -150,7 +150,7 @@ def base_list_selection(title, message, list_contents, help, allow_back, allow_n title, base_layout_with_label( message, - SelectionBox(Id(:selection_box), Opt(:vstretch, :notify), '', list_contents) + SelectionBox(Id(:selection_box), Opt(:vstretch, :notify), "", list_contents) ), help, allow_back, @@ -303,7 +303,7 @@ def base_popup_new(message, validator, handlers, *widgets) Yast::UI.CloseDialog return nil else - handlers[ui].call() if !handlers.nil? && handlers[ui] + handlers[ui].call if !handlers.nil? && handlers[ui] end end end @@ -312,13 +312,12 @@ def base_popup_new(message, validator, handlers, *widgets) # @param id_ [Symbol] widget's ID # @param label [String] combo's label # @param true_ [Boolean] 'true' option is selected - def base_true_false_combo(id_, label = '', true_ = true) + def base_true_false_combo(id_, label = "", true_ = true) ComboBox(Id(id_), Opt(:hstretch), label, [ - Item(Id(:true), 'true', true_), - Item(Id(:false), 'false', !true_) - ] - ) + Item(Id(:true), "true", true_), + Item(Id(:false), "false", !true_) + ]) end # Prompt the user for the password @@ -329,7 +328,7 @@ def password_prompt(message) Yast::UI.OpenDialog( VBox( Label(message), - MinWidth(15, Password(Id(:password), 'Password:', '')), + MinWidth(15, Password(Id(:password), "Password:", "")), Yast::Wizard.CancelOKButtonBox ) ) diff --git a/src/lib/sap_ha/wizard/cluster_nodes_page.rb b/src/lib/sap_ha/wizard/cluster_nodes_page.rb index 2536be9..c1d6927 100644 --- a/src/lib/sap_ha/wizard/cluster_nodes_page.rb +++ b/src/lib/sap_ha/wizard/cluster_nodes_page.rb @@ -20,10 +20,10 @@ # Authors: Ilya Manyugin # Authors: Peter Varkoly -require 'yast' +require "yast" require "yast/i18n" -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" module SapHA module Wizard @@ -31,7 +31,7 @@ module Wizard class ClusterNodesConfigurationPage < BaseWizardPage def initialize(model) super(model) - textdomain "hana-ha" + textdomain "hana-ha" @my_model = @model.cluster @page_validator = @my_model.method(:validate_nodes) @show_errors = true @@ -40,26 +40,26 @@ def initialize(model) def set_contents super Yast::Wizard.SetContents( - _('Cluster nodes'), + _("Cluster nodes"), base_layout_with_label( _('Define cluster nodes\' configuration'), VBox( MinHeight(4, nodes_table), HBox( - PushButton(Id(:add_node), _('Add node')), - PushButton(Id(:edit_node), _('Edit selected')), - PushButton(Id(:delete_node), _('Delete node')) + PushButton(Id(:add_node), _("Add node")), + PushButton(Id(:edit_node), _("Edit selected")), + PushButton(Id(:delete_node), _("Delete node")) ), HBox( two_widget_hbox( - InputField(Id(:expected_votes), Opt(:hstretch), _('Expected votes:'), ''), - VBox(Label(' '), Left(CheckBox(Id(:append_hosts), Opt(:stretch, :notify), - _('Append to /etc/hosts')))) + InputField(Id(:expected_votes), Opt(:hstretch), _("Expected votes:"), ""), + VBox(Label(" "), Left(CheckBox(Id(:append_hosts), Opt(:stretch, :notify), + _("Append to /etc/hosts"), @my_model.append_hosts))) ) ) ) ), - Helpers.load_help('cluster_nodes'), + Helpers.load_help("cluster_nodes"), true, true ) @@ -68,7 +68,7 @@ def set_contents def can_go_next? return true if @model.no_validators return false unless @my_model.configured? - Yast::Popup.Feedback('Please wait', 'Checking SSH connection') do + Yast::Popup.Feedback("Please wait", "Checking SSH connection") do unless check_ssh_connectivity @show_errors = false return false @@ -111,9 +111,9 @@ def update_model def nodes_table_header if @my_model.number_of_rings == 1 - Header(_('ID'), _('Host name'), _('IP in ring 1')) + Header(_("ID"), _("Host name"), _("IP in ring 1")) else - Header(_('ID'), _('Host name'), _('IP in ring 1'), _('IP in ring 2')) + Header(_("ID"), _("Host name"), _("IP in ring 1"), _("IP in ring 2")) end end @@ -124,7 +124,7 @@ def handle_user_input(input, event) edit_node when :node_definition_table update_model - edit_node if event['EventReason'] == 'Activated' + edit_node if event["EventReason"] == "Activated" when :append_hosts @my_model.append_hosts = value(:append_hosts) else @@ -146,10 +146,10 @@ def node_configuration_popup(values) base_popup( "Configuration for node #{values[:node_id]}", @my_model.method(:node_validator), - MinWidth(15, InputField(Id(:host_name), 'Host name:', values[:host_name] || "")), - MinWidth(15, InputField(Id(:ip_ring1), 'IP in ring #1:', values[:ip_ring1] || "")), + MinWidth(15, InputField(Id(:host_name), "Host name:", values[:host_name] || "")), + MinWidth(15, InputField(Id(:ip_ring1), "IP in ring #1:", values[:ip_ring1] || "")), @my_model.number_of_rings == 2 ? MinWidth(15, InputField(Id(:ip_ring2), - 'IP in ring #2:', values[:ip_ring2] || "")) : Empty(), + "IP in ring #2:", values[:ip_ring2] || "")) : Empty(), # InputField(Id(:node_id), 'Node ID:', values[:node_id] || "") ) end @@ -168,21 +168,21 @@ def check_ssh_connectivity SapHA::System::SSH.instance.check_ssh_password(ip, password) rescue SSHAuthException => e # Yast::Popup.Error(e.message) - show_message(e.message, 'Error') + show_message(e.message, "Error") return false rescue SSHException => e # Yast::Popup.Error(e.message) - show_message(e.message, 'Error') + show_message(e.message, "Error") return false else @my_model.set_host_password(ip, password) - Yast::Popup.Feedback('Please wait', 'Copying SSH keys') do + Yast::Popup.Feedback("Please wait", "Copying SSH keys") do SapHA::System::SSH.instance.copy_keys_to(ip, password) end end rescue SSHException => e # Yast::Popup.Error(e.message) - show_message(e.message, 'Error') + show_message(e.message, "Error") return false end true diff --git a/src/lib/sap_ha/wizard/comm_layer_page.rb b/src/lib/sap_ha/wizard/comm_layer_page.rb index 028ee23..1aa0ff8 100644 --- a/src/lib/sap_ha/wizard/comm_layer_page.rb +++ b/src/lib/sap_ha/wizard/comm_layer_page.rb @@ -20,10 +20,10 @@ # Authors: Ilya Manyugin # Authors: Peter Varkoly -require 'yast' +require "yast" require "yast/i18n" -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" module SapHA module Wizard @@ -33,7 +33,7 @@ class CommLayerConfigurationPage < BaseWizardPage # putting X.X.0.0 as the bind IP address def initialize(model) super(model) - textdomain "hana-ha" + textdomain "hana-ha" @my_model = model.cluster @page_validator = @my_model.method(:validate_comm_layer) @recreate_table = true @@ -42,30 +42,30 @@ def initialize(model) def set_contents super Yast::Wizard.SetContents( - _('Communication Layer'), + _("Communication Layer"), base_layout_with_label( - 'Define communication layer\'s configuration', + "Define communication layer's configuration", VBox( VBox( two_widget_hbox( - ComboBox(Id(:transport_mode), Opt(:notify, :hstretch), 'Transport mode:', - ['Unicast', 'Multicast']), + ComboBox(Id(:transport_mode), Opt(:notify, :hstretch), "Transport mode:", + ["Unicast", "Multicast"]), ComboBox(Id(:number_of_rings), Opt(:notify, :hstretch), - 'Number of rings:', ['1', '2']) + "Number of rings:", ["1", "2"]) ), - InputField(Id(:cluster_name), Opt(:hstretch), _('C&luster name:'), ''), + InputField(Id(:cluster_name), Opt(:hstretch), _("C&luster name:"), ""), VBox( MinHeight(4, ReplacePoint(Id(:rp_table), Empty())), - PushButton(Id(:edit_ring), _('Edit selected')) + PushButton(Id(:edit_ring), _("Edit selected")) ) ), - CheckBox(Id(:enable_csync2), Opt(:hstretch), 'Enable c&sync2', false), + ComboBox(Id(:fw_config), Opt(:notify, :hstretch), "Firewall configuration", fw_config_items), + CheckBox(Id(:enable_csync2), Opt(:hstretch), "Enable c&sync2", false), CheckBox(Id(:enable_secauth), Opt(:hstretch), - 'Enable &corosync secure authentication', false) - # PushButton(Id(:join_cluster), 'Join existing cluster'), + "Enable &corosync secure authentication", false) ) ), - Helpers.load_help('comm_layer'), true, true + Helpers.load_help("comm_layer"), true, true ) end @@ -92,6 +92,7 @@ def update_model @my_model.cluster_name = value(:cluster_name) @my_model.enable_secauth = value(:enable_secauth) @my_model.enable_csync2 = value(:enable_csync2) + @my_model.fw_config = value(:fw_config) end def ring_table_widget @@ -100,8 +101,8 @@ def ring_table_widget Id(:ring_definition_table), Opt(:keepSorting, :notify, :immediate, :hstretch), multicast? ? - Header(_('Ring'), _('Address'), _('Port'), _('Multicast Address')) - : Header(_('Ring'), _('Address'), _('Port')), + Header(_("Ring"), _("Address"), _("Port"), _("Multicast Address")) + : Header(_("Ring"), _("Address"), _("Port")), [] ) end @@ -111,26 +112,21 @@ def multicast? end def handle_user_input(input, event) + update_model case input when :edit_ring - update_model edit_ring when :ring_definition_table - update_model - edit_ring if event['EventReason'] == 'Activated' + edit_ring if event["EventReason"] == "Activated" when :number_of_rings - update_model number = Integer(value(:number_of_rings)) @my_model.number_of_rings = number @recreate_table = true refresh_view when :transport_mode - update_model @my_model.transport_mode = value(:transport_mode).downcase.to_sym @recreate_table = true refresh_view - when :join_cluster - return :join_cluster else super end @@ -151,14 +147,20 @@ def ring_configuration_popup(ring) base_popup( "Configuration for ring #{ring[:id]}", @my_model.method(:ring_validator), - MinWidth(15, ComboBox(Id(:address), 'IP address:', + MinWidth(15, ComboBox(Id(:address), "IP address:", [ring[:address]] | @my_model.ring_addresses)), - MinWidth(5, InputField(Id(:port), 'Port number:', ring[:port].to_s)), + MinWidth(5, InputField(Id(:port), "Port number:", ring[:port].to_s)), multicast? ? - MinWidth(15, InputField(Id(:mcast), 'Multicast address', ring[:mcast])) + MinWidth(15, InputField(Id(:mcast), "Multicast address", ring[:mcast])) : Empty() ) end + + def fw_config_items + [Item(Id("done"), _("Firewall is configured"), @my_model.fw_config == "done"), + Item(Id("off"), _("Turn off Firewall"), @my_model.fw_config == "off"), + Item(Id("setup"), _("Configure Firewall"), @my_model.fw_config == "setup")] + end end end end diff --git a/src/lib/sap_ha/wizard/fencing_page.rb b/src/lib/sap_ha/wizard/fencing_page.rb index e9da66f..f84ccbe 100644 --- a/src/lib/sap_ha/wizard/fencing_page.rb +++ b/src/lib/sap_ha/wizard/fencing_page.rb @@ -20,10 +20,10 @@ # Authors: Ilya Manyugin # Authors: Peter Varkoly -require 'yast' +require "yast" require "yast/i18n" -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" module SapHA module Wizard @@ -31,7 +31,7 @@ module Wizard class FencingConfigurationPage < BaseWizardPage def initialize(model) super(model) - textdomain "hana-ha" + textdomain "hana-ha" @my_model = model.fencing @page_validator = @my_model.method(:validate) end @@ -39,34 +39,33 @@ def initialize(model) def set_contents super Yast::Wizard.SetContents( - _('Fencing Mechanism'), + _("Fencing Mechanism"), base_layout_with_label( - 'Choose STONITH method', + "Choose STONITH method", VBox( - ComboBox(Id(:stonith_method), Opt(:hstretch), 'STONITH method:', ['SBD', 'IPMI']), + ComboBox(Id(:stonith_method), Opt(:hstretch), "STONITH method:", ["SBD", "IPMI"]), HBox( MinHeight(5, Table( Id(:sbd_dev_list_table), Opt(:keepSorting, :immediate), - Header(_('#'), _('Device path')), + Header(_("#"), _("Device path")), @model.fencing.table_items - ) - ) + )) ), HBox( - PushButton(Id(:add_sbd_device), _('Add')), - PushButton(Id(:remove_sbd_device), _('Remove')) + PushButton(Id(:add_sbd_device), _("Add")), + PushButton(Id(:remove_sbd_device), _("Remove")) ), VSpacing(1), - InputField(Id(:sbd_options), Opt(:hstretch), 'SBD options:', ''), + InputField(Id(:sbd_options), Opt(:hstretch), "SBD options:", ""), VSpacing(1), - CheckBox(Id(:sbd_delayed_start), Opt(:hstretch), 'Delay SBD start'), + CheckBox(Id(:sbd_delayed_start), Opt(:hstretch), "Delay SBD start"), VSpacing(1), Label(_("Note that all data on the selected devices will be destroyed.")) ) ), - Helpers.load_help('fencing'), + Helpers.load_help("fencing"), true, true ) @@ -82,12 +81,12 @@ def refresh_view set_value(:stonith_method, false, :Enabled) set_value(:sbd_dev_list_table, @my_model.table_items, :Items) set_value(:sbd_options, @my_model.sbd_options) - set_value(:sbd_delayed_start, @my_model.sbd_delayed_start == 'yes' ? true : false) + set_value(:sbd_delayed_start, @my_model.sbd_delayed_start == "yes" ? true : false) end def update_model @my_model.sbd_options = value(:sbd_options) - @my_model.sbd_delayed_start = value(:sbd_delayed_start) ? 'yes' : 'no' + @my_model.sbd_delayed_start = value(:sbd_delayed_start) ? "yes" : "no" end def handle_user_input(input, event) @@ -109,13 +108,13 @@ def sbd_dev_configuration items = @model.fencing.combo_items Yast::UI.OpenDialog( VBox( - Label(Opt(:boldFont), 'SBD Device Configuration'), - ComboBox(Id(:sbd_combo), Opt(:notify, :hstretch), 'Type:', items), - MinSize(55, 11, - SelectionBox(Id(:sbd_ids), Opt(:notify, :immediate), 'Identifiers:', [])), - TextEntry(Id(:dev_path), Opt(:hstretch), 'Device path:', ''), + Label(Opt(:boldFont), "SBD Device Configuration"), + ComboBox(Id(:sbd_combo), Opt(:notify, :hstretch), "Type:", items), + MinSize(55, 11, + SelectionBox(Id(:sbd_ids), Opt(:notify, :immediate), "Identifiers:", [])), + TextEntry(Id(:dev_path), Opt(:hstretch), "Device path:", ""), Yast::Wizard.CancelOKButtonBox - ) + ) ) handle_combo loop do @@ -145,7 +144,7 @@ def sbd_dev_configuration end end - private + private def handle_combo dev_type = value(:sbd_combo) diff --git a/src/lib/sap_ha/wizard/gui_installation_page.rb b/src/lib/sap_ha/wizard/gui_installation_page.rb index bb35515..e6a8982 100644 --- a/src/lib/sap_ha/wizard/gui_installation_page.rb +++ b/src/lib/sap_ha/wizard/gui_installation_page.rb @@ -19,14 +19,14 @@ # Summary: SUSE High Availability Setup for SAP Products: GUI Installation Page # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' -require 'sap_ha/sap_ha_installation' -require 'sap_ha/sap_ha_unattended_install' +require "yast" +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" +require "sap_ha/sap_ha_installation" +require "sap_ha/sap_ha_unattended_install" -Yast.import 'UI' -Yast.import 'Progress' +Yast.import "UI" +Yast.import "Progress" module SapHA module Wizard @@ -39,12 +39,13 @@ def set(nodes, titles, tasks) @task_no = 0 @tasks = tasks Yast::Progress.New( - 'SAP High-Availability Setup', - '', + "SAP High-Availability Setup", + "", titles.length, nodes, titles, - '') + "" + ) Yast::Progress.SubprogressType(:progress, @tasks.length) Yast::Progress.SubprogressTitle("") end diff --git a/src/lib/sap_ha/wizard/hana_page.rb b/src/lib/sap_ha/wizard/hana_page.rb index 912cb9e..eed77fb 100644 --- a/src/lib/sap_ha/wizard/hana_page.rb +++ b/src/lib/sap_ha/wizard/hana_page.rb @@ -20,10 +20,10 @@ # Authors: Ilya Manyugin # Authors: Peter Varkoly -require 'yast' +require "yast" require "yast/i18n" -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" module SapHA module Wizard @@ -31,28 +31,30 @@ module Wizard class HANAConfigurationPage < BaseWizardPage def initialize(model) super(model) - textdomain "hana-ha" + textdomain "hana-ha" @my_model = model.hana @my_config = model @page_validator = @my_model.method(:validate) + eval_hana_installation prepare_contents end def set_contents super - help_file = @my_model.additional_instance ? 'hana_cost_optimized' : 'hana' + help_file = @my_model.additional_instance ? "hana_cost_optimized" : "hana" Yast::Wizard.SetContents( - _('HANA Configuration'), + _("HANA Configuration"), base_layout_with_label( - 'Set HANA-specific parameters', + "Set HANA-specific parameters", @contents ), - #Helpers.load_help(help_file, @my_config.platform), + # Helpers.load_help(help_file, @my_config.platform), # The HANA Page instructions are generic for all the platforms. Helpers.load_help(help_file), true, true ) + refresh_view end def update_model @@ -114,19 +116,10 @@ def handle_user_input(input, event) when :production_constraints update_model production_constraints = hana_production_constraints_popup( - @my_model.production_constraints) + @my_model.production_constraints + ) return unless production_constraints @my_model.production_constraints = production_constraints - when :hook_script_params - update_model - unless @my_model.hook_generated? - hook_parameters = hook_script_popup(@my_model.hook_script_parameters) - return unless hook_parameters - @my_model.hook_script_parameters = hook_parameters - end - ret = generate_and_show_hook - return unless ret - @my_model.hook_script = ret[:hook_script] else super end @@ -137,9 +130,9 @@ def hana_backup_popup base_popup_new( "Initial HANA Backup Settings", @my_model.method(:hana_backup_validator), - {create_key: method(:secure_store_key_popup)}, - InputField(Id(:backup_file), 'Backup file name:', @my_model.backup_file), - InputField(Id(:backup_user), 'Secure store key:', @my_model.backup_user) + { create_key: method(:secure_store_key_popup) }, + InputField(Id(:backup_file), "Backup file name:", @my_model.backup_file), + InputField(Id(:backup_user), "Secure store key:", @my_model.backup_user) ) end @@ -147,38 +140,12 @@ def hana_backup_popup def hana_production_constraints_popup(values) log.debug "--- #{self.class}.#{__callee__} --- " base_popup( - 'Production system constraints', + "Production system constraints", @my_model.method(:production_constraints_validation), - MinWidth(20, InputField(Id(:global_alloc_limit), 'Global &allocation limit (in MB):', - values[:global_alloc_limit] || '')), - MinWidth(20, InputField(Id(:preload_column_tables), '&Preload column tables:', - values[:preload_column_tables] || '')) - ) - end - - def hook_script_popup(values) - log.debug "--- #{self.class}.#{__callee__} --- " - base_popup( - 'Hook script parameters', - # TODO: write validators for the popups - @my_model.method(:hook_script_validation), - MinWidth(15, InputField(Id(:hook_execution_order), '&Execution order:', - values[:hook_execution_order] || '')), - InputField(Id(:hook_db_user_name), Opt(:hstretch), 'DB &user name:', - values[:hook_db_user_name] || ''), - Password(Id(:hook_db_password), Opt(:hstretch), 'DB &password:', - values[:hook_db_password] || ''), - InputField(Id(:hook_port_number), Opt(:hstretch), '&Port number:', - values[:hook_port_number] || '') - ) - end - - def generate_and_show_hook - txt = @my_model.hook_script - base_popup( - 'Please review the script', - nil, - MinSize(75, 20, MultiLineEdit(Id(:hook_script), 'Hook script (Python):', txt)) + MinWidth(20, InputField(Id(:global_alloc_limit), "Global &allocation limit (in MB):", + values[:global_alloc_limit] || "")), + MinWidth(20, InputField(Id(:preload_column_tables), "&Preload column tables:", + values[:preload_column_tables] || "")) ) end @@ -186,53 +153,49 @@ def prepare_contents # Production HANA @contents = VBox( two_widget_hbox( - InputField(Id(:hana_sid), Opt(:hstretch), 'System ID:', ''), - InputField(Id(:hana_inst), Opt(:hstretch), 'Instance number:', '') + InputField(Id(:hana_sid), Opt(:hstretch), "System ID:", @my_model.system_id), + InputField(Id(:hana_inst), Opt(:hstretch), "Instance number:", @my_model.instance) ), two_widget_hbox( ComboBox(Id(:hana_replication_mode), Opt(:hstretch, :notify), - 'Replication mode:', @my_model.class::HANA_REPLICATION_MODES), + "Replication mode:", @my_model.class::HANA_REPLICATION_MODES), ComboBox(Id(:hana_operation_mode), Opt(:hstretch, :notify), - 'Operation mode:', @my_model.class::HANA_OPERATION_MODES) + "Operation mode:", @my_model.class::HANA_OPERATION_MODES) ), two_widget_hbox( - InputField(Id(:hana_vip), Opt(:hstretch), 'Virtual IP address:', ''), - InputField(Id(:hana_vip_mask), Opt(:hstretch), 'Virtual IP mask:', '') + InputField(Id(:hana_vip), Opt(:hstretch), "Virtual IP address:", ""), + InputField(Id(:hana_vip_mask), Opt(:hstretch), "Virtual IP mask:", "") ), two_widget_hbox( - base_true_false_combo(:site_takover, 'Prefer site takeover:'), - base_true_false_combo(:auto_reg, 'Automatic registration:') + base_true_false_combo(:site_takover, "Prefer site takeover:"), + base_true_false_combo(:auto_reg, "Automatic registration:") ), two_widget_hbox( - InputField(Id(:site_name_1), Opt(:hstretch), 'Site name 1', ''), - InputField(Id(:site_name_2), Opt(:hstretch), 'Site name 2', '') + InputField(Id(:site_name_1), Opt(:hstretch), "Site name 1", ""), + InputField(Id(:site_name_2), Opt(:hstretch), "Site name 2", "") ), two_widget_hbox( - @my_model.additional_instance ? + @my_model.additional_instance ? PushButton(Id(:production_constraints), Opt(:hstretch), - 'Production system constraints...') : CheckBox(Id(:create_backup), - Opt(:hstretch, :notify), 'Create initial backup'), - PushButton(Id(:configure_backup), Opt(:hstretch), 'Backup settings...') + "Production system constraints...") : CheckBox(Id(:create_backup), + Opt(:hstretch, :notify), "Create initial backup"), + PushButton(Id(:configure_backup), Opt(:hstretch), "Backup settings...") ) ) # Non-Production HANA if @my_model.additional_instance @contents << two_widget_hbox( Empty(), - CheckBox(Id(:create_backup), Opt(:hstretch, :notify), 'Create initial backup'), + CheckBox(Id(:create_backup), Opt(:hstretch, :notify), "Create initial backup"), 2.49 ) @contents = VBox( - Frame('Production instance', Yast.deep_copy(@contents)), - Frame('Non-production instance', + Frame("Production instance", Yast.deep_copy(@contents)), + Frame("Non-production instance", VBox( two_widget_hbox( - InputField(Id(:np_hana_sid), Opt(:hstretch), 'System ID:', ''), - InputField(Id(:np_hana_inst), Opt(:hstretch), 'Instance number:', '') - ), - two_widget_hbox( - Empty(), - PushButton(Id(:hook_script_params), Opt(:hstretch), 'Hook script...') + InputField(Id(:np_hana_sid), Opt(:hstretch), "System ID:", ""), + InputField(Id(:np_hana_inst), Opt(:hstretch), "Instance number:", "") ) ) ) @@ -243,9 +206,29 @@ def prepare_contents def secure_store_key_popup log.debug "--- #{self.class}.#{__callee__} --- " base_popup( - 'Create a secure store key', + "Create a secure store key", nil, - InputField(Id(:hook_db_user_name), Opt(:hstretch), 'DB &user name:', '')) + InputField(Id(:hook_db_user_name), Opt(:hstretch), "DB &user name:", "") + ) + end + + def eval_hana_installation + return if @my_model.system_id != "" + # Read hana installations created by the sap-installation wizard. + # In case of successfully installation this file(s) will be created. + # By changing the format of these summaray files the pattern should be adaptad also. + Dir.glob("/data/SAP_INST/*/installationSuccesfullyFinished.dat") do |f| + content = File.read(f) + result = content.scan(/SAP HANA System ID:\s+(\w{3}).*SAP HANA Instance:\s+(\w{2})/m) + log.info "HANAConfigurationPage found hana #{result} len: #{result.length} 0: #{result[0]}" + if result.length == 1 + @my_model.system_id = result[0][0].upcase + @my_model.instance = result[0][1] + @my_model.perform_backup = !File.exist?("/usr/sap/#{@my_model.system_id}/HDB#{@my_model.instance}/backup/") + log.info "HANAConfigurationPage hana default #{@my_model.perform_backup} #{@my_model.system_id} #{@my_model.instance}" + break + end + end end end end diff --git a/src/lib/sap_ha/wizard/join_cluster_page.rb b/src/lib/sap_ha/wizard/join_cluster_page.rb index e2d06ee..662f780 100644 --- a/src/lib/sap_ha/wizard/join_cluster_page.rb +++ b/src/lib/sap_ha/wizard/join_cluster_page.rb @@ -21,34 +21,33 @@ # Authors: Peter Varkoly require "yast/i18n" -require 'sap_ha/wizard/base_wizard_page' -require 'sap_ha/system/ssh' -require 'sap_ha/system/local' -require 'sap_ha/system/network' +require "sap_ha/wizard/base_wizard_page" +require "sap_ha/system/ssh" +require "sap_ha/system/local" +require "sap_ha/system/network" module SapHA module Wizard # Page for joining an existing cluster class JoinClusterPage < BaseWizardPage - def initialize - textdomain "hana-ha" - end + textdomain "hana-ha" + end def set_contents super Yast::Wizard.SetContents( - _('Join an Existing Cluster'), + _("Join an Existing Cluster"), base_layout_with_label( _("Please provide the IP address of the existing cluster"), VBox( - InputField(Id(:ip_address), 'IP Address', ''), - ComboBox(Id(:interface), 'Local Network Interface', + InputField(Id(:ip_address), "IP Address", ""), + ComboBox(Id(:interface), "Local Network Interface", SapHA::System::Network.interfaces), - PushButton(Id(:join), 'Join Cluster') + PushButton(Id(:join), "Join Cluster") ) ), - SapHA::Helpers.load_help('join_cluster'), + SapHA::Helpers.load_help("join_cluster"), true, true ) diff --git a/src/lib/sap_ha/wizard/list_selection.rb b/src/lib/sap_ha/wizard/list_selection.rb index 362e6ab..8198906 100644 --- a/src/lib/sap_ha/wizard/list_selection.rb +++ b/src/lib/sap_ha/wizard/list_selection.rb @@ -19,9 +19,9 @@ # Summary: SUSE High Availability Setup for SAP Products: Base List Selection view # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' -require 'sap_ha/exceptions' +require "yast" +require "sap_ha/helpers" +require "sap_ha/exceptions" module SapHA module Wizard @@ -36,11 +36,11 @@ def run(title, message, list_contents, help, allow_back, allow_next) ret = Yast::UI.WaitForEvent return :next unless ret # allow for debugging # Allow for double-clicking the item in the list - while ret['ID'] == :selection_box - return :next if ret['EventReason'] == 'Activated' + while ret["ID"] == :selection_box + return :next if ret["EventReason"] == "Activated" ret = Yast::UI.WaitForEvent end - ret['ID'] + ret["ID"] end end end diff --git a/src/lib/sap_ha/wizard/ntp_page.rb b/src/lib/sap_ha/wizard/ntp_page.rb index 04d684f..e641714 100644 --- a/src/lib/sap_ha/wizard/ntp_page.rb +++ b/src/lib/sap_ha/wizard/ntp_page.rb @@ -20,10 +20,10 @@ # Authors: Ilya Manyugin # Authors: Peter Varkoly -require 'yast' +require "yast" require "yast/i18n" -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" module SapHA module Wizard @@ -31,7 +31,7 @@ module Wizard class NTPConfigurationPage < BaseWizardPage def initialize(model) super(model) - textdomain "hana-ha" + textdomain "hana-ha" @my_model = model.ntp @page_validator = @my_model.method(:validate) end @@ -39,25 +39,24 @@ def initialize(model) def set_contents super Yast::Wizard.SetContents( - _('NTP Configuration'), + _("NTP Configuration"), base_layout_with_label( - 'Configure Network Time Protocol client', + "Configure Network Time Protocol client", VBox( - SelectionBox(Id(:ntp_servers), 'Used servers:', []), - PushButton(Id(:ntp_configure), 'Reconfigure'), + SelectionBox(Id(:ntp_servers), "Used servers:", []), + PushButton(Id(:ntp_configure), "Reconfigure"), HBox( - Label('Starts at boot:'), - Label(Id(:ntp_enabled), '') + Label("Starts at boot:"), + Label(Id(:ntp_enabled), "") ) ) ), - Helpers.load_help('ntp'), + Helpers.load_help("ntp"), true, true ) end - def can_go_next? return true if @model.no_validators return false unless @my_model.configured? @@ -67,12 +66,12 @@ def can_go_next? def handle_user_input(input, event) case input when :ntp_configure - if Yast::WFM.ClientExists('ntp-client') - Yast::WFM.CallFunction('ntp-client', []) + if Yast::WFM.ClientExists("ntp-client") + Yast::WFM.CallFunction("ntp-client", []) # NTP Client rewrites the heading Yast::Wizard.SetDialogTitle("HA Setup for SAP Products") else - Yast::Popup.Error('Could not find the yast-ntp-client module!') + Yast::Popup.Error("Could not find the yast-ntp-client module!") continue end @my_model.read_configuration diff --git a/src/lib/sap_ha/wizard/overview_page.rb b/src/lib/sap_ha/wizard/overview_page.rb index 2165286..ab9472a 100644 --- a/src/lib/sap_ha/wizard/overview_page.rb +++ b/src/lib/sap_ha/wizard/overview_page.rb @@ -19,8 +19,8 @@ # Summary: SUSE High Availability Setup for SAP Products: Configuration overview page # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' +require "yast" +require "sap_ha/helpers" module SapHA module Wizard @@ -36,11 +36,11 @@ def initialize(model) def set_contents super - tmpl = Yast::UI.TextMode ? 'tmpl_config_overview_con.erb' : 'tmpl_config_overview_gui.erb' + tmpl = Yast::UI.TextMode ? "tmpl_config_overview_con.erb" : "tmpl_config_overview_gui.erb" base_rich_text( "High-Availability Configuration Overview", Helpers.render_template(tmpl, binding), - Helpers.load_help('setup_summary'), + Helpers.load_help("setup_summary"), true, true ) @@ -62,7 +62,7 @@ def can_go_next? @config.can_install? end - protected + protected def main_loop log.debug "--- #{self.class}.#{__callee__} ---" diff --git a/src/lib/sap_ha/wizard/rich_text.rb b/src/lib/sap_ha/wizard/rich_text.rb index a3e4869..53e1018 100644 --- a/src/lib/sap_ha/wizard/rich_text.rb +++ b/src/lib/sap_ha/wizard/rich_text.rb @@ -19,9 +19,9 @@ # Summary: SUSE High Availability Setup for SAP Products: Base Rich Text view # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' -require 'sap_ha/exceptions' +require "yast" +require "sap_ha/helpers" +require "sap_ha/exceptions" module SapHA module Wizard diff --git a/src/lib/sap_ha/wizard/scenario_selection_page.rb b/src/lib/sap_ha/wizard/scenario_selection_page.rb index 3a7b855..7f57f4c 100644 --- a/src/lib/sap_ha/wizard/scenario_selection_page.rb +++ b/src/lib/sap_ha/wizard/scenario_selection_page.rb @@ -19,8 +19,8 @@ # Summary: SUSE High Availability Setup for SAP Products: Scenario Selection page # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' +require "yast" +require "sap_ha/helpers" module SapHA module Wizard @@ -63,7 +63,7 @@ def update_model :next end - protected + protected def previous_configs_popup(previous_configs) log.debug "--- #{self.class}.#{__callee__} ---" @@ -72,7 +72,8 @@ def previous_configs_popup(previous_configs) "Would you like to load a previous configuration?", nil, MinSize(55, 11, - SelectionBox(Id(:config_name), Opt(:vstretch, :notify), '', list_contents))) + SelectionBox(Id(:config_name), Opt(:vstretch, :notify), "", list_contents)) + ) log.debug "--- #{self.class}.#{__callee__}: ret from base_popup: #{ret.inspect} ---" @to_load = previous_configs.find { |e| e[0] == ret[:config_name] }[1] unless ret.nil? end @@ -86,10 +87,10 @@ def main_loop log.debug "--- #{self.class}.#{__callee__}: event=#{event} ---" return :next unless event # allow for debugging # Allow for double-clicking the item in the list - input = event['ID'] + input = event["ID"] case input when :selection_box - if event['EventReason'] == 'Activated' + if event["EventReason"] == "Activated" return update_model end when :next diff --git a/src/lib/sap_ha/wizard/summary_page.rb b/src/lib/sap_ha/wizard/summary_page.rb index 0447645..578c308 100644 --- a/src/lib/sap_ha/wizard/summary_page.rb +++ b/src/lib/sap_ha/wizard/summary_page.rb @@ -19,9 +19,9 @@ # Summary: SUSE High Availability Setup for SAP Products: Setup summary page # Authors: Ilya Manyugin -require 'yast' -require 'sap_ha/helpers' -require 'sap_ha/node_logger' +require "yast" +require "sap_ha/helpers" +require "sap_ha/node_logger" module SapHA module Wizard @@ -46,12 +46,12 @@ def set_contents HSpacing(3) ), HBox( - PushButton(Id(:save_config), 'Save configuration'), - PushButton(Id(:save_log), 'Save log'), - PushButton(Id(:open_hawk), 'Open HAWK2') + PushButton(Id(:save_config), "Save configuration"), + PushButton(Id(:save_log), "Save log"), + PushButton(Id(:open_hawk), "Open HAWK2") ) ), - '', + "", false, true ) @@ -62,11 +62,11 @@ def refresh_view Yast::Wizard.SetNextButton(:next, "&Finish") Yast::Wizard.EnableNextButton set_value(:open_hawk, false, :Enabled) if Yast::UI.TextMode - SapHA::Helpers.write_var_file('installation_log.html', SapHA::NodeLogger.html, + SapHA::Helpers.write_var_file("installation_log.html", SapHA::NodeLogger.html, timestamp: true) - SapHA::Helpers.write_var_file('installation_log.txt', SapHA::NodeLogger.text, + SapHA::Helpers.write_var_file("installation_log.txt", SapHA::NodeLogger.text, timestamp: true) - SapHA::Helpers.write_var_file('configuration.yml', @config.dump(false), + SapHA::Helpers.write_var_file("configuration.yml", @config.dump(false), timestamp: @config.timestamp) end @@ -80,28 +80,28 @@ def handle_user_input(input, _event) file_name = Yast::UI.AskForSaveFileName("/tmp", "*.txt *.log *.html", "Save log file as...") return unless file_name - log = if file_name.end_with?('html', 'htm') - SapHA::NodeLogger.html - else - SapHA::NodeLogger.text + log = if file_name.end_with?("html", "htm") + SapHA::NodeLogger.html + else + SapHA::NodeLogger.text end success = SapHA::Helpers.write_file(file_name, log) if success - show_message("Log was written to #{file_name}", 'Success') + show_message("Log was written to #{file_name}", "Success") else - show_message("Could not write log file #{file_name}", 'Error') + show_message("Could not write log file #{file_name}", "Error") end when :save_config file_name = Yast::UI.AskForSaveFileName("/tmp", "*.yml", "Save configuration file as...") return unless file_name success = SapHA::Helpers.write_file(file_name, @config.dump) if success - show_message("Configuration was written to #{file_name}", 'Success') + show_message("Configuration was written to #{file_name}", "Success") else - show_message("Could not write configuration file #{file_name}", 'Error') + show_message("Could not write configuration file #{file_name}", "Error") end when :open_hawk - SapHA::Helpers.open_url('https://localhost:7630/') + SapHA::Helpers.open_url("https://localhost:7630/") end end end diff --git a/src/lib/sap_ha/wizard/watchdog_page.rb b/src/lib/sap_ha/wizard/watchdog_page.rb index ccacedb..39aaed9 100644 --- a/src/lib/sap_ha/wizard/watchdog_page.rb +++ b/src/lib/sap_ha/wizard/watchdog_page.rb @@ -20,10 +20,10 @@ # Authors: Ilya Manyugin # Authors: Peter Varkoly -require 'yast' +require "yast" require "yast/i18n" -require 'sap_ha/helpers' -require 'sap_ha/wizard/base_wizard_page' +require "sap_ha/helpers" +require "sap_ha/wizard/base_wizard_page" module SapHA module Wizard @@ -31,7 +31,7 @@ module Wizard class WatchdogConfigurationPage < BaseWizardPage def initialize(model) super(model) - textdomain "hana-ha" + textdomain "hana-ha" @my_model = model.watchdog @page_validator = @my_model.method(:validate) end @@ -39,22 +39,22 @@ def initialize(model) def set_contents super Yast::Wizard.SetContents( - _('Watchdog Setup'), + _("Watchdog Setup"), base_layout_with_label( - 'Select appropriate watchdog modules to load at system startup', + "Select appropriate watchdog modules to load at system startup", VBox( SelectionBox(Id(:wd_to_configure), Opt(:notify, :immediate), - 'Watchdogs to configure:', []), + "Watchdogs to configure:", []), HBox( - PushButton(Id(:add_wd), 'Add'), - PushButton(Id(:remove_wd), 'Remove') + PushButton(Id(:add_wd), "Add"), + PushButton(Id(:remove_wd), "Remove") ), SelectionBox(Id(:configured_wd), Opt(:notify, :immediate), - 'Configured watchdogs:', []), - SelectionBox(Id(:loaded_wd), 'Loaded watchdogs:', []) + "Configured watchdogs:", []), + SelectionBox(Id(:loaded_wd), "Loaded watchdogs:", []) ) ), - Helpers.load_help('watchdog'), + Helpers.load_help("watchdog"), true, true ) @@ -98,7 +98,7 @@ def wd_selection_popup "Select a module to add", nil, MinHeight(10, - SelectionBox(Id(:selected), 'Available modules:', @my_model.proposals)) + SelectionBox(Id(:selected), "Available modules:", @my_model.proposals)) ) end end diff --git a/src/scrconf/sysconfig_sbd.scr b/src/scrconf/sysconfig_sbd.scr index 7a295c8..217a4f2 100644 --- a/src/scrconf/sysconfig_sbd.scr +++ b/src/scrconf/sysconfig_sbd.scr @@ -1,5 +1,5 @@ .sysconfig.sbd `ag_ini( - `SysConfigFile("/etc/sysconfig/sbd") - ) + `SysConfigFile("/etc/sysconfig/sbd") +) diff --git a/test/node_logger_spec.rb b/test/node_logger_spec.rb index 70fe3f9..6966699 100644 --- a/test/node_logger_spec.rb +++ b/test/node_logger_spec.rb @@ -18,26 +18,26 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative 'test_helper' -require 'sap_ha/node_logger' +require_relative "test_helper" +require "sap_ha/node_logger" describe SapHA::NodeLogger do subject { SapHA::NodeLogger } - let(:unknown_msg) { 'This is an unknown message' } - let(:debug_msg) { 'This is a debug message' } - let(:info_msg) { 'This is an info message' } - let(:warn_msg) { 'This is a warning message!' } - let(:error_msg) { 'This is an error message!!!' } - let(:fatal_msg) { 'This is a fatal message!!!1111' } + let(:unknown_msg) { "This is an unknown message" } + let(:debug_msg) { "This is a debug message" } + let(:info_msg) { "This is an info message" } + let(:warn_msg) { "This is a warning message!" } + let(:error_msg) { "This is an error message!!!" } + let(:fatal_msg) { "This is a fatal message!!!1111" } - describe '#instance' do - it 'works' do + describe "#instance" do + it "works" do expect(subject).not_to be_nil end end - describe '#text' do - it 'returns a plain-text log' do + describe "#text" do + it "returns a plain-text log" do subject.unknown(unknown_msg) subject.debug(debug_msg) subject.info(info_msg) @@ -54,16 +54,16 @@ end end - describe '#set_debug' do - it 'enables debug logging' do + describe "#set_debug" do + it "enables debug logging" do subject.set_debug subject.debug(debug_msg) expect(subject.text).to match(/DEBUG: #{debug_msg}/) end end - describe '#to_html' do - it 'converts a plain-text log into HTML representation' do + describe "#to_html" do + it "converts a plain-text log into HTML representation" do def colored_level(level_name) '\s+%s' % level_name diff --git a/test/semantic_checks_test.rb b/test/semantic_checks_test.rb index d691ec3..42f666e 100644 --- a/test/semantic_checks_test.rb +++ b/test/semantic_checks_test.rb @@ -18,174 +18,220 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative 'test_helper' -require 'sap_ha/semantic_checks' +require_relative "test_helper" +require "sap_ha/semantic_checks" describe SapHA::SemanticChecks do subject { SapHA::SemanticChecks.instance } - context 'SAP network configuration tests' do - describe '#ipv4' do + context "SAP network configuration tests" do + describe "#ipv4" do it "reports validness of an IP address in silent mode" do subject.silent = true - expect(subject.ipv4('100')).to eq false - expect(subject.ipv4('')).to eq false + expect(subject.ipv4("100")).to eq false + expect(subject.ipv4("")).to eq false expect(subject.ipv4(1000)).to eq false expect(subject.ipv4(nil)).to eq false - expect(subject.ipv4('192.168.100.100')).to eq true + expect(subject.ipv4("192.168.100.100")).to eq true end it "reports validness of an IP address in verbose mode" do subject.silent = false - expect(subject.ipv4('100')).to be_kind_of ::String - expect(subject.ipv4('')).to be_kind_of ::String + expect(subject.ipv4("100")).to be_kind_of ::String + expect(subject.ipv4("")).to be_kind_of ::String expect(subject.ipv4(1000)).to be_kind_of ::String expect(subject.ipv4(nil)).to be_kind_of ::String - expect(subject.ipv4('192.168.100.100')).to eq nil + expect(subject.ipv4("192.168.100.100")).to eq nil end it "works in a transaction" do flag = subject.silent_check do |check| - check.ipv4('100') - check.ipv4('192.168.100.100') + check.ipv4("100") + check.ipv4("192.168.100.100") end expect(flag).to eq false flag = subject.silent_check do |check| - check.ipv4('192.168.100.100') - check.ipv4('192.168.100.1') + check.ipv4("192.168.100.100") + check.ipv4("192.168.100.1") end expect(flag).to eq true errs = subject.verbose_check do |check| - check.ipv4('192.168.100.100') - check.ipv4('192.168.100.1') + check.ipv4("192.168.100.100") + check.ipv4("192.168.100.1") end expect(errs).to be_empty errs = subject.verbose_check do |check| - check.ipv4('10') - check.ipv4('102.100.100.100') + check.ipv4("10") + check.ipv4("102.100.100.100") end expect(errs).not_to be_empty end end - describe '#hostname' do + describe "#hostname" do it "reports validness of a hostname in silent mode" do subject.silent = true - expect(subject.hostname('100')).to eq true - expect(subject.hostname('suse')).to eq true - expect(subject.hostname('suse-01')).to eq true - expect(subject.hostname('suse-com')).to eq true - expect(subject.hostname('-suse')).to eq false - expect(subject.hostname('suse-')).to eq false - expect(subject.hostname('!suse')).to eq false + expect(subject.hostname("100")).to eq true + expect(subject.hostname("suse")).to eq true + expect(subject.hostname("suse-01")).to eq true + expect(subject.hostname("suse-com")).to eq true + expect(subject.hostname("-suse")).to eq false + expect(subject.hostname("suse-")).to eq false + expect(subject.hostname("!suse")).to eq false end it "reports validness of a hostname in silent mode" do subject.silent = false - expect(subject.hostname('100')).to eq nil - expect(subject.hostname('suse')).to eq nil - expect(subject.hostname('suse-01')).to eq nil - expect(subject.hostname('suse-com')).to eq nil - expect(subject.hostname('-suse')).to be_kind_of ::String - expect(subject.hostname('suse-')).to be_kind_of ::String - expect(subject.hostname('!suse')).to be_kind_of ::String + expect(subject.hostname("100")).to eq nil + expect(subject.hostname("suse")).to eq nil + expect(subject.hostname("suse-01")).to eq nil + expect(subject.hostname("suse-com")).to eq nil + expect(subject.hostname("-suse")).to be_kind_of ::String + expect(subject.hostname("suse-")).to be_kind_of ::String + expect(subject.hostname("!suse")).to be_kind_of ::String end it "works in a transaction" do errors = subject.verbose_check do |check| - check.hostname('suse-1') - check.hostname('suse-com') + check.hostname("suse-1") + check.hostname("suse-com") end expect(errors).to be_empty errors = subject.verbose_check do |check| - check.hostname('suse-1!') - check.hostname('suse-com') + check.hostname("suse-1!") + check.hostname("suse-com") end expect(errors).not_to be_empty flag = subject.silent_check do |check| - check.hostname('-suse') - check.hostname('suse-') + check.hostname("-suse") + check.hostname("suse-") end expect(flag).to eq false flag = subject.silent_check do |check| - check.hostname('suse') - check.hostname('suse-com') + check.hostname("suse") + check.hostname("suse-com") end expect(flag).to eq true end end - describe '#unique' do + describe "#unique" do it "reports uniqueness in silent mode" do subject.silent = true - expect(subject.unique(['100', '100', '100'])).to eq false - expect(subject.not_unique(['100', '100', '100'])).to eq true + expect(subject.unique(["100", "100", "100"])).to eq false + expect(subject.not_unique(["100", "100", "100"])).to eq true end end - describe '#ipv4_in_network_cidr' do - it 'reports if the given IPv4 belongs to the network' do + describe "#ipv4_in_network_cidr" do + it "reports if the given IPv4 belongs to the network" do subject.silent = true - expect(subject.ipv4_in_network_cidr('192.168.100.1', '192.168.100.0/24')).to eq true - expect(subject.ipv4_in_network_cidr('192.168.100.254', '192.168.100.0/24')).to eq true - expect(subject.ipv4_in_network_cidr('192.168.100.1', '192.168.100.0/28')).to eq true - expect(subject.ipv4_in_network_cidr('192.168.100.14', '192.168.100.0/28')).to eq true - expect(subject.ipv4_in_network_cidr('192.168.100.16', '192.168.100.0/28')).to eq false + expect(subject.ipv4_in_network_cidr("192.168.100.1", "192.168.100.0/24")).to eq true + expect(subject.ipv4_in_network_cidr("192.168.100.254", "192.168.100.0/24")).to eq true + expect(subject.ipv4_in_network_cidr("192.168.100.1", "192.168.100.0/28")).to eq true + expect(subject.ipv4_in_network_cidr("192.168.100.14", "192.168.100.0/28")).to eq true + expect(subject.ipv4_in_network_cidr("192.168.100.16", "192.168.100.0/28")).to eq false end end end - context 'SAP naming tests' do + context "SAP naming tests" do before do subject.silent = true end - describe "#identifier" do + describe "#identifier" do it "returns true for valid IDs" do - expect(subject.identifier('1Nuremberg')).to eq true - expect(subject.identifier('1Nuremberg-A')).to eq true - expect(subject.identifier('1Nuremberg_BA')).to eq true - expect(subject.identifier('SuSE-1_2')).to eq true + expect(subject.identifier("1Nuremberg")).to eq true + expect(subject.identifier("1Nuremberg-A")).to eq true + expect(subject.identifier("1Nuremberg_BA")).to eq true + expect(subject.identifier("SuSE-1_2")).to eq true end it "returns false for invalid IDs" do - expect(subject.identifier('Nürnberg')).to eq false - expect(subject.identifier('-Nuremberg')).to eq false - expect(subject.identifier('_Nuremberg')).to eq false - expect(subject.identifier('*WalDorf-1_2/')).to eq false + expect(subject.identifier("Nürnberg")).to eq false + expect(subject.identifier("-Nuremberg")).to eq false + expect(subject.identifier("_Nuremberg")).to eq false + expect(subject.identifier("*WalDorf-1_2/")).to eq false end end describe "#sap_sid" do it "returns true for valid IDs" do - expect(subject.sap_sid('X12')).to eq true + expect(subject.sap_sid("X12")).to eq true end it "returns false for invalid IDs" do - expect(subject.sap_sid('123')).to eq false - expect(subject.sap_sid('NOT')).to eq false + expect(subject.sap_sid("123")).to eq false + expect(subject.sap_sid("NOT")).to eq false end end describe "#sap_instance_number" do it "returns true for valid numbers" do - expect(subject.sap_instance_number('05')).to eq true - expect(subject.sap_instance_number('09')).to eq true - expect(subject.sap_instance_number('10')).to eq true - expect(subject.sap_instance_number('99')).to eq true - end + expect(subject.sap_instance_number("05")).to eq true + expect(subject.sap_instance_number("09")).to eq true + expect(subject.sap_instance_number("10")).to eq true + expect(subject.sap_instance_number("99")).to eq true + end it "returns false for invalid numbers" do - expect(subject.sap_instance_number('1')).to eq false - expect(subject.sap_instance_number('1A')).to eq false - expect(subject.sap_instance_number('999')).to eq false + expect(subject.sap_instance_number("1")).to eq false + expect(subject.sap_instance_number("1A")).to eq false + expect(subject.sap_instance_number("999")).to eq false end end + it "reports if valid instance number will be allowed" do + subject.silent = true + expect(subject.sap_instance_number("01")).to eq true + expect(subject.sap_instance_number("09")).to eq true + expect(subject.sap_instance_number("10")).to eq true + expect(subject.sap_instance_number("99")).to eq true + end + it "reports if invalid instance number will be found" do + subject.silent = true + expect(subject.sap_instance_number("1")).to eq false + expect(subject.sap_instance_number("1A")).to eq false + expect(subject.sap_instance_number("999")).to eq false + end + end + + describe 'SAP naming tests' do + it 'reports if valid SID and site names will be allowed' do + subject.silent = true + expect(subject.identifier('1Nuremberg')).to eq true + expect(subject.identifier('1Nuremberg-A')).to eq true + expect(subject.identifier('1Nuremberg_BA')).to eq true + expect(subject.identifier('SuSE-1_2')).to eq true + expect(subject.sap_sid('X12')).to eq true + end + it 'reports if invalid SID and site names will be found' do + subject.silent = true + expect(subject.identifier('Nürnberg')).to eq false + expect(subject.identifier('-Nuremberg')).to eq false + expect(subject.identifier('_Nuremberg')).to eq false + expect(subject.identifier('*WalDorf-1_2/')).to eq false + expect(subject.sap_sid('123')).to eq false + expect(subject.sap_sid('NOT')).to eq false + end + it 'reports if valid instance number will be allowed' do + subject.silent = true + expect(subject.sap_instance_number('05')).to eq true + expect(subject.sap_instance_number('09')).to eq true + expect(subject.sap_instance_number('10')).to eq true + expect(subject.sap_instance_number('99')).to eq true + end + it 'reports if invalid instance number will be found' do + subject.silent = true + expect(subject.sap_instance_number('1')).to eq false + expect(subject.sap_instance_number('1A')).to eq false + expect(subject.sap_instance_number('999')).to eq false + end end end diff --git a/test/src/lib/sap_ha/configuration/base_config_spec.rb b/test/src/lib/sap_ha/configuration/base_config_spec.rb index 165c91f..287fa79 100644 --- a/test/src/lib/sap_ha/configuration/base_config_spec.rb +++ b/test/src/lib/sap_ha/configuration/base_config_spec.rb @@ -19,9 +19,9 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../../test_helper' -require 'sap_ha/configuration/base_config' -require 'sap_ha/exceptions' +require_relative "../../../../test_helper" +require "sap_ha/configuration/base_config" +require "sap_ha/exceptions" class TestConfig < SapHA::Configuration::BaseConfig def initialize @@ -30,8 +30,8 @@ def initialize end describe SapHA::Configuration::BaseConfig do - describe '#new' do - it 'raises an exception, preventing instantiation' do + describe "#new" do + it "raises an exception, preventing instantiation" do expect { SapHA::Configuration::BaseConfig.new(nil) } .to raise_error SapHA::Exceptions::BaseConfigException expect(TestConfig.new).not_to be_nil @@ -39,50 +39,50 @@ def initialize end # TODO: test actual YAML coding - describe '#encode_with' do - it 'omits the specified attributesS' do + describe "#encode_with" do + it "omits the specified attributesS" do base_config = TestConfig.new - coder = double('coder') + coder = double("coder") allow(coder).to receive(:[]=) base_config.encode_with(coder) end end # TODO: test actual YAML restoration - describe '#init_with' do - it 'recreates the object' do + describe "#init_with" do + it "recreates the object" do base_config = TestConfig.new - coder = double('coder') - expect(coder).to receive(:[]).with('instance_variables') { [] }.at_most(2).times + coder = double("coder") + expect(coder).to receive(:[]).with("instance_variables") { [] }.at_most(2).times result = base_config.init_with(coder) expect(result).not_to be_nil end end - describe '#read_system' do - it 'reads local system configuration' do + describe "#read_system" do + it "reads local system configuration" do base_config = TestConfig.new expect { base_config.read_system }.to raise_error SapHA::Exceptions::BaseConfigException end end - describe '#configured?' do - it 'reports if the configuration is complete' do + describe "#configured?" do + it "reports if the configuration is complete" do base_config = TestConfig.new result = base_config.configured? expect(result).to eq false end end - describe '#description' do - it 'provides a description' do + describe "#description" do + it "provides a description" do base_config = TestConfig.new expect { base_config.description }.to raise_error SapHA::Exceptions::BaseConfigException end end - describe '#import' do - it 'imports the parameters from a hash to the instance' do + describe "#import" do + it "imports the parameters from a hash to the instance" do base_config = TestConfig.new hash = { something: 22 } base_config.import(hash) @@ -91,8 +91,8 @@ def initialize end # TODO: auto-generated - describe '#export' do - it 'exports instance variables to a hash' do + describe "#export" do + it "exports instance variables to a hash" do base_config = TestConfig.new hash = { something: 22 } base_config.import(hash) @@ -101,15 +101,15 @@ def initialize end end - describe '#apply' do - it 'raises an exception' do + describe "#apply" do + it "raises an exception" do base_config = TestConfig.new expect { base_config.apply(:master) }.to raise_error SapHA::Exceptions::BaseConfigException end end - describe '#validate' do - it 'raises an exception' do + describe "#validate" do + it "raises an exception" do base_config = TestConfig.new expect { base_config.validate }.to raise_error SapHA::Exceptions::BaseConfigException end diff --git a/test/src/lib/sap_ha/configuration_spec.rb b/test/src/lib/sap_ha/configuration_spec.rb index 3426d28..655bf44 100644 --- a/test/src/lib/sap_ha/configuration_spec.rb +++ b/test/src/lib/sap_ha/configuration_spec.rb @@ -19,47 +19,47 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../test_helper' -require 'sap_ha/configuration' -require 'sap_ha/exceptions' +require_relative "../../../test_helper" +require "sap_ha/configuration" +require "sap_ha/exceptions" describe SapHA::HAConfiguration do - describe '#new' do - it 'creates a valid instance' do + describe "#new" do + it "creates a valid instance" do result = SapHA::HAConfiguration.new expect(result).not_to be_nil end end - describe '#set_product_id' do - it 'sets the product ID correctly' do + describe "#set_product_id" do + it "sets the product ID correctly" do ha_configuration = SapHA::HAConfiguration.new - ha_configuration.set_product_id('HANA') + ha_configuration.set_product_id("HANA") # ha_configuration.set_product_id('NW') - expect { ha_configuration.set_product_id('Hana') } + expect { ha_configuration.set_product_id("Hana") } .to raise_error SapHA::Exceptions::ProductNotFoundException end end - describe '#set_scenario_name' do - it 'sets the scenario name correctly' do + describe "#set_scenario_name" do + it "sets the scenario name correctly" do ha_configuration = SapHA::HAConfiguration.new - expect { ha_configuration.set_scenario_name('Some scenario') } + expect { ha_configuration.set_scenario_name("Some scenario") } .to raise_error SapHA::Exceptions::ProductNotFoundException - ha_configuration.set_product_id('HANA') - ha_configuration.set_scenario_name('Scale Up: Performance-optimized') - expect { ha_configuration.set_scenario_name('Some scenario') } + ha_configuration.set_product_id("HANA") + ha_configuration.set_scenario_name("Scale Up: Performance-optimized") + expect { ha_configuration.set_scenario_name("Some scenario") } .to raise_error SapHA::Exceptions::ScenarioNotFoundException end - it 'applies the configuration sequence' do + it "applies the configuration sequence" do ha_configuration = SapHA::HAConfiguration.new - ha_configuration.set_product_id('HANA') + ha_configuration.set_product_id("HANA") for scenario_name in ha_configuration.all_scenarios - ha_configuration.set_scenario_name('Scale Up: Performance-optimized') + ha_configuration.set_scenario_name("Scale Up: Performance-optimized") # configuration sequence cs = ha_configuration.config_sequence - cluster = cs.find { |e| e[:screen_name] == 'Cluster Configuration' } + cluster = cs.find { |e| e[:screen_name] == "Cluster Configuration" } expect(cluster).not_to be_nil expect(cluster[:object]).to eq ha_configuration.cluster end @@ -68,65 +68,65 @@ end end - describe '#all_scenarios' do - it 'lists all scenarios defined for particular product' do + describe "#all_scenarios" do + it "lists all scenarios defined for particular product" do ha_configuration = SapHA::HAConfiguration.new expect { ha_configuration.all_scenarios } .to raise_error SapHA::Exceptions::ProductNotFoundException - ha_configuration.set_product_id('HANA') + ha_configuration.set_product_id("HANA") result = ha_configuration.all_scenarios expect(result.empty?).to eq false end end - describe '#scenarios_help' do - it 'generates an HTML help for all scenarios defined for the product' do + describe "#scenarios_help" do + it "generates an HTML help for all scenarios defined for the product" do ha_configuration = SapHA::HAConfiguration.new expect { ha_configuration.scenarios_help } .to raise_error SapHA::Exceptions::ProductNotFoundException - ha_configuration.set_product_id('HANA') + ha_configuration.set_product_id("HANA") result = ha_configuration.scenarios_help expect(result.empty?).to eq false end end - describe '#can_install?' do - it 'works for two-ring cluster config and unicast' do + describe "#can_install?" do + it "works for two-ring cluster config and unicast" do ha_configuration = SapHA::HAConfiguration.new expect(ha_configuration.can_install?).to eq false ha_configuration = prepare_hana_config(ha_configuration) expect(ha_configuration.can_install?).to eq(true), - lambda { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } + -> { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } end - it 'works for two-ring cluster config and multicast' do + it "works for two-ring cluster config and multicast" do ha_configuration = SapHA::HAConfiguration.new expect(ha_configuration.can_install?).to eq false ha_configuration = prepare_hana_config(ha_configuration, transport_mode: :multicast) expect(ha_configuration.can_install?).to eq(true), - lambda { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } + -> { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } end - it 'works for single-ring cluster config and unicast' do + it "works for single-ring cluster config and unicast" do ha_configuration = SapHA::HAConfiguration.new expect(ha_configuration.can_install?).to eq false ha_configuration = prepare_hana_config(ha_configuration, number_of_rings: 1) expect(ha_configuration.can_install?).to eq(true), - lambda { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } + -> { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } end - it 'works for single-ring cluster config and multicast' do + it "works for single-ring cluster config and multicast" do ha_configuration = SapHA::HAConfiguration.new expect(ha_configuration.can_install?).to eq false ha_configuration = prepare_hana_config(ha_configuration, number_of_rings: 1, transport_mode: :multicast) expect(ha_configuration.can_install?).to eq(true), - lambda { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } + -> { "Configuration errors:\n#{ha_configuration.verbose_validate.join("\n")}" } end end - describe '#dump' do - it 'works' do + describe "#dump" do + it "works" do ha_configuration = SapHA::HAConfiguration.new expect(ha_configuration.dump(true)).to be_nil ha_configuration = prepare_hana_config(ha_configuration) @@ -134,22 +134,22 @@ end end - describe '#start_setup' do - it 'works' do + describe "#start_setup" do + it "works" do ha_configuration = SapHA::HAConfiguration.new ha_configuration.start_setup end end - describe '#end_setup' do - it 'works' do + describe "#end_setup" do + it "works" do ha_configuration = SapHA::HAConfiguration.new ha_configuration.end_setup end end - describe '#collect_log' do - it 'works' do + describe "#collect_log" do + it "works" do ha_configuration = SapHA::HAConfiguration.new result = ha_configuration.collect_log expect(result).not_to be_nil diff --git a/test/src/lib/sap_ha/helpers_spec.rb b/test/src/lib/sap_ha/helpers_spec.rb index 744abbc..856860c 100644 --- a/test/src/lib/sap_ha/helpers_spec.rb +++ b/test/src/lib/sap_ha/helpers_spec.rb @@ -19,83 +19,83 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../test_helper' -require 'sap_ha/helpers' -require 'sap_ha/exceptions' -require 'sap_ha/configuration' +require_relative "../../../test_helper" +require "sap_ha/helpers" +require "sap_ha/exceptions" +require "sap_ha/configuration" describe SapHA::HelpersClass do - describe '#instance' do - it 'instantiates the Singleton' do + describe "#instance" do + it "instantiates the Singleton" do result = SapHA::HelpersClass.instance expect(result).not_to be_nil end end - describe '#render_template' do - it 'renders the GUI template for two-ring cluster and unicast' do + describe "#render_template" do + it "renders the GUI template for two-ring cluster and unicast" do @config = prepare_hana_config expect(@config.can_install?).to eq true begin - result = SapHA::Helpers.render_template('tmpl_config_overview_gui.erb', binding) + result = SapHA::Helpers.render_template("tmpl_config_overview_gui.erb", binding) rescue SapHA::Exceptions::TemplateRenderException => e raise SapHA::Exceptions::TemplateRenderException, e.renderer_message end expect(result).not_to be_nil end - it 'renders the GUI template for two-ring cluster and multicast' do + it "renders the GUI template for two-ring cluster and multicast" do @config = prepare_hana_config(nil, transport_mode: :multicast) expect(@config.can_install?).to eq true expect(@config.cluster.transport_mode).to eq(:multicast) begin - result = SapHA::Helpers.render_template('tmpl_config_overview_gui.erb', binding) + result = SapHA::Helpers.render_template("tmpl_config_overview_gui.erb", binding) rescue SapHA::Exceptions::TemplateRenderException => e raise SapHA::Exceptions::TemplateRenderException, e.renderer_message end expect(result).not_to be_nil end - it 'renders the GUI template for single-ring cluster and unicast' do + it "renders the GUI template for single-ring cluster and unicast" do @config = prepare_hana_config(nil, number_of_rings: 1) expect(@config.can_install?).to eq true begin - result = SapHA::Helpers.render_template('tmpl_config_overview_gui.erb', binding) + result = SapHA::Helpers.render_template("tmpl_config_overview_gui.erb", binding) rescue SapHA::Exceptions::TemplateRenderException => e raise SapHA::Exceptions::TemplateRenderException, e.renderer_message end expect(result).not_to be_nil end - it 'renders the GUI template for single-ring cluster and multicast' do + it "renders the GUI template for single-ring cluster and multicast" do @config = prepare_hana_config(nil, number_of_rings: 1, transport_mode: :multicast) expect(@config.can_install?).to eq true expect(@config.cluster.transport_mode).to eq(:multicast) begin - result = SapHA::Helpers.render_template('tmpl_config_overview_gui.erb', binding) + result = SapHA::Helpers.render_template("tmpl_config_overview_gui.erb", binding) rescue SapHA::Exceptions::TemplateRenderException => e raise SapHA::Exceptions::TemplateRenderException, e.renderer_message end expect(result).not_to be_nil end - it 'renders the ncurses template for two-ring cluster' do + it "renders the ncurses template for two-ring cluster" do @config = prepare_hana_config expect(@config.can_install?).to eq true begin - result = SapHA::Helpers.render_template('tmpl_config_overview_con.erb', binding) + result = SapHA::Helpers.render_template("tmpl_config_overview_con.erb", binding) rescue SapHA::Exceptions::TemplateRenderException => e raise SapHA::Exceptions::TemplateRenderException, e.renderer_message end expect(result).not_to be_nil end - it 'renders the ncurses template for single-ring cluster' do + it "renders the ncurses template for single-ring cluster" do @config = prepare_hana_config(nil, number_of_rings: 1) expect(@config.can_install?).to eq true begin - result = SapHA::Helpers.render_template('tmpl_config_overview_con.erb', binding) + result = SapHA::Helpers.render_template("tmpl_config_overview_con.erb", binding) rescue SapHA::Exceptions::TemplateRenderException => e raise SapHA::Exceptions::TemplateRenderException, e.renderer_message end @@ -103,43 +103,43 @@ end end - describe '#load_help' do - it 'loads the required help file' do + describe "#load_help" do + it "loads the required help file" do %w(comm_layer cluster_nodes fencing hana join_cluster ntp product_not_found setup_summary watchdog).each do |hn| result = SapHA::Helpers.load_help(hn) expect(result).not_to be_empty end - expect{ SapHA::Helpers.load_help('__unknown__') }.to raise_error(RuntimeError) + expect { SapHA::Helpers.load_help("__unknown__") }.to raise_error(RuntimeError) end end - describe '#data_file_path' do - it 'returns a path to the data file' do - result = SapHA::Helpers.data_file_path('scenarios.yaml') + describe "#data_file_path" do + it "returns a path to the data file" do + result = SapHA::Helpers.data_file_path("scenarios.yaml") expect(result).not_to be_nil end end - describe '#var_file_path' do - it 'returns a path to the temporary file' do - result = SapHA::Helpers.var_file_path('temporary') + describe "#var_file_path" do + it "returns a path to the temporary file" do + result = SapHA::Helpers.var_file_path("temporary") expect(result).not_to be_nil end end - describe '#version_comparison' do - it 'compares versions as expected' do + describe "#version_comparison" do + it "compares versions as expected" do # SPS12 is greater than SPS11 - result = SapHA::Helpers.version_comparison('1.00.110', '1.00.120', '>=') + result = SapHA::Helpers.version_comparison("1.00.110", "1.00.120", ">=") expect(result).to be true # SPS09 is lower than SPS11 - result = SapHA::Helpers.version_comparison('1.00.110', '1.00.090', '>=') + result = SapHA::Helpers.version_comparison("1.00.110", "1.00.090", ">=") expect(result).to be false # HANA 2.0 SPS1 is greater than HANA 1.0 SPS12 - result = SapHA::Helpers.version_comparison('1.00.110', '2.00.010', '>=') + result = SapHA::Helpers.version_comparison("1.00.110", "2.00.010", ">=") expect(result).to be true # HANA 2.0 SPS1 is greater or equal to HANA 2.0 SPS1 - result = SapHA::Helpers.version_comparison('2.00.010', '2.00.010.00.1491294693') + result = SapHA::Helpers.version_comparison("2.00.010", "2.00.010.00.1491294693") expect(result).to be true end end diff --git a/test/src/lib/sap_ha/rpc_server_spec.rb b/test/src/lib/sap_ha/rpc_server_spec.rb index 0cfe4bb..c51a4d3 100644 --- a/test/src/lib/sap_ha/rpc_server_spec.rb +++ b/test/src/lib/sap_ha/rpc_server_spec.rb @@ -19,19 +19,19 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../test_helper' -require 'sap_ha/rpc_server' -require 'xmlrpc/client' +require_relative "../../../test_helper" +require "sap_ha/rpc_server" +require "xmlrpc/client" describe SapHA::RPCServer do describe "RPC configuration" do before(:all) do - @server = SapHA::RPCServer.new(local: true, test:true) + @server = SapHA::RPCServer.new(local: true, test: true) @server_thread = Thread.new do @server.start @server = nil end - @client = XMLRPC::Client.new('127.0.0.1', "/RPC2", 8080) + @client = XMLRPC::Client.new("127.0.0.1", "/RPC2", 8080) end after(:all) do @@ -40,27 +40,27 @@ end it "exposes required methods" do - methods_list = @client.call('system.listMethods') - expect(methods_list).to include('sapha.ping', 'sapha.import_config', 'sapha.shutdown') + methods_list = @client.call("system.listMethods") + expect(methods_list).to include("sapha.ping", "sapha.import_config", "sapha.shutdown") end it "pongs" do - ret = @client.call('sapha.ping') + ret = @client.call("sapha.ping") expect(ret).to eq true end it "imports the config and exposes additional methods" do config = prepare_hana_config(nil, transport_mode: :unicast, number_of_rings: 1) yaml_config = config.dump(true) - ret = @client.call('sapha.import_config', yaml_config) + ret = @client.call("sapha.import_config", yaml_config) expect(ret).to eq true - - methods_list = @client.call('system.listMethods') + + methods_list = @client.call("system.listMethods") # check the configuration sequence config_sequence = config.config_sequence config_sequence.each do |c| - expect(methods_list).to include(c[:rpc_method]), + expect(methods_list).to include(c[:rpc_method]), "RPC method #{c[:rpc_method]} is not exposed" # sig = @client.call('system.methodSignature', c[:rpc_method]) # expect(sig).to eq(['role']), @@ -70,9 +70,9 @@ it "shuts down the server" do expect(@server).not_to be_nil - @client.call('sapha.shutdown') + @client.call("sapha.shutdown") sleep 3 - expect{@client.call('sapha.ping')}.to raise_error(Errno::ECONNREFUSED) + expect { @client.call("sapha.ping") }.to raise_error(Errno::ECONNREFUSED) end end end diff --git a/test/src/lib/sap_ha/system/hana_spec.rb b/test/src/lib/sap_ha/system/hana_spec.rb index 35d3437..2285d07 100644 --- a/test/src/lib/sap_ha/system/hana_spec.rb +++ b/test/src/lib/sap_ha/system/hana_spec.rb @@ -18,19 +18,19 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../../test_helper' -require 'sap_ha/exceptions' -require 'sap_ha/node_logger' -require 'sap_ha/system/hana' -require 'sap_ha/system/local' -require 'sap_ha/system/shell_commands' +require_relative "../../../../test_helper" +require "sap_ha/exceptions" +require "sap_ha/node_logger" +require "sap_ha/system/hana" +require "sap_ha/system/local" +require "sap_ha/system/shell_commands" describe SapHA::System::HanaClass do - let(:bad_exit) {double('ExitStatus', exitstatus: 1)} - let(:good_exit) {double('ExitStatus', exitstatus: 0)} + let(:bad_exit) { double("ExitStatus", exitstatus: 1) } + let(:good_exit) { double("ExitStatus", exitstatus: 0) } - let(:hdb_version_output) { + let(:hdb_version_output) do "HDB version info: version: 1.00.121.00.1466466057 branch: fa/hana1sp12 @@ -41,8 +41,8 @@ compile host: ld7272 compile type: rel " - } - let(:hdb_version_output_20){ + end + let(:hdb_version_output_20) do "HDB version info: version: 2.00.010.00.1491294693 branch: fa/hana2sp01 @@ -53,8 +53,8 @@ compile host: ld7270 compile type: rel " - } - let(:hdb_secure_store_list_20){ + end + let(:hdb_secure_store_list_20) do "DATA FILE : /usr/sap/XXX/home/.hdb/hana01/SSFS_HDB.DAT KEY FILE : /usr/sap/XXX/home/.hdb/hana01/SSFS_HDB.KEY @@ -80,8 +80,8 @@ ENV : hana01:30015 USER: SAPDBCTRL " - } - let(:hdb_secure_store_list){ + end + let(:hdb_secure_store_list) do "DATA FILE : /usr/sap/XXX/home/.hdb/hana01/SSFS_HDB.DAT KEY FILE : /usr/sap/XXX/home/.hdb/hana01/SSFS_HDB.KEY @@ -95,252 +95,167 @@ ENV : locahost:30150 USER: uname " - } + end - let(:hdb_global_ini) {"/hana/shared/XXX/global/hdb/custom/config/global.ini"} + let(:hdb_global_ini) { "/hana/shared/XXX/global/hdb/custom/config/global.ini" } - describe '#make_backup' do - context 'when the call to hdbsql succedes,' do - it 'creates the backup for HANA 1.0' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return([hdb_version_output, good_exit]) - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbsql', '-U', 'hanabackup', '"BACKUP DATA USING FILE (\'backup\')"') - .and_return(['', good_exit]) - result = SapHA::System::Hana.make_backup('XXX', 'hanabackup', 'backup', '10') - expect(result).to eq true - end - - it 'creates the backup for HANA 2.0' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return([hdb_version_output_20, good_exit]) + describe "#make_backup" do + context "when the call to hdbsql succedes," do + it "creates the backup for HANA 2.0" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbsql', '-U', 'hanabackup', '-d', 'SYSTEMDB', + .with("xxxadm", "hdbsql", "-i", "10", "-U", "hanabackup", "-d", "SYSTEMDB", '"BACKUP DATA FOR FULL SYSTEM USING FILE (\'backup\')"') - .and_return(['', good_exit]) - result = SapHA::System::Hana.make_backup('XXX', 'hanabackup', 'backup', '10') + .and_return(["", good_exit]) + result = SapHA::System::Hana.make_backup("XXX", "hanabackup", "backup", "10") expect(result).to eq true end end - - context 'when the call to hdbsql fails,' do - it 'does not create the backup for HANA 1.0' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return([hdb_version_output, good_exit]) - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbsql', '-U', 'hanabackup', '"BACKUP DATA USING FILE (\'backup\')"') - .and_return(['Some error', bad_exit]) - result = SapHA::System::Hana.make_backup('XXX', 'hanabackup', 'backup', '10') - expect(result).to eq false - end - - it 'does not create the backup for HANA 2.0' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return([hdb_version_output_20, good_exit]) - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbsql', '-U', 'hanabackup', '-d', 'SYSTEMDB', - '"BACKUP DATA FOR FULL SYSTEM USING FILE (\'backup\')"') - .and_return(['Some error', bad_exit]) - result = SapHA::System::Hana.make_backup('XXX', 'hanabackup', 'backup', '10') - expect(result).to eq false - end - end end - describe '#enable_primary' do - context 'when the call to hdbnsutil succedes,' do - it 'enables the SR on the primary node' do + describe "#enable_primary" do + context "when the call to hdbnsutil succedes," do + it "enables the SR on the primary node" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbnsutil', '-sr_enable', "--name=PRIMARY") - .and_return(['', good_exit]) - result = SapHA::System::Hana.enable_primary('XXX', 'PRIMARY') + .with("xxxadm", "hdbnsutil", "-sr_enable", "--name=PRIMARY") + .and_return(["", good_exit]) + result = SapHA::System::Hana.enable_primary("XXX", "PRIMARY") expect(result).to eq true end end - context 'when the call to hdbnsutil fails,' do - it 'does not enable the SR on the primary node' do + context "when the call to hdbnsutil fails," do + it "does not enable the SR on the primary node" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbnsutil', '-sr_enable', "--name=PRIMARY") - .and_return(['Some error', bad_exit]) - result = SapHA::System::Hana.enable_primary('XXX', 'PRIMARY') + .with("xxxadm", "hdbnsutil", "-sr_enable", "--name=PRIMARY") + .and_return(["Some error", bad_exit]) + result = SapHA::System::Hana.enable_primary("XXX", "PRIMARY") expect(result).to eq false end end end - describe '#version' do - context 'when the call to HDB version succedes' do - it 'returns the version string' do + describe "#version" do + context "when the call to HDB version succedes" do + it "returns the version string" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return([hdb_version_output, good_exit]) - result = SapHA::System::Hana.version('XXX') - expect(result).to eq '1.00.121' - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') + .with("xxxadm", "HDB", "version") .and_return([hdb_version_output_20, good_exit]) - result = SapHA::System::Hana.version('XXX') - expect(result).to eq '2.00.010' + result = SapHA::System::Hana.version("XXX") + expect(result).to eq "2.00.010" end end - context 'when the call to HDB version succedes, but the version string is garbled' do - it 'returns nil' do + context "when the call to HDB version succedes, but the version string is garbled" do + it "returns nil" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return(['', good_exit]) - result = SapHA::System::Hana.version('XXX') + .with("xxxadm", "HDB", "version") + .and_return(["", good_exit]) + result = SapHA::System::Hana.version("XXX") expect(result).to eq nil end end - context 'when the call to HDB version fails' do - it 'returns nil' do + context "when the call to HDB version fails" do + it "returns nil" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return(['', bad_exit]) - result = SapHA::System::Hana.version('XXX') + .with("xxxadm", "HDB", "version") + .and_return(["", bad_exit]) + result = SapHA::System::Hana.version("XXX") expect(result).to eq nil end end end - describe '#enable_secondary' do - context 'when the call to hdbnsutil succedes,' do - it 'enables the SR on the secondary node' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return(['1.00.100', good_exit]) - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbnsutil', '-sr_register', '--remoteHost=hana01', - '--remoteInstance=10', '--mode=sync', '--name=SECONDARY') - .and_return(['', good_exit]) - result = SapHA::System::Hana.enable_secondary('XXX', 'SECONDARY', 'hana01', '10', 'sync', - 'delta_datashipping') - expect(result).to eq true - end - end - - context 'when the call to hdbnsutil fails,' do - it 'does not enable the SR on the secondary node' do - # by default we assume SPS<12 and --mode parameter + describe "#enable_secondary" do + context "when the call to hdbnsutil succeeds for HANA" do + it "enables the SR on the secondary node" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return(['', good_exit]) - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbnsutil', '-sr_register', '--remoteHost=hana01', - '--remoteInstance=10', '--mode=sync', '--name=SECONDARY') - .and_return(['Some error', bad_exit]) - result = SapHA::System::Hana.enable_secondary('XXX', 'SECONDARY', 'hana01', '10', 'sync', - 'delta_datashipping') - expect(result).to eq false - end - end - - context 'when the call to hdbnsutil succeeds for HANA SPS12' do - it 'enables the SR on the secondary node' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'version') - .and_return([hdb_version_output, good_exit]) - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbnsutil', '-sr_register', '--remoteHost=hana01', - '--remoteInstance=10', '--replicationMode=sync', '--operationMode=delta_datashipping', - '--name=SECONDARY') - .and_return(['', good_exit]) - result = SapHA::System::Hana.enable_secondary('XXX', 'SECONDARY', 'hana01', '10','sync', - 'delta_datashipping') + .with("xxxadm", "hdbnsutil", "-sr_register", "--remoteHost=hana01", + "--remoteInstance=10", "--replicationMode=sync", "--operationMode=delta_datashipping", + "--name=SECONDARY") + .and_return(["", good_exit]) + result = SapHA::System::Hana.enable_secondary("XXX", "SECONDARY", "hana01", "10", "sync", + "delta_datashipping") expect(result).to eq true end end end - describe '#check_secure_store' do - context 'when the storage is empty' do - it 'returns an empty array' do + describe "#check_secure_store" do + context "when the storage is empty" do + it "returns an empty array" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbuserstore', 'list') + .with("xxxadm", "hdbuserstore", "list") .and_return ["DATA FILE : /usr/sap/XXX/home/.hdb/hana01/SSFS_HDB.DAT\n\n", good_exit] - result = SapHA::System::Hana.check_secure_store('XXX') + result = SapHA::System::Hana.check_secure_store("XXX") expect(result).to eq [] end end - context 'when the storage is not empty' do - it 'returns the list of the keys' do - expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbuserstore', 'list') - .and_return [hdb_secure_store_list, good_exit] - result = SapHA::System::Hana.check_secure_store('XXX') - expect(result).to match_array ['KEY1', 'KEY2', 'KEY10'] - end + context "when the storage is not empty" do - it 'returns the list of the keys for HANA 2.0' do + it "returns the list of the keys for HANA 2.0" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbuserstore', 'list') + .with("xxxadm", "hdbuserstore", "list") .and_return [hdb_secure_store_list_20, good_exit] - result = SapHA::System::Hana.check_secure_store('XXX') + result = SapHA::System::Hana.check_secure_store("XXX") expect(result).to match_array ["BACKUPKEY0", "BACKUPKEY1", "BACKUPKEY2", "BACKUPKEY3", - "BACKUPKEY5", "XXXSAPDBCTRL30015"] + "BACKUPKEY5", "XXXSAPDBCTRL30015"] end end - context 'when the call to hdbuserstore fails' do - it 'returns an empty array' do + context "when the call to hdbuserstore fails" do + it "returns an empty array" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'hdbuserstore', 'list') + .with("xxxadm", "hdbuserstore", "list") .and_return ["An error occured", bad_exit] - result = SapHA::System::Hana.check_secure_store('XXX') + result = SapHA::System::Hana.check_secure_store("XXX") expect(result).to eq [] end end end - describe '#hdb_start' do - context 'when the call to HDB start succeedes' do - it 'returns true' do + describe "#hdb_start" do + context "when the call to HDB start succeedes" do + it "returns true" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'start') - .and_return(['', good_exit]) - result = SapHA::System::Hana.hdb_start('XXX') + .with("xxxadm", "HDB", "start") + .and_return(["", good_exit]) + result = SapHA::System::Hana.hdb_start("XXX") expect(result).to eq true end end - context 'when the call to HDB start fails' do - it 'returns false' do + context "when the call to HDB start fails" do + it "returns false" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'start') - .and_return(['', bad_exit]) + .with("xxxadm", "HDB", "start") + .and_return(["", bad_exit]) .twice # we retry HANA stops and starts - result = SapHA::System::Hana.hdb_start('XXX') + result = SapHA::System::Hana.hdb_start("XXX") expect(result).to eq false end end end - describe '#hdb_stop' do - context 'when the call to HDB stop succeedes' do - it 'returns true' do + describe "#hdb_stop" do + context "when the call to HDB stop succeedes" do + it "returns true" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'stop') - .and_return(['', good_exit]) - result = SapHA::System::Hana.hdb_stop('XXX') + .with("xxxadm", "HDB", "stop") + .and_return(["", good_exit]) + result = SapHA::System::Hana.hdb_stop("XXX") expect(result).to eq true end end - context 'when the call to HDB stop fails' do - it 'returns false' do + context "when the call to HDB stop fails" do + it "returns false" do expect(SapHA::System::Hana).to receive(:su_exec_outerr_status) - .with('xxxadm', 'HDB', 'stop') - .and_return(['', bad_exit]) + .with("xxxadm", "HDB", "stop") + .and_return(["", bad_exit]) .twice # we retry HANA stops and starts - result = SapHA::System::Hana.hdb_stop('XXX') + result = SapHA::System::Hana.hdb_stop("XXX") expect(result).to eq false end end @@ -384,5 +299,4 @@ # end # end - end diff --git a/test/src/lib/sap_ha/system/local_spec.rb b/test/src/lib/sap_ha/system/local_spec.rb index 2f80c0e..65e20bf 100644 --- a/test/src/lib/sap_ha/system/local_spec.rb +++ b/test/src/lib/sap_ha/system/local_spec.rb @@ -18,18 +18,18 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../../test_helper' -require 'sap_ha/exceptions' -require 'sap_ha/system/local' -require 'sap_ha/system/shell_commands' +require_relative "../../../../test_helper" +require "sap_ha/exceptions" +require "sap_ha/system/local" +require "sap_ha/system/shell_commands" describe SapHA::System::LocalClass do - let(:bad_exit) {double('ExitStatus', exitstatus: 1)} - let(:good_exit) {double('ExitStatus', exitstatus: 0)} + let(:bad_exit) { double("ExitStatus", exitstatus: 1) } + let(:good_exit) { double("ExitStatus", exitstatus: 0) } - describe '#block_devices' do - it 'lists all block devices on this machine' do + describe "#block_devices" do + it "lists all block devices on this machine" do result = SapHA::System::Local.block_devices expect(result).not_to be_nil end @@ -85,13 +85,13 @@ # end # # TODO: auto-generated - # describe '#open_ports' do + # describe '#open_cluster_service' do # it 'works' do # local_class = SapHA::System::LocalClass.new # role = double('role') # rings = double('rings') # number_of_rings = double('number_of_rings') - # result = local_class.open_ports(role, rings, number_of_rings) + # result = local_class.open_cluster_service # expect(result).not_to be_nil # end # end @@ -134,8 +134,8 @@ # end # end - describe '#append_hosts_file' do - let(:hosts) { + describe "#append_hosts_file" do + let(:hosts) do { node1: { host_name: "hana01", @@ -150,26 +150,26 @@ node_id: 1 } } - } - context 'when provided with a list of nodes in 1-ring configuration' do - it 'writes the hosts file' do + end + context "when provided with a list of nodes in 1-ring configuration" do + it "writes the hosts file" do io = StringIO.new exp = "192.168.100.1\thana01 # added by yast2-sap-ha\n"\ "192.168.100.2\thana02 # added by yast2-sap-ha\n" - expect(File).to receive(:open).with('/etc/hosts', 'a').and_yield(io) + expect(File).to receive(:open).with("/etc/hosts", "a").and_yield(io) SapHA::System::Local.append_hosts_file(hosts) expect(io.string).to eq exp end end - context 'when provided with a list of nodes in 2-ring configuration' do - it 'writes the hosts file' do + context "when provided with a list of nodes in 2-ring configuration" do + it "writes the hosts file" do io = StringIO.new exp = "192.168.100.1\thana01 # added by yast2-sap-ha\n"\ "192.168.100.2\thana02 # added by yast2-sap-ha\n" - hosts[:node1][:ip_ring2] = '192.168.101.1' - hosts[:node2][:ip_ring2] = '192.168.101.2' - expect(File).to receive(:open).with('/etc/hosts', 'a').and_yield(io) + hosts[:node1][:ip_ring2] = "192.168.101.1" + hosts[:node2][:ip_ring2] = "192.168.101.2" + expect(File).to receive(:open).with("/etc/hosts", "a").and_yield(io) SapHA::System::Local.append_hosts_file(hosts) expect(io.string).to eq exp end @@ -215,16 +215,16 @@ # end # end - describe '#cluster_maintenance' do - it 'works' do + describe "#cluster_maintenance" do + it "works" do expect(SapHA::System::Local).to receive(:exec_outerr_status) - .with('crm', 'configure', 'property', 'maintenance-mode=true') - .and_return(['', good_exit]) + .with("crm", "configure", "property", "maintenance-mode=true") + .and_return(["", good_exit]) result = SapHA::System::Local.cluster_maintenance(:on) expect(result).to eq true expect(SapHA::System::Local).to receive(:exec_outerr_status) - .with('crm', 'configure', 'property', 'maintenance-mode=false') - .and_return(['', good_exit]) + .with("crm", "configure", "property", "maintenance-mode=false") + .and_return(["", good_exit]) result = SapHA::System::Local.cluster_maintenance(:off) expect(result).to eq true end diff --git a/test/src/lib/sap_ha/system/network_spec.rb b/test/src/lib/sap_ha/system/network_spec.rb index b8d89fc..6428580 100644 --- a/test/src/lib/sap_ha/system/network_spec.rb +++ b/test/src/lib/sap_ha/system/network_spec.rb @@ -19,39 +19,39 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require_relative '../../../../test_helper' -require 'sap_ha/system/network' +require_relative "../../../../test_helper" +require "sap_ha/system/network" describe SapHA::System::Network do - describe '#interfaces' do - it 'returns the list of network interfaces on the local machine' do + describe "#interfaces" do + it "returns the list of network interfaces on the local machine" do # It can happen that files under /etc/sysconfig/network are only accessible for root # Accept an empty list of interfaces for non-root user result = SapHA::System::Network.interfaces expect(result).not_to be_nil - #expect(result).not_to be_empty if user_root? + # expect(result).not_to be_empty if user_root? end end - describe '#ip_addresses' do - it 'returns the list of IP addresses of the local machine' do + describe "#ip_addresses" do + it "returns the list of IP addresses of the local machine" do result = SapHA::System::Network.ip_addresses expect(result).not_to be_nil - #expect(result).not_to be_empty unless build_service? + # expect(result).not_to be_empty unless build_service? end end - describe '#network_addresses' do - it 'returns the list of IP addresses of the networks' do + describe "#network_addresses" do + it "returns the list of IP addresses of the networks" do result = SapHA::System::Network.network_addresses expect(result).not_to be_nil expect(result).not_to be_empty unless build_service? end end - describe '#hostname' do - it 'returns the host name of the machine' do + describe "#hostname" do + it "returns the host name of the machine" do result = SapHA::System::Network.hostname expect(result).not_to be_nil expect(result).not_to be_empty diff --git a/test/test_helper.rb b/test/test_helper.rb index a06a9cf..421dfdd 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -19,10 +19,10 @@ # Summary: SUSE High Availability Setup for SAP Products # Authors: Ilya Manyugin -require 'etc' +require "etc" # Set the paths -ENV['Y2DIR'] = File.expand_path('../../src', __FILE__) +ENV["Y2DIR"] = File.expand_path("../../src", __FILE__) RSpec.configure do |config| config.mock_with :rspec do |mocks| @@ -63,7 +63,7 @@ end end -require 'yast' +require "yast" def build_service? Etc.getlogin == "abuild" @@ -78,8 +78,8 @@ def user_root? # and unicast communication def prepare_hana_config(instance = nil, options = {}) instance = SapHA::HAConfiguration.new if instance.nil? - _make_basic_ha_config(instance, 'HANA', - options.fetch(:scenario_name, 'Scale Up: Performance-optimized'), + _make_basic_ha_config(instance, "HANA", + options.fetch(:scenario_name, "Scale Up: Performance-optimized"), options) end @@ -87,60 +87,63 @@ def _make_basic_ha_config(config, product_id, scenario_name, options = {}) # stub the IP checks so the tests don't fail unless options[:notest] allow(SapHA::SemanticChecks.instance).to receive(:intersection_not_empty).with( - anything, anything, anything, 'IP addresses for ring 1').and_return(true) + anything, anything, anything, "IP addresses for ring 1" + ).and_return(true) allow(SapHA::SemanticChecks.instance).to receive(:intersection_not_empty).with( - anything, anything, anything, 'IP addresses for ring 2').and_return(true) + anything, anything, anything, "IP addresses for ring 2" + ).and_return(true) end config.set_product_id product_id config.set_scenario_name scenario_name config.cluster.import( fixed_number_of_nodes: options.fetch(:fixed_number_of_nodes, true), transport_mode: options.fetch(:transport_mode, :unicast), - cluster_name: options.fetch(:cluster_name, 'hana_sysrep'), + cluster_name: options.fetch(:cluster_name, "hana_sysrep"), number_of_rings: options.fetch(:number_of_rings, 2), number_of_nodes: options.fetch(:number_of_nodes, 2), expected_votes: options.fetch(:expected_votes, 2), - rings: { ring1: { address: '192.168.101.0/24', port: '5405', - id: 1, mcast: '239.255.255.255', - address_no_mask: '192.168.101.0/24' }, - ring2: { address: '192.168.103.0/24', port: '5405', - id: 2, mcast: '239.255.255.255', - address_no_mask: '192.168.103.0' } }, + rings: { ring1: { address: "192.168.101.0/24", port: "5405", + id: 1, mcast: "239.255.255.255", + address_no_mask: "192.168.101.0/24" }, + ring2: { address: "192.168.103.0/24", port: "5405", + id: 2, mcast: "239.255.255.255", + address_no_mask: "192.168.103.0" } }, nodes: { node1: { host_name: "hana01", ip_ring1: "192.168.101.21", - ip_ring2: "192.168.103.21", node_id: '1' }, + ip_ring2: "192.168.103.21", node_id: "1" }, node2: { host_name: "hana02", ip_ring1: "192.168.101.22", - ip_ring2: "192.168.103.22", node_id: '2' } } + ip_ring2: "192.168.103.22", node_id: "2" } } ) # let the model reduce the number of rings, if necessary config.cluster.number_of_rings = options.fetch(:number_of_rings, 2) config.fencing.import( - devices: ['/dev/vdb'] + devices: ["/dev/vdb"] ) - config.watchdog.import(to_install: ['softdog']) + config.watchdog.import(to_install: ["softdog"]) config.hana.import( - system_id: options.fetch(:system_id, 'XXX'), - instance: options.fetch(:instance, '05'), - virtual_ip: options.fetch(:virtual_ip, '192.168.101.100'), + system_id: options.fetch(:system_id, "XXX"), + instance: options.fetch(:instance, "05"), + virtual_ip: options.fetch(:virtual_ip, "192.168.101.100"), prefer_takeover: options.fetch(:prefer_takeover, true), auto_register: options.fetch(:auto_register, false), - site_name_1: options.fetch(:site_name_1, 'WALLDORF1'), - site_name_2: options.fetch(:site_name_2, 'ROT1'), - backup_user: options.fetch(:backup_user, 'mybackupuser'), - backup_file: options.fetch(:backup_file, 'mybackupfile2'), + site_name_1: options.fetch(:site_name_1, "WALLDORF1"), + site_name_2: options.fetch(:site_name_2, "ROT1"), + backup_user: options.fetch(:backup_user, "mybackupuser"), + backup_file: options.fetch(:backup_file, "mybackupfile2"), perform_backup: options.fetch(:perform_backup, false) ) ntp_cfg = { - "ntp_sync"=>"systemd", - "ntp_policy"=>"auto", - "ntp_servers"=>[ - {"address"=>"0.suse.pool.ntp.org", "iburst"=>true, "offline"=>false}, - {"address"=>"1.suse.pool.ntp.org", "iburst"=>true, "offline"=>false}, - {"address"=>"2.suse.pool.ntp.org", "iburst"=>true, "offline"=>false}, - {"address"=>"3.suse.pool.ntp.org", "iburst"=>true, "offline"=>false}] + "ntp_sync"=>"systemd", + "ntp_policy"=>"auto", + "ntp_servers"=>[ + { "address" => "0.suse.pool.ntp.org", "iburst" => true, "offline" => false }, + { "address" => "1.suse.pool.ntp.org", "iburst" => true, "offline" => false }, + { "address" => "2.suse.pool.ntp.org", "iburst" => true, "offline" => false }, + { "address" => "3.suse.pool.ntp.org", "iburst" => true, "offline" => false } + ] } config.ntp.import( config: ntp_cfg, - used_servers: ['2.suse.pool.ntp.org'] + used_servers: ["2.suse.pool.ntp.org"] ) config end