Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions salt/modules/deb_postgres.py
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'
Copy link
Contributor

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 ?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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.



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
131 changes: 131 additions & 0 deletions tests/unit/modules/deb_postgres_test.py
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)