mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into 'develop'
Conflicts: - salt/cli/cp.py - salt/modules/testinframod.py - salt/states/jenkins.py - tests/unit/utils/test_find.py
This commit is contained in:
commit
67cdfd28ad
28 changed files with 1027 additions and 955 deletions
|
@ -39,6 +39,13 @@ specified target expression.
|
|||
desitination will be assumed to be a directory. Finally, recursion is now
|
||||
supported, allowing for entire directories to be copied.
|
||||
|
||||
.. versionchanged:: 2016.11.7,2017.7.2
|
||||
Reverted back to the old copy mode to preserve backward compatibility. The
|
||||
new functionality added in 2016.6.6 and 2017.7.0 is now available using the
|
||||
``-C`` or ``--chunked`` CLI arguments. Note that compression, recursive
|
||||
copying, and support for copying large files is only available in chunked
|
||||
mode.
|
||||
|
||||
Options
|
||||
=======
|
||||
|
||||
|
@ -56,9 +63,16 @@ Options
|
|||
.. include:: _includes/target-selection.rst
|
||||
|
||||
|
||||
.. option:: -C, --chunked
|
||||
|
||||
Use new chunked mode to copy files. This mode supports large files, recursive
|
||||
directories copying and compression.
|
||||
|
||||
.. versionadded:: 2016.11.7,2017.7.2
|
||||
|
||||
.. option:: -n, --no-compression
|
||||
|
||||
Disable gzip compression.
|
||||
Disable gzip compression in chunked mode.
|
||||
|
||||
.. versionadded:: 2016.3.7,2016.11.6,2017.7.0
|
||||
|
||||
|
|
|
@ -4,23 +4,12 @@ Salt 2016.3.7 Release Notes
|
|||
|
||||
Version 2016.3.7 is a bugfix release for :ref:`2016.3.0 <release-2016-3-0>`.
|
||||
|
||||
New master configuration option `allow_minion_key_revoke`, defaults to True. This option
|
||||
controls whether a minion can request that the master revoke its key. When True, a minion
|
||||
can request a key revocation and the master will comply. If it is False, the key will not
|
||||
be revoked by the msater.
|
||||
Changes for v2016.3.6..v2016.3.7
|
||||
--------------------------------
|
||||
|
||||
New master configuration option `require_minion_sign_messages`
|
||||
This requires that minions cryptographically sign the messages they
|
||||
publish to the master. If minions are not signing, then log this information
|
||||
at loglevel 'INFO' and drop the message without acting on it.
|
||||
Security Fix
|
||||
============
|
||||
|
||||
New master configuration option `drop_messages_signature_fail`
|
||||
Drop messages from minions when their signatures do not validate.
|
||||
Note that when this option is False but `require_minion_sign_messages` is True
|
||||
minions MUST sign their messages but the validity of their signatures
|
||||
is ignored.
|
||||
CVE-2017-12791 Maliciously crafted minion IDs can cause unwanted directory traversals on the Salt-master
|
||||
|
||||
New minion configuration option `minion_sign_messages`
|
||||
Causes the minion to cryptographically sign the payload of messages it places
|
||||
on the event bus for the master. The payloads are signed with the minion's
|
||||
private key so the master can verify the signature with its public key.
|
||||
Correct a flaw in minion id validation which could allow certain minions to authenticate to a master despite not having the correct credentials. To exploit the vulnerability, an attacker must create a salt-minion with an ID containing characters that will cause a directory traversal. Credit for discovering the security flaw goes to: Vernhk@qq.com
|
||||
|
|
29
doc/topics/releases/2016.3.8.rst
Normal file
29
doc/topics/releases/2016.3.8.rst
Normal file
|
@ -0,0 +1,29 @@
|
|||
===========================
|
||||
Salt 2016.3.8 Release Notes
|
||||
===========================
|
||||
|
||||
Version 2016.3.8 is a bugfix release for :ref:`2016.3.0 <release-2016-3-0>`.
|
||||
|
||||
Changes for v2016.3.7..v2016.3.8
|
||||
--------------------------------
|
||||
|
||||
New master configuration option `allow_minion_key_revoke`, defaults to True. This option
|
||||
controls whether a minion can request that the master revoke its key. When True, a minion
|
||||
can request a key revocation and the master will comply. If it is False, the key will not
|
||||
be revoked by the msater.
|
||||
|
||||
New master configuration option `require_minion_sign_messages`
|
||||
This requires that minions cryptographically sign the messages they
|
||||
publish to the master. If minions are not signing, then log this information
|
||||
at loglevel 'INFO' and drop the message without acting on it.
|
||||
|
||||
New master configuration option `drop_messages_signature_fail`
|
||||
Drop messages from minions when their signatures do not validate.
|
||||
Note that when this option is False but `require_minion_sign_messages` is True
|
||||
minions MUST sign their messages but the validity of their signatures
|
||||
is ignored.
|
||||
|
||||
New minion configuration option `minion_sign_messages`
|
||||
Causes the minion to cryptographically sign the payload of messages it places
|
||||
on the event bus for the master. The payloads are signed with the minion's
|
||||
private key so the master can verify the signature with its public key.
|
6
salt/cache/__init__.py
vendored
6
salt/cache/__init__.py
vendored
|
@ -224,7 +224,7 @@ class Cache(object):
|
|||
fun = '{0}.flush'.format(self.driver)
|
||||
return self.modules[fun](bank, key=key, **self._kwargs)
|
||||
|
||||
def ls(self, bank):
|
||||
def list(self, bank):
|
||||
'''
|
||||
Lists entries stored in the specified bank.
|
||||
|
||||
|
@ -240,11 +240,9 @@ class Cache(object):
|
|||
Raises an exception if cache driver detected an error accessing data
|
||||
in the cache backend (auth, permissions, etc).
|
||||
'''
|
||||
fun = '{0}.ls'.format(self.driver)
|
||||
fun = '{0}.list'.format(self.driver)
|
||||
return self.modules[fun](bank, **self._kwargs)
|
||||
|
||||
list = ls
|
||||
|
||||
def contains(self, bank, key=None):
|
||||
'''
|
||||
Checks if the specified bank contains the specified key.
|
||||
|
|
4
salt/cache/consul.py
vendored
4
salt/cache/consul.py
vendored
|
@ -61,7 +61,7 @@ api = None
|
|||
# Define the module's virtual name
|
||||
__virtualname__ = 'consul'
|
||||
|
||||
__func_alias__ = {'list': 'ls'}
|
||||
__func_alias__ = {'list_': 'list'}
|
||||
|
||||
|
||||
def __virtual__():
|
||||
|
@ -139,7 +139,7 @@ def flush(bank, key=None):
|
|||
)
|
||||
|
||||
|
||||
def ls(bank):
|
||||
def list_(bank):
|
||||
'''
|
||||
Return an iterable object containing all entries stored in the specified bank.
|
||||
'''
|
||||
|
|
4
salt/cache/localfs.py
vendored
4
salt/cache/localfs.py
vendored
|
@ -24,7 +24,7 @@ import salt.utils.files
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__func_alias__ = {'list': 'ls'}
|
||||
__func_alias__ = {'list_': 'list'}
|
||||
|
||||
|
||||
def __cachedir(kwargs=None):
|
||||
|
@ -144,7 +144,7 @@ def flush(bank, key=None, cachedir=None):
|
|||
return True
|
||||
|
||||
|
||||
def ls(bank, cachedir):
|
||||
def list_(bank, cachedir):
|
||||
'''
|
||||
Return an iterable object containing all entries stored in the specified bank.
|
||||
'''
|
||||
|
|
6
salt/cache/redis_cache.py
vendored
6
salt/cache/redis_cache.py
vendored
|
@ -161,9 +161,7 @@ from salt.exceptions import SaltCacheError
|
|||
# -----------------------------------------------------------------------------
|
||||
|
||||
__virtualname__ = 'redis'
|
||||
__func_alias__ = {
|
||||
'ls': 'list'
|
||||
}
|
||||
__func_alias__ = {'list_': 'list'}
|
||||
|
||||
log = logging.getLogger(__file__)
|
||||
|
||||
|
@ -478,7 +476,7 @@ def flush(bank, key=None):
|
|||
return True
|
||||
|
||||
|
||||
def ls(bank):
|
||||
def list_(bank):
|
||||
'''
|
||||
Lists entries stored in the specified bank.
|
||||
'''
|
||||
|
|
|
@ -18,14 +18,15 @@ import sys
|
|||
|
||||
# Import salt libs
|
||||
import salt.client
|
||||
import salt.output
|
||||
import salt.utils
|
||||
import salt.utils.gzip_util
|
||||
import salt.utils.itertools
|
||||
import salt.utils.minions
|
||||
import salt.utils.parsers
|
||||
import salt.utils.platform
|
||||
import salt.utils.stringutils
|
||||
from salt.utils.verify import verify_log
|
||||
import salt.output
|
||||
import salt.utils.verify
|
||||
|
||||
# Import 3rd party libs
|
||||
from salt.ext import six
|
||||
|
@ -46,7 +47,7 @@ class SaltCPCli(salt.utils.parsers.SaltCPOptionParser):
|
|||
|
||||
# Setup file logging!
|
||||
self.setup_logfile_logger()
|
||||
verify_log(self.config)
|
||||
salt.utils.verify.verify_log(self.config)
|
||||
|
||||
cp_ = SaltCP(self.config)
|
||||
cp_.run()
|
||||
|
@ -103,10 +104,70 @@ class SaltCP(object):
|
|||
empty_dirs.update(empty_dirs_)
|
||||
return files, sorted(empty_dirs)
|
||||
|
||||
def _file_dict(self, fn_):
|
||||
'''
|
||||
Take a path and return the contents of the file as a string
|
||||
'''
|
||||
if not os.path.isfile(fn_):
|
||||
err = 'The referenced file, {0} is not available.'.format(fn_)
|
||||
sys.stderr.write(err + '\n')
|
||||
sys.exit(42)
|
||||
with salt.utils.fopen(fn_, 'r') as fp_:
|
||||
data = fp_.read()
|
||||
return {fn_: data}
|
||||
|
||||
def _load_files(self):
|
||||
'''
|
||||
Parse the files indicated in opts['src'] and load them into a python
|
||||
object for transport
|
||||
'''
|
||||
files = {}
|
||||
for fn_ in self.opts['src']:
|
||||
if os.path.isfile(fn_):
|
||||
files.update(self._file_dict(fn_))
|
||||
elif os.path.isdir(fn_):
|
||||
salt.utils.print_cli(fn_ + ' is a directory, only files are supported '
|
||||
'in non-chunked mode. Use "--chunked" command '
|
||||
'line argument.')
|
||||
sys.exit(1)
|
||||
return files
|
||||
|
||||
def run(self):
|
||||
'''
|
||||
Make the salt client call
|
||||
'''
|
||||
if self.opts['chunked']:
|
||||
ret = self.run_chunked()
|
||||
else:
|
||||
ret = self.run_oldstyle()
|
||||
|
||||
salt.output.display_output(
|
||||
ret,
|
||||
self.opts.get('output', 'nested'),
|
||||
self.opts)
|
||||
|
||||
def run_oldstyle(self):
|
||||
'''
|
||||
Make the salt client call in old-style all-in-one call method
|
||||
'''
|
||||
arg = [self._load_files(), self.opts['dest']]
|
||||
local = salt.client.get_local_client(self.opts['conf_file'])
|
||||
args = [self.opts['tgt'],
|
||||
'cp.recv',
|
||||
arg,
|
||||
self.opts['timeout'],
|
||||
]
|
||||
|
||||
selected_target_option = self.opts.get('selected_target_option', None)
|
||||
if selected_target_option is not None:
|
||||
args.append(selected_target_option)
|
||||
|
||||
return local.cmd(*args)
|
||||
|
||||
def run_chunked(self):
|
||||
'''
|
||||
Make the salt client call in the new fasion chunked multi-call way
|
||||
'''
|
||||
files, empty_dirs = self._list_files()
|
||||
dest = self.opts['dest']
|
||||
gzip = self.opts['gzip']
|
||||
|
@ -168,7 +229,7 @@ class SaltCP(object):
|
|||
)
|
||||
args = [
|
||||
tgt,
|
||||
'cp.recv',
|
||||
'cp.recv_chunked',
|
||||
[remote_path, chunk, append, gzip, mode],
|
||||
timeout,
|
||||
]
|
||||
|
@ -214,14 +275,11 @@ class SaltCP(object):
|
|||
else '',
|
||||
tgt,
|
||||
)
|
||||
args = [tgt, 'cp.recv', [remote_path, None], timeout]
|
||||
args = [tgt, 'cp.recv_chunked', [remote_path, None], timeout]
|
||||
if selected_target_option is not None:
|
||||
args.append(selected_target_option)
|
||||
|
||||
for minion_id, minion_ret in six.iteritems(local.cmd(*args)):
|
||||
ret.setdefault(minion_id, {})[remote_path] = minion_ret
|
||||
|
||||
salt.output.display_output(
|
||||
ret,
|
||||
self.opts.get('output', 'nested'),
|
||||
self.opts)
|
||||
return ret
|
||||
|
|
|
@ -3432,34 +3432,7 @@ def list_nodes_full(location=None, call=None):
|
|||
'or --function.'
|
||||
)
|
||||
|
||||
if not location:
|
||||
ret = {}
|
||||
locations = set(
|
||||
get_location(vm_) for vm_ in six.itervalues(__opts__['profiles'])
|
||||
if _vm_provider_driver(vm_)
|
||||
)
|
||||
|
||||
# If there aren't any profiles defined for EC2, check
|
||||
# the provider config file, or use the default location.
|
||||
if not locations:
|
||||
locations = [get_location()]
|
||||
|
||||
for loc in locations:
|
||||
ret.update(_list_nodes_full(loc))
|
||||
return ret
|
||||
|
||||
return _list_nodes_full(location)
|
||||
|
||||
|
||||
def _vm_provider_driver(vm_):
|
||||
alias, driver = vm_['driver'].split(':')
|
||||
if alias not in __opts__['providers']:
|
||||
return None
|
||||
|
||||
if driver not in __opts__['providers'][alias]:
|
||||
return None
|
||||
|
||||
return driver == 'ec2'
|
||||
return _list_nodes_full(location or get_location())
|
||||
|
||||
|
||||
def _extract_name_tag(item):
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -503,7 +503,7 @@ class Key(object):
|
|||
if minion not in minions and minion not in preserve_minions:
|
||||
shutil.rmtree(os.path.join(m_cache, minion))
|
||||
cache = salt.cache.factory(self.opts)
|
||||
clist = cache.ls(self.ACC)
|
||||
clist = cache.list(self.ACC)
|
||||
if clist:
|
||||
for minion in clist:
|
||||
if minion not in minions and minion not in preserve_minions:
|
||||
|
@ -981,7 +981,7 @@ class RaetKey(Key):
|
|||
if minion not in minions:
|
||||
shutil.rmtree(os.path.join(m_cache, minion))
|
||||
cache = salt.cache.factory(self.opts)
|
||||
clist = cache.ls(self.ACC)
|
||||
clist = cache.list(self.ACC)
|
||||
if clist:
|
||||
for minion in clist:
|
||||
if minion not in minions and minion not in preserve_minions:
|
||||
|
|
|
@ -60,7 +60,36 @@ def _gather_pillar(pillarenv, pillar_override):
|
|||
return ret
|
||||
|
||||
|
||||
def recv(dest, chunk, append=False, compressed=True, mode=None):
|
||||
def recv(files, dest):
|
||||
'''
|
||||
Used with salt-cp, pass the files dict, and the destination.
|
||||
|
||||
This function receives small fast copy files from the master via salt-cp.
|
||||
It does not work via the CLI.
|
||||
'''
|
||||
ret = {}
|
||||
for path, data in six.iteritems(files):
|
||||
if os.path.basename(path) == os.path.basename(dest) \
|
||||
and not os.path.isdir(dest):
|
||||
final = dest
|
||||
elif os.path.isdir(dest):
|
||||
final = os.path.join(dest, os.path.basename(path))
|
||||
elif os.path.isdir(os.path.dirname(dest)):
|
||||
final = dest
|
||||
else:
|
||||
return 'Destination unavailable'
|
||||
|
||||
try:
|
||||
with salt.utils.fopen(final, 'w+') as fp_:
|
||||
fp_.write(data)
|
||||
ret[final] = True
|
||||
except IOError:
|
||||
ret[final] = False
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def recv_chunked(dest, chunk, append=False, compressed=True, mode=None):
|
||||
'''
|
||||
This function receives files copied to the minion using ``salt-cp`` and is
|
||||
not intended to be used directly on the CLI.
|
||||
|
|
|
@ -801,7 +801,7 @@ def get_client_args():
|
|||
|
||||
salt myminion docker.get_client_args
|
||||
'''
|
||||
return salt.utils.docker.get_client_args()
|
||||
return __utils__['docker.get_client_args']()
|
||||
|
||||
|
||||
def _get_create_kwargs(image,
|
||||
|
|
|
@ -37,7 +37,7 @@ import salt.utils.files
|
|||
|
||||
# Import 3rd-party libs
|
||||
# pylint: disable=import-error,no-name-in-module,redefined-builtin
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
# pylint: enable=import-error,no-name-in-module
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -89,9 +89,22 @@ def _connect():
|
|||
password=jenkins_password)
|
||||
|
||||
|
||||
def _retrieve_config_xml(config_xml, saltenv):
|
||||
'''
|
||||
Helper to cache the config XML and raise a CommandExecutionError if we fail
|
||||
to do so. If we successfully cache the file, return the cached path.
|
||||
'''
|
||||
ret = __salt__['cp.cache_file'](config_xml, saltenv)
|
||||
|
||||
if not ret:
|
||||
raise CommandExecutionError('Failed to retrieve {0}'.format(config_xml))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def run(script):
|
||||
'''
|
||||
.. versionadded:: Carbon
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
Execute a groovy script on the jenkins master
|
||||
|
||||
|
@ -166,7 +179,7 @@ def job_exists(name=None):
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
if server.job_exists(name):
|
||||
|
@ -190,12 +203,12 @@ def get_job_info(name=None):
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exist.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
|
||||
|
||||
job_info = server.get_job_info(name)
|
||||
if job_info:
|
||||
|
@ -219,17 +232,19 @@ def build_job(name=None, parameters=None):
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exist.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist.'.format(name))
|
||||
|
||||
try:
|
||||
server.build_job(name, parameters)
|
||||
except jenkins.JenkinsException as err:
|
||||
raise SaltInvocationError('Something went wrong {0}.'.format(err))
|
||||
raise CommandExecutionError(
|
||||
'Encountered error building job \'{0}\': {1}'.format(name, err)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -254,15 +269,15 @@ def create_job(name=None,
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
if job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` already exists.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' already exists'.format(name))
|
||||
|
||||
if not config_xml:
|
||||
config_xml = jenkins.EMPTY_CONFIG_XML
|
||||
else:
|
||||
config_xml_file = __salt__['cp.cache_file'](config_xml, saltenv)
|
||||
config_xml_file = _retrieve_config_xml(config_xml, saltenv)
|
||||
|
||||
with salt.utils.files.fopen(config_xml_file) as _fp:
|
||||
config_xml = _fp.read()
|
||||
|
@ -271,7 +286,9 @@ def create_job(name=None,
|
|||
try:
|
||||
server.create_job(name, config_xml)
|
||||
except jenkins.JenkinsException as err:
|
||||
raise SaltInvocationError('Something went wrong {0}.'.format(err))
|
||||
raise CommandExecutionError(
|
||||
'Encountered error creating job \'{0}\': {1}'.format(name, err)
|
||||
)
|
||||
return config_xml
|
||||
|
||||
|
||||
|
@ -296,12 +313,12 @@ def update_job(name=None,
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
if not config_xml:
|
||||
config_xml = jenkins.EMPTY_CONFIG_XML
|
||||
else:
|
||||
config_xml_file = __salt__['cp.cache_file'](config_xml, saltenv)
|
||||
config_xml_file = _retrieve_config_xml(config_xml, saltenv)
|
||||
|
||||
with salt.utils.files.fopen(config_xml_file) as _fp:
|
||||
config_xml = _fp.read()
|
||||
|
@ -310,7 +327,9 @@ def update_job(name=None,
|
|||
try:
|
||||
server.reconfig_job(name, config_xml)
|
||||
except jenkins.JenkinsException as err:
|
||||
raise SaltInvocationError('Something went wrong {0}.'.format(err))
|
||||
raise CommandExecutionError(
|
||||
'Encountered error updating job \'{0}\': {1}'.format(name, err)
|
||||
)
|
||||
return config_xml
|
||||
|
||||
|
||||
|
@ -329,17 +348,19 @@ def delete_job(name=None):
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
|
||||
|
||||
try:
|
||||
server.delete_job(name)
|
||||
except jenkins.JenkinsException as err:
|
||||
raise SaltInvocationError('Something went wrong {0}.'.format(err))
|
||||
raise CommandExecutionError(
|
||||
'Encountered error deleting job \'{0}\': {1}'.format(name, err)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -358,17 +379,19 @@ def enable_job(name=None):
|
|||
|
||||
'''
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
|
||||
|
||||
try:
|
||||
server.enable_job(name)
|
||||
except jenkins.JenkinsException as err:
|
||||
raise SaltInvocationError('Something went wrong {0}.'.format(err))
|
||||
raise CommandExecutionError(
|
||||
'Encountered error enabling job \'{0}\': {1}'.format(name, err)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -388,17 +411,19 @@ def disable_job(name=None):
|
|||
'''
|
||||
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
|
||||
|
||||
try:
|
||||
server.disable_job(name)
|
||||
except jenkins.JenkinsException as err:
|
||||
raise SaltInvocationError('Something went wrong {0}.'.format(err))
|
||||
raise CommandExecutionError(
|
||||
'Encountered error disabling job \'{0}\': {1}'.format(name, err)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -418,12 +443,12 @@ def job_status(name=None):
|
|||
'''
|
||||
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
|
||||
|
||||
return server.get_job_info('empty')['buildable']
|
||||
|
||||
|
@ -444,12 +469,12 @@ def get_job_config(name=None):
|
|||
'''
|
||||
|
||||
if not name:
|
||||
raise SaltInvocationError('Required parameter `name` is missing.')
|
||||
raise SaltInvocationError('Required parameter \'name\' is missing')
|
||||
|
||||
server = _connect()
|
||||
|
||||
if not job_exists(name):
|
||||
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
|
||||
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
|
||||
|
||||
job_info = server.get_job_config(name)
|
||||
return job_info
|
||||
|
|
|
@ -51,20 +51,18 @@ try:
|
|||
import kubernetes.client
|
||||
from kubernetes.client.rest import ApiException
|
||||
from urllib3.exceptions import HTTPError
|
||||
try:
|
||||
# There is an API change in Kubernetes >= 2.0.0.
|
||||
from kubernetes.client import V1beta1Deployment as AppsV1beta1Deployment
|
||||
from kubernetes.client import V1beta1DeploymentSpec as AppsV1beta1DeploymentSpec
|
||||
except ImportError:
|
||||
from kubernetes.client import AppsV1beta1Deployment
|
||||
from kubernetes.client import AppsV1beta1DeploymentSpec
|
||||
|
||||
HAS_LIBS = True
|
||||
except ImportError:
|
||||
HAS_LIBS = False
|
||||
|
||||
try:
|
||||
# There is an API change in Kubernetes >= 2.0.0.
|
||||
from kubernetes.client import V1beta1Deployment as AppsV1beta1Deployment
|
||||
from kubernetes.client import V1beta1DeploymentSpec as AppsV1beta1DeploymentSpec
|
||||
except ImportError:
|
||||
from kubernetes.client import AppsV1beta1Deployment
|
||||
from kubernetes.client import AppsV1beta1DeploymentSpec
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'kubernetes'
|
||||
|
|
|
@ -37,7 +37,7 @@ def _table_attrs(table):
|
|||
'''
|
||||
Helper function to find valid table attributes
|
||||
'''
|
||||
cmd = 'osqueryi --json "pragma table_info({0})"'.format(table)
|
||||
cmd = ['osqueryi'] + ['--json'] + ['pragma table_info{0}'.format(table)]
|
||||
res = __salt__['cmd.run_all'](cmd)
|
||||
if res['retcode'] == 0:
|
||||
attrs = []
|
||||
|
@ -56,7 +56,7 @@ def _osquery(sql, format='json'):
|
|||
'result': True,
|
||||
}
|
||||
|
||||
cmd = 'osqueryi --json "{0}"'.format(sql)
|
||||
cmd = ['osqueryi'] + ['--json'] + [sql]
|
||||
res = __salt__['cmd.run_all'](cmd)
|
||||
if res['stderr']:
|
||||
ret['result'] = False
|
||||
|
|
|
@ -15,6 +15,7 @@ import logging
|
|||
# Import Salt libs
|
||||
from salt.ext import six
|
||||
from salt.ext.six.moves import zip
|
||||
from salt.exceptions import CommandExecutionError
|
||||
import salt.utils.files
|
||||
|
||||
# Import XML parser
|
||||
|
@ -36,17 +37,23 @@ def _elements_equal(e1, e2):
|
|||
return all(_elements_equal(c1, c2) for c1, c2 in zip(e1, e2))
|
||||
|
||||
|
||||
def _fail(ret, msg):
|
||||
ret['comment'] = msg
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
|
||||
def present(name,
|
||||
config=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Ensure the job is present in the Jenkins
|
||||
configured jobs
|
||||
Ensure the job is present in the Jenkins configured jobs
|
||||
|
||||
name
|
||||
The unique name for the Jenkins job
|
||||
|
||||
config
|
||||
The Salt URL for the file to use for
|
||||
configuring the job.
|
||||
The Salt URL for the file to use for configuring the job
|
||||
'''
|
||||
|
||||
ret = {'name': name,
|
||||
|
@ -54,9 +61,7 @@ def present(name,
|
|||
'changes': {},
|
||||
'comment': ['Job {0} is up to date.'.format(name)]}
|
||||
|
||||
_job_exists = __salt__['jenkins.job_exists'](name)
|
||||
|
||||
if _job_exists:
|
||||
if __salt__['jenkins.job_exists'](name):
|
||||
_current_job_config = __salt__['jenkins.get_job_config'](name)
|
||||
buf = six.moves.StringIO(_current_job_config)
|
||||
oldXML = ET.fromstring(buf.read())
|
||||
|
@ -68,21 +73,28 @@ def present(name,
|
|||
diff = difflib.unified_diff(
|
||||
ET.tostringlist(oldXML, encoding='utf8', method='xml'),
|
||||
ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='')
|
||||
__salt__['jenkins.update_job'](name, config, __env__)
|
||||
ret['changes'][name] = ''.join(diff)
|
||||
ret['comment'].append('Job {0} updated.'.format(name))
|
||||
try:
|
||||
__salt__['jenkins.update_job'](name, config, __env__)
|
||||
except CommandExecutionError as exc:
|
||||
return _fail(ret, exc.strerror)
|
||||
else:
|
||||
ret['changes'] = ''.join(diff)
|
||||
ret['comment'].append('Job \'{0}\' updated.'.format(name))
|
||||
|
||||
else:
|
||||
cached_source_path = __salt__['cp.cache_file'](config, __env__)
|
||||
with salt.utils.files.fopen(cached_source_path) as _fp:
|
||||
new_config_xml = _fp.read()
|
||||
|
||||
__salt__['jenkins.create_job'](name, config, __env__)
|
||||
try:
|
||||
__salt__['jenkins.create_job'](name, config, __env__)
|
||||
except CommandExecutionError as exc:
|
||||
return _fail(ret, exc.strerror)
|
||||
|
||||
buf = six.moves.StringIO(new_config_xml)
|
||||
diff = difflib.unified_diff('', buf.readlines(), lineterm='')
|
||||
ret['changes'][name] = ''.join(diff)
|
||||
ret['comment'].append('Job {0} added.'.format(name))
|
||||
ret['comment'].append('Job \'{0}\' added.'.format(name))
|
||||
|
||||
ret['comment'] = '\n'.join(ret['comment'])
|
||||
return ret
|
||||
|
@ -91,24 +103,23 @@ def present(name,
|
|||
def absent(name,
|
||||
**kwargs):
|
||||
'''
|
||||
Ensure the job is present in the Jenkins
|
||||
configured jobs
|
||||
Ensure the job is absent from the Jenkins configured jobs
|
||||
|
||||
name
|
||||
The name of the Jenkins job to remove.
|
||||
|
||||
The name of the Jenkins job to remove
|
||||
'''
|
||||
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': []}
|
||||
|
||||
_job_exists = __salt__['jenkins.job_exists'](name)
|
||||
|
||||
if _job_exists:
|
||||
__salt__['jenkins.delete_job'](name)
|
||||
ret['comment'] = 'Job {0} deleted.'.format(name)
|
||||
if __salt__['jenkins.job_exists'](name):
|
||||
try:
|
||||
__salt__['jenkins.delete_job'](name)
|
||||
except CommandExecutionError as exc:
|
||||
return _fail(ret, exc.strerror)
|
||||
else:
|
||||
ret['comment'] = 'Job \'{0}\' deleted.'.format(name)
|
||||
else:
|
||||
ret['comment'] = 'Job {0} already absent.'.format(name)
|
||||
ret['comment'] = 'Job \'{0}\' already absent.'.format(name)
|
||||
return ret
|
||||
|
|
|
@ -72,7 +72,7 @@ class ThorState(salt.state.HighState):
|
|||
cache = {'grains': {}, 'pillar': {}}
|
||||
if self.grains or self.pillar:
|
||||
if self.opts.get('minion_data_cache'):
|
||||
minions = self.cache.ls('minions')
|
||||
minions = self.cache.list('minions')
|
||||
if not minions:
|
||||
return cache
|
||||
for minion in minions:
|
||||
|
|
|
@ -124,7 +124,7 @@ class MasterPillarUtil(object):
|
|||
'and enfore_mine_cache are both disabled.')
|
||||
return mine_data
|
||||
if not minion_ids:
|
||||
minion_ids = self.cache.ls('minions')
|
||||
minion_ids = self.cache.list('minions')
|
||||
for minion_id in minion_ids:
|
||||
if not salt.utils.verify.valid_id(self.opts, minion_id):
|
||||
continue
|
||||
|
@ -143,7 +143,7 @@ class MasterPillarUtil(object):
|
|||
'enabled.')
|
||||
return grains, pillars
|
||||
if not minion_ids:
|
||||
minion_ids = self.cache.ls('minions')
|
||||
minion_ids = self.cache.list('minions')
|
||||
for minion_id in minion_ids:
|
||||
if not salt.utils.verify.valid_id(self.opts, minion_id):
|
||||
continue
|
||||
|
@ -366,7 +366,7 @@ class MasterPillarUtil(object):
|
|||
# in the same file, 'data.p'
|
||||
grains, pillars = self._get_cached_minion_data(*minion_ids)
|
||||
try:
|
||||
c_minions = self.cache.ls('minions')
|
||||
c_minions = self.cache.list('minions')
|
||||
for minion_id in minion_ids:
|
||||
if not salt.utils.verify.valid_id(self.opts, minion_id):
|
||||
continue
|
||||
|
|
|
@ -77,7 +77,7 @@ def get_minion_data(minion, opts):
|
|||
if opts.get('minion_data_cache', False):
|
||||
cache = salt.cache.factory(opts)
|
||||
if minion is None:
|
||||
for id_ in cache.ls('minions'):
|
||||
for id_ in cache.list('minions'):
|
||||
data = cache.fetch('minions/{0}'.format(id_), 'data')
|
||||
if data is None:
|
||||
continue
|
||||
|
@ -344,13 +344,13 @@ class CkMinions(object):
|
|||
if greedy:
|
||||
minions = self._pki_minions()
|
||||
elif cache_enabled:
|
||||
minions = self.cache.ls('minions')
|
||||
minions = self.cache.list('minions')
|
||||
else:
|
||||
return []
|
||||
|
||||
if cache_enabled:
|
||||
if greedy:
|
||||
cminions = self.cache.ls('minions')
|
||||
cminions = self.cache.list('minions')
|
||||
else:
|
||||
cminions = minions
|
||||
if cminions is None:
|
||||
|
@ -414,7 +414,7 @@ class CkMinions(object):
|
|||
mlist.append(fn_)
|
||||
return mlist
|
||||
elif cache_enabled:
|
||||
return self.cache.ls('minions')
|
||||
return self.cache.list('minions')
|
||||
else:
|
||||
return list()
|
||||
|
||||
|
@ -576,7 +576,7 @@ class CkMinions(object):
|
|||
'''
|
||||
minions = set()
|
||||
if self.opts.get('minion_data_cache', False):
|
||||
search = self.cache.ls('minions')
|
||||
search = self.cache.list('minions')
|
||||
if search is None:
|
||||
return minions
|
||||
addrs = salt.utils.network.local_port_tcp(int(self.opts['publish_port']))
|
||||
|
|
|
@ -2199,10 +2199,18 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta,
|
|||
|
||||
def _mixin_setup(self):
|
||||
file_opts_group = optparse.OptionGroup(self, 'File Options')
|
||||
file_opts_group.add_option(
|
||||
'-C', '--chunked',
|
||||
default=False,
|
||||
dest='chunked',
|
||||
action='store_true',
|
||||
help='Use chunked files transfer. Supports big files, recursive '
|
||||
'lookup and directories creation.'
|
||||
)
|
||||
file_opts_group.add_option(
|
||||
'-n', '--no-compression',
|
||||
default=True,
|
||||
dest='compression',
|
||||
dest='gzip',
|
||||
action='store_false',
|
||||
help='Disable gzip compression.'
|
||||
)
|
||||
|
@ -2223,7 +2231,6 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta,
|
|||
self.config['tgt'] = self.args[0]
|
||||
self.config['src'] = [os.path.realpath(x) for x in self.args[1:-1]]
|
||||
self.config['dest'] = self.args[-1]
|
||||
self.config['gzip'] = True
|
||||
|
||||
def setup_config(self):
|
||||
return config.master_config(self.get_config_file_path())
|
||||
|
|
|
@ -51,6 +51,7 @@ def get_invalid_docs():
|
|||
allow_failure = (
|
||||
'cmd.win_runas',
|
||||
'cp.recv',
|
||||
'cp.recv_chunked',
|
||||
'glance.warn_until',
|
||||
'ipset.long_range',
|
||||
'libcloud_compute.get_driver',
|
||||
|
|
14
tests/unit/cache/test_localfs.py
vendored
14
tests/unit/cache/test_localfs.py
vendored
|
@ -209,26 +209,26 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin):
|
|||
with patch('os.remove', MagicMock(side_effect=OSError)):
|
||||
self.assertRaises(SaltCacheError, localfs.flush, bank='', key='key', cachedir='/var/cache/salt')
|
||||
|
||||
# 'ls' function tests: 3
|
||||
# 'list' function tests: 3
|
||||
|
||||
def test_ls_no_base_dir(self):
|
||||
def test_list_no_base_dir(self):
|
||||
'''
|
||||
Tests that the ls function returns an empty list if the bank directory
|
||||
doesn't exist.
|
||||
'''
|
||||
with patch('os.path.isdir', MagicMock(return_value=False)):
|
||||
self.assertEqual(localfs.ls(bank='', cachedir=''), [])
|
||||
self.assertEqual(localfs.list_(bank='', cachedir=''), [])
|
||||
|
||||
def test_ls_error_raised_no_bank_directory_access(self):
|
||||
def test_list_error_raised_no_bank_directory_access(self):
|
||||
'''
|
||||
Tests that a SaltCacheError is raised when there is a problem accessing the
|
||||
cache bank directory.
|
||||
'''
|
||||
with patch('os.path.isdir', MagicMock(return_value=True)):
|
||||
with patch('os.listdir', MagicMock(side_effect=OSError)):
|
||||
self.assertRaises(SaltCacheError, localfs.ls, bank='', cachedir='')
|
||||
self.assertRaises(SaltCacheError, localfs.list_, bank='', cachedir='')
|
||||
|
||||
def test_ls_success(self):
|
||||
def test_list_success(self):
|
||||
'''
|
||||
Tests the return of the ls function containing bank entries.
|
||||
'''
|
||||
|
@ -240,7 +240,7 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin):
|
|||
|
||||
# Now test the return of the ls function
|
||||
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
||||
self.assertEqual(localfs.ls(bank='bank', cachedir=tmp_dir), ['key'])
|
||||
self.assertEqual(localfs.list_(bank='bank', cachedir=tmp_dir), ['key'])
|
||||
|
||||
# 'contains' function tests: 1
|
||||
|
||||
|
|
|
@ -264,6 +264,7 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin):
|
|||
with patch('salt.utils.files.fopen', mock_open(read_data=MOCK_SHELL_FILE)):
|
||||
self.assertFalse(cmdmod._is_valid_shell('foo'))
|
||||
|
||||
@skipIf(salt.utils.is_windows(), 'Do not run on Windows')
|
||||
def test_os_environment_remains_intact(self):
|
||||
'''
|
||||
Make sure the OS environment is not tainted after running a command
|
||||
|
|
|
@ -136,10 +136,11 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
|
|||
with patch.dict(docker_mod.__salt__,
|
||||
{'mine.send': mine_send,
|
||||
'container_resource.run': MagicMock(),
|
||||
'cp.cache_file': MagicMock(return_value=False),
|
||||
'docker.get_client_args': client_args_mock}):
|
||||
with patch.object(docker_mod, '_get_client', client):
|
||||
command('container', *args)
|
||||
'cp.cache_file': MagicMock(return_value=False)}):
|
||||
with patch.dict(docker_mod.__utils__,
|
||||
{'docker.get_client_args': client_args_mock}):
|
||||
with patch.object(docker_mod, '_get_client', client):
|
||||
command('container', *args)
|
||||
mine_send.assert_called_with('docker.ps', verbose=True, all=True,
|
||||
host=True)
|
||||
|
||||
|
|
|
@ -102,8 +102,9 @@ test:
|
|||
- name: echo sls_dir={{sls_dir}}
|
||||
- cwd: /
|
||||
''', sls='path.to.sls')
|
||||
self.assertEqual(result['test']['cmd.run'][0]['name'],
|
||||
'echo sls_dir=path/to')
|
||||
self.assertEqual(
|
||||
result['test']['cmd.run'][0]['name'],
|
||||
'echo sls_dir=path{0}to'.format(os.sep))
|
||||
|
||||
def test_states_declared_with_shorthand_no_args(self):
|
||||
result = self._render_sls('''
|
||||
|
|
|
@ -13,7 +13,6 @@ from tests.support.unit import skipIf, TestCase
|
|||
from tests.support.paths import TMP
|
||||
|
||||
# Import salt libs
|
||||
from salt.ext import six
|
||||
import salt.utils.files
|
||||
import salt.utils.find
|
||||
|
||||
|
@ -121,19 +120,11 @@ class TestFind(TestCase):
|
|||
|
||||
min_size, max_size = salt.utils.find._parse_size('+1m')
|
||||
self.assertEqual(min_size, 1048576)
|
||||
# sys.maxint has been removed in Python3. Use maxsize instead.
|
||||
if six.PY3:
|
||||
self.assertEqual(max_size, sys.maxsize)
|
||||
else:
|
||||
self.assertEqual(max_size, sys.maxint)
|
||||
self.assertEqual(max_size, sys.maxsize)
|
||||
|
||||
min_size, max_size = salt.utils.find._parse_size('+1M')
|
||||
self.assertEqual(min_size, 1048576)
|
||||
# sys.maxint has been removed in Python3. Use maxsize instead.
|
||||
if six.PY3:
|
||||
self.assertEqual(max_size, sys.maxsize)
|
||||
else:
|
||||
self.assertEqual(max_size, sys.maxint)
|
||||
self.assertEqual(max_size, sys.maxsize)
|
||||
|
||||
def test_option_requires(self):
|
||||
option = salt.utils.find.Option()
|
||||
|
@ -217,7 +208,7 @@ class TestFind(TestCase):
|
|||
option = salt.utils.find.TypeOption('type', 's')
|
||||
self.assertEqual(option.match('', '', [stat.S_IFSOCK]), True)
|
||||
|
||||
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
|
||||
@skipIf(sys.platform.startswith('win'), 'pwd not available on Windows')
|
||||
def test_owner_option_requires(self):
|
||||
self.assertRaises(
|
||||
ValueError, salt.utils.find.OwnerOption, 'owner', 'notexist'
|
||||
|
@ -226,7 +217,7 @@ class TestFind(TestCase):
|
|||
option = salt.utils.find.OwnerOption('owner', 'root')
|
||||
self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT)
|
||||
|
||||
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
|
||||
@skipIf(sys.platform.startswith('win'), 'pwd not available on Windows')
|
||||
def test_owner_option_match(self):
|
||||
option = salt.utils.find.OwnerOption('owner', 'root')
|
||||
self.assertEqual(option.match('', '', [0] * 5), True)
|
||||
|
@ -234,7 +225,7 @@ class TestFind(TestCase):
|
|||
option = salt.utils.find.OwnerOption('owner', '500')
|
||||
self.assertEqual(option.match('', '', [500] * 5), True)
|
||||
|
||||
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
|
||||
@skipIf(sys.platform.startswith('win'), 'grp not available on Windows')
|
||||
def test_group_option_requires(self):
|
||||
self.assertRaises(
|
||||
ValueError, salt.utils.find.GroupOption, 'group', 'notexist'
|
||||
|
@ -247,7 +238,7 @@ class TestFind(TestCase):
|
|||
option = salt.utils.find.GroupOption('group', group_name)
|
||||
self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT)
|
||||
|
||||
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
|
||||
@skipIf(sys.platform.startswith('win'), 'grp not available on Windows')
|
||||
def test_group_option_match(self):
|
||||
if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')):
|
||||
group_name = 'wheel'
|
||||
|
@ -412,7 +403,7 @@ class TestPrintOption(TestCase):
|
|||
option.execute('test_name', [0] * 9), [0, 'test_name']
|
||||
)
|
||||
|
||||
@skipIf(sys.platform.startswith('Windows'), "no /dev/null on windows")
|
||||
@skipIf(sys.platform.startswith('win'), "pwd not available on Windows")
|
||||
def test_print_user(self):
|
||||
option = salt.utils.find.PrintOption('print', 'user')
|
||||
self.assertEqual(option.execute('', [0] * 10), 'root')
|
||||
|
@ -420,7 +411,7 @@ class TestPrintOption(TestCase):
|
|||
option = salt.utils.find.PrintOption('print', 'user')
|
||||
self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31)
|
||||
|
||||
@skipIf(sys.platform.startswith('Windows'), "no /dev/null on windows")
|
||||
@skipIf(sys.platform.startswith('win'), "grp not available on Windows")
|
||||
def test_print_group(self):
|
||||
option = salt.utils.find.PrintOption('print', 'group')
|
||||
if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')):
|
||||
|
@ -433,7 +424,7 @@ class TestPrintOption(TestCase):
|
|||
#option = salt.utils.find.PrintOption('print', 'group')
|
||||
#self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31)
|
||||
|
||||
@skipIf(sys.platform.startswith('Windows'), "no /dev/null on windows")
|
||||
@skipIf(sys.platform.startswith('win'), "no /dev/null on windows")
|
||||
def test_print_md5(self):
|
||||
option = salt.utils.find.PrintOption('print', 'md5')
|
||||
self.assertEqual(option.execute('/dev/null', os.stat('/dev/null')), '')
|
||||
|
|
|
@ -118,7 +118,7 @@ class NetworkTestCase(TestCase):
|
|||
(2, 1, 6, '', ('192.30.255.113', 0)),
|
||||
],
|
||||
'ipv6host.foo': [
|
||||
(10, 1, 6, '', ('2001:a71::1', 0, 0, 0)),
|
||||
(socket.AF_INET6, 1, 6, '', ('2001:a71::1', 0, 0, 0)),
|
||||
],
|
||||
}[host]
|
||||
except KeyError:
|
||||
|
|
Loading…
Add table
Reference in a new issue