mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
* fix bug in _run_psql() with password handling
* use password through PGPASSFILE file * fix checking maintenance_db and runas params: '' and None is equal * fix bug with escaping in psql_query(), hope default delemiter and quote is enough * psql_query() now returns list instead of dict, just according table nature * add gathering tablespace parameter of databases in db_list() * remove `locale` argument of db_create(), which is probably obsolete * add db_alter() function for changing db parameters * present() state now tries to fix db params if it's needed and possible (what we disscussed in issue #4164) * fix pep8, formatting and double quotes
This commit is contained in:
parent
120b4a08d9
commit
7def5edf63
@ -23,6 +23,7 @@ import pipes
|
||||
import logging
|
||||
import csv
|
||||
import StringIO
|
||||
import os
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
@ -49,28 +50,33 @@ def _run_psql(cmd, runas=None, password=None, host=None,
|
||||
if runas is None:
|
||||
if not host:
|
||||
host = __salt__['config.option']('postgres.host')
|
||||
if host is None or \
|
||||
host == '' or \
|
||||
host[0] == '/':
|
||||
if not host or host[0] == '/':
|
||||
if 'FreeBSD' in __grains__['os_family']:
|
||||
runas = 'pgsql'
|
||||
else:
|
||||
runas = 'postgres'
|
||||
|
||||
if runas is not None:
|
||||
kwargs["runas"] = runas
|
||||
if runas:
|
||||
kwargs['runas'] = runas
|
||||
|
||||
if password is not None:
|
||||
if password is None:
|
||||
password = __salt__['config.option']('postgres.pass')
|
||||
if password is not None:
|
||||
kwargs["env"] = {"PGPASSWORD": password}
|
||||
# PGPASSWORD has been deprecated, supposedly leading to
|
||||
# protests. Currently, this seems the simplest way to solve
|
||||
# this. If needed in the future, a tempfile could also be
|
||||
# written and the filename set to the PGPASSFILE variable. see
|
||||
# http://www.postgresql.org/docs/8.4/static/libpq-pgpass.html
|
||||
pgpassfile = salt.utils.mkstemp(text=True)
|
||||
with salt.utils.fopen(pgpassfile, 'w') as fp_:
|
||||
fp_.write('{0}:*:*:{1}:{2}'.format(
|
||||
'localhost' if not host or host[0] == '/' else host,
|
||||
runas if runas else '*',
|
||||
password
|
||||
))
|
||||
__salt__['file.chown'](pgpassfile, runas, '')
|
||||
kwargs['env'] = {'PGPASSFILE': pgpassfile}
|
||||
|
||||
return __salt__[run_cmd](cmd, **kwargs)
|
||||
ret = __salt__[run_cmd](cmd, **kwargs)
|
||||
if not __salt__['file.remove'](pgpassfile):
|
||||
log.warning('Remove PGPASSFILE failed')
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def version(user=None, host=None, port=None, maintenance_db=None,
|
||||
@ -138,34 +144,36 @@ def _psql_cmd(*args, **kwargs):
|
||||
cmd += ['--host', host]
|
||||
if port:
|
||||
cmd += ['--port', port]
|
||||
if maintenance_db is None:
|
||||
maintenance_db = "postgres"
|
||||
if not maintenance_db:
|
||||
maintenance_db = 'postgres'
|
||||
cmd += ['--dbname', maintenance_db]
|
||||
cmd += args
|
||||
cmdstr = ' '.join(map(pipes.quote, cmd))
|
||||
return cmdstr
|
||||
|
||||
|
||||
def psql_query(query, user=None, host=None, port=None, db=None,
|
||||
password=None, runas=None):
|
||||
def psql_query(query, user=None, host=None, port=None, maintenance_db=None,
|
||||
password=None, runas=None):
|
||||
'''
|
||||
Run an SQL-Query and return the results as a dictionary. This command
|
||||
Run an SQL-Query and return the results as a list. This command
|
||||
only supports SELECT statements.
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' postgres.psql_query 'select * from pg_stat_activity'
|
||||
'''
|
||||
ret = {}
|
||||
ret = []
|
||||
|
||||
csv_query = 'COPY ({0}) TO STDOUT WITH CSV DELIMITER \';\' QUOTE \'"\' HEADER'.format(query.strip().rstrip(';'))
|
||||
csv_query = 'COPY ({0}) TO STDOUT WITH CSV HEADER'.format(
|
||||
query.strip().rstrip(';'))
|
||||
|
||||
cmd = _psql_cmd(
|
||||
# always use the same datestyle settings to allow parsing dates
|
||||
# regardless what server settings are configured
|
||||
'-v', 'datestyle=ISO,MDY',
|
||||
'-c', csv_query,
|
||||
host=host, user=user, port=port, db=db, password=password)
|
||||
# always use the same datestyle settings to allow parsing dates
|
||||
# regardless what server settings are configured
|
||||
'-v', 'datestyle=ISO,MDY',
|
||||
'-c', csv_query,
|
||||
host=host, user=user, port=port, maintenance_db=maintenance_db,
|
||||
password=password)
|
||||
|
||||
cmdret = _run_psql(cmd, runas=runas, password=password)
|
||||
|
||||
@ -174,13 +182,14 @@ def psql_query(query, user=None, host=None, port=None, db=None,
|
||||
|
||||
csv_file = StringIO.StringIO(cmdret['stdout'])
|
||||
header = {}
|
||||
row_counter = 0
|
||||
for row in csv.reader(csv_file, delimiter=';', quotechar='"'):
|
||||
if row_counter == 0:
|
||||
for row in csv.reader(csv_file, delimiter=',', quotechar='"'):
|
||||
if not row:
|
||||
continue
|
||||
if not header:
|
||||
header = row
|
||||
else:
|
||||
ret[row_counter-1] = dict(zip(header, row))
|
||||
row_counter += 1
|
||||
continue
|
||||
ret.append(dict(zip(header, row)))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@ -201,13 +210,15 @@ def db_list(user=None, host=None, port=None, maintenance_db=None,
|
||||
query = 'SELECT datname as "Name", pga.rolname as "Owner", ' \
|
||||
'pg_encoding_to_char(encoding) as "Encoding", ' \
|
||||
'datcollate as "Collate", datctype as "Ctype", ' \
|
||||
'datacl as "Access privileges" FROM pg_database pgd, ' \
|
||||
'pg_roles pga WHERE pga.oid = pgd.datdba'
|
||||
'datacl as "Access privileges", spcname as "Tablespace" ' \
|
||||
'FROM pg_database pgd, pg_roles pga, pg_tablespace pgts ' \
|
||||
'WHERE pga.oid = pgd.datdba AND pgts.oid = pgd.dattablespace'
|
||||
|
||||
rows = psql_query(query, runas=runas,
|
||||
host=host, user=user, port=port, db=maintenance_db, password=password)
|
||||
rows = psql_query(query, runas=runas, host=host, user=user,
|
||||
port=port, maintenance_db=maintenance_db,
|
||||
password=password)
|
||||
|
||||
for row in rows.itervalues():
|
||||
for row in rows:
|
||||
ret[row['Name']] = row
|
||||
ret[row['Name']].pop('Name')
|
||||
|
||||
@ -238,7 +249,6 @@ def db_create(name,
|
||||
password=None,
|
||||
tablespace=None,
|
||||
encoding=None,
|
||||
locale=None,
|
||||
lc_collate=None,
|
||||
lc_ctype=None,
|
||||
owner=None,
|
||||
@ -287,6 +297,37 @@ def db_create(name,
|
||||
return ret['retcode'] == 0
|
||||
|
||||
|
||||
def db_alter(name, user=None, host=None, port=None, maintenance_db=None,
|
||||
password=None, tablespace=None, owner=None, runas=None):
|
||||
'''
|
||||
Change tablesbase or/and owner of databse.
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt '*' postgres.db_alter dbname owner=otheruser
|
||||
'''
|
||||
if not any((tablespace, owner)):
|
||||
return True # Nothing todo?
|
||||
|
||||
queries = []
|
||||
if owner:
|
||||
queries.append('ALTER DATABASE "{0}" OWNER TO "{1}"'.format(
|
||||
name, owner
|
||||
))
|
||||
if tablespace:
|
||||
queries.append('ALTER DATABASE "{0}" SET TABLESPACE "{1}"'.format(
|
||||
name, tablespace
|
||||
))
|
||||
for Q in queries:
|
||||
cmd = _psql_cmd('-c', Q, user=user, host=host, port=port,
|
||||
maintenance_db=maintenance_db, password=password)
|
||||
ret = _run_psql(cmd, runas=runas, password=password, host=host)
|
||||
if ret['retcode'] != 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def db_remove(name, user=None, host=None, port=None, maintenance_db=None,
|
||||
password=None, runas=None):
|
||||
'''
|
||||
@ -328,26 +369,33 @@ def user_list(user=None, host=None, port=None, maintenance_db=None,
|
||||
if len(ver) >= 2 and int(ver[0]) >= 9 and int(ver[1]) >= 1:
|
||||
query = (
|
||||
'SELECT rolname as "name", rolsuper as "superuser", '
|
||||
'rolinherit as "inherits privileges", rolcreaterole as "can create roles", '
|
||||
'rolcreatedb as "can create databases", rolcatupdate as "can update system catalogs", '
|
||||
'rolinherit as "inherits privileges", '
|
||||
'rolcreaterole as "can create roles", '
|
||||
'rolcreatedb as "can create databases", '
|
||||
'rolcatupdate as "can update system catalogs", '
|
||||
'rolcanlogin as "can login", rolreplication as "replication", '
|
||||
'rolconnlimit as "connections", rolvaliduntil::timestamp(0) as "expiry time", '
|
||||
'rolconfig as "defaults variables"'
|
||||
'rolconnlimit as "connections", '
|
||||
'rolvaliduntil::timestamp(0) as "expiry time", '
|
||||
'rolconfig as "defaults variables" '
|
||||
'FROM pg_roles'
|
||||
)
|
||||
else:
|
||||
query = (
|
||||
'SELECT rolname as "name", rolsuper as "superuser", '
|
||||
'rolinherit as "inherits privileges", rolcreaterole as "can create roles", '
|
||||
'rolcreatedb as "can create databases", rolcatupdate as "can update system catalogs", '
|
||||
'rolinherit as "inherits privileges", '
|
||||
'rolcreaterole as "can create roles", '
|
||||
'rolcreatedb as "can create databases", '
|
||||
'rolcatupdate as "can update system catalogs", '
|
||||
'rolcanlogin as "can login", NULL as "replication", '
|
||||
'rolconnlimit as "connections", rolvaliduntil::timestamp(0) as "expiry time", '
|
||||
'rolconfig as "defaults variables"'
|
||||
'rolconnlimit as "connections", '
|
||||
'rolvaliduntil::timestamp(0) as "expiry time", '
|
||||
'rolconfig as "defaults variables" '
|
||||
'FROM pg_roles'
|
||||
)
|
||||
|
||||
rows = psql_query(query, runas=runas,
|
||||
host=host, user=user, port=port, db=maintenance_db, password=password)
|
||||
rows = psql_query(query, runas=runas, host=host, user=user,
|
||||
port=port, maintenance_db=maintenance_db,
|
||||
password=password)
|
||||
|
||||
def get_bool(rowdict, key):
|
||||
if rowdict[key] == 't':
|
||||
@ -357,16 +405,16 @@ def user_list(user=None, host=None, port=None, maintenance_db=None,
|
||||
else:
|
||||
return None
|
||||
|
||||
for row in rows.itervalues():
|
||||
for row in rows:
|
||||
retrow = {}
|
||||
for key in ('superuser', 'inherits privileges', 'can create roles',
|
||||
'can create databases', 'can update system catalogs',
|
||||
'can login', 'replication', 'connections'):
|
||||
'can create databases', 'can update system catalogs',
|
||||
'can login', 'replication', 'connections'):
|
||||
retrow[key] = get_bool(row, key)
|
||||
for date_key in ('expiry time',):
|
||||
try:
|
||||
retrow[date_key] = datetime.datetime.strptime(
|
||||
row['date_key'], '%Y-%m-%d %H:%M:%S')
|
||||
row['date_key'], '%Y-%m-%d %H:%M:%S')
|
||||
except (ValueError, KeyError):
|
||||
retrow[date_key] = None
|
||||
retrow['defaults variables'] = row['defaults variables']
|
||||
|
@ -15,7 +15,6 @@ Databases can be set as either absent or present
|
||||
def present(name,
|
||||
tablespace=None,
|
||||
encoding=None,
|
||||
locale=None,
|
||||
lc_collate=None,
|
||||
lc_ctype=None,
|
||||
owner=None,
|
||||
@ -34,9 +33,6 @@ def present(name,
|
||||
encoding
|
||||
The character encoding scheme to be used in this database
|
||||
|
||||
locale
|
||||
The locale to be used in this database
|
||||
|
||||
lc_collate
|
||||
The LC_COLLATE setting to be used in this database
|
||||
|
||||
@ -57,26 +53,57 @@ def present(name,
|
||||
'result': True,
|
||||
'comment': 'Database {0} is already present'.format(name)}
|
||||
|
||||
# check if database exists
|
||||
if __salt__['postgres.db_exists'](name, runas=runas):
|
||||
dbs = __salt__['postgres.db_list'](runas=runas)
|
||||
db_params = dbs.get(name, {})
|
||||
|
||||
if all((
|
||||
db_params.get('Tablespace') == tablespace if tablespace else True,
|
||||
db_params.get('Encoding') == encoding 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') != encoding 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
|
||||
ret['comment'] = 'Database {0} is set to be created'.format(name)
|
||||
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 __salt__['postgres.db_create'](name,
|
||||
tablespace=tablespace,
|
||||
encoding=encoding,
|
||||
locale=locale,
|
||||
lc_collate=lc_collate,
|
||||
lc_ctype=lc_ctype,
|
||||
owner=owner,
|
||||
template=template,
|
||||
runas=runas):
|
||||
if name not in dbs and __salt__['postgres.db_create'](
|
||||
name,
|
||||
tablespace=tablespace,
|
||||
encoding=encoding,
|
||||
lc_collate=lc_collate,
|
||||
lc_ctype=lc_ctype,
|
||||
owner=owner,
|
||||
template=template,
|
||||
runas=runas):
|
||||
ret['comment'] = 'The database {0} has been created'.format(name)
|
||||
ret['changes'][name] = 'Present'
|
||||
elif name in dbs and __salt__['postgres.db_alter'](name,
|
||||
tablespace=tablespace,
|
||||
owner=owner):
|
||||
ret['comment'] = ('Parameters for database {0} have been changed'
|
||||
).format(name)
|
||||
ret['changes'][name] = 'Parameters changed'
|
||||
elif name in dbs:
|
||||
ret['comment'] = ('Failed to change parameters for database {0}'
|
||||
).format(name)
|
||||
ret['result'] = False
|
||||
else:
|
||||
ret['comment'] = 'Failed to create database {0}'.format(name)
|
||||
ret['result'] = False
|
||||
|
Loading…
Reference in New Issue
Block a user