[cubicweb/postgresql] fix creating cluster with correct encoding and correct initialisation when installing postgresql-9.4
authorArthur Lutz <arthur.lutz@logilab.fr>
Tue, 21 Jul 2015 15:39:17 +0200
changeset 49 9f0e63dcce57
parent 48 1c70821b96bf
child 50 0ad07261a1dc
[cubicweb/postgresql] fix creating cluster with correct encoding and correct initialisation when installing postgresql-9.4 jessie install fails when not finding locale, this could be fixed by finding which locale it is looking for and installing, but we want utf8 anyway, so better to put it here.
_modules/deb_postgres.py
_states/postgres_cluster.py
cubicweb/postgres/init.sls
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_modules/deb_postgres.py	Tue Jul 21 15:39:17 2015 +0200
@@ -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 += [str(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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_states/postgres_cluster.py	Tue Jul 21 15:39:17 2015 +0200
@@ -0,0 +1,210 @@
+# -*- coding: utf-8 -*-
+'''
+Management of PostgreSQL databases
+==================================
+
+The postgres_database module is used to create and manage Postgres databases.
+Databases can be set as either absent or present
+
+.. code-block:: yaml
+
+    create cluster 9.3 main:
+      postgres_cluster.present:
+          - name: 'main'
+          - version: '9.3'
+'''
+from __future__ import absolute_import
+
+
+def __virtual__():
+    '''
+    Only load if the deb_postgres module is present
+    '''
+    return 'postgres.cluster_exists' in __salt__
+
+
+def present(name,
+            version=None,
+            encoding=None,
+            lc_collate=None,
+            lc_ctype=None,
+            owner=None,
+            template=None,
+            user=None,
+            maintenance_db=None,
+            db_password=None,
+            db_host=None,
+            db_port=None,
+            db_user=None,
+            locale=None,
+            datadir=None):
+    '''
+    Ensure that the named cluster is present with the specified properties.
+    For more information about all of these options see man pg_createcluster(1)
+
+    name
+        The name of the cluster
+
+    version
+        Version of the postgresql cluster
+
+    encoding
+        The character encoding scheme to be used in this database
+
+    lc_collate
+        The LC_COLLATE setting to be used in this database
+
+    lc_ctype
+        The LC_CTYPE setting to be used in this database
+
+    owner
+        The username of the database owner
+
+    template
+        The template database from which to build this database
+
+    user
+        System user all operations should be performed on behalf of
+
+    db_user
+        database username if different from config or default
+
+    db_password
+        user password if any password for a specified user
+
+    db_host
+        Database host if different from config or default
+
+    db_port
+        Database port if different from config or default
+
+    locale
+        Locale with which to create cluster
+
+    datadir
+        Where the cluster is stored
+
+        .. versionadded:: Berylyum
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': 'Database {0} is already present'.format(name)}
+
+    db_args = {
+        'maintenance_db': maintenance_db,
+        'runas': user,
+        'host': db_host,
+        'user': db_user,
+        'port': db_port,
+        'password': db_password,
+    }
+    dbs = __salt__['postgres.db_list'](**db_args)
+    db_params = dbs.get(name, {})
+
+    if name in dbs and all((
+        db_params.get('Tablespace') == tablespace if tablespace else True,
+        (
+            db_params.get('Encoding').lower() == encoding.lower()
+            if encoding else True
+        ),
+        db_params.get('Collate') == lc_collate if lc_collate else True,
+        db_params.get('Ctype') == lc_ctype if lc_ctype else True,
+        db_params.get('Owner') == owner if owner else True
+    )):
+        return ret
+    elif name in dbs and any((
+        db_params.get('Encoding').lower() != encoding.lower() if encoding else False,
+        db_params.get('Collate') != lc_collate if lc_collate else False,
+        db_params.get('Ctype') != lc_ctype if lc_ctype else False
+    )):
+        ret['comment'] = 'Database {0} has wrong parameters ' \
+                         'which couldn\'t be changed on fly.'.format(name)
+        ret['result'] = False
+        return ret
+
+    # The database is not present, make it!
+    if __opts__['test']:
+        ret['result'] = None
+        if name not in dbs:
+            ret['comment'] = 'Database {0} is set to be created'.format(name)
+        else:
+            ret['comment'] = 'Database {0} exists, but parameters ' \
+                             'need to be changed'.format(name)
+        return ret
+    if (
+        name not in dbs and __salt__['postgres.cluster_create'](
+            version=version,
+            name=name,
+            port=db_port,
+            locale=locale,
+            encoding=encoding,
+            datadir=datadir)
+    ):
+        ret['comment'] = 'The cluster {0}/{1} has been created'.format(version,name)
+        ret['changes']['{0}/{1}'.format(version, name)] = 'Present'
+    else:
+        ret['comment'] = 'Failed to create database {0}'.format(name)
+        ret['result'] = False
+
+    return ret
+
+
+def absent(name,
+           user=None,
+           maintenance_db=None,
+           db_password=None,
+           db_host=None,
+           db_port=None,
+           db_user=None):
+    '''
+    Ensure that the named database is absent
+
+    name
+        The name of the database to remove
+
+    db_user
+        database username if different from config or defaul
+
+    db_password
+        user password if any password for a specified user
+
+    db_host
+        Database host if different from config or default
+
+    db_port
+        Database port if different from config or default
+
+    user
+        System user all operations should be performed on behalf of
+
+        .. versionadded:: 0.17.0
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': True,
+           'comment': ''}
+
+    db_args = {
+        'maintenance_db': maintenance_db,
+        'runas': user,
+        'host': db_host,
+        'user': db_user,
+        'port': db_port,
+        'password': db_password,
+    }
+    #check if db exists and remove it
+    if __salt__['postgres.db_exists'](name, **db_args):
+        if __opts__['test']:
+            ret['result'] = None
+            ret['comment'] = 'Database {0} is set to be removed'.format(name)
+            return ret
+        if __salt__['postgres.db_remove'](name, **db_args):
+            ret['comment'] = 'Database {0} has been removed'.format(name)
+            ret['changes'][name] = 'Absent'
+            return ret
+
+    # fallback
+    ret['comment'] = 'Database {0} is not present, so it cannot ' \
+                     'be removed'.format(name)
+    return ret
--- a/cubicweb/postgres/init.sls	Fri Jul 10 18:03:08 2015 +0200
+++ b/cubicweb/postgres/init.sls	Tue Jul 21 15:39:17 2015 +0200
@@ -23,8 +23,14 @@
     - require_in:
       - service: postgresql
   {% endif %}
+  postgres_cluster.present:
+    - name: 'main'
+    - version: {{ psql_version }}
+    - encoding: UTF8
   service.running:
     - name: postgresql
+    - require:
+      - postgres_cluster: pg-server
 
 postgresql-plpython-{{ psql_version }}:
   pkg.installed
@@ -47,6 +53,8 @@
     - template: jinja
     - context:
         users: {{ psql_users }}
+    - require:
+      - postgres_cluster: pg-server
     - watch_in:
         service: pg-server