Merge pull request #40809 from gtmanfred/develop

Add zookeeper managing module and state
This commit is contained in:
Mike Place 2017-04-21 12:04:25 -06:00 committed by GitHub
commit 26f5482576
6 changed files with 1019 additions and 63 deletions

View File

@ -0,0 +1,6 @@
======================
salt.modules.zookeeper
======================
.. automodule:: salt.modules.zookeeper
:members:

View File

@ -0,0 +1,6 @@
=====================
salt.states.zookeeper
=====================
.. automodule:: salt.states.zookeeper
:members:

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
'''
:depends: kazoo
:configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions.
Concurrency controls in zookeeper
=========================================================================
@ -13,7 +16,7 @@ import logging
import sys
try:
from kazoo.client import KazooClient
import kazoo.client
from kazoo.retry import (
ForceRetryError
@ -90,8 +93,6 @@ try:
except ImportError:
HAS_DEPS = False
ZK_CONNECTION = None
SEMAPHORE_MAP = {}
__virtualname__ = 'zk_concurrency'
@ -100,33 +101,67 @@ def __virtual__():
if not HAS_DEPS:
return (False, "Module zk_concurrency: dependencies failed")
__context__['semaphore_map'] = {}
return __virtualname__
def _get_zk_conn(hosts):
global ZK_CONNECTION
if ZK_CONNECTION is None:
ZK_CONNECTION = KazooClient(hosts=hosts)
ZK_CONNECTION.start()
def _get_zk_conn(profile=None, **connection_args):
if profile:
prefix = 'zookeeper:' + profile
else:
prefix = 'zookeeper'
return ZK_CONNECTION
def get(key, default=None):
'''
look in connection_args first, then default to config file
'''
return connection_args.get(key) or __salt__['config.get'](':'.join([prefix, key]), default)
hosts = get('hosts', '127.0.0.1:2181')
scheme = get('scheme', None)
username = get('username', None)
password = get('password', None)
default_acl = get('default_acl', None)
def _close_zk_conn():
global ZK_CONNECTION
if ZK_CONNECTION is None:
return
if isinstance(hosts, list):
hosts = ','.join(hosts)
ZK_CONNECTION.stop()
ZK_CONNECTION = None
if username is not None and password is not None and scheme is None:
scheme = 'digest'
auth_data = None
if scheme and username and password:
auth_data = [(scheme, ':'.join([username, password]))]
if default_acl is not None:
if isinstance(default_acl, list):
default_acl = [__salt__['zookeeper.make_digest_acl'](**acl) for acl in default_acl]
else:
default_acl = [__salt__['zookeeper.make_digest_acl'](**default_acl)]
__context__.setdefault('zkconnection', {}).setdefault(profile or hosts,
kazoo.client.KazooClient(hosts=hosts,
default_acl=default_acl,
auth_data=auth_data))
if not __context__['zkconnection'][profile or hosts].connected:
__context__['zkconnection'][profile or hosts].start()
return __context__['zkconnection'][profile or hosts]
def lock_holders(path,
zk_hosts,
zk_hosts=None,
identifier=None,
max_concurrency=1,
timeout=None,
ephemeral_lease=False):
ephemeral_lease=False,
profile=None,
scheme=None,
username=None,
password=None,
default_acl=None):
'''
Return an un-ordered list of lock holders
@ -154,24 +189,27 @@ def lock_holders(path,
salt minion zk_concurrency.lock_holders /lock/path host1:1234,host2:1234
'''
zk = _get_zk_conn(zk_hosts)
if path not in SEMAPHORE_MAP:
SEMAPHORE_MAP[path] = _Semaphore(zk,
path,
identifier,
zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
if path not in __context__['semaphore_map']:
__context__['semaphore_map'][path] = _Semaphore(zk, path, identifier,
max_leases=max_concurrency,
ephemeral_lease=ephemeral_lease)
return SEMAPHORE_MAP[path].lease_holders()
return __context__['semaphore_map'][path].lease_holders()
def lock(path,
zk_hosts,
zk_hosts=None,
identifier=None,
max_concurrency=1,
timeout=None,
ephemeral_lease=False,
force=False, # foricble get the lock regardless of open slots
profile=None,
scheme=None,
username=None,
password=None,
default_acl=None,
):
'''
Get lock (with optional timeout)
@ -203,35 +241,39 @@ def lock(path,
salt minion zk_concurrency.lock /lock/path host1:1234,host2:1234
'''
zk = _get_zk_conn(zk_hosts)
if path not in SEMAPHORE_MAP:
SEMAPHORE_MAP[path] = _Semaphore(zk,
path,
identifier,
zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
if path not in __context__['semaphore_map']:
__context__['semaphore_map'][path] = _Semaphore(zk, path, identifier,
max_leases=max_concurrency,
ephemeral_lease=ephemeral_lease)
# forcibly get the lock regardless of max_concurrency
if force:
SEMAPHORE_MAP[path].assured_path = True
SEMAPHORE_MAP[path].max_leases = sys.maxint
__context__['semaphore_map'][path].assured_path = True
__context__['semaphore_map'][path].max_leases = sys.maxint
# block waiting for lock acquisition
if timeout:
logging.info('Acquiring lock {0} with timeout={1}'.format(path, timeout))
SEMAPHORE_MAP[path].acquire(timeout=timeout)
__context__['semaphore_map'][path].acquire(timeout=timeout)
else:
logging.info('Acquiring lock {0} with no timeout'.format(path))
SEMAPHORE_MAP[path].acquire()
__context__['semaphore_map'][path].acquire()
return SEMAPHORE_MAP[path].is_acquired
return __context__['semaphore_map'][path].is_acquired
def unlock(path,
zk_hosts=None, # in case you need to unlock without having run lock (failed execution for example)
identifier=None,
max_concurrency=1,
ephemeral_lease=False
ephemeral_lease=False,
scheme=None,
profile=None,
username=None,
password=None,
default_acl=None
):
'''
Remove lease from semaphore
@ -260,19 +302,18 @@ def unlock(path,
salt minion zk_concurrency.unlock /lock/path host1:1234,host2:1234
'''
# if someone passed in zk_hosts, and the path isn't in SEMAPHORE_MAP, lets
# if someone passed in zk_hosts, and the path isn't in __context__['semaphore_map'], lets
# see if we can find it
if zk_hosts is not None and path not in SEMAPHORE_MAP:
zk = _get_zk_conn(zk_hosts)
SEMAPHORE_MAP[path] = _Semaphore(zk,
path,
identifier,
zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
if path not in __context__['semaphore_map']:
__context__['semaphore_map'][path] = _Semaphore(zk, path, identifier,
max_leases=max_concurrency,
ephemeral_lease=ephemeral_lease)
if path in SEMAPHORE_MAP:
SEMAPHORE_MAP[path].release()
del SEMAPHORE_MAP[path]
if path in __context__['semaphore_map']:
__context__['semaphore_map'][path].release()
del __context__['semaphore_map'][path]
return True
else:
logging.error('Unable to find lease for path {0}'.format(path))
@ -280,9 +321,14 @@ def unlock(path,
def party_members(path,
zk_hosts,
zk_hosts=None,
min_nodes=1,
blocking=False
blocking=False,
profile=None,
scheme=None,
username=None,
password=None,
default_acl=None,
):
'''
Get the List of identifiers in a particular party, optionally waiting for the
@ -307,7 +353,8 @@ def party_members(path,
salt minion zk_concurrency.party_members /lock/path host1:1234,host2:1234
salt minion zk_concurrency.party_members /lock/path host1:1234,host2:1234 min_nodes=3 blocking=True
'''
zk = _get_zk_conn(zk_hosts)
zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
party = kazoo.recipe.party.ShallowParty(zk, path)
if blocking:
barrier = kazoo.recipe.barrier.DoubleBarrier(zk, path, min_nodes)

516
salt/modules/zookeeper.py Normal file
View File

@ -0,0 +1,516 @@
# -*- coding: utf-8 -*-
'''
Zookeeper Module
~~~~~~~~~~~~~~~~
:maintainer: SaltStack
:maturity: new
:platform: all
:depends: kazoo
Configuration
=============
:configuration: This module is not usable until the following are specified
either in a pillar or in the minion's config file:
.. code-block:: yaml
zookeeper:
hosts: zoo1,zoo2,zoo3
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
If configuration for multiple zookeeper environments is required, they can
be set up as different configuration profiles. For example:
.. code-block:: yaml
zookeeper:
prod:
hosts: zoo1,zoo2,zoo3
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
dev:
hosts:
- dev1
- dev2
- dev3
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
'''
from __future__ import absolute_import
# Import python libraries
try:
import kazoo.client
import kazoo.security
HAS_KAZOO = True
except ImportError:
HAS_KAZOO = False
# Import Salt libraries
__virtualname__ = 'zookeeper'
def __virtual__():
if HAS_KAZOO:
return __virtualname__
return False
def _get_zk_conn(profile=None, **connection_args):
if profile:
prefix = 'zookeeper:' + profile
else:
prefix = 'zookeeper'
def get(key, default=None):
'''
look in connection_args first, then default to config file
'''
return connection_args.get(key) or __salt__['config.get'](':'.join([prefix, key]), default)
hosts = get('hosts', '127.0.0.1:2181')
scheme = get('scheme', None)
username = get('username', None)
password = get('password', None)
default_acl = get('default_acl', None)
if isinstance(hosts, list):
hosts = ','.join(hosts)
if username is not None and password is not None and scheme is None:
scheme = 'digest'
auth_data = None
if scheme and username and password:
auth_data = [(scheme, ':'.join([username, password]))]
if default_acl is not None:
if isinstance(default_acl, list):
default_acl = [make_digest_acl(**acl) for acl in default_acl]
else:
default_acl = [make_digest_acl(**default_acl)]
__context__.setdefault('zkconnection', {}).setdefault(profile or hosts,
kazoo.client.KazooClient(hosts=hosts,
default_acl=default_acl,
auth_data=auth_data))
if not __context__['zkconnection'][profile or hosts].connected:
__context__['zkconnection'][profile or hosts].start()
return __context__['zkconnection'][profile or hosts]
def create(path, value='', acls=None, ephemeral=False, sequence=False, makepath=False, profile=None,
hosts=None, scheme=None, username=None, password=None, default_acl=None):
'''
Create Znode
path
path of znode to create
value
value to assign to znode (Default: '')
acls
list of acl dictionaries to be assigned (Default: None)
ephemeral
indicate node is ephemeral (Default: False)
sequence
indicate node is suffixed with a unique index (Default: False)
makepath
Create parent paths if they do not exist (Default: False)
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.create /test/name daniel profile=prod
'''
if acls is None:
acls = []
acls = [make_digest_acl(**acl) for acl in acls]
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return conn.create(path, value, acls, ephemeral, sequence, makepath)
def ensure_path(path, acls=None, profile=None, hosts=None, scheme=None,
username=None, password=None, default_acl=None):
'''
Ensure Znode path exists
path
Parent path to create
acls
list of acls dictionaries to be assigned (Default: None)
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.ensure_path /test/name profile=prod
'''
if acls is None:
acls = []
acls = [make_digest_acl(**acl) for acl in acls]
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return conn.ensure_path(path, acls)
def exists(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None):
'''
Check if path exists
path
path to check
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.exists /test/name profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return bool(conn.exists(path))
def get(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None):
'''
Get value saved in znode
path
path to check
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.get /test/name profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
ret, _ = conn.get(path)
return ret
def get_children(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None):
'''
Get children in znode path
path
path to check
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.get_children /test profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
ret = conn.get_children(path)
return ret or []
def set(path, value, version=-1, profile=None, hosts=None, scheme=None,
username=None, password=None, default_acl=None):
'''
Update znode with new value
path
znode to update
value
value to set in znode
version
only update znode if version matches (Default: -1 (always matches))
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.set /test/name gtmanfred profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return conn.set(path, value, version=version)
def get_acls(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None):
'''
Get acls on a znode
path
path to znode
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.get_acls /test/name profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return conn.get_acls(path)[0]
def set_acls(path, acls, version=-1, profile=None, hosts=None, scheme=None,
username=None, password=None, default_acl=None):
'''
Set acls on a znode
path
path to znode
acls
list of acl dictionaries to set on the znode
version
only set acls if version matches (Default: -1 (always matches))
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.set_acls /test/name acls='[{"username": "gtmanfred", "password": "test", "all": True}]' profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
if acls is None:
acls = []
acls = [make_digest_acl(**acl) for acl in acls]
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return conn.set_acls(path, acls, version)
def delete(path, version=-1, recursive=False, profile=None, hosts=None, scheme=None,
username=None, password=None, default_acl=None):
'''
Delete znode
path
path to znode
version
only delete if version matches (Default: -1 (always matches))
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
CLI Example:
.. code-block:: bash
salt minion1 zookeeper.delete /test/name profile=prod
'''
conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme,
username=username, password=password, default_acl=default_acl)
return conn.delete(path, version, recursive)
def make_digest_acl(username, password, read=False, write=False, create=False, delete=False, admin=False,
allperms=False):
return kazoo.security.make_digest_acl(username, password, read, write, create, delete, admin, allperms)

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
'''
:depends: kazoo
:configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions.
Control concurrency of steps within state execution using zookeeper
===================================================================
@ -60,12 +63,16 @@ def __virtual__():
def lock(name,
zk_hosts,
zk_hosts=None,
identifier=None,
max_concurrency=1,
timeout=None,
ephemeral_lease=False,
):
profile=None,
scheme=None,
username=None,
password=None,
default_acl=None):
'''
Block state execution until you are able to get the lock (or hit the timeout)
@ -74,6 +81,8 @@ def lock(name,
'changes': {},
'result': False,
'comment': ''}
conn_kwargs = {'profile': profile, 'scheme': scheme,
'username': username, 'password': password, 'default_acl': default_acl}
if __opts__['test']:
ret['result'] = None
@ -88,7 +97,8 @@ def lock(name,
identifier=identifier,
max_concurrency=max_concurrency,
timeout=timeout,
ephemeral_lease=ephemeral_lease)
ephemeral_lease=ephemeral_lease,
**conn_kwargs)
if locked:
ret['result'] = True
ret['comment'] = 'lock acquired'
@ -102,8 +112,12 @@ def unlock(name,
zk_hosts=None, # in case you need to unlock without having run lock (failed execution for example)
identifier=None,
max_concurrency=1,
ephemeral_lease=False
):
ephemeral_lease=False,
profile=None,
scheme=None,
username=None,
password=None,
default_acl=None):
'''
Remove lease from semaphore.
'''
@ -111,6 +125,8 @@ def unlock(name,
'changes': {},
'result': False,
'comment': ''}
conn_kwargs = {'profile': profile, 'scheme': scheme,
'username': username, 'password': password, 'default_acl': default_acl}
if __opts__['test']:
ret['result'] = None
@ -124,7 +140,8 @@ def unlock(name,
zk_hosts=zk_hosts,
identifier=identifier,
max_concurrency=max_concurrency,
ephemeral_lease=ephemeral_lease)
ephemeral_lease=ephemeral_lease,
**conn_kwargs)
if unlocked:
ret['result'] = True
@ -137,8 +154,12 @@ def unlock(name,
def min_party(name,
zk_hosts,
min_nodes,
blocking=False
):
blocking=False,
profile=None,
scheme=None,
username=None,
password=None,
default_acl=None):
'''
Ensure that there are `min_nodes` in the party at `name`, optionally blocking if not available.
'''
@ -146,13 +167,15 @@ def min_party(name,
'changes': {},
'result': False,
'comment': ''}
conn_kwargs = {'profile': profile, 'scheme': scheme,
'username': username, 'password': password, 'default_acl': default_acl}
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Attempt to ensure min_party'
return ret
nodes = __salt__['zk_concurrency.party_members'](name, zk_hosts, min_nodes, blocking=blocking)
nodes = __salt__['zk_concurrency.party_members'](name, zk_hosts, min_nodes, blocking=blocking, **conn_kwargs)
if not isinstance(nodes, list):
raise Exception('Error from zk_concurrency.party_members, return was not a list: {0}'.format(nodes))

358
salt/states/zookeeper.py Normal file
View File

@ -0,0 +1,358 @@
# -*- coding: utf-8 -*-
'''
:depends: kazoo
:configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions.
ACLS
~~~~
For more information about acls, please checkout the kazoo documentation.
http://kazoo.readthedocs.io/en/latest/api/security.html#kazoo.security.make_digest_acl
The following options can be included in the acl dictionary:
:param username: Username to use for the ACL.
:param password: A plain-text password to hash.
:param write: Write permission.
:type write: bool
:param create: Create permission.
:type create: bool
:param delete: Delete permission.
:type delete: bool
:param admin: Admin permission.
:type admin: bool
:param all: All permissions.
:type all: bool
'''
__virtualname__ = 'zookeeper'
def __virtual__():
if 'zookeeper.create' in __salt__:
return __virtualname__
return False
def _check_acls(left, right):
first = not bool(set(left) - set(right))
second = not bool(set(right) - set(left))
return first and second
def present(name, value, acls=None, ephemeral=False, sequence=False, makepath=False, version=-1,
profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None):
'''
Make sure znode is present in the correct state with the correct acls
name
path to znode
value
value znode should be set to
acls
list of acl dictionaries to set on znode (make sure the ones salt is connected with are included)
Default: None
ephemeral
Boolean to indicate if ephemeral znode should be created
Default: False
sequence
Boolean to indicate if znode path is suffixed with a unique index
Default: False
makepath
Boolean to indicate if the parent paths should be created
Default: False
version
For updating, specify the version which should be updated
Default: -1 (always match)
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
.. code-block:: yaml
add znode:
zookeeper.present:
- name: /test/name
- value: gtmanfred
- makepath: True
update znode:
zookeeper.present:
- name: /test/name
- value: daniel
- acls:
- username: daniel
password: test
read: true
- username: gtmanfred
password: test
read: true
write: true
create: true
delete: true
admin: true
- makepath: True
'''
ret = {'name': name,
'result': False,
'comment': 'Failed to setup znode {0}'.format(name),
'changes': {}}
connkwargs = {'profile': profile, 'hosts': hosts, 'scheme': scheme,
'username': username, 'password': password,
'default_acl': default_acl}
if acls is None:
chk_acls = []
else:
chk_acls = [__salt__['zookeeper.make_digest_acl'](**acl) for acl in acls]
if __salt__['zookeeper.exists'](name, **connkwargs):
cur_value = __salt__['zookeeper.get'](name, **connkwargs)
cur_acls = __salt__['zookeeper.get_acls'](name, **connkwargs)
if cur_value == value and _check_acls(cur_acls, chk_acls):
ret['result'] = True
ret['comment'] = 'Znode {0} is already set to the correct value with the correct acls'.format(name)
return ret
elif __opts__['test'] is True:
ret['result'] = None
ret['comment'] = 'Znode {0} is will be updated'.format(name)
ret['changes']['old'] = {}
ret['changes']['new'] = {}
if value != cur_value:
ret['changes']['old']['value'] = cur_value
ret['changes']['new']['value'] = value
if not _check_acls(chk_acls, cur_acls):
ret['changes']['old']['acls'] = cur_acls
ret['changes']['new']['acls'] = chk_acls
return ret
else:
value_result, acl_result = True, True
changes = {}
if value != cur_value:
__salt__['zookeeper.set'](name, value, version, **connkwargs)
new_value = __salt__['zookeeper.get'](name, **connkwargs)
value_result = new_value == value
changes.setdefault('new', {}).setdefault('value', new_value)
changes.setdefault('old', {}).setdefault('value', cur_value)
if not _check_acls(chk_acls, cur_acls):
__salt__['zookeeper.set_acls'](name, acls, version, **connkwargs)
new_acls = __salt__['zookeeper.get_acls'](name, **connkwargs)
acl_result = _check_acls(new_acls, chk_acls)
changes.setdefault('new', {}).setdefault('acls', new_acls)
changes.setdefault('old', {}).setdefault('value', cur_acls)
ret['changes'] = changes
if value_result and acl_result:
ret['result'] = True
ret['comment'] = 'Znode {0} successfully updated'.format(name)
return ret
if __opts__['test'] is True:
ret['result'] = None
ret['comment'] = '{0} is will be created'.format(name)
ret['changes']['old'] = {}
ret['changes']['new'] = {}
ret['changes']['new']['acls'] = chk_acls
ret['changes']['new']['value'] = value
return ret
__salt__['zookeeper.create'](name, value, acls, ephemeral, sequence, makepath, **connkwargs)
value_result, acl_result = True, True
changes = {'old': {}}
new_value = __salt__['zookeeper.get'](name, **connkwargs)
value_result = new_value == value
changes.setdefault('new', {}).setdefault('value', new_value)
new_acls = __salt__['zookeeper.get_acls'](name, **connkwargs)
acl_result = acls is None or _check_acls(new_acls, chk_acls)
changes.setdefault('new', {}).setdefault('acls', new_acls)
ret['changes'] = changes
if value_result and acl_result:
ret['result'] = True
ret['comment'] = 'Znode {0} successfully created'.format(name)
return ret
def absent(name, version=-1, recursive=False, profile=None, hosts=None, scheme=None,
username=None, password=None, default_acl=None):
'''
Make sure znode is absent
name
path to znode
version
Specify the version which should be deleted
Default: -1 (always match)
recursive
Boolean to indicate if children should be recursively deleted
Default: False
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
.. code-block:: yaml
delete znode:
zookeeper.absent:
- name: /test
- recursive: True
'''
ret = {'name': name,
'result': False,
'comment': 'Failed to delete znode {0}'.format(name),
'changes': {}}
connkwargs = {'profile': profile, 'hosts': hosts, 'scheme': scheme,
'username': username, 'password': password,
'default_acl': default_acl}
if __salt__['zookeeper.exists'](name, **connkwargs) is False:
ret['result'] = True
ret['comment'] = 'Znode {0} does not exist'.format(name)
return ret
changes = {}
changes['value'] = __salt__['zookeeper.get'](name, **connkwargs)
changes['acls'] = __salt__['zookeeper.get_acls'](name, **connkwargs)
if recursive is True:
changes['children'] = __salt__['zookeeper.get_children'](name, **connkwargs)
if __opts__['test'] is True:
ret['result'] = None
ret['comment'] = 'Znode {0} will be removed'.format(name)
ret['changes']['old'] = changes
return ret
__salt__['zookeeper.delete'](name, version, recursive, **connkwargs)
if __salt__['zookeeper.exists'](name, **connkwargs) is False:
ret['result'] = True
ret['comment'] = 'Znode {0} has been removed'.format(name)
ret['changes']['old'] = changes
return ret
def acls(name, acls, version=-1, profile=None, hosts=None, scheme=None,
username=None, password=None, default_acl=None):
'''
Update acls on a znode
name
path to znode
acls
list of acl dictionaries to set on znode
version
Specify the version which should be deleted
Default: -1 (always match)
profile
Configured Zookeeper profile to authenticate with (Default: None)
hosts
Lists of Zookeeper Hosts (Default: '127.0.0.1:2181)
scheme
Scheme to authenticate with (Default: 'digest')
username
Username to authenticate (Default: None)
password
Password to authenticate (Default: None)
default_acl
Default acls to assign if a node is created in this connection (Default: None)
.. code-block:: yaml
update acls:
zookeeper.acls:
- name: /test/name
- acls:
- username: daniel
password: test
all: True
- username: gtmanfred
password: test
all: True
'''
ret = {'name': name,
'result': False,
'comment': 'Failed to set acls on znode {0}'.format(name),
'changes': {}}
connkwargs = {'profile': profile, 'hosts': hosts, 'scheme': scheme,
'username': username, 'password': password,
'default_acl': default_acl}
if isinstance(acls, dict):
acls = [acls]
chk_acls = [__salt__['zookeeper.make_digest_acl'](**acl) for acl in acls]
if not __salt__['zookeeper.exists'](name, **connkwargs):
ret['comment'] += ': Znode does not exist'
return ret
cur_acls = __salt__['zookeeper.get_acls'](name, **connkwargs)
if _check_acls(cur_acls, chk_acls):
ret['result'] = True
ret['comment'] = 'Znode {0} acls already set'.format(name)
return ret
if __opts__['test'] is True:
ret['result'] = None
ret['comment'] = 'Znode {0} acls will be updated'.format(name)
ret['changes']['old'] = cur_acls
ret['changes']['new'] = chk_acls
return ret
__salt__['zookeeper.set_acls'](name, acls, version, **connkwargs)
new_acls = __salt__['zookeeper.get_acls'](name, **connkwargs)
ret['changes'] = {'old': cur_acls, 'new': new_acls}
if _check_acls(new_acls, chk_acls):
ret['result'] = True
ret['comment'] = 'Znode {0} acls updated'.format(name)
return ret
ret['comment'] = 'Znode {0} acls failed to update'.format(name)
return ret