-
Notifications
You must be signed in to change notification settings - Fork 5.6k
[modules/postgresql] modules for cluster management in postgresql (debian specific) #21314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
592e0a1
[modules/postgresql] modules for cluster management in postgresql (de…
arthurzenika f1111bd
lint
kiorky f34b2a3
Merge pull request #1 from makinacorpus/postgresql
arthurzenika 5f4905d
fix tests
kiorky 5f8d442
Merge pull request #2 from makinacorpus/postgresql
arthurzenika File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| # -*- coding: utf-8 -*- | ||
| ''' | ||
| Module to provide Postgres compatibility to salt for debian family specific tools. | ||
|
|
||
| ''' | ||
|
|
||
| # Import python libs | ||
| from __future__ import absolute_import | ||
| import logging | ||
| import pipes | ||
|
|
||
| # Import salt libs | ||
| import salt.utils | ||
|
|
||
| # Import 3rd-party libs | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| __virtualname__ = 'postgres' | ||
|
|
||
|
|
||
| def __virtual__(): | ||
| ''' | ||
| Only load this module if the pg_createcluster bin exists | ||
| ''' | ||
| if salt.utils.which('pg_createcluster'): | ||
| return __virtualname__ | ||
| return False | ||
|
|
||
|
|
||
| def cluster_create(version, | ||
| name='main', | ||
| port=None, | ||
| locale=None, | ||
| encoding=None, | ||
| datadir=None): | ||
| ''' | ||
| Adds a cluster to the Postgres server. | ||
|
|
||
| .. warning: | ||
|
|
||
| Only works for debian family distros so far. | ||
|
|
||
| CLI Example: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| salt '*' postgres.cluster_create '9.3' | ||
|
|
||
| salt '*' postgres.cluster_create '9.3' 'main' | ||
|
|
||
| salt '*' postgres.cluster_create '9.3' locale='fr_FR' | ||
|
|
||
| ''' | ||
| cmd = [salt.utils.which('pg_createcluster')] | ||
| if port: | ||
| cmd += ['--port', str(port)] | ||
| if locale: | ||
| cmd += ['--locale', locale] | ||
| if encoding: | ||
| cmd += ['--encoding', encoding] | ||
| if datadir: | ||
| cmd += ['--datadir', datadir] | ||
| cmd += [version, name] | ||
| cmdstr = ' '.join([pipes.quote(c) for c in cmd]) | ||
| ret = __salt__['cmd.run_all'](cmdstr, python_shell=False) | ||
| if ret.get('retcode', 0) != 0: | ||
| log.error('Error creating a Postgresql' | ||
| ' cluster {0}/{1}'.format(version, name)) | ||
| return False | ||
| return ret | ||
|
|
||
|
|
||
| def cluster_list(verbose=False): | ||
| ''' | ||
| Return a list of cluster of Postgres server (tuples of version and name). | ||
|
|
||
| CLI Example: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| salt '*' postgres.cluster_list | ||
|
|
||
| salt '*' postgres.cluster_list verbose=True | ||
| ''' | ||
| cmd = [salt.utils.which('pg_lsclusters'), '--no-header'] | ||
| ret = __salt__['cmd.run_all'](' '.join([pipes.quote(c) for c in cmd])) | ||
| if ret.get('retcode', 0) != 0: | ||
| log.error('Error listing clusters') | ||
| cluster_dict = _parse_pg_lscluster(ret['stdout']) | ||
| if verbose: | ||
| return cluster_dict | ||
| return cluster_dict.keys() | ||
|
|
||
|
|
||
| def cluster_exists(version, | ||
| name='main'): | ||
| ''' | ||
| Checks if a given version and name of a cluster exists. | ||
|
|
||
| CLI Example: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| salt '*' postgres.cluster_exists '9.3' | ||
|
|
||
| salt '*' postgres.cluster_exists '9.3' 'main' | ||
| ''' | ||
| return '{0}/{1}'.format(version, name) in cluster_list() | ||
|
|
||
|
|
||
| def cluster_remove(version, | ||
| name='main', | ||
| stop=False): | ||
| ''' | ||
| Remove a cluster on a Postgres server. By default it doesn't try | ||
| to stop the cluster. | ||
|
|
||
| CLI Example: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| salt '*' postgres.cluster_remove '9.3' | ||
|
|
||
| salt '*' postgres.cluster_remove '9.3' 'main' | ||
|
|
||
| salt '*' postgres.cluster_remove '9.3' 'main' stop=True | ||
|
|
||
| ''' | ||
| cmd = [salt.utils.which('pg_dropcluster')] | ||
| if stop: | ||
| cmd += ['--stop'] | ||
| cmd += [version, name] | ||
| cmdstr = ' '.join([pipes.quote(c) for c in cmd]) | ||
| ret = __salt__['cmd.run_all'](cmdstr, python_shell=False) | ||
| # FIXME - return Boolean ? | ||
| if ret.get('retcode', 0) != 0: | ||
| log.error('Error removing a Postgresql' | ||
| ' cluster {0}/{1}'.format(version, name)) | ||
| else: | ||
| ret['changes'] = ('Successfully removed' | ||
| ' cluster {0}/{1}').format(version, name) | ||
| return ret | ||
|
|
||
|
|
||
| def _parse_pg_lscluster(output): | ||
| ''' | ||
| Helper function to parse the output of pg_lscluster | ||
| ''' | ||
| cluster_dict = {} | ||
| for line in output.splitlines(): | ||
| version, name, port, status, user, datadir, log = ( | ||
| line.split()) | ||
| cluster_dict['{0}/{1}'.format(version, name)] = { | ||
| 'port': int(port), | ||
| 'status': status, | ||
| 'user': user, | ||
| 'datadir': datadir, | ||
| 'log': log} | ||
| return cluster_dict | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| # -*- coding: utf-8 -*- | ||
|
|
||
| # Import python libs | ||
| from __future__ import absolute_import, print_function | ||
| import os | ||
|
|
||
| # Import Salt Testing libs | ||
| from salttesting import skipIf, TestCase | ||
| from salttesting.helpers import ensure_in_syspath | ||
| from salttesting.mock import NO_MOCK, NO_MOCK_REASON, Mock, patch | ||
| ensure_in_syspath( | ||
| os.path.join(os.path.abspath(os.path.dirname(__file__)), '../../../')) | ||
| ensure_in_syspath( | ||
| os.path.join(os.path.abspath(os.path.dirname(__file__)), '../../')) | ||
|
|
||
| # Import salt libs | ||
| from salt.modules import deb_postgres | ||
|
|
||
| deb_postgres.__grains__ = None # in order to stub it w/patch below | ||
| deb_postgres.__salt__ = None # in order to stub it w/patch below | ||
|
|
||
|
|
||
| LSCLUSTER = '''\ | ||
| 8.4 main 5432 online postgres /srv/8.4/main \ | ||
| /var/log/postgresql/postgresql-8.4-main.log | ||
| 9.1 main 5433 online postgres /srv/9.1/main \ | ||
| /var/log/postgresql/postgresql-9.1-main.log | ||
| ''' | ||
| if NO_MOCK is False: | ||
| SALT_STUB = { | ||
| 'config.option': Mock(), | ||
| 'cmd.run_all': Mock(return_value={'stdout': LSCLUSTER}), | ||
| 'file.chown': Mock(), | ||
| 'file.remove': Mock(), | ||
| } | ||
| else: | ||
| SALT_STUB = {} | ||
|
|
||
|
|
||
| @skipIf(NO_MOCK, NO_MOCK_REASON) | ||
| @patch.multiple(deb_postgres, | ||
| __salt__=SALT_STUB) | ||
| @patch('salt.utils.which', Mock(return_value='/usr/bin/pg_createcluster')) | ||
| class PostgresClusterTestCase(TestCase): | ||
|
|
||
| def test_cluster_create(self): | ||
| deb_postgres.cluster_create( | ||
| '9.3', | ||
| 'main', | ||
| port='5432', | ||
| locale='fr_FR', | ||
| encoding='UTF-8', | ||
| datadir='/opt/postgresql' | ||
| ) | ||
| cmd = SALT_STUB['cmd.run_all'] | ||
|
|
||
| cmdstr = '/usr/bin/pg_createcluster ' \ | ||
| '--port 5432 --locale fr_FR --encoding UTF-8 ' \ | ||
| '--datadir /opt/postgresql ' \ | ||
| '9.3 main' | ||
| self.assertEqual(cmdstr, cmd.call_args[0][0]) | ||
|
|
||
| # XXX version should be a string but from cmdline you get a float | ||
| # def test_cluster_create_with_float(self): | ||
| # self.assertRaises(AssertionError, deb_postgres.cluster_create, | ||
| # (9.3,'main',), | ||
| # dict(port='5432', | ||
| # locale='fr_FR', | ||
| # encoding='UTF-8', | ||
| # datadir='/opt/postgresql')) | ||
|
|
||
|
|
||
| @skipIf(NO_MOCK, NO_MOCK_REASON) | ||
| @patch.multiple(deb_postgres, | ||
| __salt__=SALT_STUB) | ||
| @patch('salt.utils.which', Mock(return_value='/usr/bin/pg_lsclusters')) | ||
| class PostgresLsClusterTestCase(TestCase): | ||
|
|
||
| def test_parse_pg_lsclusters(self): | ||
| stdout = LSCLUSTER | ||
| self.maxDiff = None | ||
| self.assertDictEqual( | ||
| {('8.4/main'): { | ||
| 'port': 5432, | ||
| 'status': 'online', | ||
| 'user': 'postgres', | ||
| 'datadir': '/srv/8.4/main', | ||
| 'log': '/var/log/postgresql/postgresql-8.4-main.log'}, | ||
| ('9.1/main'): { | ||
| 'port': 5433, | ||
| 'status': 'online', | ||
| 'user': 'postgres', | ||
| 'datadir': '/srv/9.1/main', | ||
| 'log': '/var/log/postgresql/postgresql-9.1-main.log'}}, | ||
| deb_postgres._parse_pg_lscluster(stdout)) | ||
|
|
||
| def test_cluster_list(self): | ||
| return_list = deb_postgres.cluster_list() | ||
| cmd = SALT_STUB['cmd.run_all'] | ||
| self.assertEqual('/usr/bin/pg_lsclusters --no-header', | ||
| cmd.call_args[0][0]) | ||
| self.assertIsInstance(return_list, list) | ||
| return_dict = deb_postgres.cluster_list(verbose=True) | ||
| self.assertIsInstance(return_dict, dict) | ||
|
|
||
| def test_cluster_exists(self): | ||
| self.assertTrue(deb_postgres.cluster_exists('8.4') is True) | ||
| self.assertTrue(deb_postgres.cluster_exists('8.4', 'main') is True) | ||
| self.assertFalse(deb_postgres.cluster_exists('3.4', 'main')) | ||
|
|
||
|
|
||
| @skipIf(NO_MOCK, NO_MOCK_REASON) | ||
| @patch.multiple(deb_postgres, | ||
| __salt__=SALT_STUB) | ||
| @patch('salt.utils.which', Mock(return_value='/usr/bin/pg_dropcluster')) | ||
| class PostgresDeleteClusterTestCase(TestCase): | ||
|
|
||
| def test_cluster_delete(self): | ||
| deb_postgres.cluster_remove('9.3', 'main') | ||
| cmd = SALT_STUB['cmd.run_all'] | ||
| self.assertEqual('/usr/bin/pg_dropcluster 9.3 main', | ||
| cmd.call_args[0][0]) | ||
| deb_postgres.cluster_remove('9.3', 'main', stop=True) | ||
| cmd = SALT_STUB['cmd.run_all'] | ||
| self.assertEqual('/usr/bin/pg_dropcluster --stop 9.3 main', | ||
| cmd.call_args[0][0]) | ||
|
|
||
| if __name__ == '__main__': | ||
| from integration import run_tests | ||
| run_tests(PostgresClusterTestCase, PostgresLsClusterTestCase, | ||
| PostgresDeleteClusterTestCase, needs_daemon=False) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the virtualname conflicts with the 'postgres' module, what about postgres_cluster ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kiorky this was deliberate, the command line is then unified for postgres operations. But if this is not common pratice I'm can change that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhm, i though one of the modules declaring the same virtualname wins the name and then they are not merged together, but i can be wrong here.
Interresting, i'm testing this to verify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, modules are merged, something i did not known about the loader.
So yes, as there are no function no conflict, there is no issue left here.