Merge branch 'pgsql' into develop

Conflicts:
	salt/modules/postgres.py
This commit is contained in:
Denis Generalov 2013-02-13 14:54:30 +04:00
commit aa8b50b92e
4 changed files with 227 additions and 177 deletions

View File

@ -16,6 +16,7 @@ Module to provide Postgres compatibility to salt.
'''
# Import python libs
from datetime import datetime
import pipes
import logging
@ -35,23 +36,27 @@ def __virtual__():
return False
def version():
def version(user=None, host=None, port=None, db=None, runas=None):
'''
Return the version of a Postgres server using the output
from the ``psql --version`` cmd.
Return the version of a Postgres server.
CLI Example::
salt '*' postgres.version
'''
version_line = __salt__['cmd.run']('psql --version').split("\n")[0]
name = version_line.split(" ")[1]
ver = version_line.split(" ")[2]
return '{0} {1}'.format(name, ver)
query = 'SELECT setting FROM pg_catalog.pg_settings ' \
'WHERE name = \'server_version\''
cmd = _psql_cmd('-c', query, '-t',
host=host, user=user, port=port, db=db)
ret = __salt__['cmd.run_all'](cmd, runas=runas)
def _connection_defaults(user=None, host=None, port=None):
for line in ret['stdout'].splitlines():
return line
def _connection_defaults(user=None, host=None, port=None, db=None):
'''
Returns a tuple of (user, host, port) with config, pillar, or default
Returns a tuple of (user, host, port, db) with config, pillar, or default
values assigned to missing values.
'''
if not user:
@ -60,8 +65,11 @@ def _connection_defaults(user=None, host=None, port=None):
host = __salt__['config.option']('postgres.host')
if not port:
port = __salt__['config.option']('postgres.port')
if not db:
db = __salt__['config.option']('postgres.db')
return (user, host, port, db)
return (user, host, port)
def _psql_cmd(*args, **kwargs):
'''
@ -70,16 +78,22 @@ def _psql_cmd(*args, **kwargs):
Accept optional keyword arguments: user, host and port as well as any
number or positional arguments to be added to the end of command.
'''
(user, host, port) = _connection_defaults(kwargs.get('user'),
kwargs.get('host'),
kwargs.get('port'))
cmd = ['psql', '--no-align', '--no-readline', '--no-password']
(user, host, port, db) = _connection_defaults(kwargs.get('user'),
kwargs.get('host'),
kwargs.get('port'),
kwargs.get('db'))
cmd = [salt.utils.which('psql'),
'--no-align',
'--no-readline',
'--no-password']
if user:
cmd += ['--username', user]
if host:
cmd += ['--host', host]
if port:
cmd += ['--port', port]
if db:
cmd += ['--dbname', db]
cmd += args
cmdstr = ' '.join(map(pipes.quote, cmd))
return cmdstr
@ -87,41 +101,48 @@ def _psql_cmd(*args, **kwargs):
# Database related actions
def db_list(user=None, host=None, port=None, runas=None):
def db_list(user=None, host=None, port=None, db=None, runas=None):
'''
Return a list of databases of a Postgres server using the output
from the ``psql -l`` query.
Return dictionary with information about databases of a Postgres server.
CLI Example::
salt '*' postgres.db_list
'''
(user, host, port) = _connection_defaults(user, host, port)
ret = []
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_authid pga WHERE pga.oid = pgd.datdba"""
ret = {}
header = ['Name',
'Owner',
'Encoding',
'Collate',
'Ctype',
'Access privileges']
cmd = _psql_cmd('-c', query,
host=host, user=user, port=port)
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_authid pga WHERE pga.oid = pgd.datdba'
cmdret = __salt__['cmd.run'](cmd, runas=runas)
lines = [x for x in cmdret.splitlines() if len(x.split("|")) == 6]
if not lines:
log.error("no results from postgres.db_list")
else:
log.debug(lines)
header = [x.strip() for x in lines[0].split("|")]
for line in lines[1:]:
line = [x.strip() for x in line.split("|")]
if not line[0] == "":
ret.append(list(zip(header[:-1], line[:-1])))
cmd = _psql_cmd('-c', query, '-t',
host=host, user=user, port=port, db=db)
cmdret = __salt__['cmd.run_all'](cmd, runas=runas)
if cmdret['retcode'] > 0:
return ret
for line in cmdret['stdout'].splitlines():
if line.count('|') != 5:
log.warning('Unexpected string: {0}'.format(line))
continue
comps = line.split('|')
ret[comps[0]] = dict(zip(header[1:], comps[1:]))
return ret
def db_exists(name, user=None, host=None, port=None, runas=None):
def db_exists(name, user=None, host=None, port=None, db=None, runas=None):
'''
Checks if a database exists on the Postgres server.
@ -129,20 +150,16 @@ def db_exists(name, user=None, host=None, port=None, runas=None):
salt '*' postgres.db_exists 'dbname'
'''
(user, host, port) = _connection_defaults(user, host, port)
databases = db_list(user=user, host=host, port=port, runas=runas)
for database in databases:
if name == dict(database).get('Name'):
return True
return False
databases = db_list(user=user, host=host, port=port, db=db, runas=runas)
return name in databases
def db_create(name,
user=None,
host=None,
port=None,
db=None,
tablespace=None,
encoding=None,
locale=None,
@ -161,18 +178,6 @@ def db_create(name,
salt '*' postgres.db_create 'dbname' template=template_postgis
'''
(user, host, port) = _connection_defaults(user, host, port)
# check if db exists
if db_exists(name, user, host, port, runas=runas):
log.info("DB '{0}' already exists".format(name,))
return False
# check if template exists
if template:
if not db_exists(template, user, host, port, runas=runas):
log.info("template '{0}' does not exist.".format(template, ))
return False
# Base query to create a database
query = 'CREATE DATABASE "{0}"'.format(name)
@ -183,9 +188,9 @@ def db_create(name,
# doesn't get thrown by dashes in the name
'OWNER': owner and '"{0}"'.format(owner),
'TEMPLATE': template,
'ENCODING': encoding and "'{0}'".format(encoding),
'LC_COLLATE': lc_collate and "'{0}'".format(lc_collate),
'LC_CTYPE': lc_ctype and "'{0}'".format(lc_ctype),
'ENCODING': encoding and '\'{0}\''.format(encoding),
'LC_COLLATE': lc_collate and '\'{0}\''.format(lc_collate),
'LC_CTYPE': lc_ctype and '\'{0}\''.format(lc_ctype),
'TABLESPACE': tablespace,
}
with_chunks = []
@ -198,18 +203,13 @@ def db_create(name,
query += ' '.join(with_chunks)
# Execute the command
cmd = _psql_cmd('-c', query, user=user, host=host, port=port)
__salt__['cmd.run'](cmd, runas=runas)
cmd = _psql_cmd('-c', query, user=user, host=host, port=port, db=db)
ret = __salt__['cmd.run_all'](cmd, runas=runas)
# Check the result
if db_exists(name, user, host, port, runas=runas):
return True
else:
log.info("Failed to create DB '{0}'".format(name,))
return False
return ret['retcode'] == 0
def db_remove(name, user=None, host=None, port=None, runas=None):
def db_remove(name, user=None, host=None, port=None, db=None, runas=None):
'''
Removes a databases from the Postgres server.
@ -217,56 +217,88 @@ def db_remove(name, user=None, host=None, port=None, runas=None):
salt '*' postgres.db_remove 'dbname'
'''
(user, host, port) = _connection_defaults(user, host, port)
# check if db exists
if not db_exists(name, user, host, port, runas=runas):
log.info("DB '{0}' does not exist".format(name,))
return False
# db doesnt exist, proceed
query = 'DROP DATABASE {0}'.format(name)
cmd = _psql_cmd('-c', query, user=user, host=host, port=port)
__salt__['cmd.run'](cmd, runas=runas)
if not db_exists(name, user, host, port, runas=runas):
return True
else:
log.info("Failed to delete DB '{0}'.".format(name, ))
return False
cmd = _psql_cmd('-c', query, user=user, host=host, port=port, db=db)
ret = __salt__['cmd.run_all'](cmd, runas=runas)
return ret['retcode'] == 0
# User related actions
def user_list(user=None, host=None, port=None, runas=None):
def user_list(user=None, host=None, port=None, db=None, runas=None):
'''
Return a list of users of a Postgres server.
Return a dict with information about users of a Postgres server.
CLI Example::
salt '*' postgres.user_list
'''
(user, host, port) = _connection_defaults(user, host, port)
ret = []
query = (
'''SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
rolcatupdate, rolcanlogin, rolreplication, rolconnlimit, rolvaliduntil, rolconfig, oid
FROM pg_roles'''
)
cmd = _psql_cmd('-c', query,
host=host, user=user, port=port)
ret = {}
header = ['name',
'superuser',
'inherits privileges',
'can create roles',
'can create databases',
'can update system catalogs',
'can login',
'replication',
'connections',
'expiry time',
'defaults variables']
cmdret = __salt__['cmd.run'](cmd, runas=runas)
lines = [x for x in cmdret.splitlines() if len(x.split("|")) == 12]
log.debug(lines)
header = [x.strip() for x in lines[0].split("|")]
for line in lines[1:]:
line = [x.strip() for x in line.split("|")]
if not line[0] == "":
ret.append(list(zip(header[:-1], line[:-1])))
ver = version(user=None,
host=None,
port=None,
db=None,
runas=None).split('.')
if len(ver) >= 2 and ver[0] >= 9 and ver[1] >= 1:
query = (
'SELECT rolname, rolsuper, rolinherit, rolcreaterole, '
'rolcreatedb, rolcatupdate, rolcanlogin, rolreplication, '
'rolconnlimit, rolvaliduntil::timestamp(0), rolconfig '
'FROM pg_roles'
)
else:
query = (
'SELECT rolname, rolsuper, rolinherit, rolcreaterole, '
'rolcreatedb, rolcatupdate, rolcanlogin, NULL, '
'rolconnlimit, rolvaliduntil::timestamp(0), rolconfig '
'FROM pg_roles'
)
cmd = _psql_cmd('-c', query, '-t',
host=host, user=user, port=port, db=db)
cmdret = __salt__['cmd.run_all'](cmd, runas=runas)
if cmdret['retcode'] > 0:
return ret
for line in cmdret['stdout'].splitlines():
comps = line.split('|')
# type casting
for i in range(1, 8):
if comps[i] == 't':
comps[i] = True
elif comps[i] == 'f':
comps[i] = False
else:
comps[i] = None
comps[8] = int(comps[8])
if comps[9]:
comps[9] = datetime.strptime(comps[9], '%Y-%m-%d %H:%M:%S')
else:
comps[9] = None
if not comps[10]:
comps[10] = None
ret[comps[0]] = dict(zip(header[1:], comps[1:]))
return ret
def user_exists(name, user=None, host=None, port=None, runas=None):
def user_exists(name, user=None, host=None, port=None, db=None, runas=None):
'''
Checks if a user exists on the Postgres server.
@ -274,29 +306,20 @@ def user_exists(name, user=None, host=None, port=None, runas=None):
salt '*' postgres.user_exists 'username'
'''
(user, host, port) = _connection_defaults(user, host, port)
query = (
"SELECT true "
"FROM pg_roles "
"WHERE EXISTS "
"(SELECT rolname WHERE rolname='{role}')".format(role=name)
)
cmd = _psql_cmd('-c', query, host=host, user=user, port=port)
cmdret = __salt__['cmd.run'](cmd, runas=runas)
log.debug(cmdret.splitlines())
try:
val = cmdret.splitlines()[1]
except IndexError:
log.error("Invalid PostgreSQL result: '%s'", cmdret)
return False
return True if val.strip() == 't' else False
return name in user_list(user=user,
host=host,
port=port,
db=db,
runas=runas)
def _role_create(name,
login,
user=None,
host=None,
port=None,
db=None,
createdb=False,
createuser=False,
encrypted=False,
@ -309,7 +332,6 @@ def _role_create(name,
Creates a Postgres role. Users and Groups are both roles in postgres.
However, users can login, groups cannot.
'''
(user, host, port) = _connection_defaults(user, host, port)
if login:
create_type = 'USER'
@ -317,37 +339,39 @@ def _role_create(name,
create_type = 'ROLE'
# check if role exists
if user_exists(name, user, host, port, runas=runas):
log.info("{0} '{1}' already exists".format(create_type, name,))
if user_exists(name, user, host, port, db, runas=runas):
log.info('{0} \'{1}\' already exists'.format(create_type, name,))
return False
sub_cmd = 'CREATE {0} "{1}" WITH'.format(create_type, name, )
if password:
if encrypted:
sub_cmd = "{0} ENCRYPTED".format(sub_cmd, )
escaped_password = password.replace("'", "''")
sub_cmd = "{0} PASSWORD '{1}'".format(sub_cmd, escaped_password)
sub_cmd = '{0} ENCRYPTED'.format(sub_cmd, )
escaped_password = password.replace('\'', '\'\'')
sub_cmd = '{0} PASSWORD \'{1}\''.format(sub_cmd, escaped_password)
if createdb:
sub_cmd = "{0} CREATEDB".format(sub_cmd, )
sub_cmd = '{0} CREATEDB'.format(sub_cmd, )
if createuser:
sub_cmd = "{0} CREATEUSER".format(sub_cmd, )
sub_cmd = '{0} CREATEUSER'.format(sub_cmd, )
if superuser:
sub_cmd = "{0} SUPERUSER".format(sub_cmd, )
sub_cmd = '{0} SUPERUSER'.format(sub_cmd, )
if replication:
sub_cmd = "{0} REPLICATION".format(sub_cmd, )
sub_cmd = '{0} REPLICATION'.format(sub_cmd, )
if groups:
sub_cmd = "{0} IN GROUP {1}".format(sub_cmd, groups, )
sub_cmd = '{0} IN GROUP {1}'.format(sub_cmd, groups, )
if sub_cmd.endswith("WITH"):
sub_cmd = sub_cmd.replace(" WITH", "")
if sub_cmd.endswith('WITH'):
sub_cmd = sub_cmd.replace(' WITH', '')
cmd = _psql_cmd('-c', sub_cmd, host=host, user=user, port=port)
cmd = _psql_cmd('-c', sub_cmd, host=host, user=user, port=port, db=db)
return __salt__['cmd.run'](cmd, runas=runas)
def user_create(username,
user=None,
host=None,
port=None,
db=None,
createdb=False,
createuser=False,
encrypted=False,
@ -368,6 +392,7 @@ def user_create(username,
user,
host,
port,
db,
createdb,
createuser,
encrypted,
@ -377,53 +402,56 @@ def user_create(username,
groups,
runas)
def _role_update(name,
user=None,
host=None,
port=None,
createdb=False,
createuser=False,
encrypted=False,
replication=False,
password=None,
groups=None,
runas=None):
user=None,
host=None,
port=None,
db=None,
createdb=False,
createuser=False,
encrypted=False,
replication=False,
password=None,
groups=None,
runas=None):
'''
Updates a postgres role.
'''
(user, host, port) = _connection_defaults(user, host, port)
# check if user exists
if not user_exists(name, user, host, port, runas=runas):
log.info("User '{0}' does not exist".format(name,))
if not user_exists(name, user, host, port, db, runas=runas):
log.info('User \'{0}\' does not exist'.format(name,))
return False
sub_cmd = "ALTER ROLE {0} WITH".format(name, )
sub_cmd = 'ALTER ROLE {0} WITH'.format(name, )
if password:
sub_cmd = "{0} PASSWORD '{1}'".format(sub_cmd, password)
sub_cmd = '{0} PASSWORD \'{1}\''.format(sub_cmd, password)
if createdb:
sub_cmd = "{0} CREATEDB".format(sub_cmd, )
sub_cmd = '{0} CREATEDB'.format(sub_cmd, )
if createuser:
sub_cmd = "{0} CREATEUSER".format(sub_cmd, )
sub_cmd = '{0} CREATEUSER'.format(sub_cmd, )
if encrypted:
sub_cmd = "{0} ENCRYPTED".format(sub_cmd, )
sub_cmd = '{0} ENCRYPTED'.format(sub_cmd, )
if encrypted:
sub_cmd = "{0} REPLICATION".format(sub_cmd, )
sub_cmd = '{0} REPLICATION'.format(sub_cmd, )
if sub_cmd.endswith("WITH"):
sub_cmd = sub_cmd.replace(" WITH", "")
if sub_cmd.endswith('WITH'):
sub_cmd = sub_cmd.replace(' WITH', '')
if groups:
for group in groups.split(','):
sub_cmd = "{0}; GRANT {1} TO {2}".format(sub_cmd, group, name)
sub_cmd = '{0}; GRANT {1} TO {2}'.format(sub_cmd, group, name)
cmd = _psql_cmd('-c', sub_cmd, host=host, user=user, port=port)
cmd = _psql_cmd('-c', sub_cmd, host=host, user=user, port=port, db=db)
return __salt__['cmd.run'](cmd, runas=runas)
def user_update(username,
user=None,
host=None,
port=None,
db=None,
createdb=False,
createuser=False,
encrypted=False,
@ -442,6 +470,7 @@ def user_update(username,
user,
host,
port,
db,
createdb,
createuser,
encrypted,
@ -450,27 +479,33 @@ def user_update(username,
groups,
runas)
def _role_remove(name, user=None, host=None, port=None, runas=None):
def _role_remove(name, user=None, host=None, port=None, db=None, runas=None):
'''
Removes a role from the Postgres Server
'''
(user, host, port) = _connection_defaults(user, host, port)
# check if user exists
if not user_exists(name, user, host, port, runas=runas):
log.info("User '{0}' does not exist".format(name,))
if not user_exists(name, user, host, port, db, runas=runas):
log.info('User \'{0}\' does not exist'.format(name,))
return False
# user exists, proceed
sub_cmd = 'DROP ROLE {0}'.format(name)
cmd = _psql_cmd('-c', sub_cmd, host=host, user=user, port=port)
cmd = _psql_cmd('-c', sub_cmd, host=host, user=user, port=port, db=db)
__salt__['cmd.run'](cmd, runas=runas)
if not user_exists(name, user, host, port, runas=runas):
if not user_exists(name, user, host, port, db, runas=runas):
return True
else:
log.info("Failed to delete user '{0}'.".format(name, ))
log.info('Failed to delete user \'{0}\'.'.format(name, ))
def user_remove(username, user=None, host=None, port=None, runas=None):
def user_remove(username,
user=None,
host=None,
port=None,
db=None,
runas=None):
'''
Removes a user from the Postgres server.
@ -478,7 +513,8 @@ def user_remove(username, user=None, host=None, port=None, runas=None):
salt '*' postgres.user_remove 'username'
'''
return _role_remove(username, user, host, port, runas)
return _role_remove(username, user, host, port, db, runas)
# Group related actions
@ -486,6 +522,7 @@ def group_create(groupname,
user=None,
host=None,
port=None,
db=None,
createdb=False,
createuser=False,
encrypted=False,
@ -507,6 +544,7 @@ def group_create(groupname,
user,
host,
port,
db,
createdb,
createuser,
encrypted,
@ -516,10 +554,12 @@ def group_create(groupname,
groups,
runas)
def group_update(groupname,
user=None,
host=None,
port=None,
db=None,
createdb=False,
createuser=False,
encrypted=False,
@ -538,6 +578,7 @@ def group_update(groupname,
user,
host,
port,
db,
createdb,
createuser,
encrypted,
@ -546,7 +587,13 @@ def group_update(groupname,
groups,
runas)
def group_remove(groupname, user=None, host=None, port=None, runas=None):
def group_remove(groupname,
user=None,
host=None,
port=None,
db=None,
runas=None):
'''
Removes a group from the Postgres server.
@ -554,4 +601,4 @@ def group_remove(groupname, user=None, host=None, port=None, runas=None):
salt '*' postgres.group_remove 'groupname'
'''
return _role_remove(groupname, user, host, port, runas)
return _role_remove(groupname, user, host, port, db, runas)

View File

@ -67,14 +67,14 @@ def present(name,
ret['comment'] = 'Database {0} is set to be created'.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):
tablespace=tablespace,
encoding=encoding,
locale=locale,
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'
else:
@ -111,7 +111,6 @@ def absent(name, runas=None):
return ret
# fallback
ret['comment'] = (
'Database {0} is not present, so it cannot be removed'
).format(name)
ret['comment'] = 'Database {0} is not present, so it cannot ' \
'be removed'.format(name)
return ret

View File

@ -10,6 +10,7 @@ The postgres_group module is used to create and manage Postgres groups.
postgres_group.present
'''
def present(name,
createdb=False,
createuser=False,
@ -107,6 +108,7 @@ def absent(name, runas=None):
ret['changes'][name] = 'Absent'
return ret
else:
ret['comment'] = 'Group {0} is not present, so it cannot be removed'.format(name)
ret['comment'] = 'Group {0} is not present, so it cannot ' \
'be removed'.format(name)
return ret

View File

@ -10,6 +10,7 @@ The postgres_users module is used to create and manage Postgres users.
postgres_user.present
'''
def present(name,
createdb=False,
createuser=False,
@ -107,6 +108,7 @@ def absent(name, runas=None):
ret['changes'][name] = 'Absent'
return ret
else:
ret['comment'] = 'User {0} is not present, so it cannot be removed'.format(name)
ret['comment'] = 'User {0} is not present, so it cannot ' \
'be removed'.format(name)
return ret