mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 01:18:58 +00:00
Merge branch 'develop' of https://github.com/saltstack/salt into develop
Increase syndic_master_publish_port parameters, to address the situation Top Master inconsistencies in nat port
This commit is contained in:
commit
a7d2444a60
@ -101,6 +101,24 @@ class LoadAuth(object):
|
||||
time.sleep(0.001)
|
||||
return False
|
||||
|
||||
def get_groups(self, load):
|
||||
'''
|
||||
Read in a load and return the groups a user is a member of
|
||||
by asking the appropriate provider
|
||||
'''
|
||||
if 'eauth' not in load:
|
||||
return False
|
||||
fstr = '{0}.groups'.format(load['eauth'])
|
||||
if fstr not in self.auth:
|
||||
return False
|
||||
fcall = salt.utils.format_call(self.auth[fstr], load)
|
||||
try:
|
||||
return self.auth[fstr](*fcall['args'])
|
||||
except IndexError:
|
||||
return False
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def mk_token(self, load):
|
||||
'''
|
||||
Run time_auth and create a token. Return False or the token
|
||||
|
@ -188,3 +188,7 @@ def auth(username, password):
|
||||
)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def groups(username, *args, **kwargs):
|
||||
return None
|
||||
|
@ -23,6 +23,9 @@ from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof
|
||||
from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int
|
||||
from ctypes.util import find_library
|
||||
|
||||
# Import Salt libs
|
||||
from salt.utils import get_group_list
|
||||
|
||||
LIBPAM = CDLL(find_library('pam'))
|
||||
LIBC = CDLL(find_library('c'))
|
||||
|
||||
@ -163,3 +166,12 @@ def auth(username, password, **kwargs):
|
||||
Authenticate via pam
|
||||
'''
|
||||
return authenticate(username, password, kwargs.get('service', 'login'))
|
||||
|
||||
|
||||
def groups(username, *args, **kwargs):
|
||||
'''
|
||||
Retreive groups for a given user for this auth provider
|
||||
|
||||
Uses system groups
|
||||
'''
|
||||
return get_group_list(username)
|
||||
|
@ -473,6 +473,13 @@ class ExecutorNix(ioflo.base.deeding.Deed):
|
||||
'dst': (msg['route']['src'][0], None, 'remote_cmd')}
|
||||
ret['cmd'] = '_return'
|
||||
ret['id'] = self.opts['id']
|
||||
try:
|
||||
oput = self.modules.value[ret['fun']].__outputter__
|
||||
except (KeyError, AttributeError, TypeError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(oput, str):
|
||||
ret['out'] = oput
|
||||
msg = {'route': route, 'load': ret}
|
||||
ret_stack.transmit(msg, 'yard0')
|
||||
ret_stack.serviceAll()
|
||||
|
@ -73,7 +73,7 @@ def clean_old_jobs(opts):
|
||||
'''
|
||||
if opts['keep_jobs'] != 0:
|
||||
jid_root = os.path.join(opts['cachedir'], 'jobs')
|
||||
cur = '{0:%Y%m%d%H}'.format(datetime.datetime.now())
|
||||
cur = datetime.datetime.now()
|
||||
|
||||
if os.path.exists(jid_root):
|
||||
for top in os.listdir(jid_root):
|
||||
@ -82,15 +82,30 @@ def clean_old_jobs(opts):
|
||||
f_path = os.path.join(t_path, final)
|
||||
jid_file = os.path.join(f_path, 'jid')
|
||||
if not os.path.isfile(jid_file):
|
||||
continue
|
||||
# No jid file means corrupted cache entry, scrub it
|
||||
shutil.rmtree(f_path)
|
||||
with salt.utils.fopen(jid_file, 'r') as fn_:
|
||||
jid = fn_.read()
|
||||
if len(jid) < 18:
|
||||
# Invalid jid, scrub the dir
|
||||
shutil.rmtree(f_path)
|
||||
elif int(cur) - int(jid[:10]) > \
|
||||
opts['keep_jobs']:
|
||||
shutil.rmtree(f_path)
|
||||
else:
|
||||
# Parse the jid into a proper datetime object. We only
|
||||
# parse down to the minute, since keep_jobs is measured
|
||||
# in hours, so a minute difference is not important
|
||||
try:
|
||||
jidtime = datetime.datetime(int(jid[0:4]),
|
||||
int(jid[4:6]),
|
||||
int(jid[6:8]),
|
||||
int(jid[8:10]),
|
||||
int(jid[10:12]))
|
||||
except ValueError as e:
|
||||
# Invalid jid, scrub the dir
|
||||
shutil.rmtree(f_path)
|
||||
difference = cur - jidtime
|
||||
hours_difference = difference.seconds / 3600.0
|
||||
if hours_difference > opts['keep_jobs']:
|
||||
shutil.rmtree(f_path)
|
||||
|
||||
|
||||
def access_keys(opts):
|
||||
|
@ -1370,6 +1370,11 @@ def _smartos_zone_data():
|
||||
# Provides:
|
||||
# pkgsrcversion
|
||||
# imageversion
|
||||
# pkgsrcpath
|
||||
# zonename
|
||||
# zoneid
|
||||
# hypervisor_uuid
|
||||
# datacenter
|
||||
|
||||
if 'proxyminion' in __opts__:
|
||||
return {}
|
||||
@ -1378,6 +1383,7 @@ def _smartos_zone_data():
|
||||
|
||||
pkgsrcversion = re.compile('^release:\\s(.+)')
|
||||
imageversion = re.compile('Image:\\s(.+)')
|
||||
pkgsrcpath = re.compile('PKG_PATH=(.+)')
|
||||
if os.path.isfile('/etc/pkgsrc_version'):
|
||||
with salt.utils.fopen('/etc/pkgsrc_version', 'r') as fp_:
|
||||
for line in fp_:
|
||||
@ -1390,10 +1396,25 @@ def _smartos_zone_data():
|
||||
match = imageversion.match(line)
|
||||
if match:
|
||||
grains['imageversion'] = match.group(1)
|
||||
if os.path.isfile('/opt/local/etc/pkg_install.conf'):
|
||||
with salt.utils.fopen('/opt/local/etc/pkg_install.conf', 'r') as fp_:
|
||||
for line in fp_:
|
||||
match = pkgsrcpath.match(line)
|
||||
if match:
|
||||
grains['pkgsrcpath'] = match.group(1)
|
||||
if 'pkgsrcversion' not in grains:
|
||||
grains['pkgsrcversion'] = 'Unknown'
|
||||
if 'imageversion' not in grains:
|
||||
grains['imageversion'] = 'Unknown'
|
||||
if 'pkgsrcpath' not in grains:
|
||||
grains['pkgsrcpath'] = 'Unknown'
|
||||
|
||||
grains['zonename'] = __salt__['cmd.run']('zonename')
|
||||
grains['zoneid'] = __salt__['cmd.run']('zoneadm list -p | awk -F: \'{ print $1 }\'')
|
||||
grains['hypervisor_uuid'] = __salt__['cmd.run']('mdata-get sdc:server_uuid')
|
||||
grains['datacenter'] = __salt__['cmd.run']('mdata-get sdc:datacenter_name')
|
||||
if "FAILURE" in grains['datacenter'] or "No metadata" in grains['datacenter']:
|
||||
grains['datacenter'] = "Unknown"
|
||||
|
||||
return grains
|
||||
|
||||
|
@ -2445,18 +2445,26 @@ class ClearFuncs(object):
|
||||
'Authentication failure of type "eauth" occurred.'
|
||||
)
|
||||
return ''
|
||||
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
'Exception occurred while authenticating: {0}'.format(exc)
|
||||
)
|
||||
return ''
|
||||
|
||||
auth_list = self.opts['external_auth'][extra['eauth']][name] if name in self.opts['external_auth'][extra['eauth']] else self.opts['external_auth'][extra['eauth']]['*']
|
||||
|
||||
# Auth has succeeded, get groups this user is a member of
|
||||
groups = self.loadauth.get_groups(extra)
|
||||
|
||||
if groups:
|
||||
auth_list = self.ckminions.gather_groups(self.opts['external_auth'][extra['eauth']], groups, auth_list)
|
||||
good = self.ckminions.auth_check(
|
||||
self.opts['external_auth'][extra['eauth']][name]
|
||||
if name in self.opts['external_auth'][extra['eauth']]
|
||||
else self.opts['external_auth'][extra['eauth']]['*'],
|
||||
auth_list,
|
||||
clear_load['fun'],
|
||||
clear_load['tgt'],
|
||||
clear_load.get('tgt_type', 'glob'))
|
||||
clear_load.get('tgt_type', 'glob')
|
||||
)
|
||||
if not good:
|
||||
# Accept find_job so the CLI will function cleanly
|
||||
if clear_load['fun'] != 'saltutil.find_job':
|
||||
|
@ -160,14 +160,7 @@ def parse_args_and_kwargs(func, args, data=None):
|
||||
invalid_kwargs = []
|
||||
|
||||
for arg in args:
|
||||
# support old yamlify syntax
|
||||
if isinstance(arg, string_types):
|
||||
salt.utils.warn_until(
|
||||
'Boron',
|
||||
'This minion received a job where kwargs were passed as '
|
||||
'string\'d args, which has been deprecated. This functionality will '
|
||||
'be removed in Salt Boron.'
|
||||
)
|
||||
arg_name, arg_value = salt.utils.parse_kwarg(arg)
|
||||
if arg_name:
|
||||
if argspec.keywords or arg_name in argspec.args:
|
||||
@ -186,7 +179,10 @@ def parse_args_and_kwargs(func, args, data=None):
|
||||
for key, val in arg.iteritems():
|
||||
if key == '__kwarg__':
|
||||
continue
|
||||
kwargs[key] = val
|
||||
if isinstance(val, string_types):
|
||||
kwargs[key] = yamlify_arg(val)
|
||||
else:
|
||||
kwargs[key] = val
|
||||
continue
|
||||
_args.append(yamlify_arg(arg))
|
||||
if argspec.keywords and isinstance(data, dict):
|
||||
|
136
salt/modules/etcd_mod.py
Normal file
136
salt/modules/etcd_mod.py
Normal file
@ -0,0 +1,136 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Execution module to work with etcd
|
||||
|
||||
:depends: - python-etcd
|
||||
|
||||
In order to use an etcd server, a profile should be created in the master
|
||||
configuration file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my_etd_config:
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
It is technically possible to configure etcd without using a profile, but this
|
||||
is not consided to be a best practice, especially when multiple etcd servers or
|
||||
clusters are available.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
import salt.utils.etcd_util
|
||||
HAS_LIBS = True
|
||||
except Exception:
|
||||
HAS_LIBS = False
|
||||
|
||||
__virtualname__ = 'etcd'
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Define a function alias in order not to shadow built-in's
|
||||
__func_alias__ = {
|
||||
'get_': 'get',
|
||||
'set_': 'set',
|
||||
'rm_': 'rm',
|
||||
'ls_': 'ls'
|
||||
}
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only return if python-etcd is installed
|
||||
'''
|
||||
return __virtualname__ if HAS_LIBS else False
|
||||
|
||||
|
||||
def get_(key, recurse=False, profile=None):
|
||||
'''
|
||||
Get a value from etcd, by direct path
|
||||
|
||||
CLI Examples:
|
||||
|
||||
salt myminion etcd.get /path/to/key
|
||||
salt myminion etcd.get /path/to/key profile=my_etcd_config
|
||||
salt myminion etcd.get /path/to/key recurse=True profile=my_etcd_config
|
||||
'''
|
||||
client = salt.utils.etcd_util.get_conn(__opts__, profile)
|
||||
result = client.get(key)
|
||||
|
||||
if recurse:
|
||||
return salt.utils.etcd_util.tree(client, key)
|
||||
else:
|
||||
return result.value
|
||||
|
||||
|
||||
def set_(key, value, profile=None):
|
||||
'''
|
||||
Set a value in etcd, by direct path
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt myminion etcd.set /path/to/key value
|
||||
salt myminion etcd.set /path/to/key value profile=my_etcd_config
|
||||
'''
|
||||
client = salt.utils.etcd_util.get_conn(__opts__, profile)
|
||||
return client.write(key, value)
|
||||
|
||||
|
||||
def ls_(path='/', profile=None):
|
||||
'''
|
||||
Return all keys and dirs inside a specific path
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt myminion etcd.ls /path/to/dir/
|
||||
salt myminion etcd.ls /path/to/dir/ profile=my_etcd_config
|
||||
'''
|
||||
ret = {}
|
||||
client = salt.utils.etcd_util.get_conn(__opts__, profile)
|
||||
items = client.get(path)
|
||||
for item in items.children:
|
||||
if item.dir is True:
|
||||
dir_name = '{0}/'.format(item.key)
|
||||
ret[dir_name] = {}
|
||||
else:
|
||||
ret[item.key] = item.value
|
||||
return {path: ret}
|
||||
|
||||
|
||||
def rm_(key, recurse=False, profile=None):
|
||||
'''
|
||||
Delete a key from etcd
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt myminion etcd.rm /path/to/key
|
||||
salt myminion etcd.rm /path/to/key profile=my_etcd_config
|
||||
salt myminion etcd.rm /path/to/dir recurse=True profile=my_etcd_config
|
||||
'''
|
||||
client = salt.utils.etcd_util.get_conn(__opts__, profile)
|
||||
return client.delete(key, recursive=recurse)
|
||||
|
||||
|
||||
def tree(path='/', profile=None):
|
||||
'''
|
||||
Recurse through etcd and return all values
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt myminion etcd.tree
|
||||
salt myminion etcd.tree profile=my_etcd_config
|
||||
salt myminion etcd.tree /path/to/keys profile=my_etcd_config
|
||||
'''
|
||||
client = salt.utils.etcd_util.get_conn(__opts__, profile)
|
||||
return salt.utils.etcd_util.tree(client, path)
|
@ -166,7 +166,8 @@ def init(name,
|
||||
[nic=nic_profile] [profile=lxc_profile] \\
|
||||
[nic_opts=nic_opts] [start=(True|False)] \\
|
||||
[seed=(True|False)] [install=(True|False)] \\
|
||||
[config=minion_config]
|
||||
[config=minion_config] [approve_key=(True|False) \\
|
||||
[clone=original]
|
||||
|
||||
name
|
||||
Name of the container.
|
||||
@ -202,28 +203,48 @@ def init(name,
|
||||
config
|
||||
Optional config parameters. By default, the id is set to the name of the
|
||||
container.
|
||||
|
||||
approve_key
|
||||
Attempt to request key approval from the master. Default: ``True``
|
||||
|
||||
clone
|
||||
Original from which to use a clone operation to create the container. Default: ``None``
|
||||
'''
|
||||
nicp = _nic_profile(nic)
|
||||
start_ = kwargs.pop('start', False)
|
||||
seed = kwargs.pop('seed', True)
|
||||
install = kwargs.pop('install', True)
|
||||
seed_cmd = kwargs.pop('seed_cmd', None)
|
||||
config = kwargs.pop('config', None)
|
||||
profile = _lxc_profile(profile)
|
||||
|
||||
def select(k, default=None):
|
||||
kw = kwargs.pop(k, None)
|
||||
p = profile.pop(k, default)
|
||||
return kw or p
|
||||
|
||||
start_ = select('start', False)
|
||||
seed = select('seed', True)
|
||||
install = select('install', True)
|
||||
seed_cmd = select('seed_cmd')
|
||||
config = select('config')
|
||||
approve_key = select('approve_key', True)
|
||||
clone = select('clone')
|
||||
|
||||
with tempfile.NamedTemporaryFile() as cfile:
|
||||
cfile.write(_gen_config(cpuset=cpuset, cpushare=cpushare,
|
||||
memory=memory, nicp=nicp, nic_opts=nic_opts))
|
||||
cfile.flush()
|
||||
ret = create(name, config=cfile.name, profile=profile, **kwargs)
|
||||
if not ret['created']:
|
||||
if clone:
|
||||
ret = __salt__['lxc.clone'](name, clone, config=cfile.name,
|
||||
profile=profile, **kwargs)
|
||||
else:
|
||||
ret = __salt__['lxc.create'](name, config=cfile.name,
|
||||
profile=profile, **kwargs)
|
||||
if not (ret.get('created', False) or ret.get('cloned', False)):
|
||||
return ret
|
||||
rootfs = info(name)['rootfs']
|
||||
if seed:
|
||||
__salt__['seed.apply'](rootfs, id_=name, config=config,
|
||||
install=install)
|
||||
ret['seeded'] = __salt__['lxc.bootstrap'](
|
||||
name, config=config, approve_key=approve_key, install=install)
|
||||
elif seed_cmd:
|
||||
__salt__[seed_cmd](rootfs, name, config)
|
||||
if start_ and ret['created']:
|
||||
ret['seeded'] = __salt__[seed_cmd](rootfs, name, config)
|
||||
if start_:
|
||||
ret['state'] = start(name)['state']
|
||||
else:
|
||||
ret['state'] = state(name)
|
||||
@ -240,7 +261,7 @@ def create(name, config=None, profile=None, options=None, **kwargs):
|
||||
|
||||
salt 'minion' lxc.create name [config=config_file] \\
|
||||
[profile=profile] [template=template_name] \\
|
||||
[backing=backing_store] [ vgname=volume_group] \\
|
||||
[backing=backing_store] [vgname=volume_group] \\
|
||||
[size=filesystem_size] [options=template_options]
|
||||
|
||||
name
|
||||
@ -280,7 +301,8 @@ def create(name, config=None, profile=None, options=None, **kwargs):
|
||||
|
||||
cmd = 'lxc-create -n {0}'.format(name)
|
||||
|
||||
profile = _lxc_profile(profile)
|
||||
if not isinstance(profile, dict):
|
||||
profile = _lxc_profile(profile)
|
||||
|
||||
def select(k, default=None):
|
||||
kw = kwargs.pop(k, None)
|
||||
@ -343,7 +365,9 @@ def clone(name,
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'minion' lxc.clone name ARGS
|
||||
salt 'minion' lxc.clone name orig [snapshot=(True|False)] \\
|
||||
[size=filesystem_size] [vgname=volume_group] \\
|
||||
[profile=profile_name]
|
||||
|
||||
name
|
||||
Name of the container.
|
||||
@ -372,16 +396,25 @@ def clone(name,
|
||||
'''
|
||||
|
||||
if exists(name):
|
||||
return {'created': False, 'error': 'container already exists'}
|
||||
if not exists(orig):
|
||||
return {'created': False,
|
||||
'error': 'original container does not exist'.format(orig)}
|
||||
return {'cloned': False, 'error': 'container already exists'}
|
||||
|
||||
orig_state = state(orig)
|
||||
if orig_state is None:
|
||||
return {'cloned': False,
|
||||
'error':
|
||||
'original container \'{0}\' does not exist'.format(orig)}
|
||||
elif orig_state != 'stopped':
|
||||
return {'cloned': False,
|
||||
'error': 'original container \'{0}\' is running'.format(orig)}
|
||||
|
||||
if not snapshot:
|
||||
snapshot = ''
|
||||
else:
|
||||
snapshot = '-s'
|
||||
cmd = 'lxc-clone {2} -o {0} -n {1}'.format(orig, name, snapshot)
|
||||
profile = _lxc_profile(profile)
|
||||
|
||||
if not isinstance(profile, dict):
|
||||
profile = _lxc_profile(profile)
|
||||
|
||||
def select(k, default=None):
|
||||
kw = kwargs.pop(k, None)
|
||||
@ -405,7 +438,7 @@ def clone(name,
|
||||
cmd = 'lxc-destroy -n {0}'.format(name)
|
||||
__salt__['cmd.retcode'](cmd)
|
||||
log.warn('lxc-clone failed to create container')
|
||||
return {'created': False, 'error':
|
||||
return {'cloned': False, 'error':
|
||||
'container could not be created: {0}'.format(ret['stderr'])}
|
||||
|
||||
|
||||
@ -1037,12 +1070,12 @@ def bootstrap(name, config=None, approve_key=True, install=True):
|
||||
if not infos:
|
||||
return None
|
||||
|
||||
prior_state = _ensure_running(name)
|
||||
|
||||
__salt__['seed.apply'](infos['rootfs'], id_=name, config=config,
|
||||
approve_key=approve_key, install=False,
|
||||
prep_install=True)
|
||||
|
||||
prior_state = _ensure_running(name)
|
||||
|
||||
cmd = 'bash -c "if type salt-minion; then exit 0; '
|
||||
if install:
|
||||
cmd += 'else sh /tmp/bootstrap.sh -c /tmp; '
|
||||
|
@ -49,9 +49,15 @@ def add(name, gid=None, **kwargs):
|
||||
raise SaltInvocationError(
|
||||
'Salt will not create groups beginning with underscores'
|
||||
)
|
||||
|
||||
if gid is not None and not isinstance(gid, int):
|
||||
raise SaltInvocationError('gid must be an integer')
|
||||
# check if gid is already in use
|
||||
gid_list = _list_gids()
|
||||
if str(gid) in gid_list:
|
||||
raise CommandExecutionError(
|
||||
'gid {0!r} already exists'.format(gid)
|
||||
)
|
||||
|
||||
cmd = 'dseditgroup -o create '
|
||||
if gid:
|
||||
cmd += '-i {0} '.format(gid)
|
||||
@ -59,6 +65,19 @@ def add(name, gid=None, **kwargs):
|
||||
return __salt__['cmd.retcode'](cmd) == 0
|
||||
|
||||
|
||||
def _list_gids():
|
||||
'''
|
||||
Return a list of gids in use
|
||||
'''
|
||||
cmd = __salt__['cmd.run']('dscacheutil -q group | grep gid:',
|
||||
output_loglevel='quiet')
|
||||
data_list = cmd.split()
|
||||
for item in data_list:
|
||||
if item == 'gid:':
|
||||
data_list.remove(item)
|
||||
return sorted(set(data_list))
|
||||
|
||||
|
||||
def delete(name):
|
||||
'''
|
||||
Remove the named group
|
||||
|
@ -80,16 +80,15 @@ def version():
|
||||
def build_rule(table=None, chain=None, command=None, position='', full=None, family='ipv4',
|
||||
**kwargs):
|
||||
'''
|
||||
Build a well-formatted iptables rule based on kwargs. Long options must be
|
||||
used (`--jump` instead of `-j`) because they will have the `--` added to
|
||||
them. A `table` and `chain` are not required, unless `full` is True.
|
||||
Build a well-formatted nftables rule based on kwargs.
|
||||
A `table` and `chain` are not required, unless `full` is True.
|
||||
|
||||
If `full` is `True`, then `table`, `chain` and `command` are required.
|
||||
`command` may be specified as either a short option ('I') or a long option
|
||||
(`--insert`). This will return the iptables command, exactly as it would
|
||||
`command` may be specified as either insert, append, or delete.
|
||||
This will return the nftables command, exactly as it would
|
||||
be used from the command line.
|
||||
|
||||
If a position is required (as with `-I` or `-D`), it may be specified as
|
||||
If a position is required (as with `insert` or `delete`), it may be specified as
|
||||
`position`. This will only be useful if `full` is True.
|
||||
|
||||
If `connstate` is passed in, it will automatically be changed to `state`.
|
||||
@ -98,17 +97,17 @@ def build_rule(table=None, chain=None, command=None, position='', full=None, fam
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' iptables.build_rule match=state \\
|
||||
salt '*' nftables.build_rule match=state \\
|
||||
connstate=RELATED,ESTABLISHED jump=ACCEPT
|
||||
salt '*' iptables.build_rule filter INPUT command=I position=3 \\
|
||||
full=True match=state state=RELATED,ESTABLISHED jump=ACCEPT
|
||||
salt '*' nftables.build_rule filter input command=insert position=3 \\
|
||||
full=True match=state state=related,established jump=accept
|
||||
|
||||
IPv6:
|
||||
salt '*' iptables.build_rule match=state \\
|
||||
connstate=RELATED,ESTABLISHED jump=ACCEPT \\
|
||||
salt '*' nftables.build_rule match=state \\
|
||||
connstate=related,established jump=accept \\
|
||||
family=ipv6
|
||||
salt '*' iptables.build_rule filter INPUT command=I position=3 \\
|
||||
full=True match=state state=RELATED,ESTABLISHED jump=ACCEPT \\
|
||||
salt '*' nftables.build_rule filter input command=insert position=3 \\
|
||||
full=True match=state state=related,established jump=accept \\
|
||||
family=ipv6
|
||||
|
||||
'''
|
||||
@ -134,7 +133,6 @@ def build_rule(table=None, chain=None, command=None, position='', full=None, fam
|
||||
del kwargs['of']
|
||||
|
||||
if 'proto' in kwargs:
|
||||
#rule += '-p {0} '.format(kwargs['proto'])
|
||||
proto = kwargs['proto']
|
||||
|
||||
if 'state' in kwargs:
|
||||
@ -439,7 +437,7 @@ def check(table='filter', chain=None, rule=None, family='ipv4'):
|
||||
|
||||
def check_chain(table='filter', chain=None, family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Check for the existence of a chain in the table
|
||||
|
||||
@ -493,7 +491,7 @@ def check_table(table=None, family='ipv4'):
|
||||
|
||||
def new_table(table, family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Create new custom table.
|
||||
|
||||
@ -525,7 +523,7 @@ def new_table(table, family='ipv4'):
|
||||
|
||||
def delete_table(table, family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Create new custom table.
|
||||
|
||||
@ -556,7 +554,7 @@ def delete_table(table, family='ipv4'):
|
||||
|
||||
def new_chain(table='filter', chain=None, table_type=None, hook=None, priority=None, family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Create new chain to the specified table.
|
||||
|
||||
@ -612,7 +610,7 @@ def new_chain(table='filter', chain=None, table_type=None, hook=None, priority=N
|
||||
|
||||
def delete_chain(table='filter', chain=None, family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Delete the chain from the specified table.
|
||||
|
||||
|
@ -21,7 +21,7 @@ def __virtual__():
|
||||
Only load the module if nginx is installed
|
||||
'''
|
||||
if __detect_os():
|
||||
return 'nginx'
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
@ -439,6 +439,37 @@ def running():
|
||||
return ret
|
||||
|
||||
|
||||
def cached():
|
||||
'''
|
||||
Return the data on all cached salt jobs on the minion
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.cached
|
||||
'''
|
||||
ret = []
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
proc_dir = os.path.join(__opts__['cachedir'], 'minion_jobs')
|
||||
if not os.path.isdir(proc_dir):
|
||||
return []
|
||||
for fn_ in os.listdir(proc_dir):
|
||||
path = os.path.join(proc_dir, fn_, 'return.p')
|
||||
with salt.utils.fopen(path, 'rb') as fp_:
|
||||
buf = fp_.read()
|
||||
fp_.close()
|
||||
if buf:
|
||||
data = serial.loads(buf)
|
||||
else:
|
||||
continue
|
||||
if not isinstance(data, dict):
|
||||
# Invalid serial object
|
||||
continue
|
||||
ret.append(data)
|
||||
return ret
|
||||
|
||||
|
||||
def find_job(jid):
|
||||
'''
|
||||
Return the data for a specific job id
|
||||
@ -455,6 +486,22 @@ def find_job(jid):
|
||||
return {}
|
||||
|
||||
|
||||
def find_cached_job(jid):
|
||||
'''
|
||||
Return the data for a specific cached job id
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.find_cached_job <job id>
|
||||
'''
|
||||
for data in cached():
|
||||
if data['jid'] == jid:
|
||||
return data
|
||||
return {}
|
||||
|
||||
|
||||
def signal_job(jid, sig):
|
||||
'''
|
||||
Sends a signal to the named salt job's process
|
||||
|
@ -84,9 +84,7 @@ def apply_(path, id_=None, config=None, approve_key=True, install=True,
|
||||
Install salt-minion, if absent. Default: true.
|
||||
|
||||
prep_install
|
||||
Prepare the bootstrap script, but don't run it. The files needed for
|
||||
installation (bootstrap.py, config, and keys) will be placed in /tmp
|
||||
on the target path/device. Default: false
|
||||
Prepare the bootstrap script, but don't run it. Default: false
|
||||
'''
|
||||
stats = __salt__['file.stats'](path, follow_symlinks=True)
|
||||
if not stats:
|
||||
@ -199,8 +197,16 @@ def _check_resolv(mpt):
|
||||
|
||||
|
||||
def _check_install(root):
|
||||
cmd = ('chroot {0} /bin/sh -c if ! type salt-minion; '
|
||||
'then exit 1; fi').format(root)
|
||||
sh_ = '/bin/sh'
|
||||
if os.path.isfile(os.path.join(root, 'bin/bash')):
|
||||
sh_ = '/bin/bash'
|
||||
|
||||
cmd = ('if ! type salt-minion; then exit 1; fi').format(root)
|
||||
cmd = 'chroot {0} {1} -c {2!r}'.format(
|
||||
root,
|
||||
sh_,
|
||||
cmd)
|
||||
|
||||
return not __salt__['cmd.retcode'](cmd, output_loglevel='quiet')
|
||||
|
||||
|
||||
|
@ -63,7 +63,15 @@ def __virtual__():
|
||||
return False
|
||||
|
||||
|
||||
def send_msg(recipient, message, subject='Message from Salt', sender=None, server=None, use_ssl='True', username=None, password=None, profile=None):
|
||||
def send_msg(recipient,
|
||||
message,
|
||||
subject='Message from Salt',
|
||||
sender=None,
|
||||
server=None,
|
||||
use_ssl='True',
|
||||
username=None,
|
||||
password=None,
|
||||
profile=None):
|
||||
'''
|
||||
Send a message to an SMTP recipient. Designed for use in states.
|
||||
|
||||
|
74
salt/pillar/etcd_pillar.py
Normal file
74
salt/pillar/etcd_pillar.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Use etcd data as a Pillar source
|
||||
|
||||
:depends: - python-etcd
|
||||
|
||||
In order to use an etcd server, a profile must be created in the master
|
||||
configuration file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my_etd_config:
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
After the profile is created, configure the external pillar system to use it.
|
||||
Optionally, a root may be specified.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ext_pillar:
|
||||
- etcd: my_etcd_config
|
||||
|
||||
ext_pillar:
|
||||
- etcd: my_etcd_config root=/salt
|
||||
|
||||
Using these configuration profiles, multiple etcd sources may also be used:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ext_pillar:
|
||||
- etcd: my_etcd_config
|
||||
- etcd: my_other_etcd_config
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
import salt.utils.etcd_util
|
||||
HAS_LIBS = True
|
||||
except Exception:
|
||||
HAS_LIBS = False
|
||||
|
||||
__virtualname__ = 'etcd'
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only return if python-etcd is installed
|
||||
'''
|
||||
return __virtualname__ if HAS_LIBS else False
|
||||
|
||||
|
||||
def ext_pillar(minion_id, pillar, conf): # pylint: disable=W0613
|
||||
'''
|
||||
Check etcd for all data
|
||||
'''
|
||||
comps = conf.split()
|
||||
|
||||
profile = None
|
||||
if comps[0]:
|
||||
profile = comps[0]
|
||||
client = salt.utils.etcd_util.get_conn(__opts__, profile)
|
||||
|
||||
path = '/'
|
||||
if len(comps) > 1 and comps[1].startswith('root='):
|
||||
path = comps[1].replace('root=', '')
|
||||
|
||||
return salt.utils.etcd_util.tree(client, path)
|
166
salt/returners/etcd_return.py
Normal file
166
salt/returners/etcd_return.py
Normal file
@ -0,0 +1,166 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Return data to an etcd server or cluster
|
||||
|
||||
:depends: - python-etcd
|
||||
|
||||
In order to return to an etcd server, a profile should be created in the master
|
||||
configuration file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my_etd_config:
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
It is technically possible to configure etcd without using a profile, but this
|
||||
is not consided to be a best practice, especially when multiple etcd servers or
|
||||
clusters are available.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
Additionally, two more options must be specified in the top-level configuration
|
||||
in order to use the etcd returner:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
etcd.returner: my_etcd_config
|
||||
etcd.returner_root: /salt/return
|
||||
|
||||
The ``etcd.returner`` option specifies which configuration profile to use. The
|
||||
``etcd.returner_root`` option specifies the path inside etcd to use as the root
|
||||
of the returner system.
|
||||
|
||||
Once the etcd options are configured, the returner may be used:
|
||||
|
||||
CLI Example:
|
||||
|
||||
salt '*' test.ping --return etcd
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
import json
|
||||
import logging
|
||||
|
||||
# Import salt libs
|
||||
try:
|
||||
import salt.utils.etcd_util
|
||||
HAS_LIBS = True
|
||||
except Exception:
|
||||
HAS_LIBS = False
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'etcd'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only return if python-etcd is installed
|
||||
'''
|
||||
return __virtualname__ if HAS_LIBS else False
|
||||
|
||||
|
||||
def _get_conn(opts):
|
||||
'''
|
||||
Establish a connection to etcd
|
||||
'''
|
||||
profile = opts.get('etcd.returner', None)
|
||||
path = opts.get('etcd.returner_root', '/salt/return')
|
||||
return salt.utils.etcd_util.get_conn(opts, profile), path
|
||||
|
||||
|
||||
def returner(ret):
|
||||
'''
|
||||
Return data to an etcd server or cluster
|
||||
'''
|
||||
client, path = _get_conn(__opts__)
|
||||
|
||||
# Make a note of this minion for the external job cache
|
||||
client.write(
|
||||
'/'.join((path, 'minions', ret['id'])),
|
||||
ret['jid'],
|
||||
)
|
||||
|
||||
for field in ret.keys():
|
||||
# Not using os.path.join because we're not dealing with file paths
|
||||
dest = '/'.join((
|
||||
path,
|
||||
'jobs',
|
||||
ret['jid'],
|
||||
ret['id'],
|
||||
field
|
||||
))
|
||||
client.write(dest, json.dumps(ret[field]))
|
||||
|
||||
|
||||
def save_load(jid, load):
|
||||
'''
|
||||
Save the load to the specified jid
|
||||
'''
|
||||
client, path = _get_conn(__opts__)
|
||||
client.write(
|
||||
'/'.join((path, 'jobs', jid, '.load.p')),
|
||||
json.dumps(load)
|
||||
)
|
||||
|
||||
|
||||
def get_load(jid):
|
||||
'''
|
||||
Return the load data that marks a specified jid
|
||||
'''
|
||||
client, path = _get_conn(__opts__)
|
||||
return json.loads(client.get('/'.join(path, 'jobs', jid, '.load.p')))
|
||||
|
||||
|
||||
def get_jid(jid):
|
||||
'''
|
||||
Return the information returned when the specified job id was executed
|
||||
'''
|
||||
client, path = _get_conn(__opts__)
|
||||
jid_path = '/'.join((path, 'jobs', jid))
|
||||
return salt.utils.etcd_util.tree(client, jid_path)
|
||||
|
||||
|
||||
def get_fun(fun):
|
||||
'''
|
||||
Return a dict of the last function called for all minions
|
||||
'''
|
||||
ret = {}
|
||||
client, path = _get_conn(__opts__)
|
||||
items = client.get('/'.join((path, 'minions')))
|
||||
for item in items.children:
|
||||
comps = str(item.key).split('/')
|
||||
ret[comps[-1]] = item.value
|
||||
return ret
|
||||
|
||||
|
||||
def get_jids():
|
||||
'''
|
||||
Return a list of all job ids
|
||||
'''
|
||||
ret = []
|
||||
client, path = _get_conn(__opts__)
|
||||
items = client.get('/'.join((path, 'jobs')))
|
||||
for item in items.children:
|
||||
if item.dir is True:
|
||||
comps = str(item.key).split('/')
|
||||
ret.append(comps[-1])
|
||||
return ret
|
||||
|
||||
|
||||
def get_minions():
|
||||
'''
|
||||
Return a list of minions
|
||||
'''
|
||||
ret = []
|
||||
client, path = _get_conn(__opts__)
|
||||
items = client.get('/'.join((path, 'minions')))
|
||||
for item in items.children:
|
||||
comps = str(item.key).split('/')
|
||||
ret.append(comps[-1])
|
||||
return ret
|
@ -79,7 +79,8 @@ def init(name,
|
||||
[nic=nic_profile] [profile=lxc_profile] \\
|
||||
[nic_opts=nic_opts] [start=(true|false)] \\
|
||||
[seed=(true|false)] [install=(true|false)] \\
|
||||
[config=minion_config]
|
||||
[config=minion_config] [clone=original] \\
|
||||
[snapshot=(true|false)]
|
||||
|
||||
name
|
||||
Name of the container.
|
||||
@ -123,45 +124,52 @@ def init(name,
|
||||
data = __salt__['lxc.list'](host, quiet=True)
|
||||
for host, containers in data.items():
|
||||
if name in sum(containers.values(), []):
|
||||
print('Container "{0}" is already deployed'.format(name))
|
||||
return 'fail'
|
||||
print('Container \'{0}\' already exists on host \'{1}\''.format(
|
||||
host))
|
||||
return False
|
||||
|
||||
if host is None:
|
||||
#TODO: Support selection of host based on available memory/cpu/etc.
|
||||
print('A host must be provided.')
|
||||
return 'fail'
|
||||
print('A host must be provided')
|
||||
return False
|
||||
|
||||
if host not in data:
|
||||
print('Host "{0}" was not found'.format(host))
|
||||
return 'fail'
|
||||
print('Host \'{0}\' was not found'.format(host))
|
||||
return False
|
||||
|
||||
seed = kwargs.get('seed', True)
|
||||
if seed:
|
||||
kw = dict((k, v) for k, v in kwargs.items() if not k.startswith('__'))
|
||||
approve_key = kw.get('approve_key', True)
|
||||
if approve_key:
|
||||
kv = salt.utils.virt.VirtKey(host, name, __opts__)
|
||||
if kv.authorize():
|
||||
print('Minion will be preseeded.')
|
||||
print('Container key will be preauthorized')
|
||||
else:
|
||||
print('Preauthorization failed.')
|
||||
return 'fail'
|
||||
print('Container key preauthorization failed')
|
||||
return False
|
||||
|
||||
client = salt.client.LocalClient(__opts__['conf_file'])
|
||||
|
||||
print('Creating container {0} on host {1}.'.format(name, host))
|
||||
args = [name]
|
||||
print('Creating container \'{0}\' on host \'{1}\''.format(name, host))
|
||||
|
||||
args = [name]
|
||||
cmd_ret = client.cmd_iter(host,
|
||||
'lxc.init',
|
||||
args,
|
||||
kwarg=kwargs,
|
||||
timeout=600)
|
||||
|
||||
ret = next(cmd_ret)
|
||||
if not ret:
|
||||
print('Container {0} was not initialized.'.format(name))
|
||||
return 'fail'
|
||||
if ret and host in ret:
|
||||
ret = ret[host]['ret']
|
||||
else:
|
||||
ret = {}
|
||||
|
||||
print('Container {0} initialized on host {1}'.format(name, host))
|
||||
return 'good'
|
||||
if ret.get('created', False) or ret.get('cloned', False):
|
||||
print('Container \'{0}\' initialized on host \'{1}\''.format(
|
||||
name, host))
|
||||
else:
|
||||
error = ret.get('error', 'unknown error')
|
||||
print('Container \'{0}\' was not initialized: {1}'.format(name, error))
|
||||
return ret or None
|
||||
|
||||
|
||||
def list_(host=None, quiet=False):
|
||||
|
@ -23,8 +23,8 @@ no disk space:
|
||||
cmd.run:
|
||||
- unless: echo 'foo' > /tmp/.test
|
||||
|
||||
Only run if the file specified by ``creates`` does not exist, in this case touch
|
||||
/tmp/foo if it does not exist.
|
||||
Only run if the file specified by ``creates`` does not exist, in this case
|
||||
touch /tmp/foo if it does not exist.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
@ -118,8 +118,9 @@ it can also watch a git state for changes
|
||||
- git: my-project
|
||||
|
||||
|
||||
Should I use :mod:`cmd.run <salt.states.cmd.run>` or :mod:`cmd.wait <salt.states.cmd.wait>`?
|
||||
--------------------------------------------------------------------------------------------
|
||||
Should I use :mod:`cmd.run <salt.states.cmd.run>` or :mod:`cmd.wait
|
||||
<salt.states.cmd.wait>`?
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
These two states are often confused. The important thing to remember about them
|
||||
is that :mod:`cmd.run <salt.states.cmd.run>` states are run each time the SLS
|
||||
@ -147,10 +148,10 @@ executed when the state it is watching changes. Example:
|
||||
- file: /usr/local/bin/postinstall.sh
|
||||
|
||||
How do I create a environment from a pillar map?
|
||||
---------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The map that comes from a pillar cannot be directly consumed by the env option. To use it
|
||||
one must convert it to a list. Example:
|
||||
The map that comes from a pillar cannot be directly consumed by the env option.
|
||||
To use it one must convert it to a list. Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
@ -279,28 +280,53 @@ def _run_check(cmd_kwargs, onlyif, unless, group, creates):
|
||||
'result': False}
|
||||
|
||||
if onlyif is not None:
|
||||
if not isinstance(onlyif, string_types):
|
||||
if not onlyif:
|
||||
if isinstance(onlyif, string_types):
|
||||
if __salt__['cmd.retcode'](onlyif, **cmd_kwargs) != 0:
|
||||
return {'comment': 'onlyif execution failed',
|
||||
'result': True}
|
||||
elif isinstance(onlyif, string_types):
|
||||
if __salt__['cmd.retcode'](onlyif, **cmd_kwargs) != 0:
|
||||
elif isinstance(onlyif, list):
|
||||
if all([
|
||||
__salt__['cmd.retcode'](
|
||||
entry,
|
||||
**cmd_kwargs
|
||||
) != 0 for entry in onlyif
|
||||
]):
|
||||
|
||||
return {'comment': 'onlyif execution failed',
|
||||
'result': True}
|
||||
elif not isinstance(onlyif, string_types):
|
||||
if not onlyif:
|
||||
return {'comment': 'onlyif execution failed',
|
||||
'result': True}
|
||||
|
||||
if unless is not None:
|
||||
if not isinstance(unless, string_types):
|
||||
if unless:
|
||||
if isinstance(unless, string_types):
|
||||
if __salt__['cmd.retcode'](unless, **cmd_kwargs) == 0:
|
||||
return {'comment': 'unless execution succeeded',
|
||||
'result': True}
|
||||
elif isinstance(unless, string_types):
|
||||
if __salt__['cmd.retcode'](unless, **cmd_kwargs) == 0:
|
||||
elif isinstance(unless, list):
|
||||
if all([
|
||||
__salt__['cmd.retcode'](
|
||||
entry,
|
||||
**cmd_kwargs
|
||||
) == 0 for entry in unless
|
||||
]):
|
||||
|
||||
return {'comment': 'unless execution succeeded',
|
||||
'result': True}
|
||||
elif not isinstance(unless, string_types):
|
||||
if unless:
|
||||
return {'comment': 'unless execution succeeded',
|
||||
'result': True}
|
||||
|
||||
if isinstance(creates, string_types) and os.path.exists(creates):
|
||||
return {'comment': '{0} exists'.format(creates),
|
||||
'result': True}
|
||||
elif isinstance(creates, list) and all([
|
||||
os.path.exists(path) for path in creates
|
||||
]):
|
||||
return {'comment': 'All files in creates exist'.format(creates),
|
||||
'result': True}
|
||||
|
||||
# No reason to stop, return True
|
||||
return True
|
||||
@ -550,7 +576,10 @@ def run(name,
|
||||
'comment': ''}
|
||||
|
||||
if cwd and not os.path.isdir(cwd):
|
||||
ret['comment'] = 'Desired working directory "{0}" is not available'.format(cwd)
|
||||
ret['comment'] = (
|
||||
'Desired working directory "{0}" '
|
||||
'is not available'
|
||||
).format(cwd)
|
||||
return ret
|
||||
|
||||
if env:
|
||||
@ -654,9 +683,9 @@ def script(name,
|
||||
Download a script and execute it with specified arguments.
|
||||
|
||||
source
|
||||
The location of the script to download. If the file is located on the master
|
||||
in the directory named spam, and is called eggs, the source string is
|
||||
salt://spam/eggs
|
||||
The location of the script to download. If the file is located on the
|
||||
master in the directory named spam, and is called eggs, the source
|
||||
string is salt://spam/eggs
|
||||
|
||||
template
|
||||
If this setting is applied then the named templating engine will be
|
||||
@ -664,13 +693,16 @@ def script(name,
|
||||
are supported
|
||||
|
||||
name
|
||||
Either "cmd arg1 arg2 arg3..." (cmd is not used) or a source "salt://...".
|
||||
Either "cmd arg1 arg2 arg3..." (cmd is not used) or a source
|
||||
"salt://...".
|
||||
|
||||
onlyif
|
||||
Run the named command only if the command passed to the ``onlyif`` option returns true
|
||||
Run the named command only if the command passed to the ``onlyif``
|
||||
option returns true
|
||||
|
||||
unless
|
||||
Run the named command only if the command passed to the ``unless`` option returns false
|
||||
Run the named command only if the command passed to the ``unless``
|
||||
option returns false
|
||||
|
||||
cwd
|
||||
The current working directory to execute the command in, defaults to
|
||||
@ -701,8 +733,9 @@ def script(name,
|
||||
|
||||
args
|
||||
String of command line args to pass to the script. Only used if no
|
||||
args are specified as part of the `name` argument. To pass a string containing
|
||||
spaces in YAML, you will need to doubly-quote it: "arg1 'arg two' arg3"
|
||||
args are specified as part of the `name` argument. To pass a string
|
||||
containing spaces in YAML, you will need to doubly-quote it: "arg1
|
||||
'arg two' arg3"
|
||||
|
||||
creates
|
||||
Only run if the file specified by ``creates`` does not exist.
|
||||
@ -715,7 +748,10 @@ def script(name,
|
||||
'result': False}
|
||||
|
||||
if cwd and not os.path.isdir(cwd):
|
||||
ret['comment'] = 'Desired working directory "{0}" is not available'.format(cwd)
|
||||
ret['comment'] = (
|
||||
'Desired working directory "{0}" '
|
||||
'is not available'
|
||||
).format(cwd)
|
||||
return ret
|
||||
|
||||
if isinstance(env, string_types):
|
||||
@ -913,7 +949,9 @@ def mod_watch(name, **kwargs):
|
||||
else:
|
||||
return {'name': name,
|
||||
'changes': {},
|
||||
'comment': 'cmd.{0[sfun]} needs a named parameter func'.format(kwargs),
|
||||
'comment': (
|
||||
'cmd.{0[sfun]} needs a named parameter func'
|
||||
).format(kwargs),
|
||||
'result': False}
|
||||
|
||||
return {'name': name,
|
||||
|
@ -184,7 +184,7 @@ def user_present(name,
|
||||
for role in roles[0][tenant_role]:
|
||||
__salt__['keystone.user_role_add'](user=name,
|
||||
role=role,
|
||||
tenant_role=tenant_role,
|
||||
tenant=tenant_role,
|
||||
profile=profile,
|
||||
**connection_args)
|
||||
ret['comment'] = 'Keystone user {0} has been added'.format(name)
|
||||
|
@ -3,7 +3,7 @@
|
||||
Management of nftables
|
||||
======================
|
||||
|
||||
This is an iptables-specific module designed to manage Linux firewalls. It is
|
||||
This is an nftables-specific module designed to manage Linux firewalls. It is
|
||||
expected that this state module, and other system-specific firewall states, may
|
||||
at some point be deprecated in favor of a more generic `firewall` state.
|
||||
|
||||
@ -115,7 +115,7 @@ def __virtual__():
|
||||
|
||||
def chain_present(name, table='filter', table_type=None, hook=None, priority=None, family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Verify the chain is exist.
|
||||
|
||||
@ -169,7 +169,7 @@ def chain_present(name, table='filter', table_type=None, hook=None, priority=Non
|
||||
|
||||
def chain_absent(name, table='filter', family='ipv4'):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Verify the chain is absent.
|
||||
|
||||
@ -195,7 +195,7 @@ def chain_absent(name, table='filter', family='ipv4'):
|
||||
if command is True:
|
||||
ret['changes'] = {'locale': name}
|
||||
ret['result'] = True
|
||||
ret['comment'] = ('iptables {0} chain in {1} table delete success for {2}'
|
||||
ret['comment'] = ('nftables {0} chain in {1} table delete success for {2}'
|
||||
.format(name, table, family))
|
||||
else:
|
||||
ret['result'] = False
|
||||
@ -226,7 +226,7 @@ def append(name, family='ipv4', **kwargs):
|
||||
Network family, ipv4 or ipv6.
|
||||
|
||||
All other arguments are passed in with the same name as the long option
|
||||
that would normally be used for iptables, with one exception: `--state` is
|
||||
that would normally be used for nftables, with one exception: `--state` is
|
||||
specified as `connstate` instead of `state` (not to be confused with
|
||||
`ctstate`).
|
||||
'''
|
||||
@ -281,7 +281,7 @@ def append(name, family='ipv4', **kwargs):
|
||||
|
||||
def insert(name, family='ipv4', **kwargs):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Insert a rule into a chain
|
||||
|
||||
@ -293,7 +293,7 @@ def insert(name, family='ipv4', **kwargs):
|
||||
Networking family, either ipv4 or ipv6
|
||||
|
||||
All other arguments are passed in with the same name as the long option
|
||||
that would normally be used for iptables, with one exception: `--state` is
|
||||
that would normally be used for nftables, with one exception: `--state` is
|
||||
specified as `connstate` instead of `state` (not to be confused with
|
||||
`ctstate`).
|
||||
'''
|
||||
@ -347,7 +347,7 @@ def insert(name, family='ipv4', **kwargs):
|
||||
|
||||
def delete(name, family='ipv4', **kwargs):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Delete a rule to a chain
|
||||
|
||||
@ -359,7 +359,7 @@ def delete(name, family='ipv4', **kwargs):
|
||||
Networking family, either ipv4 or ipv6
|
||||
|
||||
All other arguments are passed in with the same name as the long option
|
||||
that would normally be used for iptables, with one exception: `--state` is
|
||||
that would normally be used for nftables, with one exception: `--state` is
|
||||
specified as `connstate` instead of `state` (not to be confused with
|
||||
`ctstate`).
|
||||
'''
|
||||
@ -424,64 +424,9 @@ def delete(name, family='ipv4', **kwargs):
|
||||
return ret
|
||||
|
||||
|
||||
def set_policy(name, family='ipv4', **kwargs):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
|
||||
Sets the default policy for iptables firewall tables
|
||||
|
||||
family
|
||||
Networking family, either ipv4 or ipv6
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': None,
|
||||
'comment': ''}
|
||||
|
||||
for ignore in _STATE_INTERNAL_KEYWORDS:
|
||||
if ignore in kwargs:
|
||||
del kwargs[ignore]
|
||||
|
||||
if __salt__['iptables.get_policy'](
|
||||
kwargs['table'],
|
||||
kwargs['chain'],
|
||||
family) == kwargs['policy']:
|
||||
ret['result'] = True
|
||||
ret['comment'] = ('iptables default policy for {0} for {1} already set to {2}'
|
||||
.format(kwargs['table'], family, kwargs['policy']))
|
||||
return ret
|
||||
|
||||
if not __salt__['iptables.set_policy'](
|
||||
kwargs['table'],
|
||||
kwargs['chain'],
|
||||
kwargs['policy'],
|
||||
family):
|
||||
ret['changes'] = {'locale': name}
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Set default policy for {0} to {1} family {2}'.format(
|
||||
kwargs['chain'],
|
||||
kwargs['policy'],
|
||||
family
|
||||
)
|
||||
if 'save' in kwargs:
|
||||
if kwargs['save']:
|
||||
__salt__['iptables.save'](filename=None, family=family)
|
||||
ret['comment'] = 'Set and Saved default policy for {0} to {1} family {2}'.format(
|
||||
kwargs['chain'],
|
||||
kwargs['policy'],
|
||||
family
|
||||
)
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to set iptables default policy'
|
||||
return ret
|
||||
|
||||
|
||||
def flush(name, family='ipv4', **kwargs):
|
||||
'''
|
||||
.. versionadded:: 2014.1.0 (Hydrogen)
|
||||
.. versionadded:: Helium
|
||||
|
||||
Flush current nftables state
|
||||
|
||||
@ -531,5 +476,5 @@ def flush(name, family='ipv4', **kwargs):
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to flush iptables rules'
|
||||
ret['comment'] = 'Failed to flush nftables rules'
|
||||
return ret
|
||||
|
@ -26,6 +26,7 @@ as either absent or present
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
@ -51,6 +52,7 @@ def _changes(name,
|
||||
optional_groups=None,
|
||||
remove_groups=True,
|
||||
home=None,
|
||||
createhome=True,
|
||||
password=None,
|
||||
enforce_password=True,
|
||||
shell=None,
|
||||
@ -104,6 +106,9 @@ def _changes(name,
|
||||
if home:
|
||||
if lusr['home'] != home:
|
||||
change['home'] = home
|
||||
if createhome and not os.path.isdir(home):
|
||||
change['homeDoesNotExist'] = home
|
||||
|
||||
if shell:
|
||||
if lusr['shell'] != shell:
|
||||
change['shell'] = shell
|
||||
@ -327,6 +332,7 @@ def present(name,
|
||||
present_optgroups,
|
||||
remove_groups,
|
||||
home,
|
||||
createhome,
|
||||
password,
|
||||
enforce_password,
|
||||
shell,
|
||||
@ -360,6 +366,12 @@ def present(name,
|
||||
if key == 'date':
|
||||
__salt__['shadow.set_date'](name, date)
|
||||
continue
|
||||
if key == 'home' or key == 'homeDoesNotExist':
|
||||
if createhome:
|
||||
__salt__['user.chhome'](name, val, True)
|
||||
else:
|
||||
__salt__['user.chhome'](name, val, False)
|
||||
continue
|
||||
if key == 'mindays':
|
||||
__salt__['shadow.set_mindays'](name, mindays)
|
||||
continue
|
||||
@ -404,6 +416,7 @@ def present(name,
|
||||
present_optgroups,
|
||||
remove_groups,
|
||||
home,
|
||||
createhome,
|
||||
password,
|
||||
enforce_password,
|
||||
shell,
|
||||
|
@ -96,7 +96,7 @@ class TxHead(Head):
|
||||
data['hl'] = hl
|
||||
|
||||
if self.packet.coat.size > raeting.MAX_MESSAGE_SIZE:
|
||||
emsg = "Packet length of {0}, exceeds max of {1}".format(
|
||||
emsg = "Packed message length of {0}, exceeds max of {1}".format(
|
||||
self.packet.coat.size, raeting.MAX_MESSAGE_SIZE)
|
||||
raise raeting.PacketError(emsg)
|
||||
pl = hl + self.packet.coat.size + data['fl']
|
||||
@ -411,7 +411,6 @@ class Packet(object):
|
||||
emsg = "Unrecognizible packet kind."
|
||||
raise raeting.PacketError(emsg)
|
||||
self.data.update(pk=kind)
|
||||
self.segments = None
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
@ -420,13 +419,6 @@ class Packet(object):
|
||||
'''
|
||||
return len(self.packed)
|
||||
|
||||
@property
|
||||
def segmented(self):
|
||||
'''
|
||||
Property is True if packet has at least one entry in .segments
|
||||
'''
|
||||
return (True if self.segments else False)
|
||||
|
||||
@property
|
||||
def segmentive(self):
|
||||
'''
|
||||
@ -466,15 +458,9 @@ class TxPacket(Packet):
|
||||
data = self.data
|
||||
le = data['se']
|
||||
if le == 0:
|
||||
#host = data['sh']
|
||||
#if host == '0.0.0.0':
|
||||
#host = '127.0.0.1'
|
||||
le = (data['sh'], data['sp'])
|
||||
re = data['de']
|
||||
if re == 0:
|
||||
#host = data['dh']
|
||||
#if host == '0.0.0.0':
|
||||
#host = '127.0.0.1'
|
||||
re = (data['dh'], data['dp'])
|
||||
return ((data['cf'], le, re, data['si'], data['ti'], data['bf']))
|
||||
|
||||
@ -501,9 +487,10 @@ class TxPacket(Packet):
|
||||
remote = self.stack.estates[self.data['de']]
|
||||
return (remote.privee.encrypt(msg, remote.publee.key))
|
||||
|
||||
def pack(self):
|
||||
def prepack(self):
|
||||
'''
|
||||
Pack the parts of the packet and then the full packet into .packed
|
||||
Pre Pack the parts of the packet .packed but do not sign so can see
|
||||
if needs to be segmented
|
||||
'''
|
||||
self.body.pack()
|
||||
self.coat.pack()
|
||||
@ -513,46 +500,18 @@ class TxPacket(Packet):
|
||||
self.coat.packed,
|
||||
self.foot.packed])
|
||||
|
||||
if self.size <= raeting.MAX_PACKET_SIZE:
|
||||
self.sign()
|
||||
else:
|
||||
#print "****Segmentize**** packet size = {0}".format(self.size)
|
||||
self.segmentize()
|
||||
|
||||
def segmentize(self):
|
||||
def pack(self):
|
||||
'''
|
||||
Create packeted segments from existing packet
|
||||
Pack the parts of the packet and then the full packet into .packed
|
||||
'''
|
||||
self.segments = odict()
|
||||
fullsize = self.coat.size
|
||||
full = self.coat.packed
|
||||
|
||||
extrasize = 0
|
||||
if self.data['hk'] == raeting.headKinds.json:
|
||||
extrasize = 32 # extra header size as a result of segmentation
|
||||
|
||||
hotelsize = self.head.size + extrasize + self.foot.size
|
||||
haulsize = raeting.MAX_PACKET_SIZE - hotelsize
|
||||
|
||||
segcount = (fullsize // haulsize) + (1 if fullsize % haulsize else 0)
|
||||
for i in range(segcount):
|
||||
if i == segcount - 1: #last segment
|
||||
haul = full[i * haulsize:]
|
||||
else:
|
||||
haul = full[i * haulsize: (i+1) * haulsize]
|
||||
|
||||
segment = TxPacket( stack=self.stack,
|
||||
data=self.data)
|
||||
segment.data.update(sn=i, sc=segcount, ml=fullsize, sf=True)
|
||||
segment.coat.packed = segment.body.packed = haul
|
||||
segment.foot.pack()
|
||||
segment.head.pack()
|
||||
segment.packed = ''.join([ segment.head.packed,
|
||||
segment.coat.packed,
|
||||
self.foot.packed])
|
||||
segment.sign()
|
||||
self.segments[i] = segment
|
||||
self.prepack()
|
||||
if self.size > raeting.MAX_PACKET_SIZE:
|
||||
emsg = "Packet length of {0}, exceeds max of {1}".format(
|
||||
self.size, raeting.MAX_PACKET_SIZE)
|
||||
raise raeting.PacketError(emsg)
|
||||
|
||||
self.sign()
|
||||
|
||||
class RxPacket(Packet):
|
||||
'''
|
||||
@ -567,7 +526,6 @@ class RxPacket(Packet):
|
||||
self.body = RxBody(packet=self)
|
||||
self.coat = RxCoat(packet=self)
|
||||
self.foot = RxFoot(packet=self)
|
||||
self.segments = odict() # desegmentize assumes odict()
|
||||
self.packed = packed or ''
|
||||
|
||||
@property
|
||||
@ -638,14 +596,6 @@ class RxPacket(Packet):
|
||||
|
||||
self.foot.parse() #foot unpacks itself
|
||||
|
||||
def parseSegment(self, packet):
|
||||
'''
|
||||
If packet is segmentive then adds to .segments at index of segment number
|
||||
Assumes packet parseOuter has already happened
|
||||
'''
|
||||
if packet.segmentive:
|
||||
self.segments[packet.data['sn']] = packet
|
||||
|
||||
def unpackInner(self, packed=None):
|
||||
'''
|
||||
Unpacks the body, and coat parts of .packed
|
||||
@ -667,46 +617,155 @@ class RxPacket(Packet):
|
||||
Result is .body.data and .data
|
||||
Raises PacketError exception If failure
|
||||
'''
|
||||
if not self.segmented: #since head and foot are not valid
|
||||
self.unpackInner()
|
||||
self.unpackInner()
|
||||
self.coat.parse()
|
||||
self.body.parse()
|
||||
|
||||
def desegmentable(self):
|
||||
'''
|
||||
Return True of .segments is complete
|
||||
'''
|
||||
if not self.segmentive or not self.segmented:
|
||||
return False
|
||||
|
||||
sc = self.data['sc']
|
||||
for i in range(0, sc):
|
||||
if i not in self.segments:
|
||||
return False
|
||||
if not self.segments[i]:
|
||||
return False
|
||||
return True
|
||||
class Tray(object):
|
||||
'''
|
||||
Manages messages, segmentation when needed and the associated packets
|
||||
'''
|
||||
def __init__(self, stack=None, data=None, body=None, packed=None, **kwa):
|
||||
'''
|
||||
Setup instance
|
||||
'''
|
||||
self.stack = stack
|
||||
self.packed = packed or ''
|
||||
self.data = odict(raeting.PACKET_DEFAULTS)
|
||||
if data:
|
||||
self.data.update(data)
|
||||
self.body = body #body data of message
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
'''
|
||||
Property is the length of the .packed
|
||||
'''
|
||||
return len(self.packed)
|
||||
|
||||
class TxTray(Tray):
|
||||
'''
|
||||
Manages an outgoing message and ites associated packet(s)
|
||||
'''
|
||||
def __init__(self, **kwa):
|
||||
'''
|
||||
Setup instance
|
||||
'''
|
||||
super(TxTray, self).__init__(**kwa)
|
||||
self.packets = []
|
||||
self.current = 0 #current packet to send
|
||||
|
||||
def pack(self, data=None, body=None):
|
||||
'''
|
||||
Convert message in .body into one or more packets
|
||||
'''
|
||||
if data:
|
||||
self.data.update(data)
|
||||
if body is not None:
|
||||
self.body = body
|
||||
|
||||
self.current = 0
|
||||
self.packets = []
|
||||
packet = TxPacket(stack=self.stack,
|
||||
kind=raeting.pcktKinds.message,
|
||||
embody=self.body,
|
||||
data=self.data)
|
||||
|
||||
packet.prepack()
|
||||
if packet.size <= raeting.MAX_PACKET_SIZE:
|
||||
packet.sign()
|
||||
self.packets.append(packet)
|
||||
else:
|
||||
self.packed = packet.coat.packed
|
||||
self.packetize(headsize=packet.head.size, footsize=packet.foot.size)
|
||||
|
||||
def packetize(self, headsize, footsize):
|
||||
'''
|
||||
Create packeted segments from .packed using headsize footsize
|
||||
'''
|
||||
extrasize = 0
|
||||
if self.data['hk'] == raeting.headKinds.json:
|
||||
extrasize = 32 # extra header size as a result of segmentation
|
||||
|
||||
hotelsize = headsize + extrasize + footsize
|
||||
segsize = raeting.MAX_PACKET_SIZE - hotelsize
|
||||
|
||||
segcount = (self.size // segsize) + (1 if self.size % segsize else 0)
|
||||
for i in range(segcount):
|
||||
if i == segcount - 1: #last segment
|
||||
segment = self.packed[i * segsize:]
|
||||
else:
|
||||
segment = self.packed[i * segsize: (i+1) * segsize]
|
||||
|
||||
packet = TxPacket( stack=self.stack,
|
||||
data=self.data)
|
||||
packet.data.update(sn=i, sc=segcount, ml=self.size, sf=True)
|
||||
packet.coat.packed = packet.body.packed = segment
|
||||
packet.foot.pack()
|
||||
packet.head.pack()
|
||||
packet.packed = ''.join([ packet.head.packed,
|
||||
packet.coat.packed,
|
||||
packet.foot.packed])
|
||||
packet.sign()
|
||||
self.packets.append(packet)
|
||||
|
||||
|
||||
class RxTray(Tray):
|
||||
'''
|
||||
Manages segmentated messages and the associated packets
|
||||
'''
|
||||
def __init__(self, segments=None, **kwa):
|
||||
'''
|
||||
Setup instance
|
||||
'''
|
||||
super(RxTray, self).__init__(**kwa)
|
||||
self.segments = segments if segments is not None else []
|
||||
self.complete = False
|
||||
|
||||
def parse(self, packet):
|
||||
'''
|
||||
Process a given packet
|
||||
'''
|
||||
sc = packet.data['sc']
|
||||
console.verbose("segment count = {0} tid={1}\n".format(sc, packet.data['ti']))
|
||||
|
||||
if not self.segments: #get data from first packet received
|
||||
self.data.update(packet.data)
|
||||
self.segments = [None] * sc
|
||||
|
||||
hl = packet.data['hl']
|
||||
fl = packet.data['fl']
|
||||
segment = packet.packed[hl:packet.size - fl]
|
||||
sn = packet.data['sn']
|
||||
|
||||
self.segments[sn] = segment
|
||||
if None in self.segments: #don't have all segments yet
|
||||
return None
|
||||
self.body = self.desegmentize()
|
||||
return self.body
|
||||
|
||||
|
||||
def desegmentize(self):
|
||||
'''
|
||||
Reassemble packet from segments
|
||||
When done ready to call parseInner on self
|
||||
Process message packet assumes already parsed outer so verified signature
|
||||
and processed header data
|
||||
'''
|
||||
hauls = []
|
||||
sc = self.data['sc']
|
||||
for i in range(0, sc):
|
||||
segment = self.segments[i]
|
||||
hl = segment.data['hl']
|
||||
fl = segment.data['fl']
|
||||
haul = segment.packed[hl:segment.size - fl]
|
||||
hauls.append(haul)
|
||||
packed = "".join(hauls)
|
||||
self.coat.packed = packed
|
||||
self.packed = "".join(self.segments)
|
||||
ml = self.data['ml']
|
||||
if self.coat.size != ml:
|
||||
emsg = ("Full segmented payload length '{0}' does not equal head field"
|
||||
" '{1}'".format(self.cost.size, ml))
|
||||
if sc > 1 and self.size != ml:
|
||||
emsg = ("Full message payload length '{0}' does not equal head field"
|
||||
" '{1}'".format(self.size, ml))
|
||||
raise raeting.PacketError(emsg)
|
||||
|
||||
packet = RxPacket(stack = self.stack, data=self.data)
|
||||
packet.coat.packed = self.packed
|
||||
|
||||
packet.coat.parse()
|
||||
packet.body.parse()
|
||||
self.complete = True
|
||||
|
||||
return packet.body.data
|
||||
|
||||
|
||||
|
@ -50,7 +50,6 @@ header data =
|
||||
ti: Transaction ID (TID) Default 0
|
||||
tk: Transaction Kind (TrnsKind)
|
||||
|
||||
|
||||
dt: Datetime Stamp (Datetime) Default 0
|
||||
oi: Order index (OrdrIndx) Default 0
|
||||
|
||||
@ -294,6 +293,17 @@ class PacketError(RaetError):
|
||||
'''
|
||||
pass
|
||||
|
||||
class PacketSizeError(PacketError):
|
||||
'''
|
||||
Packet too large error needs to be segmented
|
||||
|
||||
Usage:
|
||||
emsg = "Packet size {0} too large needs to be segmented".format(size)
|
||||
raise raeting.PacketSizeError(emsg)
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
class KeepError(RaetError):
|
||||
'''
|
||||
Exceptions in RAET keep processing
|
||||
|
@ -35,20 +35,25 @@ def test( bk = raeting.bodyKinds.json):
|
||||
stuff = "".join(stuff)
|
||||
data.update(bk=raeting.bodyKinds.raw)
|
||||
packet0 = packeting.TxPacket(embody=stuff, data=data, )
|
||||
packet0.pack()
|
||||
print packet0.packed
|
||||
packet1 = packeting.RxPacket(packed=packet0.packed)
|
||||
packet1.parse()
|
||||
print packet1.data
|
||||
print packet1.body.data
|
||||
try:
|
||||
packet0.pack()
|
||||
except raeting.PacketError as ex:
|
||||
print ex
|
||||
print "Need to use tray"
|
||||
|
||||
rejoin = []
|
||||
if packet0.segmented:
|
||||
for index, segment in packet0.segments.items():
|
||||
print index, segment.packed
|
||||
rejoin.append(segment.body.packed)
|
||||
rejoin = "".join(rejoin)
|
||||
print stuff == rejoin
|
||||
tray0 = packeting.TxTray(data=data, body=stuff)
|
||||
tray0.pack()
|
||||
print tray0.packed
|
||||
print tray0.packets
|
||||
|
||||
tray1 = packeting.RxTray()
|
||||
for packet in tray0.packets:
|
||||
tray1.parse(packet)
|
||||
|
||||
print tray1.data
|
||||
print tray1.body
|
||||
|
||||
print stuff == tray1.body
|
||||
|
||||
#master stack
|
||||
masterName = "master"
|
||||
@ -106,110 +111,59 @@ def test( bk = raeting.bodyKinds.json):
|
||||
|
||||
print "\n___________Raw Body Test"
|
||||
data.update(se=1, de=2, bk=raeting.bodyKinds.raw, fk=raeting.footKinds.nacl)
|
||||
packet0 = packeting.TxPacket(stack=stack0, embody=stuff, data=data, )
|
||||
packet0.pack()
|
||||
print packet0.packed #not signed if segmented each segment is signed
|
||||
tray0 = packeting.TxTray(stack=stack0, data=data, body=stuff)
|
||||
tray0.pack()
|
||||
print tray0.packed
|
||||
print tray0.packets
|
||||
|
||||
rejoin = []
|
||||
if packet0.segmented:
|
||||
for index, segment in packet0.segments.items():
|
||||
print index, segment.packed
|
||||
rejoin.append(segment.coat.packed)
|
||||
tray1 = packeting.RxTray(stack=stack1)
|
||||
for packet in tray0.packets:
|
||||
tray1.parse(packet)
|
||||
|
||||
rejoin = "".join(rejoin)
|
||||
print stuff == rejoin
|
||||
print tray1.data
|
||||
print tray1.body
|
||||
|
||||
segmentage = None
|
||||
if packet0.segmented:
|
||||
for segment in packet0.segments.values():
|
||||
packet = packeting.RxPacket(stack=stack1, packed=segment.packed)
|
||||
packet.parseOuter()
|
||||
if packet.segmentive:
|
||||
if not segmentage:
|
||||
segmentage = packeting.RxPacket(stack=packet.stack,
|
||||
data=packet.data)
|
||||
segmentage.parseSegment(packet)
|
||||
if segmentage.desegmentable():
|
||||
segmentage.desegmentize()
|
||||
break
|
||||
if segmentage:
|
||||
if not stack1.parseInner(segmentage):
|
||||
print "*******BAD SEGMENTAGE********"
|
||||
return
|
||||
print segmentage.body.packed
|
||||
print segmentage.body.data
|
||||
print segmentage.body.packed == packet0.body.packed
|
||||
|
||||
body = odict(stuff=stuff)
|
||||
print body
|
||||
print stuff == tray1.body
|
||||
|
||||
print "\n_____________ Packed Body Test"
|
||||
data.update(se=1, de=2, bk=bk, fk=raeting.footKinds.nacl)
|
||||
packet0 = packeting.TxPacket(stack=stack0, embody=body, data=data, )
|
||||
packet0.pack()
|
||||
print packet0.packed
|
||||
|
||||
segmentage = None
|
||||
if packet0.segmented:
|
||||
for segment in packet0.segments.values():
|
||||
packet = packeting.RxPacket(stack=stack1, packed=segment.packed)
|
||||
packet.parseOuter()
|
||||
if packet.segmentive:
|
||||
if not segmentage:
|
||||
segmentage = packeting.RxPacket(stack=packet.stack,
|
||||
data=packet.data)
|
||||
segmentage.parseSegment(packet)
|
||||
if segmentage.desegmentable():
|
||||
segmentage.desegmentize()
|
||||
break
|
||||
if segmentage:
|
||||
if not stack1.parseInner(segmentage):
|
||||
print "*******BAD SEGMENTAGE********"
|
||||
return
|
||||
print segmentage.body.packed
|
||||
print segmentage.body.data
|
||||
print segmentage.body.packed == packet0.body.packed
|
||||
|
||||
body = odict(stuff=stuff)
|
||||
print body
|
||||
data.update(se=1, de=2, bk=bk, fk=raeting.footKinds.nacl)
|
||||
tray0 = packeting.TxTray(stack=stack0, data=data, body=body)
|
||||
tray0.pack()
|
||||
print tray0.packed
|
||||
print tray0.packets
|
||||
|
||||
tray1 = packeting.RxTray(stack=stack1)
|
||||
for packet in tray0.packets:
|
||||
tray1.parse(packet)
|
||||
|
||||
print tray1.data
|
||||
print tray1.body
|
||||
|
||||
print body == tray1.body
|
||||
|
||||
|
||||
print "\n___________ Encrypted Coat Test "
|
||||
body = odict(stuff=stuff)
|
||||
print body
|
||||
data.update(se=1, de=2,
|
||||
bk=raeting.bodyKinds.json,
|
||||
ck=raeting.coatKinds.nacl,
|
||||
fk=raeting.footKinds.nacl)
|
||||
packet0 = packeting.TxPacket(stack=stack0, embody=body, data=data, )
|
||||
packet0.pack()
|
||||
print "Body"
|
||||
print packet0.body.size, packet0.body.packed
|
||||
print "Coat"
|
||||
print packet0.coat.size, packet0.coat.packed
|
||||
print "Head"
|
||||
print packet0.head.size, packet0.head.packed
|
||||
print "Foot"
|
||||
print packet0.foot.size, packet0.foot.packed
|
||||
print "Packet"
|
||||
print packet0.size, packet0.packed
|
||||
tray0 = packeting.TxTray(stack=stack0, data=data, body=body)
|
||||
tray0.pack()
|
||||
print tray0.packed
|
||||
print tray0.packets
|
||||
|
||||
segmentage = None
|
||||
if packet0.segmented:
|
||||
for segment in packet0.segments.values():
|
||||
packet = packeting.RxPacket(stack=stack1, packed=segment.packed)
|
||||
packet.parseOuter()
|
||||
if packet.segmentive:
|
||||
if not segmentage:
|
||||
segmentage = packeting.RxPacket(stack=packet.stack,
|
||||
data=packet.data)
|
||||
segmentage.parseSegment(packet)
|
||||
if segmentage.desegmentable():
|
||||
segmentage.desegmentize()
|
||||
break
|
||||
if segmentage:
|
||||
if not stack1.parseInner(segmentage):
|
||||
print "*******BAD SEGMENTAGE********"
|
||||
print segmentage.body.packed
|
||||
print segmentage.body.data
|
||||
print segmentage.body.packed == packet0.body.packed
|
||||
tray1 = packeting.RxTray(stack=stack1)
|
||||
for packet in tray0.packets:
|
||||
tray1.parse(packet)
|
||||
|
||||
print tray1.data
|
||||
print tray1.body
|
||||
|
||||
print body == tray1.body
|
||||
|
||||
|
||||
stack0.server.close()
|
||||
|
@ -884,7 +884,7 @@ class Allower(Initiator):
|
||||
RAET protocol Allower Initiator class Dual of Allowent
|
||||
CurveCP handshake
|
||||
'''
|
||||
Timeout = 2.0
|
||||
Timeout = 4.0
|
||||
RedoTimeoutMin = 0.25 # initial timeout
|
||||
RedoTimeoutMax = 1.0 # max timeout
|
||||
|
||||
@ -959,7 +959,6 @@ class Allower(Initiator):
|
||||
self.transmit(self.txPacket) # redo
|
||||
console.concise("Allower Redo Ack Final at {0}\n".format(self.stack.store.stamp))
|
||||
|
||||
|
||||
def prep(self):
|
||||
'''
|
||||
Prepare .txData
|
||||
@ -1165,7 +1164,7 @@ class Allowent(Correspondent):
|
||||
RAET protocol Allowent Correspondent class Dual of Allower
|
||||
CurveCP handshake
|
||||
'''
|
||||
Timeout = 2.0
|
||||
Timeout = 4.0
|
||||
RedoTimeoutMin = 0.25 # initial timeout
|
||||
RedoTimeoutMax = 1.0 # max timeout
|
||||
|
||||
@ -1517,13 +1516,22 @@ class Messenger(Initiator):
|
||||
RAET protocol Messenger Initiator class Dual of Messengent
|
||||
Generic messages
|
||||
'''
|
||||
def __init__(self, **kwa):
|
||||
Timeout = 10.0
|
||||
RedoTimeoutMin = 1.0 # initial timeout
|
||||
RedoTimeoutMax = 3.0 # max timeout
|
||||
|
||||
def __init__(self, redoTimeoutMin=None, redoTimeoutMax=None, **kwa):
|
||||
'''
|
||||
Setup instance
|
||||
'''
|
||||
kwa['kind'] = raeting.trnsKinds.message
|
||||
super(Messenger, self).__init__(**kwa)
|
||||
self.segmentage = None # special packet to hold segments if any
|
||||
|
||||
self.redoTimeoutMax = redoTimeoutMax or self.RedoTimeoutMax
|
||||
self.redoTimeoutMin = redoTimeoutMin or self.RedoTimeoutMin
|
||||
self.redoTimer = aiding.StoreTimer(self.stack.store,
|
||||
duration=self.redoTimeoutMin)
|
||||
|
||||
if self.reid is None:
|
||||
self.reid = self.stack.estates.values()[0].eid # zeroth is channel master
|
||||
remote = self.stack.estates[self.reid]
|
||||
@ -1536,6 +1544,7 @@ class Messenger(Initiator):
|
||||
self.sid = remote.sid
|
||||
self.tid = remote.nextTid()
|
||||
self.prep() # prepare .txData
|
||||
self.tray = packeting.TxTray(stack=self.stack)
|
||||
self.add(self.index)
|
||||
|
||||
def receive(self, packet):
|
||||
@ -1546,10 +1555,32 @@ class Messenger(Initiator):
|
||||
|
||||
if packet.data['tk'] == raeting.trnsKinds.message:
|
||||
if packet.data['pk'] == raeting.pcktKinds.ack:
|
||||
self.done()
|
||||
self.again()
|
||||
elif packet.data['pk'] == raeting.pcktKinds.nack: # rejected
|
||||
self.rejected()
|
||||
|
||||
def process(self):
|
||||
'''
|
||||
Perform time based processing of transaction
|
||||
'''
|
||||
if self.timeout > 0.0 and self.timer.expired:
|
||||
self.remove()
|
||||
console.concise("Messenger timed out at {0}\n".format(self.stack.store.stamp))
|
||||
return
|
||||
|
||||
# need keep sending message until completed or timed out
|
||||
if self.redoTimer.expired:
|
||||
duration = min(
|
||||
max(self.redoTimeoutMin,
|
||||
self.redoTimer.duration) * 2.0,
|
||||
self.redoTimeoutMin)
|
||||
self.redoTimer.restart(duration=duration)
|
||||
if self.txPacket:
|
||||
if self.txPacket.data['pk'] == raeting.pcktKinds.message:
|
||||
self.transmit(self.txPacket) # redo
|
||||
console.concise("Messenger Redo Segment {0} at {1}\n".format(
|
||||
self.tray.current, self.stack.store.stamp))
|
||||
|
||||
def prep(self):
|
||||
'''
|
||||
Prepare .txData
|
||||
@ -1579,31 +1610,35 @@ class Messenger(Initiator):
|
||||
self.remove()
|
||||
return
|
||||
|
||||
if body is None:
|
||||
body = odict()
|
||||
if not self.tray.packets:
|
||||
try:
|
||||
self.tray.pack(data=self.txData, body=body)
|
||||
except raeting.PacketError as ex:
|
||||
console.terse(ex + '\n')
|
||||
self.stack.incStat("packing_error")
|
||||
self.remove()
|
||||
return
|
||||
|
||||
packet = packeting.TxPacket(stack=self.stack,
|
||||
kind=raeting.pcktKinds.message,
|
||||
embody=body,
|
||||
data=self.txData)
|
||||
try:
|
||||
packet.pack()
|
||||
except raeting.PacketError as ex:
|
||||
console.terse(ex + '\n')
|
||||
self.stack.incStat("packing_error")
|
||||
self.remove()
|
||||
if self.tray.current >= len(self.tray.packets):
|
||||
return
|
||||
if packet.segmented:
|
||||
self.segmentage = packet
|
||||
for segment in self.segmentage.segments.values():
|
||||
self.transmit(segment)
|
||||
|
||||
packet = self.tray.packets[self.tray.current]
|
||||
self.transmit(packet)
|
||||
self.stack.incStat("message_segment_tx")
|
||||
console.concise("Messenger Do Message Segment {0} at {1}\n".format(
|
||||
self.tray.current, self.stack.store.stamp))
|
||||
self.tray.current += 1
|
||||
|
||||
def again(self):
|
||||
'''
|
||||
Process ack packet
|
||||
'''
|
||||
if self.tray.current >= len(self.tray.packets):
|
||||
self.complete()
|
||||
else:
|
||||
self.transmit(packet)
|
||||
self.message()
|
||||
|
||||
console.concise("Messenger Do Message at {0}\n".format(self.stack.store.stamp))
|
||||
|
||||
def done(self):
|
||||
def complete(self):
|
||||
'''
|
||||
Complete transaction and remove
|
||||
'''
|
||||
@ -1627,7 +1662,11 @@ class Messengent(Correspondent):
|
||||
RAET protocol Messengent Correspondent class Dual of Messenger
|
||||
Generic Messages
|
||||
'''
|
||||
def __init__(self, **kwa):
|
||||
Timeout = 10.0
|
||||
RedoTimeoutMin = 1.0 # initial timeout
|
||||
RedoTimeoutMax = 3.0 # max timeout
|
||||
|
||||
def __init__(self, redoTimeoutMin=None, redoTimeoutMax=None, **kwa):
|
||||
'''
|
||||
Setup instance
|
||||
'''
|
||||
@ -1636,7 +1675,12 @@ class Messengent(Correspondent):
|
||||
emsg = "Missing required keyword argumens: '{0}'".format('reid')
|
||||
raise TypeError(emsg)
|
||||
super(Messengent, self).__init__(**kwa)
|
||||
self.segmentage = None # special packet to hold segments if any
|
||||
|
||||
self.redoTimeoutMax = redoTimeoutMax or self.RedoTimeoutMax
|
||||
self.redoTimeoutMin = redoTimeoutMin or self.RedoTimeoutMin
|
||||
self.redoTimer = aiding.StoreTimer(self.stack.store,
|
||||
duration=self.redoTimeoutMin)
|
||||
|
||||
remote = self.stack.estates[self.reid]
|
||||
if not remote.allowed:
|
||||
emsg = "Must be allowed first"
|
||||
@ -1654,6 +1698,7 @@ class Messengent(Correspondent):
|
||||
remote.rsid = self.sid #update last received rsid for estate
|
||||
remote.rtid = self.tid #update last received rtid for estate
|
||||
self.prep() # prepare .txData
|
||||
self.tray = packeting.RxTray(stack=self.stack)
|
||||
self.add(self.index)
|
||||
|
||||
def receive(self, packet):
|
||||
@ -1669,6 +1714,30 @@ class Messengent(Correspondent):
|
||||
elif packet.data['pk'] == raeting.pcktKinds.nack: # rejected
|
||||
self.rejected()
|
||||
|
||||
def process(self):
|
||||
'''
|
||||
Perform time based processing of transaction
|
||||
|
||||
'''
|
||||
if self.timeout > 0.0 and self.timer.expired:
|
||||
self.nack()
|
||||
console.concise("Messengent timed out at {0}\n".format(self.stack.store.stamp))
|
||||
return
|
||||
|
||||
# need to include current segment in ack or resend
|
||||
#if self.redoTimer.expired:
|
||||
#duration = min(
|
||||
#max(self.redoTimeoutMin,
|
||||
#self.redoTimer.duration) * 2.0,
|
||||
#self.redoTimeoutMax)
|
||||
#self.redoTimer.restart(duration=duration)
|
||||
|
||||
#if self.txPacket:
|
||||
#if self.txPacket.data['pk'] == raeting.pcktKinds.ack:
|
||||
#self.transmit(self.txPacket) #redo
|
||||
#console.concise("Messengent Redo Ack at {0}\n".format(self.stack.store.stamp))
|
||||
|
||||
|
||||
def prep(self):
|
||||
'''
|
||||
Prepare .txData
|
||||
@ -1690,27 +1759,23 @@ class Messengent(Correspondent):
|
||||
'''
|
||||
Process message packet
|
||||
'''
|
||||
console.verbose("segment count = {0} tid={1}\n".format(
|
||||
self.rxPacket.data['sc'], self.tid))
|
||||
if self.rxPacket.segmentive:
|
||||
if not self.segmentage:
|
||||
self.segmentage = packeting.RxPacket(stack=self.stack,
|
||||
data=self.rxPacket.data)
|
||||
self.segmentage.parseSegment(self.rxPacket)
|
||||
if not self.segmentage.desegmentable():
|
||||
return
|
||||
self.segmentage.desegmentize()
|
||||
if not self.stack.parseInner(self.segmentage):
|
||||
return
|
||||
body = self.segmentage.body.data
|
||||
else:
|
||||
if not self.stack.parseInner(self.rxPacket):
|
||||
return
|
||||
body = self.rxPacket.body.data
|
||||
try:
|
||||
body = self.tray.parse(self.rxPacket)
|
||||
except raeting.PacketError as ex:
|
||||
console.terse(ex + '\n')
|
||||
self.incStat('parsing_message_error')
|
||||
self.remove()
|
||||
return
|
||||
|
||||
self.stack.rxMsgs.append(body)
|
||||
self.ackMessage()
|
||||
|
||||
if self.tray.complete:
|
||||
console.verbose("{0} received message body\n{1}\n".format(
|
||||
self.stack.name, body))
|
||||
self.stack.rxMsgs.append(body)
|
||||
self.complete()
|
||||
|
||||
|
||||
def ackMessage(self):
|
||||
'''
|
||||
Send ack to message
|
||||
@ -1736,9 +1801,17 @@ class Messengent(Correspondent):
|
||||
self.remove()
|
||||
return
|
||||
self.transmit(packet)
|
||||
self.stack.incStat("message_segment_rx")
|
||||
console.concise("Messengent Do Ack Segment at {0}\n".format(
|
||||
self.stack.store.stamp))
|
||||
|
||||
def complete(self):
|
||||
'''
|
||||
Complete transaction and remove
|
||||
'''
|
||||
self.remove()
|
||||
console.concise("Messengent Do Ack at {0}\n".format(self.stack.store.stamp))
|
||||
self.stack.incStat("message_correspond_complete")
|
||||
console.concise("Messengent Complete at {0}\n".format(self.stack.store.stamp))
|
||||
self.stack.incStat("messagent_correspond_complete")
|
||||
|
||||
def rejected(self):
|
||||
'''
|
||||
|
@ -2104,6 +2104,7 @@ def get_group_list(user=None, include_default=True):
|
||||
is a member.
|
||||
'''
|
||||
group_names = None
|
||||
ugroups = set()
|
||||
if not isinstance(user, string_types):
|
||||
raise Exception
|
||||
if hasattr(os, 'getgrouplist'):
|
||||
@ -2111,7 +2112,6 @@ def get_group_list(user=None, include_default=True):
|
||||
log.trace('Trying os.getgrouplist for {0!r}'.format(user))
|
||||
try:
|
||||
group_names = list(os.getgrouplist(user, pwd.getpwnam(user).pw_gid))
|
||||
log.trace('os.getgrouplist for user {0!r}: {1!r}'.format(user, group_names))
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
@ -2120,7 +2120,6 @@ def get_group_list(user=None, include_default=True):
|
||||
try:
|
||||
import pysss
|
||||
group_names = list(pysss.getgrouplist(user))
|
||||
log.trace('pysss.getgrouplist for user {0!r}: {1!r}'.format(user, group_names))
|
||||
except Exception:
|
||||
pass
|
||||
if group_names is None:
|
||||
@ -2136,7 +2135,7 @@ def get_group_list(user=None, include_default=True):
|
||||
except KeyError:
|
||||
# If for some reason the user does not have a default group
|
||||
pass
|
||||
log.trace('Generic group list for user {0!r}: {1!r}'.format(user, group_names))
|
||||
ugroups.update(group_names)
|
||||
if include_default is False:
|
||||
# Historically, saltstack code for getting group lists did not
|
||||
# include the default group. Some things may only want
|
||||
@ -2144,11 +2143,12 @@ def get_group_list(user=None, include_default=True):
|
||||
# default group.
|
||||
try:
|
||||
default_group = grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name
|
||||
group_names.remove(default_group)
|
||||
ugroups.remove(default_group)
|
||||
except KeyError:
|
||||
# If for some reason the user does not have a default group
|
||||
pass
|
||||
return sorted(group_names)
|
||||
log.trace('Group list for user {0!r}: {1!r}'.format(user, sorted(ugroups)))
|
||||
return sorted(ugroups)
|
||||
|
||||
|
||||
def get_group_dict(user=None, include_default=True):
|
||||
@ -2170,4 +2170,4 @@ def get_gid_list(user=None, include_default=True):
|
||||
is a member.
|
||||
'''
|
||||
gid_list = [gid for (group, gid) in salt.utils.get_group_dict(user, include_default=include_default).items()]
|
||||
return gid_list
|
||||
return sorted(set(gid_list))
|
||||
|
79
salt/utils/etcd_util.py
Normal file
79
salt/utils/etcd_util.py
Normal file
@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Utilities for working with etcd
|
||||
|
||||
:depends: - python-etcd
|
||||
|
||||
This library sets up a client object for etcd, using the configuration passed
|
||||
into the client() function. Normally, this is __opts__. Optionally, a profile
|
||||
may be passed in. The following configurations are both valid:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# No profile name
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
# One or more profiles defined
|
||||
my_etcd_config:
|
||||
etcd.host: 127.0.0.1
|
||||
etcd.port: 4001
|
||||
|
||||
Once configured, the client() function is passed a set of opts, and optionally,
|
||||
the name of a profile to be used.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import salt.utils.etcd_utils
|
||||
client = salt.utils.etcd_utils.client(__opts__, profile='my_etcd_config')
|
||||
|
||||
It should be noted that some usages of etcd require a profile to be specified,
|
||||
rather than top-level configurations. This being the case, it is better to
|
||||
always use a named configuration profile, as shown above.
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
import etcd
|
||||
HAS_LIBS = True
|
||||
except Exception:
|
||||
HAS_LIBS = False
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_conn(opts, profile=None):
|
||||
'''
|
||||
Return a client object for accessing etcd
|
||||
'''
|
||||
if profile:
|
||||
conf = opts.get(profile, {})
|
||||
else:
|
||||
conf = opts
|
||||
|
||||
host = conf.get('etcd.host', '127.0.0.1')
|
||||
port = conf.get('etcd.port', 4001)
|
||||
|
||||
return etcd.Client(host, port)
|
||||
|
||||
|
||||
def tree(client, path):
|
||||
'''
|
||||
Recurse through etcd and return all values
|
||||
'''
|
||||
ret = {}
|
||||
items = client.get(path)
|
||||
|
||||
for item in items.children:
|
||||
comps = str(item.key).split('/')
|
||||
if item.dir is True:
|
||||
if item.key == path:
|
||||
continue
|
||||
ret[comps[-1]] = tree(client, item.key)
|
||||
else:
|
||||
ret[comps[-1]] = item.value
|
||||
return ret
|
@ -528,7 +528,7 @@ class CkMinions(object):
|
||||
fun,
|
||||
form)
|
||||
|
||||
def auth_check(self, auth_list, funs, tgt, tgt_type='glob'):
|
||||
def auth_check(self, auth_list, funs, tgt, tgt_type='glob', groups=None):
|
||||
'''
|
||||
Returns a bool which defines if the requested function is authorized.
|
||||
Used to evaluate the standard structure under external master
|
||||
@ -537,7 +537,6 @@ class CkMinions(object):
|
||||
# compound commands will come in a list so treat everything as a list
|
||||
if not isinstance(funs, list):
|
||||
funs = [funs]
|
||||
|
||||
for fun in funs:
|
||||
for ind in auth_list:
|
||||
if isinstance(ind, str):
|
||||
@ -564,6 +563,23 @@ class CkMinions(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def gather_groups(self, auth_provider, user_groups, auth_list):
|
||||
'''
|
||||
Returns the list of groups, if any, for a given authentication provider type
|
||||
|
||||
Groups are defined as any dict in which a key has a trailing '%'
|
||||
'''
|
||||
group_perm_keys = filter(lambda(item): item.endswith('%'), auth_provider)
|
||||
groups = {}
|
||||
if group_perm_keys:
|
||||
for group_perm in group_perm_keys:
|
||||
for matcher in auth_provider[group_perm]:
|
||||
if group_perm[:-1] in user_groups:
|
||||
groups[group_perm] = matcher
|
||||
for item in groups.values():
|
||||
auth_list.append(item)
|
||||
return auth_list
|
||||
|
||||
def wheel_check(self, auth_list, fun):
|
||||
'''
|
||||
Check special API permissions
|
||||
|
5
setup.py
5
setup.py
@ -389,7 +389,10 @@ class InstallLib(install_lib):
|
||||
inp = self.get_inputs()
|
||||
out = self.get_outputs()
|
||||
|
||||
idx = inp.index('build/lib/salt/templates/git/ssh-id-wrapper')
|
||||
#idx = inp.index('build/lib/salt/templates/git/ssh-id-wrapper')
|
||||
for i, word in enumerate(inp):
|
||||
if word.endswith('salt/templates/git/ssh-id-wrapper'):
|
||||
idx = i
|
||||
filename = out[idx]
|
||||
|
||||
os.chmod(filename, 0755)
|
||||
|
@ -37,6 +37,12 @@ def parse():
|
||||
help=('State if this listener will attach to a master or a '
|
||||
'minion daemon, pass "master" or "minion"'))
|
||||
|
||||
parser.add_option('-f',
|
||||
'--func_count',
|
||||
default='',
|
||||
help=('Retun a count of the number of minons which have '
|
||||
'replied to a job with a given func.'))
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
opts = {}
|
||||
@ -67,15 +73,27 @@ def listen(sock_dir, node, id=None):
|
||||
id=id
|
||||
)
|
||||
print(event.puburi)
|
||||
jid_counter = 0
|
||||
found_minions = []
|
||||
while True:
|
||||
ret = event.get_event(full=True)
|
||||
if ret is None:
|
||||
continue
|
||||
print('Event fired at {0}'.format(time.asctime()))
|
||||
print('*' * 25)
|
||||
print('Tag: {0}'.format(ret['tag']))
|
||||
print('Data:')
|
||||
pprint.pprint(ret['data'])
|
||||
if opts['func_count']:
|
||||
data = ret.get('data', False)
|
||||
if data:
|
||||
if 'id' in data.keys() and data.get('id', False) not in found_minions:
|
||||
if data['fun'] == opts['func_count']:
|
||||
jid_counter += 1
|
||||
found_minions.append(data['id'])
|
||||
print('Reply received from [{0}]. Total replies now: [{1}].'.format(ret['data']['id'], jid_counter))
|
||||
continue
|
||||
else:
|
||||
print('Event fired at {0}'.format(time.asctime()))
|
||||
print('*' * 25)
|
||||
print('Tag: {0}'.format(ret['tag']))
|
||||
print('Data:')
|
||||
pprint.pprint(ret['data'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
137
tests/integration/modules/mac_group.py
Normal file
137
tests/integration/modules/mac_group.py
Normal file
@ -0,0 +1,137 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Nicole Thomas <nicole@saltstack.com>`
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf
|
||||
from salttesting.helpers import (
|
||||
destructiveTest,
|
||||
ensure_in_syspath,
|
||||
requires_system_grains
|
||||
)
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
import integration
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
|
||||
def __random_string(size=6):
|
||||
'''
|
||||
Generates a random username
|
||||
'''
|
||||
return 'RS-' + ''.join(
|
||||
random.choice(string.ascii_uppercase + string.digits)
|
||||
for x in range(size)
|
||||
)
|
||||
|
||||
# Create group name strings for tests
|
||||
ADD_GROUP = __random_string()
|
||||
DEL_GROUP = __random_string()
|
||||
CHANGE_GROUP = __random_string()
|
||||
|
||||
|
||||
class MacGroupModuleTest(integration.ModuleCase):
|
||||
'''
|
||||
Integration tests for the mac_group module
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
'''
|
||||
Sets up test requirements
|
||||
'''
|
||||
super(MacGroupModuleTest, self).setUp()
|
||||
os_grain = self.run_function('grains.item', ['kernel'])
|
||||
if os_grain['kernel'] not in 'Darwin':
|
||||
self.skipTest(
|
||||
'Test not applicable to \'{kernel}\' kernel'.format(
|
||||
**os_grain
|
||||
)
|
||||
)
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
|
||||
@requires_system_grains
|
||||
def test_mac_group_add(self, grains=None):
|
||||
'''
|
||||
Tests the add group function
|
||||
'''
|
||||
try:
|
||||
self.run_function('group.add', [ADD_GROUP, 3456])
|
||||
group_info = self.run_function('group.info', [ADD_GROUP])
|
||||
self.assertEqual(group_info['name'], ADD_GROUP)
|
||||
except CommandExecutionError:
|
||||
self.run_function('group.delete', [ADD_GROUP])
|
||||
raise
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
|
||||
@requires_system_grains
|
||||
def test_mac_group_delete(self, grains=None):
|
||||
'''
|
||||
Tests the delete group function
|
||||
'''
|
||||
# Create a group to delete - If unsuccessful, skip the test
|
||||
if self.run_function('group.add', [DEL_GROUP, 4567]) is not True:
|
||||
self.run_function('group.delete', [DEL_GROUP])
|
||||
self.skipTest('Failed to create a group to delete')
|
||||
|
||||
try:
|
||||
# Now try to delete the added group
|
||||
ret = self.run_function('group.delete', [DEL_GROUP])
|
||||
self.assertTrue(ret)
|
||||
except CommandExecutionError:
|
||||
raise
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.getuid() != 0, 'You must be logged in as root to run this test')
|
||||
@requires_system_grains
|
||||
def test_mac_group_chgid(self, grains=None):
|
||||
'''
|
||||
Tests changing the group id
|
||||
'''
|
||||
# Create a group to delete - If unsuccessful, skip the test
|
||||
if self.run_function('group.add', [CHANGE_GROUP, 5678]) is not True:
|
||||
self.run_function('group.delete', [CHANGE_GROUP])
|
||||
self.skipTest('Failed to create a group to manipulate')
|
||||
|
||||
try:
|
||||
self.run_function('group.chgid', [CHANGE_GROUP, 6789])
|
||||
group_info = self.run_function('group.info', [CHANGE_GROUP])
|
||||
self.assertEqual(group_info['gid'], 6789)
|
||||
except AssertionError:
|
||||
self.run_function('group.delete', [CHANGE_GROUP])
|
||||
raise
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
|
||||
@requires_system_grains
|
||||
def tearDown(self, grains=None):
|
||||
'''
|
||||
Clean up after tests
|
||||
'''
|
||||
# Delete ADD_GROUP
|
||||
add_info = self.run_function('group.info', [ADD_GROUP])
|
||||
if add_info:
|
||||
self.run_function('group.delete', [ADD_GROUP])
|
||||
|
||||
# Delete DEL_GROUP if something failed
|
||||
del_info = self.run_function('group.info', [DEL_GROUP])
|
||||
if del_info:
|
||||
self.run_function('group.delete', [DEL_GROUP])
|
||||
|
||||
# Delete CHANGE_GROUP
|
||||
change_info = self.run_function('group.info', [CHANGE_GROUP])
|
||||
if change_info:
|
||||
self.run_function('group.delete', [CHANGE_GROUP])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(MacGroupModuleTest)
|
@ -34,6 +34,7 @@ def __random_string(size=6):
|
||||
# Create user strings for tests
|
||||
ADD_USER = __random_string()
|
||||
DEL_USER = __random_string()
|
||||
CHANGE_USER = __random_string()
|
||||
|
||||
|
||||
class MacUserModuleTest(integration.ModuleCase):
|
||||
@ -79,6 +80,7 @@ class MacUserModuleTest(integration.ModuleCase):
|
||||
|
||||
# Create a user to delete - If unsuccessful, skip the test
|
||||
if self.run_function('user.add', [DEL_USER]) is not True:
|
||||
self.run_function('user.delete', [DEL_USER])
|
||||
self.skipTest('Failed to create a user to delete')
|
||||
|
||||
try:
|
||||
@ -88,6 +90,53 @@ class MacUserModuleTest(integration.ModuleCase):
|
||||
except CommandExecutionError:
|
||||
raise
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
|
||||
@requires_system_grains
|
||||
def test_mac_user_changes(self, grains=None):
|
||||
'''
|
||||
Tests mac_user functions that change user properties
|
||||
'''
|
||||
# Create a user to manipulate - if unsuccessful, skip the test
|
||||
if self.run_function('user.add', [CHANGE_USER]) is not True:
|
||||
self.run_function('user.delete', [CHANGE_USER])
|
||||
self.skipTest('Failed to create a user')
|
||||
|
||||
try:
|
||||
# Test mac_user.chudi
|
||||
self.run_function('user.chuid', [CHANGE_USER, 4376])
|
||||
uid_info = self.run_function('user.info', [CHANGE_USER])
|
||||
self.assertEqual(uid_info['uid'], 4376)
|
||||
|
||||
# Test mac_user.chgid
|
||||
self.run_function('user.chgid', [CHANGE_USER, 4376])
|
||||
gid_info = self.run_function('user.info', [CHANGE_USER])
|
||||
self.assertEqual(gid_info['gid'], 4376)
|
||||
|
||||
# Test mac.user.chshell
|
||||
self.run_function('user.chshell', [CHANGE_USER, '/bin/zsh'])
|
||||
shell_info = self.run_function('user.info', [CHANGE_USER])
|
||||
self.assertEqual(shell_info['shell'], '/bin/zsh')
|
||||
|
||||
# Test mac_user.chhome
|
||||
self.run_function('user.chhome', [CHANGE_USER, '/Users/foo'])
|
||||
home_info = self.run_function('user.info', [CHANGE_USER])
|
||||
self.assertEqual(home_info['home'], '/Users/foo')
|
||||
|
||||
# Test mac_user.chfullname
|
||||
self.run_function('user.chfullname', [CHANGE_USER, 'Foo Bar'])
|
||||
fullname_info = self.run_function('user.info', [CHANGE_USER])
|
||||
self.assertEqual(fullname_info['fullname'], 'Foo Bar')
|
||||
|
||||
# Test mac_user.chgroups
|
||||
self.run_function('user.chgroups', [CHANGE_USER, 'wheel'])
|
||||
groups_info = self.run_function('user.info', [CHANGE_USER])
|
||||
self.assertEqual(groups_info['groups'], ['wheel'])
|
||||
|
||||
except AssertionError:
|
||||
self.run_function('user.delete', [CHANGE_USER])
|
||||
raise
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test')
|
||||
@requires_system_grains
|
||||
@ -96,16 +145,21 @@ class MacUserModuleTest(integration.ModuleCase):
|
||||
Clean up after tests
|
||||
'''
|
||||
|
||||
# Delete add_user
|
||||
# Delete ADD_USER
|
||||
add_info = self.run_function('user.info', [ADD_USER])
|
||||
if add_info:
|
||||
self.run_function('user.delete', [ADD_USER])
|
||||
|
||||
# Delete del_user if something failed
|
||||
# Delete DEL_USER if something failed
|
||||
del_info = self.run_function('user.info', [DEL_USER])
|
||||
if del_info:
|
||||
self.run_function('user.delete', [DEL_USER])
|
||||
|
||||
# Delete CHANGE_USER
|
||||
change_info = self.run_function('user.info', [CHANGE_USER])
|
||||
if change_info:
|
||||
self.run_function('user.delete', [CHANGE_USER])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
|
@ -114,6 +114,68 @@ class FileTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn):
|
||||
self.assertEqual(master_data, minion_data)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
def test_managed_file_mode(self):
|
||||
'''
|
||||
file.managed, correct file permissions
|
||||
'''
|
||||
desired_mode = 504 # 0770 octal
|
||||
name = os.path.join(integration.TMP, 'grail_scene33')
|
||||
ret = self.run_state(
|
||||
'file.managed', name=name, mode='0770', source='salt://grail/scene33'
|
||||
)
|
||||
|
||||
resulting_mode = stat.S_IMODE(
|
||||
os.stat(name).st_mode
|
||||
)
|
||||
self.assertEqual(oct(desired_mode), oct(resulting_mode))
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
def test_managed_file_mode_file_exists_replace(self):
|
||||
'''
|
||||
file.managed, existing file with replace=True, change permissions
|
||||
'''
|
||||
initial_mode = 504 # 0770 octal
|
||||
desired_mode = 384 # 0600 octal
|
||||
name = os.path.join(integration.TMP, 'grail_scene33')
|
||||
ret = self.run_state(
|
||||
'file.managed', name=name, mode=oct(initial_mode), source='salt://grail/scene33'
|
||||
)
|
||||
|
||||
resulting_mode = stat.S_IMODE(
|
||||
os.stat(name).st_mode
|
||||
)
|
||||
self.assertEqual(oct(initial_mode), oct(resulting_mode))
|
||||
|
||||
name = os.path.join(integration.TMP, 'grail_scene33')
|
||||
ret = self.run_state(
|
||||
'file.managed', name=name, replace=True, mode=oct(desired_mode), source='salt://grail/scene33'
|
||||
)
|
||||
resulting_mode = stat.S_IMODE(
|
||||
os.stat(name).st_mode
|
||||
)
|
||||
self.assertEqual(oct(desired_mode), oct(resulting_mode))
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
def test_managed_file_mode_file_exists_noreplace(self):
|
||||
'''
|
||||
file.managed, existing file with replace=False, change permissions
|
||||
'''
|
||||
initial_mode = 504 # 0770 octal
|
||||
desired_mode = 384 # 0600 octal
|
||||
name = os.path.join(integration.TMP, 'grail_scene33')
|
||||
ret = self.run_state(
|
||||
'file.managed', name=name, replace=True, mode=oct(initial_mode), source='salt://grail/scene33'
|
||||
)
|
||||
|
||||
ret = self.run_state(
|
||||
'file.managed', name=name, replace=False, mode=oct(desired_mode), source='salt://grail/scene33'
|
||||
)
|
||||
resulting_mode = stat.S_IMODE(
|
||||
os.stat(name).st_mode
|
||||
)
|
||||
self.assertEqual(oct(desired_mode), oct(resulting_mode))
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
def test_managed_dir_mode(self):
|
||||
'''
|
||||
Tests to ensure that file.managed creates directories with the
|
||||
|
@ -26,7 +26,7 @@ class MacGroupTestCase(TestCase):
|
||||
mock_group = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']}
|
||||
mock_getgrall = [grp.struct_group(('foo', '*', 20, ['test']))]
|
||||
|
||||
# 'add' function tests: 5
|
||||
# 'add' function tests: 6
|
||||
|
||||
@patch('salt.modules.mac_group.info', MagicMock(return_value=mock_group))
|
||||
def test_add_group_exists(self):
|
||||
@ -57,6 +57,15 @@ class MacGroupTestCase(TestCase):
|
||||
self.assertRaises(SaltInvocationError, mac_group.add, 'foo', 'foo')
|
||||
|
||||
@patch('salt.modules.mac_group.info', MagicMock(return_value={}))
|
||||
@patch('salt.modules.mac_group._list_gids', MagicMock(return_value=['3456']))
|
||||
def test_add_gid_exists(self):
|
||||
'''
|
||||
Tests if the gid is already in use or not
|
||||
'''
|
||||
self.assertRaises(CommandExecutionError, mac_group.add, 'foo', 3456)
|
||||
|
||||
@patch('salt.modules.mac_group.info', MagicMock(return_value={}))
|
||||
@patch('salt.modules.mac_group._list_gids', MagicMock(return_value=[]))
|
||||
def test_add(self):
|
||||
'''
|
||||
Tests if specified group was added
|
||||
|
Loading…
Reference in New Issue
Block a user