Hello community,
here is the log from the commit of package python-shaptools for
openSUSE:Factory checked in at 2019-07-23 22:40:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-shaptools (Old)
and /work/SRC/openSUSE:Factory/.python-shaptools.new.4126 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-shaptools"
Tue Jul 23 22:40:21 2019 rev:5 rq:717873 version:0.3.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-shaptools/python-shaptools.changes
2019-07-21 11:33:40.348784109 +0200
+++
/work/SRC/openSUSE:Factory/.python-shaptools.new.4126/python-shaptools.changes
2019-07-23 22:40:26.514933434 +0200
@@ -1,0 +2,11 @@
+Tue Jul 23 11:04:25 UTC 2019 - Xabier Arbulu Insausti <[email protected]>
+
+- Create package version 0.3.1
+- Add support for Power machines
+
+-------------------------------------------------------------------
+Thu Jul 18 08:46:15 UTC 2019 - Xabier Arbulu Insausti <[email protected]>
+
+- Add an option to run the commands in remote nodes to shapcli
+
+-------------------------------------------------------------------
Old:
----
shaptools-0.3.0.tar.gz
New:
----
shaptools-0.3.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-shaptools.spec ++++++
--- /var/tmp/diff_new_pack.5aSiMD/_old 2019-07-23 22:40:26.926933349 +0200
+++ /var/tmp/diff_new_pack.5aSiMD/_new 2019-07-23 22:40:26.930933348 +0200
@@ -22,7 +22,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-shaptools
-Version: 0.3.0
+Version: 0.3.1
Release: 0
Summary: Python tools to interact with SAP HANA utilities
License: Apache-2.0
++++++ shaptools-0.3.0.tar.gz -> shaptools-0.3.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/docs/SHAPCLI.md
new/shaptools-0.3.1/docs/SHAPCLI.md
--- old/shaptools-0.3.0/docs/SHAPCLI.md 2019-07-18 11:03:12.820176367 +0200
+++ new/shaptools-0.3.1/docs/SHAPCLI.md 2019-07-23 13:12:24.071282674 +0200
@@ -49,3 +49,17 @@
```
shapcli hana -h
```
+
+### Running commands in remote nodes
+
+The commands can be executed in remote nodes too. For that the `-r` or
`--remote` flag have to be
+used (or adding the `remote` entry in the configuration file [the `-r` flag
has priority over the configuration file entry]).
+
+```
+shapcli -c config.json -r remotehost hana version
+```
+
+If the ssh keys of the current node is not installed in the remote host, the
password must be
+provided after the command. To avoid this, the ssh key of the current node can
be authorized in the
+remote node. By default, the ssh public key must be added in:
`/usr/sap/PRD/home/.ssh/authorized_keys`
+(where `PRD` is the SAP HANA instanse sid in uppercase)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/python-shaptools.changes
new/shaptools-0.3.1/python-shaptools.changes
--- old/shaptools-0.3.0/python-shaptools.changes 2019-07-18
11:03:12.820176367 +0200
+++ new/shaptools-0.3.1/python-shaptools.changes 2019-07-23
13:12:24.071282674 +0200
@@ -1,8 +1,19 @@
-------------------------------------------------------------------
+Tue Jul 23 11:04:25 UTC 2019 - Xabier Arbulu Insausti <[email protected]>
+
+- Create package version 0.3.1
+- Add support for Power machines
+
+-------------------------------------------------------------------
+Thu Jul 18 08:46:15 UTC 2019 - Xabier Arbulu Insausti <[email protected]>
+
+- Add an option to run the commands in remote nodes to shapcli
+
+-------------------------------------------------------------------
Wed Jul 17 09:34:22 UTC 2019 - Xabier Arbulu Insausti <[email protected]>
- Create package version 0.3.0
-- shapcli is provided to expose shaptools api methods as command line tool
+- shapcli is provided to expose shaptools api methods as command line tool
-------------------------------------------------------------------
Tue Jun 11 11:29:44 UTC 2019 - Xabier Arbulu Insausti <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/python-shaptools.spec
new/shaptools-0.3.1/python-shaptools.spec
--- old/shaptools-0.3.0/python-shaptools.spec 2019-07-18 11:03:12.820176367
+0200
+++ new/shaptools-0.3.1/python-shaptools.spec 2019-07-23 13:12:24.071282674
+0200
@@ -22,7 +22,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-shaptools
-Version: 0.3.0
+Version: 0.3.1
Release: 0
Summary: Python tools to interact with SAP HANA utilities
License: Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/shaptools/__init__.py
new/shaptools-0.3.1/shaptools/__init__.py
--- old/shaptools-0.3.0/shaptools/__init__.py 2019-07-18 11:03:12.820176367
+0200
+++ new/shaptools-0.3.1/shaptools/__init__.py 2019-07-23 13:12:24.071282674
+0200
@@ -6,4 +6,4 @@
:since: 2018-11-15
"""
-__version__ = "0.3.0"
+__version__ = "0.3.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/shaptools/hana.py
new/shaptools-0.3.1/shaptools/hana.py
--- old/shaptools-0.3.0/shaptools/hana.py 2019-07-18 11:03:12.820176367
+0200
+++ new/shaptools-0.3.1/shaptools/hana.py 2019-07-23 13:12:24.071282674
+0200
@@ -18,6 +18,7 @@
import fileinput
import re
import time
+import platform
from shaptools import shell
@@ -55,7 +56,7 @@
}
-class HanaInstance:
+class HanaInstance(object):
"""
SAP HANA instance implementation
@@ -63,17 +64,21 @@
sid (str): SAP HANA sid to enable
inst (str): SAP HANA instance number
password (str): HANA instance password
+ remote_host (str, opt): Remote host where the command will be executed
"""
PATH = '/usr/sap/{sid}/HDB{inst}/'
- INSTALL_EXEC = '{software_path}/DATA_UNITS/HDB_LCM_LINUX_X86_64/hdblcm'
+ INSTALL_EXEC = '{software_path}/DATA_UNITS/HDB_LCM_LINUX_{platform}/hdblcm'
+ SUPPORTED_PLATFORMS = [
+ 'x86_64', 'ppc64le'
+ ]
# SID is usualy written uppercased, but the OS user is always created
lower case.
HANAUSER = '{sid}adm'.lower()
SYNCMODES = ['sync', 'syncmem', 'async']
SUCCESSFULLY_REGISTERED = 0 # Node correctly registered as secondary node
SSFS_DIFFERENT_ERROR = 149 # ssfs files are different in the two nodes
error return code
- def __init__(self, sid, inst, password):
+ def __init__(self, sid, inst, password, **kwargs):
# Force instance nr always with 2 positions.
inst = '{:0>2}'.format(inst)
if not all(isinstance(i, basestring) for i in [sid, inst, password]):
@@ -84,6 +89,19 @@
self.sid = sid
self.inst = inst
self._password = password
+ self.remote_host = kwargs.get('remote_host', None)
+
+ @classmethod
+ def get_platform(cls):
+ """
+ Get the SAP HANA installation folder by platform
+ """
+ current_platform = platform.machine()
+ logger = logging.getLogger('__name__')
+ logger.info('current platform is %s', current_platform)
+ if current_platform not in cls.SUPPORTED_PLATFORMS:
+ raise ValueError('not supported platform:
{}'.format(current_platform))
+ return current_platform.upper()
def _run_hana_command(self, cmd, exception=True):
"""
@@ -99,7 +117,7 @@
"""
#TODO: Add absolute paths to hana commands using sid and inst number
user = self.HANAUSER.format(sid=self.sid)
- result = shell.execute_cmd(cmd, user, self._password)
+ result = shell.execute_cmd(cmd, user, self._password, self.remote_host)
if exception and result.returncode != 0:
raise HanaError('Error running hana command:
{}'.format(result.cmd))
@@ -115,7 +133,7 @@
"""
user = self.HANAUSER.format(sid=self.sid)
try:
- result = shell.execute_cmd('HDB info', user, self._password)
+ result = shell.execute_cmd('HDB info', user, self._password,
self.remote_host)
return not result.returncode
except EnvironmentError as err: #FileNotFoundError is not compatible
with python2
self._logger.error(err)
@@ -145,7 +163,7 @@
@classmethod
def create_conf_file(
- cls, software_path, conf_file, root_user, root_password):
+ cls, software_path, conf_file, root_user, root_password,
remote_host=None):
"""
Create SAP HANA configuration file
@@ -154,18 +172,21 @@
conf_file (str): Path where configuration file will be created
root_user (str): Root user name
root_password (str): Root user password
+ remote_host (str, opt): Remote host where the command will be
executed
+
"""
- executable = cls.INSTALL_EXEC.format(software_path=software_path)
+ platform_folder = cls.get_platform()
+ executable = cls.INSTALL_EXEC.format(software_path=software_path,
platform=platform_folder)
cmd = '{executable} --action=install '\
'--dump_configfile_template={conf_file}'.format(
executable=executable, conf_file=conf_file)
- result = shell.execute_cmd(cmd, root_user, root_password)
+ result = shell.execute_cmd(cmd, root_user, root_password, remote_host)
if result.returncode:
raise HanaError('SAP HANA configuration file creation failed')
return conf_file
@classmethod
- def install(cls, software_path, conf_file, root_user, password):
+ def install(cls, software_path, conf_file, root_user, password,
remote_host=None):
"""
Install SAP HANA platform providing a configuration file
@@ -174,13 +195,15 @@
conf_file (str): Path to the configuration file
root_user (str): Root user name
password (str): Root user password
+ remote_host (str, opt): Remote host where the command will be
executed
"""
# TODO: mount partition if needed
# TODO: do some integrity check stuff
- executable = cls.INSTALL_EXEC.format(software_path=software_path)
+ platform_folder = cls.get_platform()
+ executable = cls.INSTALL_EXEC.format(software_path=software_path,
platform=platform_folder)
cmd = '{executable} -b --configfile={conf_file}'.format(
executable=executable, conf_file=conf_file)
- result = shell.execute_cmd(cmd, root_user, password)
+ result = shell.execute_cmd(cmd, root_user, password, remote_host)
if result.returncode:
raise HanaError('SAP HANA installation failed')
@@ -191,7 +214,7 @@
cmd = '{installation_folder}/{sid}/hdblcm/hdblcm '\
'--uninstall -b'.format(
installation_folder=installation_folder, sid=self.sid.upper())
- result = shell.execute_cmd(cmd, root_user, password)
+ result = shell.execute_cmd(cmd, root_user, password, self.remote_host)
if result.returncode:
raise HanaError('SAP HANA uninstallation failed')
@@ -204,7 +227,7 @@
"""
cmd = 'pidof hdb.sap{sid}_HDB{inst}'.format(
sid=self.sid.upper(), inst=self.inst)
- result = shell.execute_cmd(cmd)
+ result = self._run_hana_command(cmd, exception=False)
return not result.returncode
# pylint:disable=W1401
@@ -591,10 +614,11 @@
user_name = kwargs.get('user_name', None)
user_password = kwargs.get('user_password', None)
- self._manage_ini_file(parameter_str=parameter_str, database=database,
- file_name=file_name, layer=layer,
layer_name=layer_name,
- set_value=True, reconfig=reconfig,
key_name=key_name,
- user_name=user_name, user_password=user_password)
+ self._manage_ini_file(
+ parameter_str=parameter_str, database=database,
+ file_name=file_name, layer=layer, layer_name=layer_name,
+ set_value=True, reconfig=reconfig, key_name=key_name,
+ user_name=user_name, user_password=user_password)
def unset_ini_parameter(
self, ini_parameter_names, database, file_name, layer,
@@ -631,7 +655,8 @@
user_name = kwargs.get('user_name', None)
user_password = kwargs.get('user_password', None)
- self._manage_ini_file(parameter_str=parameter_str, database=database,
- file_name=file_name, layer=layer,
layer_name=layer_name,
- set_value=False, reconfig=reconfig,
key_name=key_name,
- user_name=user_name, user_password=user_password)
+ self._manage_ini_file(
+ parameter_str=parameter_str, database=database,
+ file_name=file_name, layer=layer, layer_name=layer_name,
+ set_value=False, reconfig=reconfig, key_name=key_name,
+ user_name=user_name, user_password=user_password)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/shaptools/shapcli.py
new/shaptools-0.3.1/shaptools/shapcli.py
--- old/shaptools-0.3.0/shaptools/shapcli.py 2019-07-18 11:03:12.820176367
+0200
+++ new/shaptools-0.3.1/shaptools/shapcli.py 2019-07-23 13:12:24.071282674
+0200
@@ -30,6 +30,23 @@
return message
+class ConfigData(object):
+ """
+ Class to store the required configuration data
+ """
+
+ def __init__(self, data_dict, logger):
+ try:
+ self.sid = data_dict['sid']
+ self.instance = data_dict['instance']
+ self.password = data_dict['password']
+ self.remote = data_dict.get('remote', None)
+ except KeyError as err:
+ logger.error(err)
+ logger.error('Configuration file must have the sid, instance and
password entries')
+ raise
+
+
def setup_logger(level):
"""
Setup logging
@@ -53,7 +70,7 @@
'-v', '--verbosity',
help='Python logging level. Options: DEBUG, INFO, WARN, ERROR (INFO by
default)')
parser.add_argument(
- '-r', '--remotely',
+ '-r', '--remote',
help='Run the command in other machine using ssh')
parser.add_argument(
'-c', '--config',
@@ -284,7 +301,7 @@
# hana_instance.get_sr_status()
cmd = 'HDBSettings.sh systemReplicationStatus.py{}'.format(
' --sapcontrol=1' if sr_args.sapcontrol else '')
- hana_instance._run_hana_command(cmd)
+ hana_instance._run_hana_command(cmd, exception=False)
elif str_args == 'disable':
hana_instance.sr_disable_primary()
elif str_args == 'cleanup':
@@ -309,12 +326,7 @@
"""
with open(config_file, 'r') as f_ptr:
json_data = json.load(f_ptr)
- try:
- return (json_data['sid'], json_data['instance'], json_data['password'])
- except KeyError as err:
- logger.error(err)
- logger.error('Configuration file must have the sid, instance and
password entries')
- raise
+ return json_data
# pylint:disable=W0212
@@ -325,19 +337,25 @@
parser, args = parse_arguments()
logger = setup_logger(args.verbosity or logging.DEBUG)
+ # If -c or --config flag is received data is loaded from the configuration
file
if args.config:
- sid, instance, password = load_config_file(args.config, logger)
+ data = load_config_file(args.config, logger)
+ config_data = ConfigData(data, logger)
elif args.sid and args.instance and args.password:
- sid = args.sid
- instance = args.instance
- password = args.password
+ config_data = ConfigData(vars(args), logger)
else:
logger.info(
- 'Configuration file or sid,instance and passwords parameters must
be provided\n')
+ 'Configuration file or sid, instance and passwords parameters must
be provided\n')
parser.print_help()
exit(1)
+
+ if args.remote:
+ config_data.remote = args.remote
+
try:
- hana_instance = hana.HanaInstance(sid, instance, password)
+ hana_instance = hana.HanaInstance(
+ config_data.sid, config_data.instance,
+ config_data.password, remote_host=config_data.remote)
if vars(args).get('hana'):
run_hana_subcommands(hana_instance, args, logger)
elif vars(args).get('sr'):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/shaptools/shell.py
new/shaptools-0.3.1/shaptools/shell.py
--- old/shaptools-0.3.0/shaptools/shell.py 2019-07-18 11:03:12.820176367
+0200
+++ new/shaptools-0.3.1/shaptools/shell.py 2019-07-23 13:12:24.071282674
+0200
@@ -82,6 +82,26 @@
return 'su -lc "{cmd}" {user}'.format(cmd=cmd, user=user)
+def format_remote_cmd(cmd, remote_host, user):
+ """
+ Format cmd to run remotely using ssh
+
+ Args:
+ cmd (str): Command to be executed
+ remote_host (str): User password
+ user (str): User to execute the command
+
+ Returns:
+ str: cmd adapted to be executed remotely
+ """
+ if not user:
+ raise ValueError('user must be provided')
+
+ cmd = 'ssh {user}@{remote_host} "bash --login -c \'{cmd}\'"'.format(
+ user=user, remote_host=remote_host, cmd=cmd)
+ return cmd
+
+
def create_ssh_askpass(password, cmd):
"""
Create ask pass command
@@ -98,7 +118,7 @@
return ssh_askpass_str
-def execute_cmd(cmd, user=None, password=None):
+def execute_cmd(cmd, user=None, password=None, remote_host=None):
"""
Execute a shell command. If user and password are provided it will be
executed with this user.
@@ -107,6 +127,7 @@
cmd (str): Command to be executed
user (str, opt): User to execute the command
password (str, opt): User password
+ remote_host (str, opt): Remote host where the command will be executed
Returns:
ProcessResult: ProcessResult instance storing subprocess returncode,
@@ -115,7 +136,11 @@
LOGGER.debug('Executing command "%s" with user %s', cmd, user)
- if user is not None:
+ if remote_host:
+ cmd = format_remote_cmd(cmd, remote_host, user)
+ LOGGER.debug('Command updated to "%s"', cmd)
+
+ elif user:
cmd = format_su_cmd(cmd, user)
LOGGER.debug('Command updated to "%s"', cmd)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/tests/hana_test.py
new/shaptools-0.3.1/tests/hana_test.py
--- old/shaptools-0.3.0/tests/hana_test.py 2019-07-18 11:03:12.820176367
+0200
+++ new/shaptools-0.3.1/tests/hana_test.py 2019-07-23 13:12:24.071282674
+0200
@@ -78,6 +78,26 @@
'provided sid, inst and password parameters must be str type' in
str(err.exception))
+ @mock.patch('platform.machine')
+ def test_get_platform(self, mock_machine):
+ mock_machine.return_value = 'x86_64'
+ machine = hana.HanaInstance.get_platform()
+ self.assertEqual(machine, 'X86_64')
+ mock_machine.assert_called_once_with()
+
+ mock_machine.reset_mock()
+ mock_machine.return_value = 'ppc64le'
+ machine = hana.HanaInstance.get_platform()
+ self.assertEqual(machine, 'PPC64LE')
+ mock_machine.assert_called_once_with()
+
+ @mock.patch('platform.machine')
+ def test_get_platform_error(self, mock_machine):
+ mock_machine.return_value = 'ppc64'
+ with self.assertRaises(ValueError) as err:
+ hana.HanaInstance.get_platform()
+ self.assertTrue('not supported platform: {}'.format('ppc64') in
str(err.exception))
+ mock_machine.assert_called_once_with()
@mock.patch('shaptools.shell.execute_cmd')
def test_run_hana_command(self, mock_execute):
@@ -88,7 +108,7 @@
result = self._hana._run_hana_command('test command')
- mock_execute.assert_called_once_with('test command', 'prdadm', 'pass')
+ mock_execute.assert_called_once_with('test command', 'prdadm', 'pass',
None)
self.assertEqual(proc_mock, result)
@mock.patch('shaptools.shell.execute_cmd')
@@ -101,7 +121,7 @@
with self.assertRaises(hana.HanaError) as err:
self._hana._run_hana_command('test command')
- mock_execute.assert_called_once_with('test command', 'prdadm', 'pass')
+ mock_execute.assert_called_once_with('test command', 'prdadm', 'pass',
None)
self.assertTrue(
'Error running hana command: {}'.format(
'updated command') in str(err.exception))
@@ -114,7 +134,7 @@
result = self._hana.is_installed()
- mock_execute.assert_called_once_with('HDB info', 'prdadm', 'pass')
+ mock_execute.assert_called_once_with('HDB info', 'prdadm', 'pass',
None)
self.assertTrue(result)
@@ -126,7 +146,7 @@
result = self._hana.is_installed()
- mock_execute.assert_called_once_with('HDB info', 'prdadm', 'pass')
+ mock_execute.assert_called_once_with('HDB info', 'prdadm', 'pass',
None)
self.assertFalse(result)
@@ -140,7 +160,7 @@
result = self._hana.is_installed()
- mock_execute.assert_called_once_with('HDB info', 'prdadm', 'pass')
+ mock_execute.assert_called_once_with('HDB info', 'prdadm', 'pass',
None)
self.assertFalse(result)
logger.assert_called_once_with(error)
@@ -159,67 +179,80 @@
**{'sid': 'PRD', 'password': 'Qwerty1234', 'system_user_password':
'Qwerty1234'})
self.assertTrue(filecmp.cmp(pwd+'/support/modified.conf', conf_file))
+ @mock.patch('shaptools.hana.HanaInstance.get_platform')
@mock.patch('shaptools.shell.execute_cmd')
- def test_create_conf_file(self, mock_execute):
+ def test_create_conf_file(self, mock_execute, mock_get_platform):
proc_mock = mock.Mock()
proc_mock.returncode = 0
mock_execute.return_value = proc_mock
+ mock_get_platform.return_value = 'my_arch'
conf_file = hana.HanaInstance.create_conf_file(
'software_path', 'conf_file.conf', 'root', 'pass')
mock_execute.assert_called_once_with(
- 'software_path/DATA_UNITS/HDB_LCM_LINUX_X86_64/hdblcm '
+ 'software_path/DATA_UNITS/HDB_LCM_LINUX_my_arch/hdblcm '
'--action=install --dump_configfile_template={conf_file}'.format(
- conf_file='conf_file.conf'), 'root', 'pass')
+ conf_file='conf_file.conf'), 'root', 'pass', None)
+ mock_get_platform.assert_called_once_with()
self.assertEqual('conf_file.conf', conf_file)
+ @mock.patch('shaptools.hana.HanaInstance.get_platform')
@mock.patch('shaptools.shell.execute_cmd')
- def test_create_conf_file_error(self, mock_execute):
+ def test_create_conf_file_error(self, mock_execute, mock_get_platform):
proc_mock = mock.Mock()
proc_mock.returncode = 1
mock_execute.return_value = proc_mock
+ mock_get_platform.return_value = 'my_arch'
with self.assertRaises(hana.HanaError) as err:
hana.HanaInstance.create_conf_file(
'software_path', 'conf_file.conf', 'root', 'pass')
mock_execute.assert_called_once_with(
- 'software_path/DATA_UNITS/HDB_LCM_LINUX_X86_64/hdblcm '
+ 'software_path/DATA_UNITS/HDB_LCM_LINUX_my_arch/hdblcm '
'--action=install --dump_configfile_template={conf_file}'.format(
- conf_file='conf_file.conf'), 'root', 'pass')
+ conf_file='conf_file.conf'), 'root', 'pass', None)
+
+ mock_get_platform.assert_called_once_with()
self.assertTrue(
'SAP HANA configuration file creation failed' in
str(err.exception))
+ @mock.patch('shaptools.hana.HanaInstance.get_platform')
@mock.patch('shaptools.shell.execute_cmd')
- def test_install(self, mock_execute):
+ def test_install(self, mock_execute, mock_get_platform):
proc_mock = mock.Mock()
proc_mock.returncode = 0
mock_execute.return_value = proc_mock
+ mock_get_platform.return_value = 'my_arch'
hana.HanaInstance.install(
'software_path', 'conf_file.conf', 'root', 'pass')
mock_execute.assert_called_once_with(
- 'software_path/DATA_UNITS/HDB_LCM_LINUX_X86_64/hdblcm '
+ 'software_path/DATA_UNITS/HDB_LCM_LINUX_my_arch/hdblcm '
'-b --configfile={conf_file}'.format(
- conf_file='conf_file.conf'), 'root', 'pass')
+ conf_file='conf_file.conf'), 'root', 'pass', None)
+ mock_get_platform.assert_called_once_with()
+ @mock.patch('shaptools.hana.HanaInstance.get_platform')
@mock.patch('shaptools.shell.execute_cmd')
- def test_install_error(self, mock_execute):
+ def test_install_error(self, mock_execute, mock_get_platform):
proc_mock = mock.Mock()
proc_mock.returncode = 1
mock_execute.return_value = proc_mock
+ mock_get_platform.return_value = 'my_arch'
with self.assertRaises(hana.HanaError) as err:
hana.HanaInstance.install(
'software_path', 'conf_file.conf', 'root', 'pass')
mock_execute.assert_called_once_with(
- 'software_path/DATA_UNITS/HDB_LCM_LINUX_X86_64/hdblcm '
+ 'software_path/DATA_UNITS/HDB_LCM_LINUX_my_arch/hdblcm '
'-b --configfile={conf_file}'.format(
- conf_file='conf_file.conf'), 'root', 'pass')
+ conf_file='conf_file.conf'), 'root', 'pass', None)
+ mock_get_platform.assert_called_once_with()
self.assertTrue(
'SAP HANA installation failed' in str(err.exception))
@@ -233,7 +266,7 @@
self._hana.uninstall('root', 'pass')
mock_execute.assert_called_once_with(
- '/hana/shared/PRD/hdblcm/hdblcm --uninstall -b', 'root', 'pass')
+ '/hana/shared/PRD/hdblcm/hdblcm --uninstall -b', 'root', 'pass',
None)
@mock.patch('shaptools.shell.execute_cmd')
def test_uninstall_error(self, mock_execute):
@@ -245,20 +278,19 @@
self._hana.uninstall('root', 'pass', 'path')
mock_execute.assert_called_once_with(
- 'path/PRD/hdblcm/hdblcm --uninstall -b', 'root', 'pass')
+ 'path/PRD/hdblcm/hdblcm --uninstall -b', 'root', 'pass', None)
self.assertTrue(
'SAP HANA uninstallation failed' in str(err.exception))
@mock.patch('shaptools.shell.execute_cmd')
def test_is_running(self, mock_execute):
- proc_mock = mock.Mock()
- proc_mock.returncode = 0
- mock_execute.return_value = proc_mock
-
+ mock_command = mock.Mock()
+ self._hana._run_hana_command = mock_command
+ mock_result = mock.Mock(returncode=0)
+ self._hana._run_hana_command.return_value = mock_result
result = self._hana.is_running()
-
- mock_execute.assert_called_once_with('pidof hdb.sapPRD_HDB00')
+ mock_command.assert_called_once_with('pidof hdb.sapPRD_HDB00',
exception=False)
self.assertTrue(result)
@mock.patch('subprocess.Popen')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/tests/shapcli_test.py
new/shaptools-0.3.1/tests/shapcli_test.py
--- old/shaptools-0.3.0/tests/shapcli_test.py 2019-07-18 11:03:12.824176437
+0200
+++ new/shaptools-0.3.1/tests/shapcli_test.py 2019-07-23 13:12:24.071282674
+0200
@@ -79,6 +79,34 @@
assert logger == mock_logger_instance
+ @mock.patch('shaptools.shapcli.json.load')
+ @mock.patch('shaptools.shapcli.open')
+ def test_config_data(self, mock_open, mock_json_load):
+
+ mock_logger = mock.Mock()
+
+ data = shapcli.ConfigData({'sid': 'prd', 'instance': '00', 'password':
'pass'}, mock_logger)
+
+ assert data.sid == 'prd'
+ assert data.instance == '00'
+ assert data.password == 'pass'
+ assert data.remote == None
+
+ data = shapcli.ConfigData(
+ {'sid': 'prd', 'instance': '00', 'password': 'pass', 'remote':
'host'}, mock_logger)
+
+ assert data.sid == 'prd'
+ assert data.instance == '00'
+ assert data.password == 'pass'
+ assert data.remote == 'host'
+
+ with pytest.raises(KeyError) as err:
+ shapcli.ConfigData({'sid': 'prd', 'instance': '00'}, mock_logger)
+
+ mock_logger.error.assert_has_calls([
+ mock.call('Configuration file must have the sid, instance and
password entries')
+ ])
+
@mock.patch('argparse.ArgumentParser')
@mock.patch('shaptools.shapcli.parse_hana_arguments')
@mock.patch('shaptools.shapcli.parse_sr_arguments')
@@ -103,7 +131,7 @@
mock_argument_parser_instance.add_argument.assert_has_calls([
mock.call('-v', '--verbosity',
help='Python logging level. Options: DEBUG, INFO, WARN, ERROR
(INFO by default)'),
- mock.call('-r', '--remotely',
+ mock.call('-r', '--remote',
help='Run the command in other machine using ssh'),
mock.call('-c', '--config',
help='JSON configuration file with SAP HANA instance data
(sid, instance and password)'),
@@ -410,13 +438,13 @@
mock_hana_args = mock.Mock(sr='status', sapcontrol=True)
shapcli.run_sr_subcommands(mock_hana_instance, mock_hana_args,
mock_logger)
cmd = 'HDBSettings.sh systemReplicationStatus.py --sapcontrol=1'
- mock_hana_instance._run_hana_command.assert_called_once_with(cmd)
+ mock_hana_instance._run_hana_command.assert_called_once_with(cmd,
exception=False)
mock_hana_instance.reset_mock()
mock_hana_args = mock.Mock(sr='status', sapcontrol=False)
shapcli.run_sr_subcommands(mock_hana_instance, mock_hana_args,
mock_logger)
cmd = 'HDBSettings.sh systemReplicationStatus.py'
- mock_hana_instance._run_hana_command.assert_called_once_with(cmd)
+ mock_hana_instance._run_hana_command.assert_called_once_with(cmd,
exception=False)
mock_hana_instance.reset_mock()
mock_hana_args = mock.Mock(sr='disable')
@@ -467,23 +495,7 @@
mock_json_load.return_value = {'sid': 'prd', 'instance': '00',
'password': 'pass'}
data = shapcli.load_config_file('config.json', mock_logger)
- assert data[0] == 'prd'
- assert data[1] == '00'
- assert data[2] == 'pass'
-
- @mock.patch('shaptools.shapcli.json.load')
- @mock.patch('shaptools.shapcli.open')
- def test_load_config_file_error(self, mock_open, mock_json_load):
-
- mock_logger = mock.Mock()
- mock_json_load.return_value = {'sid': 'prd', 'instance': '00'}
-
- with pytest.raises(KeyError) as err:
- shapcli.load_config_file('config.json', mock_logger)
-
- mock_logger.error.assert_has_calls([
- mock.call('Configuration file must have the sid, instance and
password entries')
- ])
+ assert data == {'sid': 'prd', 'instance': '00', 'password': 'pass'}
@mock.patch('shaptools.shapcli.run_hana_subcommands')
@mock.patch('shaptools.shapcli.hana.HanaInstance')
@@ -496,12 +508,13 @@
mock_run_hana_subcommands):
mock_parser = mock.Mock()
- mock_args = mock.Mock(verbosity='INFO', config='config.json',
hana=True)
+ mock_args = mock.Mock(verbosity='INFO', config='config.json',
hana=True, remote=None)
mock_logger = mock.Mock()
mock_hana_instance = mock.Mock()
mock_parse_arguments.return_value = [mock_parser, mock_args]
mock_setup_logger.return_value = mock_logger
- mock_load_config_file.return_value = ['prd', '00', 'pass']
+ mock_load_config_file.return_value = {
+ 'sid': 'prd', 'instance': '00', 'password': 'pass', 'remote':
'host'}
mock_hana.return_value = mock_hana_instance
shapcli.run()
@@ -509,7 +522,7 @@
mock_parse_arguments.assert_called_once_with()
mock_setup_logger.assert_called_once_with('INFO')
mock_load_config_file.assert_called_once_with('config.json',
mock_logger)
- mock_hana.assert_called_once_with('prd', '00', 'pass')
+ mock_hana.assert_called_once_with('prd', '00', 'pass',
remote_host='host')
mock_run_hana_subcommands.assert_called_once_with(mock_hana_instance,
mock_args, mock_logger)
@mock.patch('shaptools.shapcli.run_sr_subcommands')
@@ -522,7 +535,8 @@
mock_parser = mock.Mock()
mock_args = mock.Mock(
- verbosity='INFO', config=False, sid='qas', instance='01',
password='mypass', sr=True)
+ verbosity='INFO', config=False, sid='qas', instance='01',
password='mypass',
+ remote='remote', sr=True)
mock_logger = mock.Mock()
mock_hana_instance = mock.Mock()
mock_parse_arguments.return_value = [mock_parser, mock_args]
@@ -533,7 +547,7 @@
mock_parse_arguments.assert_called_once_with()
mock_setup_logger.assert_called_once_with('INFO')
- mock_hana.assert_called_once_with('qas', '01', 'mypass')
+ mock_hana.assert_called_once_with('qas', '01', 'mypass',
remote_host='remote')
mock_run_sr_subcommands.assert_called_once_with(mock_hana_instance,
mock_args, mock_logger)
@mock.patch('shaptools.shapcli.hana.HanaInstance')
@@ -544,7 +558,7 @@
mock_parser = mock.Mock()
mock_args = mock.Mock(
- verbosity='INFO', config=False, sid='qas', instance='01',
password='mypass')
+ verbosity='INFO', config=False, sid='qas', instance='01',
password='mypass', remote=None)
mock_logger = mock.Mock()
mock_hana_instance = mock.Mock()
mock_parse_arguments.return_value = [mock_parser, mock_args]
@@ -555,7 +569,7 @@
mock_parse_arguments.assert_called_once_with()
mock_setup_logger.assert_called_once_with('INFO')
- mock_hana.assert_called_once_with('qas', '01', 'mypass')
+ mock_hana.assert_called_once_with('qas', '01', 'mypass',
remote_host=None)
mock_parser.print_help.assert_called_once_with()
@@ -580,7 +594,7 @@
mock_parse_arguments.assert_called_once_with()
mock_setup_logger.assert_called_once_with(logging.DEBUG)
mock_logger.info.assert_called_once_with(
- 'Configuration file or sid,instance and passwords parameters must
be provided\n')
+ 'Configuration file or sid, instance and passwords parameters must
be provided\n')
mock_parser.print_help.assert_called_once_with()
@mock.patch('shaptools.shapcli.run_sr_subcommands')
@@ -593,7 +607,8 @@
mock_parser = mock.Mock()
mock_args = mock.Mock(
- verbosity='INFO', config=False, sid='qas', instance='01',
password='mypass', sr=True)
+ verbosity='INFO', config=False, sid='qas', instance='01',
+ password='mypass', sr=True, remote=None)
mock_logger = mock.Mock()
mock_hana_instance = mock.Mock()
mock_parse_arguments.return_value = [mock_parser, mock_args]
@@ -609,5 +624,5 @@
mock_parse_arguments.assert_called_once_with()
mock_setup_logger.assert_called_once_with('INFO')
- mock_hana.assert_called_once_with('qas', '01', 'mypass')
+ mock_hana.assert_called_once_with('qas', '01', 'mypass',
remote_host=None)
mock_run_sr_subcommands.assert_called_once_with(mock_hana_instance,
mock_args, mock_logger)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shaptools-0.3.0/tests/shell_test.py
new/shaptools-0.3.1/tests/shell_test.py
--- old/shaptools-0.3.0/tests/shell_test.py 2019-07-18 11:03:12.824176437
+0200
+++ new/shaptools-0.3.1/tests/shell_test.py 2019-07-23 13:12:24.071282674
+0200
@@ -106,6 +106,19 @@
cmd = shell.format_su_cmd('hdbnsutil -sr_enable --name=PRAGUE',
'prdadm')
self.assertEqual('su -lc "hdbnsutil -sr_enable --name=PRAGUE" prdadm',
cmd)
+ def test_format_remote_cmd(self):
+ cmd = shell.format_remote_cmd('ls -la', 'remote', 'test')
+ self.assertEqual('ssh test@remote "bash --login -c \'ls -la\'"', cmd)
+
+ cmd = shell.format_remote_cmd('hdbnsutil -sr_enable --name=PRAGUE',
'remote', 'prdadm')
+ self.assertEqual(
+ 'ssh prdadm@remote "bash --login -c \'hdbnsutil -sr_enable
--name=PRAGUE\'"', cmd)
+
+ def test_format_remote_cmd_error(self):
+ with self.assertRaises(ValueError) as err:
+ shell.format_remote_cmd('ls -la', 'remote', None)
+ self.assertTrue('user must be provided' in str(err.exception))
+
def test_execute_cmd_popen(self):
# This test is used to check popen correct usage
result = shell.execute_cmd('ls -la')
@@ -139,6 +152,42 @@
self.assertEqual(mock_process_inst, result)
+ @mock.patch('shaptools.shell.format_remote_cmd')
+ @mock.patch('shaptools.shell.ProcessResult')
+ @mock.patch('subprocess.Popen')
+ @mock.patch('logging.Logger.debug')
+ def test_execute_cmd_remote(
+ self, logger, mock_popen, mock_process, mock_format):
+
+ mock_format.return_value = 'updated command'
+
+ mock_popen_inst = mock.Mock()
+ mock_popen_inst.returncode = 5
+ mock_popen_inst.communicate.return_value = (b'out', b'err')
+ mock_popen.return_value = mock_popen_inst
+
+ mock_process_inst = mock.Mock()
+ mock_process.return_value = mock_process_inst
+
+ result = shell.execute_cmd('ls -la', 'test', 'pass', 'remote')
+
+ logger.assert_has_calls([
+ mock.call('Executing command "%s" with user %s', 'ls -la', 'test'),
+ mock.call('Command updated to "%s"', 'updated command')
+ ])
+
+ mock_format.assert_called_once_with('ls -la', 'remote', 'test')
+
+ mock_popen.assert_called_once_with(
+ ['updated', 'command'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ mock_popen_inst.communicate.assert_called_once_with(input=b'pass')
+
+ mock_process.assert_called_once_with('updated command', 5, b'out',
b'err')
+
+ self.assertEqual(mock_process_inst, result)
+
@mock.patch('shaptools.shell.format_su_cmd')
@mock.patch('shaptools.shell.ProcessResult')
@mock.patch('subprocess.Popen')